Home | History | Annotate | Download | only in rendering
      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