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/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