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 #include "config.h" 27 #include "TilesManager.h" 28 29 #if USE(ACCELERATED_COMPOSITING) 30 31 #include "BaseTile.h" 32 #include "PaintedSurface.h" 33 #include "SkCanvas.h" 34 #include "SkDevice.h" 35 #include "SkPaint.h" 36 #include <android/native_window.h> 37 #include <cutils/atomic.h> 38 #include <gui/SurfaceTexture.h> 39 #include <gui/SurfaceTextureClient.h> 40 41 42 #include <cutils/log.h> 43 #include <wtf/CurrentTime.h> 44 #include <wtf/text/CString.h> 45 46 #undef XLOGC 47 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TilesManager", __VA_ARGS__) 48 49 #ifdef DEBUG 50 51 #undef XLOG 52 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TilesManager", __VA_ARGS__) 53 54 #else 55 56 #undef XLOG 57 #define XLOG(...) 58 59 #endif // DEBUG 60 61 // Important: We need at least twice as many textures as is needed to cover 62 // one viewport, otherwise the allocation may stall. 63 // We need n textures for one TiledPage, and another n textures for the 64 // second page used when scaling. 65 // In our case, we use 256*256 textures. On the tablet, this equates to 66 // at least 60 textures, or 112 with expanded tile boundaries. 67 // 112(tiles)*256*256*4(bpp)*2(pages) = 56MB 68 // It turns out the viewport dependent value m_maxTextureCount is a reasonable 69 // number to cap the layer tile texturs, it worked on both phones and tablets. 70 // TODO: after merge the pool of base tiles and layer tiles, we should revisit 71 // the logic of allocation management. 72 #define MAX_TEXTURE_ALLOCATION ((6+TILE_PREFETCH_DISTANCE*2)*(5+TILE_PREFETCH_DISTANCE*2)*4) 73 #define TILE_WIDTH 256 74 #define TILE_HEIGHT 256 75 #define LAYER_TILE_WIDTH 256 76 #define LAYER_TILE_HEIGHT 256 77 78 #define BYTES_PER_PIXEL 4 // 8888 config 79 80 #define LAYER_TEXTURES_DESTROY_TIMEOUT 60 // If we do not need layers for 60 seconds, free the textures 81 82 namespace WebCore { 83 84 GLint TilesManager::getMaxTextureSize() 85 { 86 static GLint maxTextureSize = 0; 87 if (!maxTextureSize) 88 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); 89 return maxTextureSize; 90 } 91 92 int TilesManager::getMaxTextureAllocation() 93 { 94 return MAX_TEXTURE_ALLOCATION; 95 } 96 97 TilesManager::TilesManager() 98 : m_layerTexturesRemain(true) 99 , m_maxTextureCount(0) 100 , m_maxLayerTextureCount(0) 101 , m_generatorReady(false) 102 , m_showVisualIndicator(false) 103 , m_invertedScreen(false) 104 , m_invertedScreenSwitch(false) 105 , m_useMinimalMemory(true) 106 , m_drawGLCount(1) 107 , m_lastTimeLayersUsed(0) 108 , m_hasLayerTextures(false) 109 { 110 XLOG("TilesManager ctor"); 111 m_textures.reserveCapacity(MAX_TEXTURE_ALLOCATION); 112 m_availableTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); 113 m_tilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); 114 m_availableTilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); 115 m_pixmapsGenerationThread = new TexturesGenerator(); 116 m_pixmapsGenerationThread->run("TexturesGenerator", android::PRIORITY_BACKGROUND); 117 } 118 119 void TilesManager::allocateTiles() 120 { 121 int nbTexturesToAllocate = m_maxTextureCount - m_textures.size(); 122 XLOG("%d tiles to allocate (%d textures planned)", nbTexturesToAllocate, m_maxTextureCount); 123 int nbTexturesAllocated = 0; 124 for (int i = 0; i < nbTexturesToAllocate; i++) { 125 BaseTileTexture* texture = new BaseTileTexture( 126 tileWidth(), tileHeight()); 127 // the atomic load ensures that the texture has been fully initialized 128 // before we pass a pointer for other threads to operate on 129 BaseTileTexture* loadedTexture = 130 reinterpret_cast<BaseTileTexture*>( 131 android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); 132 m_textures.append(loadedTexture); 133 nbTexturesAllocated++; 134 } 135 136 int nbLayersTexturesToAllocate = m_maxLayerTextureCount - m_tilesTextures.size(); 137 XLOG("%d layers tiles to allocate (%d textures planned)", 138 nbLayersTexturesToAllocate, m_maxLayerTextureCount); 139 int nbLayersTexturesAllocated = 0; 140 for (int i = 0; i < nbLayersTexturesToAllocate; i++) { 141 BaseTileTexture* texture = new BaseTileTexture( 142 layerTileWidth(), layerTileHeight()); 143 // the atomic load ensures that the texture has been fully initialized 144 // before we pass a pointer for other threads to operate on 145 BaseTileTexture* loadedTexture = 146 reinterpret_cast<BaseTileTexture*>( 147 android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); 148 m_tilesTextures.append(loadedTexture); 149 nbLayersTexturesAllocated++; 150 } 151 XLOG("allocated %d textures for base (total: %d, %d Mb), %d textures for layers (total: %d, %d Mb)", 152 nbTexturesAllocated, m_textures.size(), 153 m_textures.size() * TILE_WIDTH * TILE_HEIGHT * 4 / 1024 / 1024, 154 nbLayersTexturesAllocated, m_tilesTextures.size(), 155 m_tilesTextures.size() * LAYER_TILE_WIDTH * LAYER_TILE_HEIGHT * 4 / 1024 / 1024); 156 } 157 158 void TilesManager::deallocateTextures(bool allTextures) 159 { 160 const unsigned int max = m_textures.size(); 161 162 unsigned long long sparedDrawCount = ~0; // by default, spare no textures 163 if (!allTextures) { 164 // if we're not deallocating all textures, spare those with max drawcount 165 sparedDrawCount = 0; 166 for (unsigned int i = 0; i < max; i++) { 167 TextureOwner* owner = m_textures[i]->owner(); 168 if (owner) 169 sparedDrawCount = std::max(sparedDrawCount, owner->drawCount()); 170 } 171 } 172 deallocateTexturesVector(sparedDrawCount, m_textures); 173 deallocateTexturesVector(sparedDrawCount, m_tilesTextures); 174 } 175 176 void TilesManager::deallocateTexturesVector(unsigned long long sparedDrawCount, 177 WTF::Vector<BaseTileTexture*>& textures) 178 { 179 const unsigned int max = textures.size(); 180 int dealloc = 0; 181 for (unsigned int i = 0; i < max; i++) { 182 TextureOwner* owner = textures[i]->owner(); 183 if (!owner || owner->drawCount() < sparedDrawCount) { 184 textures[i]->discardGLTexture(); 185 dealloc++; 186 } 187 } 188 XLOG("Deallocated %d gl textures (out of %d base tiles and %d layer tiles)", 189 dealloc, max, maxLayer); 190 } 191 192 void TilesManager::gatherTexturesNumbers(int* nbTextures, int* nbAllocatedTextures, 193 int* nbLayerTextures, int* nbAllocatedLayerTextures) 194 { 195 *nbTextures = m_textures.size(); 196 for (unsigned int i = 0; i < m_textures.size(); i++) { 197 BaseTileTexture* texture = m_textures[i]; 198 if (texture->m_ownTextureId) 199 *nbAllocatedTextures += 1; 200 } 201 *nbLayerTextures = m_tilesTextures.size(); 202 for (unsigned int i = 0; i < m_tilesTextures.size(); i++) { 203 BaseTileTexture* texture = m_tilesTextures[i]; 204 if (texture->m_ownTextureId) 205 *nbAllocatedLayerTextures += 1; 206 } 207 } 208 209 void TilesManager::printTextures() 210 { 211 #ifdef DEBUG 212 XLOG("++++++"); 213 for (unsigned int i = 0; i < m_textures.size(); i++) { 214 BaseTileTexture* texture = m_textures[i]; 215 BaseTile* o = 0; 216 if (texture->owner()) 217 o = (BaseTile*) texture->owner(); 218 int x = -1; 219 int y = -1; 220 if (o) { 221 x = o->x(); 222 y = o->y(); 223 } 224 XLOG("[%d] texture %x busy: %d owner: %x (%d, %d) page: %x scale: %.2f", 225 i, texture, 226 texture->busy(), o, x, y, o ? o->page() : 0, o ? o->scale() : 0); 227 } 228 XLOG("------"); 229 #endif // DEBUG 230 } 231 232 void TilesManager::addPaintedSurface(PaintedSurface* surface) 233 { 234 m_paintedSurfaces.append(surface); 235 } 236 237 void TilesManager::gatherTextures() 238 { 239 android::Mutex::Autolock lock(m_texturesLock); 240 m_availableTextures = m_textures; 241 } 242 243 void TilesManager::gatherLayerTextures() 244 { 245 android::Mutex::Autolock lock(m_texturesLock); 246 m_availableTilesTextures = m_tilesTextures; 247 m_layerTexturesRemain = true; 248 } 249 250 BaseTileTexture* TilesManager::getAvailableTexture(BaseTile* owner) 251 { 252 android::Mutex::Autolock lock(m_texturesLock); 253 254 // Sanity check that the tile does not already own a texture 255 if (owner->backTexture() && owner->backTexture()->owner() == owner) { 256 XLOG("same owner (%d, %d), getAvailableBackTexture(%x) => texture %x", 257 owner->x(), owner->y(), owner, owner->backTexture()); 258 if (owner->isLayerTile()) 259 m_availableTilesTextures.remove(m_availableTilesTextures.find(owner->backTexture())); 260 else 261 m_availableTextures.remove(m_availableTextures.find(owner->backTexture())); 262 return owner->backTexture(); 263 } 264 265 WTF::Vector<BaseTileTexture*>* availableTexturePool; 266 if (owner->isLayerTile()) { 267 availableTexturePool = &m_availableTilesTextures; 268 } else { 269 availableTexturePool = &m_availableTextures; 270 } 271 272 // The heuristic for selecting a texture is as follows: 273 // 1. Skip textures currently being painted, they can't be painted while 274 // busy anyway 275 // 2. If a tile isn't owned, break with that one 276 // 3. Don't let tiles acquire their front textures 277 // 4. If we find a tile in the same page with a different scale, 278 // it's old and not visible. Break with that one 279 // 5. Otherwise, use the least recently prepared tile, but ignoring tiles 280 // drawn in the last frame to avoid flickering 281 282 BaseTileTexture* farthestTexture = 0; 283 unsigned long long oldestDrawCount = getDrawGLCount() - 1; 284 const unsigned int max = availableTexturePool->size(); 285 for (unsigned int i = 0; i < max; i++) { 286 BaseTileTexture* texture = (*availableTexturePool)[i]; 287 BaseTile* currentOwner = static_cast<BaseTile*>(texture->owner()); 288 289 if (texture->busy()) { 290 // don't bother, since the acquire() will likely fail 291 continue; 292 } 293 294 if (!currentOwner) { 295 // unused texture! take it! 296 farthestTexture = texture; 297 break; 298 } 299 300 if (currentOwner == owner) { 301 // Don't let a tile acquire its own front texture, as the 302 // acquisition logic doesn't handle that 303 continue; 304 } 305 306 if (currentOwner->painter() == owner->painter() && texture->scale() != owner->scale()) { 307 // if we render the back page with one scale, then another while 308 // still zooming, we recycle the tiles with the old scale instead of 309 // taking ones from the front page 310 farthestTexture = texture; 311 break; 312 } 313 314 unsigned long long textureDrawCount = currentOwner->drawCount(); 315 if (oldestDrawCount > textureDrawCount) { 316 farthestTexture = texture; 317 oldestDrawCount = textureDrawCount; 318 } 319 } 320 321 if (farthestTexture) { 322 BaseTile* previousOwner = static_cast<BaseTile*>(farthestTexture->owner()); 323 if (farthestTexture->acquire(owner)) { 324 if (previousOwner) { 325 previousOwner->removeTexture(farthestTexture); 326 327 XLOG("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)", 328 owner->isLayerTile() ? "LAYER" : "BASE", 329 farthestTexture, previousOwner->x(), previousOwner->y(), 330 owner->x(), owner->y(), 331 oldestDrawCount, getDrawGLCount()); 332 } 333 334 availableTexturePool->remove(availableTexturePool->find(farthestTexture)); 335 return farthestTexture; 336 } 337 } else { 338 if (owner->isLayerTile()) { 339 // couldn't find a tile for a layer, layers shouldn't request redraw 340 // TODO: once we do layer prefetching, don't set this for those 341 // tiles 342 m_layerTexturesRemain = false; 343 } 344 } 345 346 XLOG("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available", 347 owner->isLayerTile() ? "LAYER" : "BASE", 348 owner, owner->x(), owner->y(), max); 349 #ifdef DEBUG 350 printTextures(); 351 #endif // DEBUG 352 return 0; 353 } 354 355 int TilesManager::maxTextureCount() 356 { 357 android::Mutex::Autolock lock(m_texturesLock); 358 return m_maxTextureCount; 359 } 360 361 int TilesManager::maxLayerTextureCount() 362 { 363 android::Mutex::Autolock lock(m_texturesLock); 364 return m_maxLayerTextureCount; 365 } 366 367 void TilesManager::setMaxTextureCount(int max) 368 { 369 XLOG("setMaxTextureCount: %d (current: %d, total:%d)", 370 max, m_maxTextureCount, MAX_TEXTURE_ALLOCATION); 371 if (m_maxTextureCount == MAX_TEXTURE_ALLOCATION || 372 max <= m_maxTextureCount) 373 return; 374 375 android::Mutex::Autolock lock(m_texturesLock); 376 377 if (max < MAX_TEXTURE_ALLOCATION) 378 m_maxTextureCount = max; 379 else 380 m_maxTextureCount = MAX_TEXTURE_ALLOCATION; 381 382 allocateTiles(); 383 } 384 385 void TilesManager::setMaxLayerTextureCount(int max) 386 { 387 XLOG("setMaxLayerTextureCount: %d (current: %d, total:%d)", 388 max, m_maxLayerTextureCount, MAX_TEXTURE_ALLOCATION); 389 if (!max && m_hasLayerTextures) { 390 double secondsSinceLayersUsed = WTF::currentTime() - m_lastTimeLayersUsed; 391 if (secondsSinceLayersUsed > LAYER_TEXTURES_DESTROY_TIMEOUT) { 392 unsigned long long sparedDrawCount = ~0; // by default, spare no textures 393 deallocateTexturesVector(sparedDrawCount, m_tilesTextures); 394 m_hasLayerTextures = false; 395 } 396 return; 397 } 398 m_lastTimeLayersUsed = WTF::currentTime(); 399 if (m_maxLayerTextureCount == MAX_TEXTURE_ALLOCATION || 400 max <= m_maxLayerTextureCount) 401 return; 402 403 android::Mutex::Autolock lock(m_texturesLock); 404 405 if (max < MAX_TEXTURE_ALLOCATION) 406 m_maxLayerTextureCount = max; 407 else 408 m_maxLayerTextureCount = MAX_TEXTURE_ALLOCATION; 409 410 allocateTiles(); 411 m_hasLayerTextures = true; 412 } 413 414 415 float TilesManager::tileWidth() 416 { 417 return TILE_WIDTH; 418 } 419 420 float TilesManager::tileHeight() 421 { 422 return TILE_HEIGHT; 423 } 424 425 float TilesManager::layerTileWidth() 426 { 427 return LAYER_TILE_WIDTH; 428 } 429 430 float TilesManager::layerTileHeight() 431 { 432 return LAYER_TILE_HEIGHT; 433 } 434 435 void TilesManager::paintedSurfacesCleanup(GLWebViewState* state) 436 { 437 // PaintedSurfaces are created by LayerAndroid with a refcount of 1, 438 // and just transferred to new (corresponding) layers when a new layer tree 439 // is received. 440 // PaintedSurface also keep a reference on the Layer it currently has, so 441 // when we unref the tree of layer, those layers with a PaintedSurface will 442 // still be around if we do nothing. 443 // Here, if the surface does not have any associated layer, it means that we 444 // received a new layer tree without a corresponding layer (i.e. a layer 445 // using a texture has been removed by webkit). 446 // In that case, we remove the PaintedSurface from our list, and unref it. 447 // If the surface does have a layer, but the GLWebViewState associated to 448 // that layer is different from the one passed in parameter, it means we can 449 // also remove the surface (and we also remove/unref any layer that surface 450 // has). We do this when we deallocate GLWebViewState (i.e. the webview has 451 // been destroyed) and also when we switch to a page without 452 // composited layers. 453 454 WTF::Vector<PaintedSurface*> collect; 455 for (unsigned int i = 0; i < m_paintedSurfaces.size(); i++) { 456 PaintedSurface* surface = m_paintedSurfaces[i]; 457 458 Layer* drawing = surface->drawingLayer(); 459 Layer* painting = surface->paintingLayer(); 460 461 XLOG("considering PS %p, drawing %p, painting %p", surface, drawing, painting); 462 463 bool drawingMatchesState = state && drawing && (drawing->state() == state); 464 bool paintingMatchesState = state && painting && (painting->state() == state); 465 466 if ((!painting && !drawing) || drawingMatchesState || paintingMatchesState) { 467 XLOG("trying to remove PS %p, painting %p, drawing %p, DMS %d, PMS %d", 468 surface, painting, drawing, drawingMatchesState, paintingMatchesState); 469 collect.append(surface); 470 } 471 } 472 for (unsigned int i = 0; i < collect.size(); i++) { 473 PaintedSurface* surface = collect[i]; 474 m_paintedSurfaces.remove(m_paintedSurfaces.find(surface)); 475 SkSafeUnref(surface); 476 } 477 } 478 479 void TilesManager::unregisterGLWebViewState(GLWebViewState* state) 480 { 481 // Discard the whole queue b/c we lost GL context already. 482 // Note the real updateTexImage will still wait for the next draw. 483 transferQueue()->discardQueue(); 484 } 485 486 TilesManager* TilesManager::instance() 487 { 488 if (!gInstance) { 489 gInstance = new TilesManager(); 490 XLOG("instance(), new gInstance is %x", gInstance); 491 XLOG("Waiting for the generator..."); 492 gInstance->waitForGenerator(); 493 XLOG("Generator ready!"); 494 } 495 return gInstance; 496 } 497 498 TilesManager* TilesManager::gInstance = 0; 499 500 } // namespace WebCore 501 502 #endif // USE(ACCELERATED_COMPOSITING) 503