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 ((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 72namespace WebCore { 73 74int 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 88TilesManager::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 121TilesManager::~TilesManager() 122{ 123 delete[] m_textureGenerators; 124} 125 126 127void 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 166void 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 184void 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 192void 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 236void 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 253void 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 262void TilesManager::dirtyAllTiles() 263{ 264 dirtyTexturesVector(m_textures); 265 dirtyTexturesVector(m_tilesTextures); 266} 267 268void 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 290void 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 298TileTexture* 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 385void TilesManager::setHighEndGfx(bool highEnd) 386{ 387 m_highEndGfx = highEnd; 388} 389 390bool TilesManager::highEndGfx() 391{ 392 return m_highEndGfx; 393} 394 395int TilesManager::currentTextureCount() 396{ 397 android::Mutex::Autolock lock(m_texturesLock); 398 return m_currentTextureCount; 399} 400 401int TilesManager::currentLayerTextureCount() 402{ 403 android::Mutex::Autolock lock(m_texturesLock); 404 return m_currentLayerTextureCount; 405} 406 407void 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 422void 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 449TransferQueue* 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 463void 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 472void 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 506void 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 513bool 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 522void 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 529int TilesManager::tileWidth() 530{ 531 return TILE_WIDTH; 532} 533 534int TilesManager::tileHeight() 535{ 536 return TILE_HEIGHT; 537} 538 539TilesManager* TilesManager::instance() 540{ 541 if (!gInstance) { 542 gInstance = new TilesManager(); 543 ALOGV("instance(), new gInstance is %x", gInstance); 544 } 545 return gInstance; 546} 547 548TilesManager* TilesManager::gInstance = 0; 549 550} // namespace WebCore 551 552#endif // USE(ACCELERATED_COMPOSITING) 553