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