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 "BaseTile.h"
     28 
     29 #if USE(ACCELERATED_COMPOSITING)
     30 
     31 #include "GLUtils.h"
     32 #include "RasterRenderer.h"
     33 #include "TextureInfo.h"
     34 #include "TilesManager.h"
     35 
     36 #include <cutils/atomic.h>
     37 
     38 #include <cutils/log.h>
     39 #include <wtf/CurrentTime.h>
     40 #include <wtf/text/CString.h>
     41 
     42 #undef XLOGC
     43 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTile", __VA_ARGS__)
     44 
     45 #ifdef DEBUG
     46 
     47 #undef XLOG
     48 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTile", __VA_ARGS__)
     49 
     50 #else
     51 
     52 #undef XLOG
     53 #define XLOG(...)
     54 
     55 #endif // DEBUG
     56 
     57 namespace WebCore {
     58 
     59 BaseTile::BaseTile(bool isLayerTile)
     60     : m_glWebViewState(0)
     61     , m_painter(0)
     62     , m_x(-1)
     63     , m_y(-1)
     64     , m_page(0)
     65     , m_frontTexture(0)
     66     , m_backTexture(0)
     67     , m_scale(1)
     68     , m_dirty(true)
     69     , m_repaintPending(false)
     70     , m_lastDirtyPicture(0)
     71     , m_isTexturePainted(false)
     72     , m_isLayerTile(isLayerTile)
     73     , m_drawCount(0)
     74     , m_state(Unpainted)
     75 {
     76 #ifdef DEBUG_COUNT
     77     ClassTracker::instance()->increment("BaseTile");
     78 #endif
     79     m_currentDirtyAreaIndex = 0;
     80 
     81     // For EglImage Mode, the internal buffer should be 2.
     82     // For Surface Texture mode, we only need one.
     83     if (TilesManager::instance()->getSharedTextureMode() == EglImageMode)
     84         m_maxBufferNumber = 2;
     85     else
     86         m_maxBufferNumber = 1;
     87 
     88     m_dirtyArea = new SkRegion[m_maxBufferNumber];
     89     m_fullRepaint = new bool[m_maxBufferNumber];
     90     for (int i = 0; i < m_maxBufferNumber; i++)
     91         m_fullRepaint[i] = true;
     92 
     93     m_renderer = BaseRenderer::createRenderer();
     94 }
     95 
     96 BaseTile::~BaseTile()
     97 {
     98     if (m_backTexture)
     99         m_backTexture->release(this);
    100     if (m_frontTexture)
    101         m_frontTexture->release(this);
    102 
    103     delete m_renderer;
    104     delete[] m_dirtyArea;
    105     delete[] m_fullRepaint;
    106 
    107 #ifdef DEBUG_COUNT
    108     ClassTracker::instance()->decrement("BaseTile");
    109 #endif
    110 }
    111 
    112 // All the following functions must be called from the main GL thread.
    113 
    114 void BaseTile::setContents(TilePainter* painter, int x, int y, float scale)
    115 {
    116     if ((m_painter != painter)
    117         || (m_x != x)
    118         || (m_y != y)
    119         || (m_scale != scale)) {
    120         // neither texture is relevant
    121         discardTextures();
    122     }
    123 
    124     android::AutoMutex lock(m_atomicSync);
    125     m_painter = painter;
    126     m_x = x;
    127     m_y = y;
    128     m_scale = scale;
    129     m_drawCount = TilesManager::instance()->getDrawGLCount();
    130 }
    131 
    132 void BaseTile::reserveTexture()
    133 {
    134     BaseTileTexture* texture = TilesManager::instance()->getAvailableTexture(this);
    135 
    136     android::AutoMutex lock(m_atomicSync);
    137     if (texture && m_backTexture != texture) {
    138         XLOG("tile %p reserving texture %p, back was %p (front %p)",
    139              this, texture, m_backTexture, m_frontTexture);
    140         m_state = Unpainted;
    141         m_backTexture = texture;
    142     }
    143 
    144     if (m_state == UpToDate) {
    145         XLOG("moving tile %p to unpainted, since it reserved while up to date", this);
    146         m_dirty = true;
    147         m_state = Unpainted;
    148     }
    149 }
    150 
    151 bool BaseTile::removeTexture(BaseTileTexture* texture)
    152 {
    153     XLOG("%p removeTexture %p, back %p front %p... page %p",
    154          this, texture, m_backTexture, m_frontTexture, m_page);
    155     // We update atomically, so paintBitmap() can see the correct value
    156     android::AutoMutex lock(m_atomicSync);
    157     if (m_frontTexture == texture) {
    158         if (m_state == UpToDate) {
    159             XLOG("front texture removed, state was UpToDate, now becoming unpainted, bt is %p", m_backTexture);
    160             m_state = Unpainted;
    161         }
    162 
    163         m_frontTexture = 0;
    164     }
    165     if (m_backTexture == texture) {
    166         m_state = Unpainted;
    167         m_backTexture = 0;
    168     }
    169 
    170     // mark dirty regardless of which texture was taken - the back texture may
    171     // have been ready to swap
    172     m_dirty = true;
    173 
    174     return true;
    175 }
    176 
    177 void BaseTile::markAsDirty(int unsigned pictureCount,
    178                            const SkRegion& dirtyArea)
    179 {
    180     if (dirtyArea.isEmpty())
    181         return;
    182     android::AutoMutex lock(m_atomicSync);
    183     m_lastDirtyPicture = pictureCount;
    184     for (int i = 0; i < m_maxBufferNumber; i++)
    185         m_dirtyArea[i].op(dirtyArea, SkRegion::kUnion_Op);
    186 
    187     // Check if we actually intersect with the area
    188     bool intersect = false;
    189     SkRegion::Iterator cliperator(dirtyArea);
    190     int tileWidth = TilesManager::instance()->tileWidth();
    191     int tileHeight = TilesManager::instance()->tileHeight();
    192     if (m_isLayerTile) {
    193         tileWidth = TilesManager::instance()->layerTileWidth();
    194         tileHeight = TilesManager::instance()->layerTileHeight();
    195     }
    196     SkRect realTileRect;
    197     SkRect dirtyRect;
    198     while (!cliperator.done()) {
    199         dirtyRect.set(cliperator.rect());
    200         if (intersectWithRect(m_x, m_y, tileWidth, tileHeight,
    201                               m_scale, dirtyRect, realTileRect)) {
    202             intersect = true;
    203             break;
    204         }
    205         cliperator.next();
    206     }
    207 
    208     if (!intersect)
    209         return;
    210 
    211     m_dirty = true;
    212     if (m_state == UpToDate) {
    213         // We only mark a tile as unpainted in 'markAsDirty' if its status is
    214         // UpToDate: marking dirty means we need to repaint, but don't stop the
    215         // current paint
    216         m_state = Unpainted;
    217     } else if (m_state != Unpainted) {
    218         // TODO: fix it so that they can paint while deferring the markAsDirty
    219         // call (or block updates)
    220         XLOG("Warning: tried to mark tile %p at %d, %d islayertile %d as dirty, state %d, page %p",
    221               this, m_x, m_y, isLayerTile(), m_state, m_page);
    222 
    223         // prefetch tiles can be marked dirty while in the process of painting,
    224         // due to not using an update lock. force them to fail validate step.
    225         m_state = Unpainted;
    226     }
    227 }
    228 
    229 bool BaseTile::isDirty()
    230 {
    231     android::AutoMutex lock(m_atomicSync);
    232     return m_dirty;
    233 }
    234 
    235 bool BaseTile::isRepaintPending()
    236 {
    237     android::AutoMutex lock(m_atomicSync);
    238     return m_repaintPending;
    239 }
    240 
    241 void BaseTile::setRepaintPending(bool pending)
    242 {
    243     android::AutoMutex lock(m_atomicSync);
    244     m_repaintPending = pending;
    245 }
    246 
    247 void BaseTile::draw(float transparency, SkRect& rect, float scale)
    248 {
    249     if (m_x < 0 || m_y < 0 || m_scale != scale)
    250         return;
    251 
    252     // No need to mutex protect reads of m_backTexture as it is only written to by
    253     // the consumer thread.
    254     if (!m_frontTexture)
    255         return;
    256 
    257     // Early return if set to un-usable in purpose!
    258     m_atomicSync.lock();
    259     bool isTexturePainted = m_isTexturePainted;
    260     m_atomicSync.unlock();
    261 
    262     if (!isTexturePainted)
    263         return;
    264 
    265     TextureInfo* textureInfo = m_frontTexture->consumerLock();
    266     if (!textureInfo) {
    267         m_frontTexture->consumerRelease();
    268         return;
    269     }
    270 
    271     if (m_frontTexture->readyFor(this)) {
    272         if (isLayerTile() && m_painter && m_painter->transform())
    273             TilesManager::instance()->shader()->drawLayerQuad(*m_painter->transform(),
    274                                                               rect, m_frontTexture->m_ownTextureId,
    275                                                               transparency, true);
    276         else
    277             TilesManager::instance()->shader()->drawQuad(rect, m_frontTexture->m_ownTextureId,
    278                                                          transparency);
    279     } else {
    280         XLOG("tile %p at %d, %d not readyfor (at draw),", this, m_x, m_y);
    281     }
    282 
    283     m_frontTexture->consumerRelease();
    284 }
    285 
    286 bool BaseTile::isTileReady()
    287 {
    288     // Return true if the tile's most recently drawn texture is up to date
    289     android::AutoMutex lock(m_atomicSync);
    290     BaseTileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture;
    291 
    292     if (!texture)
    293         return false;
    294 
    295     if (texture->owner() != this)
    296         return false;
    297 
    298     if (m_dirty)
    299         return false;
    300 
    301     if (m_state != ReadyToSwap && m_state != UpToDate)
    302         return false;
    303 
    304     texture->consumerLock();
    305     bool ready = texture->readyFor(this);
    306     texture->consumerRelease();
    307 
    308     if (ready)
    309         return true;
    310 
    311     XLOG("tile %p at %d, %d not readyfor (at isTileReady)", this, m_x, m_y);
    312 
    313     return false;
    314 }
    315 
    316 bool BaseTile::intersectWithRect(int x, int y, int tileWidth, int tileHeight,
    317                                  float scale, const SkRect& dirtyRect,
    318                                  SkRect& realTileRect)
    319 {
    320     // compute the rect to corresponds to pixels
    321     realTileRect.fLeft = x * tileWidth;
    322     realTileRect.fTop = y * tileHeight;
    323     realTileRect.fRight = realTileRect.fLeft + tileWidth;
    324     realTileRect.fBottom = realTileRect.fTop + tileHeight;
    325 
    326     // scale the dirtyRect for intersect computation.
    327     SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale,
    328                                           dirtyRect.height() * scale);
    329     realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale);
    330 
    331     if (!realTileRect.intersect(realDirtyRect))
    332         return false;
    333     return true;
    334 }
    335 
    336 bool BaseTile::isTileVisible(const IntRect& viewTileBounds)
    337 {
    338     return (m_x >= viewTileBounds.x()
    339             && m_x < viewTileBounds.x() + viewTileBounds.width()
    340             && m_y >= viewTileBounds.y()
    341             && m_y < viewTileBounds.y() + viewTileBounds.height());
    342 }
    343 
    344 // This is called from the texture generation thread
    345 void BaseTile::paintBitmap()
    346 {
    347     // We acquire the values below atomically. This ensures that we are reading
    348     // values correctly across cores. Further, once we have these values they
    349     // can be updated by other threads without consequence.
    350     m_atomicSync.lock();
    351     bool dirty = m_dirty;
    352     BaseTileTexture* texture = m_backTexture;
    353     SkRegion dirtyArea = m_dirtyArea[m_currentDirtyAreaIndex];
    354     float scale = m_scale;
    355     const int x = m_x;
    356     const int y = m_y;
    357     TilePainter* painter = m_painter;
    358 
    359     if (!dirty || !texture) {
    360         m_atomicSync.unlock();
    361         return;
    362     }
    363     if (m_state != Unpainted) {
    364         XLOG("Warning: started painting tile %p, but was at state %d, ft %p bt %p",
    365               this, m_state, m_frontTexture, m_backTexture);
    366     }
    367     m_state = PaintingStarted;
    368 
    369     texture->producerAcquireContext();
    370     TextureInfo* textureInfo = texture->producerLock();
    371     m_atomicSync.unlock();
    372 
    373     // at this point we can safely check the ownership (if the texture got
    374     // transferred to another BaseTile under us)
    375     if (texture->owner() != this) {
    376         texture->producerRelease();
    377         return;
    378     }
    379 
    380     unsigned int pictureCount = 0;
    381 
    382     // swap out the renderer if necessary
    383     BaseRenderer::swapRendererIfNeeded(m_renderer);
    384 
    385     // setup the common renderInfo fields;
    386     TileRenderInfo renderInfo;
    387     renderInfo.x = x;
    388     renderInfo.y = y;
    389     renderInfo.scale = scale;
    390     renderInfo.tileSize = texture->getSize();
    391     renderInfo.tilePainter = painter;
    392     renderInfo.baseTile = this;
    393     renderInfo.textureInfo = textureInfo;
    394 
    395     const float tileWidth = renderInfo.tileSize.width();
    396     const float tileHeight = renderInfo.tileSize.height();
    397 
    398     SkRegion::Iterator cliperator(dirtyArea);
    399 
    400     bool fullRepaint = false;
    401 
    402     if (m_fullRepaint[m_currentDirtyAreaIndex]
    403         || textureInfo->m_width != tileWidth
    404         || textureInfo->m_height != tileHeight) {
    405         fullRepaint = true;
    406     }
    407 
    408     bool surfaceTextureMode = textureInfo->getSharedTextureMode() == SurfaceTextureMode;
    409 
    410     if (surfaceTextureMode)
    411         fullRepaint = true;
    412 
    413     while (!fullRepaint && !cliperator.done()) {
    414         SkRect realTileRect;
    415         SkRect dirtyRect;
    416         dirtyRect.set(cliperator.rect());
    417         bool intersect = intersectWithRect(x, y, tileWidth, tileHeight,
    418                                            scale, dirtyRect, realTileRect);
    419 
    420         // With SurfaceTexture, just repaint the entire tile if we intersect
    421         // TODO: Implement the partial invalidate in Surface Texture Mode
    422         if (intersect && surfaceTextureMode) {
    423             fullRepaint = true;
    424             break;
    425         }
    426 
    427         if (intersect && !surfaceTextureMode) {
    428             // initialize finalRealRect to the rounded values of realTileRect
    429             SkIRect finalRealRect;
    430             realTileRect.roundOut(&finalRealRect);
    431 
    432             // stash the int values of the current width and height
    433             const int iWidth = finalRealRect.width();
    434             const int iHeight = finalRealRect.height();
    435 
    436             if (iWidth == tileWidth || iHeight == tileHeight) {
    437                 fullRepaint = true;
    438                 break;
    439             }
    440 
    441             // translate the rect into tile space coordinates
    442             finalRealRect.fLeft = finalRealRect.fLeft % static_cast<int>(tileWidth);
    443             finalRealRect.fTop = finalRealRect.fTop % static_cast<int>(tileHeight);
    444             finalRealRect.fRight = finalRealRect.fLeft + iWidth;
    445             finalRealRect.fBottom = finalRealRect.fTop + iHeight;
    446 
    447             renderInfo.invalRect = &finalRealRect;
    448             renderInfo.measurePerf = false;
    449 
    450             pictureCount = m_renderer->renderTiledContent(renderInfo);
    451         }
    452 
    453         cliperator.next();
    454     }
    455 
    456     // Do a full repaint if needed
    457     if (fullRepaint) {
    458         SkIRect rect;
    459         rect.set(0, 0, tileWidth, tileHeight);
    460 
    461         renderInfo.invalRect = &rect;
    462         renderInfo.measurePerf = TilesManager::instance()->getShowVisualIndicator();
    463 
    464         pictureCount = m_renderer->renderTiledContent(renderInfo);
    465     }
    466 
    467     m_atomicSync.lock();
    468 
    469 #if DEPRECATED_SURFACE_TEXTURE_MODE
    470     texture->setTile(textureInfo, x, y, scale, painter, pictureCount);
    471 #endif
    472     texture->producerReleaseAndSwap();
    473     if (texture == m_backTexture) {
    474         m_isTexturePainted = true;
    475 
    476         // set the fullrepaint flags
    477         m_fullRepaint[m_currentDirtyAreaIndex] = false;
    478 
    479         // The various checks to see if we are still dirty...
    480 
    481         m_dirty = false;
    482 
    483         if (m_scale != scale)
    484             m_dirty = true;
    485 
    486         if (fullRepaint)
    487             m_dirtyArea[m_currentDirtyAreaIndex].setEmpty();
    488         else
    489             m_dirtyArea[m_currentDirtyAreaIndex].op(dirtyArea, SkRegion::kDifference_Op);
    490 
    491         if (!m_dirtyArea[m_currentDirtyAreaIndex].isEmpty())
    492             m_dirty = true;
    493 
    494         // Now we can swap the dirty areas
    495         // TODO: For surface texture in Async mode, the index will be updated
    496         // according to the current buffer just dequeued.
    497         m_currentDirtyAreaIndex = (m_currentDirtyAreaIndex+1) % m_maxBufferNumber;
    498 
    499         if (!m_dirtyArea[m_currentDirtyAreaIndex].isEmpty())
    500             m_dirty = true;
    501 
    502         XLOG("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty);
    503 
    504         validatePaint();
    505     } else {
    506         XLOG("tile %p no longer owns texture %p, m_state %d. ft %p bt %p",
    507              this, texture, m_state, m_frontTexture, m_backTexture);
    508     }
    509 
    510     m_atomicSync.unlock();
    511 }
    512 
    513 void BaseTile::discardTextures() {
    514     android::AutoMutex lock(m_atomicSync);
    515     XLOG("%p discarding bt %p, ft %p",
    516          this, m_backTexture, m_frontTexture);
    517     if (m_frontTexture) {
    518         m_frontTexture->release(this);
    519         m_frontTexture = 0;
    520     }
    521     if (m_backTexture) {
    522         m_backTexture->release(this);
    523         m_backTexture = 0;
    524     }
    525     for (int i = 0; i < m_maxBufferNumber; i++) {
    526         m_dirtyArea[i].setEmpty();
    527         m_fullRepaint[i] = true;
    528     }
    529     m_dirty = true;
    530     m_state = Unpainted;
    531 }
    532 
    533 void BaseTile::discardBackTexture() {
    534     android::AutoMutex lock(m_atomicSync);
    535     if (m_backTexture) {
    536         m_backTexture->release(this);
    537         m_backTexture = 0;
    538     }
    539     m_state = Unpainted;
    540     m_dirty = true;
    541 }
    542 
    543 bool BaseTile::swapTexturesIfNeeded() {
    544     android::AutoMutex lock(m_atomicSync);
    545     if (m_state == ReadyToSwap) {
    546         // discard old texture and swap the new one in its place
    547         if (m_frontTexture)
    548             m_frontTexture->release(this);
    549 
    550         m_frontTexture = m_backTexture;
    551         m_backTexture = 0;
    552         m_state = UpToDate;
    553         XLOG("display texture for %p at %d, %d front is now %p, back is %p",
    554              this, m_x, m_y, m_frontTexture, m_backTexture);
    555 
    556         return true;
    557     }
    558     return false;
    559 }
    560 
    561 void BaseTile::backTextureTransfer() {
    562     android::AutoMutex lock(m_atomicSync);
    563     if (m_state == PaintingStarted)
    564         m_state = TransferredUnvalidated;
    565     else if (m_state == ValidatedUntransferred)
    566         m_state = ReadyToSwap;
    567     else {
    568         // shouldn't have transferred a tile in any other state, log
    569         XLOG("Note: transferred tile %p at %d %d, state wasn't paintingstarted or validated: %d",
    570              this, m_x, m_y, m_state);
    571     }
    572 }
    573 
    574 void BaseTile::backTextureTransferFail() {
    575     // transfer failed for some reason, mark dirty so it will (repaint and) be
    576     // retransferred.
    577     android::AutoMutex lock(m_atomicSync);
    578     m_state = Unpainted;
    579     m_dirty = true;
    580     // whether validatePaint is called before or after, it won't do anything
    581 }
    582 
    583 void BaseTile::validatePaint() {
    584     // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap())
    585 
    586     if (!m_dirty) {
    587         // since after the paint, the tile isn't dirty, 'validate' it - this
    588         // may happed before or after the transfer queue operation. Only
    589         // when both have happened, mark as 'ReadyToSwap'
    590         if (m_state == PaintingStarted)
    591             m_state = ValidatedUntransferred;
    592         else if (m_state == TransferredUnvalidated)
    593             m_state = ReadyToSwap;
    594         else {
    595             XLOG("Note: validated tile %p at %d %d, state wasn't paintingstarted or transferred %d",
    596                   this, m_x, m_y, m_state);
    597             // failed transferring, in which case mark dirty (since
    598             // paintBitmap() may have cleared m_dirty)
    599             m_dirty = true;
    600         }
    601 
    602         if (m_deferredDirty) {
    603             XLOG("Note: deferred dirty flag set, possibly a missed paint on tile %p", this);
    604             m_deferredDirty = false;
    605         }
    606     } else {
    607         XLOG("Note: paint was unsuccessful.");
    608         m_state = Unpainted;
    609     }
    610 
    611 }
    612 
    613 } // namespace WebCore
    614 
    615 #endif // USE(ACCELERATED_COMPOSITING)
    616