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 "Tile"
     27 #define LOG_NDEBUG 1
     28 
     29 #include "config.h"
     30 #include "Tile.h"
     31 
     32 #if USE(ACCELERATED_COMPOSITING)
     33 
     34 #include "AndroidLog.h"
     35 #include "GLUtils.h"
     36 #include "BaseRenderer.h"
     37 #include "TextureInfo.h"
     38 #include "TileTexture.h"
     39 #include "TilesManager.h"
     40 
     41 // If the dirty portion of a tile exceeds this ratio, fully repaint.
     42 // Lower values give fewer partial repaints, thus fewer front-to-back
     43 // texture copies (cost will vary by device). It's a tradeoff between
     44 // the rasterization cost and the FBO texture recopy cost when using
     45 // GPU for the transfer queue.
     46 #define MAX_INVAL_AREA 0.6
     47 
     48 namespace WebCore {
     49 
     50 Tile::Tile(bool isLayerTile)
     51     : m_x(-1)
     52     , m_y(-1)
     53     , m_frontTexture(0)
     54     , m_backTexture(0)
     55     , m_lastDrawnTexture(0)
     56     , m_scale(1)
     57     , m_dirty(true)
     58     , m_repaintsPending(0)
     59     , m_fullRepaint(true)
     60     , m_isLayerTile(isLayerTile)
     61     , m_drawCount(0)
     62     , m_state(Unpainted)
     63 {
     64 #ifdef DEBUG_COUNT
     65     ClassTracker::instance()->increment("Tile");
     66 #endif
     67 }
     68 
     69 Tile::~Tile()
     70 {
     71     if (m_backTexture)
     72         m_backTexture->release(this);
     73     if (m_frontTexture)
     74         m_frontTexture->release(this);
     75 
     76 #ifdef DEBUG_COUNT
     77     ClassTracker::instance()->decrement("Tile");
     78 #endif
     79 }
     80 
     81 // All the following functions must be called from the main GL thread.
     82 
     83 void Tile::setContents(int x, int y, float scale, bool isExpandedPrefetchTile)
     84 {
     85     // TODO: investigate whether below check/discard is necessary
     86     if ((m_x != x)
     87         || (m_y != y)
     88         || (m_scale != scale)) {
     89         // neither texture is relevant
     90         discardTextures();
     91     }
     92 
     93     android::AutoMutex lock(m_atomicSync);
     94     m_x = x;
     95     m_y = y;
     96     m_scale = scale;
     97     m_drawCount = TilesManager::instance()->getDrawGLCount();
     98     if (isExpandedPrefetchTile)
     99         m_drawCount--; // deprioritize expanded painting region
    100 }
    101 
    102 void Tile::reserveTexture()
    103 {
    104     TileTexture* texture = TilesManager::instance()->getAvailableTexture(this);
    105 
    106     android::AutoMutex lock(m_atomicSync);
    107     if (texture && m_backTexture != texture) {
    108         ALOGV("tile %p reserving texture %p, back was %p (front %p)",
    109               this, texture, m_backTexture, m_frontTexture);
    110         m_state = Unpainted;
    111         m_backTexture = texture;
    112     }
    113 
    114     if (m_state == UpToDate) {
    115         ALOGV("moving tile %p to unpainted, since it reserved while up to date", this);
    116         m_dirty = true;
    117         m_state = Unpainted;
    118     }
    119 }
    120 
    121 bool Tile::removeTexture(TileTexture* texture)
    122 {
    123     ALOGV("%p removeTexture %p, back %p front %p",
    124           this, texture, m_backTexture, m_frontTexture);
    125     // We update atomically, so paintBitmap() can see the correct value
    126     android::AutoMutex lock(m_atomicSync);
    127     if (m_frontTexture == texture) {
    128         if (m_state == UpToDate) {
    129             ALOGV("front texture removed, state was UpToDate, now becoming unpainted, bt is %p", m_backTexture);
    130             m_state = Unpainted;
    131         }
    132 
    133         m_frontTexture = 0;
    134     }
    135     if (m_backTexture == texture) {
    136         m_state = Unpainted;
    137         m_backTexture = 0;
    138     }
    139 
    140     // mark dirty regardless of which texture was taken - the back texture may
    141     // have been ready to swap
    142     m_dirty = true;
    143 
    144     return true;
    145 }
    146 
    147 void Tile::markAsDirty()
    148 {
    149     android::AutoMutex lock(m_atomicSync);
    150     m_dirtyArea.setEmpty(); // empty dirty rect prevents fast blit path
    151     markAsDirtyInternal();
    152 }
    153 
    154 void Tile::markAsDirty(const SkRegion& dirtyArea)
    155 {
    156     if (dirtyArea.isEmpty())
    157         return;
    158     android::AutoMutex lock(m_atomicSync);
    159     m_dirtyArea.op(dirtyArea, SkRegion::kUnion_Op);
    160 
    161     // Check if we actually intersect with the area
    162     bool intersect = false;
    163     SkRegion::Iterator cliperator(dirtyArea);
    164     SkRect realTileRect;
    165     SkRect dirtyRect;
    166     while (!cliperator.done()) {
    167         dirtyRect.set(cliperator.rect());
    168         if (intersectWithRect(m_x, m_y, TilesManager::tileWidth(), TilesManager::tileHeight(),
    169                               m_scale, dirtyRect, realTileRect)) {
    170             intersect = true;
    171             break;
    172         }
    173         cliperator.next();
    174     }
    175 
    176     if (!intersect)
    177         return;
    178 
    179     markAsDirtyInternal();
    180 }
    181 
    182 void Tile::markAsDirtyInternal()
    183 {
    184     // NOTE: callers must hold lock on m_atomicSync
    185 
    186     m_dirty = true;
    187     if (m_state == UpToDate) {
    188         // We only mark a tile as unpainted in 'markAsDirty' if its status is
    189         // UpToDate: marking dirty means we need to repaint, but don't stop the
    190         // current paint
    191         m_state = Unpainted;
    192     } else if (m_state != Unpainted) {
    193         // TODO: fix it so that they can paint while deferring the markAsDirty
    194         // call (or block updates)
    195         ALOGV("Warning: tried to mark tile %p at %d, %d islayertile %d as dirty, state %d",
    196               this, m_x, m_y, isLayerTile(), m_state);
    197 
    198         // prefetch tiles can be marked dirty while in the process of painting,
    199         // due to not using an update lock. force them to fail validate step.
    200         m_state = Unpainted;
    201     }
    202 }
    203 
    204 bool Tile::isDirty()
    205 {
    206     android::AutoMutex lock(m_atomicSync);
    207     return m_dirty;
    208 }
    209 
    210 bool Tile::isRepaintPending()
    211 {
    212     android::AutoMutex lock(m_atomicSync);
    213     return m_repaintsPending != 0;
    214 }
    215 
    216 void Tile::setRepaintPending(bool pending)
    217 {
    218     android::AutoMutex lock(m_atomicSync);
    219     m_repaintsPending += pending ? 1 : -1;
    220 }
    221 
    222 bool Tile::drawGL(float opacity, const SkRect& rect, float scale,
    223                   const TransformationMatrix* transform,
    224                   bool forceBlending, bool usePointSampling,
    225                   const FloatRect& fillPortion)
    226 {
    227     if (m_x < 0 || m_y < 0 || m_scale != scale)
    228         return false;
    229 
    230     // No need to mutex protect reads of m_backTexture as it is only written to by
    231     // the consumer thread.
    232     if (!m_frontTexture)
    233         return false;
    234 
    235     if (fillPortion.maxX() < 1.0f || fillPortion.maxY() < 1.0f
    236         || fillPortion.x() > 0.0f || fillPortion.y() > 0.0f)
    237         ALOGV("drawing tile %p (%d, %d with fill portions %f %f->%f, %f",
    238               this, m_x, m_y, fillPortion.x(), fillPortion.y(),
    239               fillPortion.maxX(), fillPortion.maxY());
    240 
    241     m_frontTexture->drawGL(isLayerTile(), rect, opacity, transform,
    242                            forceBlending, usePointSampling, fillPortion);
    243     m_lastDrawnTexture = m_frontTexture;
    244     return true;
    245 }
    246 
    247 bool Tile::isTileReady()
    248 {
    249     // Return true if the tile's most recently drawn texture is up to date
    250     android::AutoMutex lock(m_atomicSync);
    251     TileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture;
    252 
    253     if (!texture)
    254         return false;
    255 
    256     if (texture->owner() != this)
    257         return false;
    258 
    259     if (m_dirty)
    260         return false;
    261 
    262     if (m_state != ReadyToSwap && m_state != UpToDate)
    263         return false;
    264 
    265     return true;
    266 }
    267 
    268 bool Tile::intersectWithRect(int x, int y, int tileWidth, int tileHeight,
    269                              float scale, const SkRect& dirtyRect,
    270                              SkRect& realTileRect)
    271 {
    272     // compute the rect to corresponds to pixels
    273     realTileRect.fLeft = x * tileWidth;
    274     realTileRect.fTop = y * tileHeight;
    275     realTileRect.fRight = realTileRect.fLeft + tileWidth;
    276     realTileRect.fBottom = realTileRect.fTop + tileHeight;
    277 
    278     // scale the dirtyRect for intersect computation.
    279     SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale,
    280                                           dirtyRect.height() * scale);
    281     realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale);
    282 
    283     if (!realTileRect.intersect(realDirtyRect))
    284         return false;
    285     return true;
    286 }
    287 
    288 bool Tile::isTileVisible(const IntRect& viewTileBounds)
    289 {
    290     return (m_x >= viewTileBounds.x()
    291             && m_x < viewTileBounds.x() + viewTileBounds.width()
    292             && m_y >= viewTileBounds.y()
    293             && m_y < viewTileBounds.y() + viewTileBounds.height());
    294 }
    295 
    296 // This is called from the texture generation thread
    297 void Tile::paintBitmap(TilePainter* painter, BaseRenderer* renderer)
    298 {
    299     // We acquire the values below atomically. This ensures that we are reading
    300     // values correctly across cores. Further, once we have these values they
    301     // can be updated by other threads without consequence.
    302     m_atomicSync.lock();
    303     bool dirty = m_dirty;
    304     TileTexture* texture = m_backTexture;
    305     SkRegion dirtyArea = m_dirtyArea;
    306     float scale = m_scale;
    307     const int x = m_x;
    308     const int y = m_y;
    309 
    310     if (!dirty || !texture) {
    311         m_atomicSync.unlock();
    312         return;
    313     }
    314     if (m_state != Unpainted) {
    315         ALOGV("Warning: started painting tile %p, but was at state %d, ft %p bt %p",
    316               this, m_state, m_frontTexture, m_backTexture);
    317     }
    318     m_state = PaintingStarted;
    319     TextureInfo* textureInfo = texture->getTextureInfo();
    320     m_atomicSync.unlock();
    321 
    322     // at this point we can safely check the ownership (if the texture got
    323     // transferred to another Tile under us)
    324     if (texture->owner() != this) {
    325         return;
    326     }
    327 
    328     // setup the common renderInfo fields;
    329     TileRenderInfo renderInfo;
    330     renderInfo.x = x;
    331     renderInfo.y = y;
    332     renderInfo.scale = scale;
    333     renderInfo.tileSize = texture->getSize();
    334     renderInfo.tilePainter = painter;
    335     renderInfo.baseTile = this;
    336     renderInfo.textureInfo = textureInfo;
    337 
    338     const float tileWidth = renderInfo.tileSize.width();
    339     const float tileHeight = renderInfo.tileSize.height();
    340 
    341     renderer->renderTiledContent(renderInfo);
    342 
    343     m_atomicSync.lock();
    344 
    345     if (texture == m_backTexture) {
    346         // set the fullrepaint flags
    347         m_fullRepaint = false;
    348 
    349         // The various checks to see if we are still dirty...
    350 
    351         m_dirty = false;
    352 
    353         if (m_scale != scale)
    354             m_dirty = true;
    355 
    356         m_dirtyArea.setEmpty();
    357 
    358         ALOGV("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty);
    359 
    360         validatePaint();
    361     } else {
    362         ALOGV("tile %p no longer owns texture %p, m_state %d. ft %p bt %p",
    363               this, texture, m_state, m_frontTexture, m_backTexture);
    364     }
    365 
    366     m_atomicSync.unlock();
    367 }
    368 
    369 void Tile::discardTextures() {
    370     android::AutoMutex lock(m_atomicSync);
    371     ALOGV("%p discarding bt %p, ft %p",
    372           this, m_backTexture, m_frontTexture);
    373     if (m_frontTexture) {
    374         m_frontTexture->release(this);
    375         m_frontTexture = 0;
    376     }
    377     if (m_backTexture) {
    378         m_backTexture->release(this);
    379         m_backTexture = 0;
    380     }
    381     m_dirtyArea.setEmpty();
    382     m_fullRepaint = true;
    383 
    384     m_dirty = true;
    385     m_state = Unpainted;
    386 }
    387 
    388 void Tile::discardBackTexture() {
    389     android::AutoMutex lock(m_atomicSync);
    390     if (m_backTexture) {
    391         m_backTexture->release(this);
    392         m_backTexture = 0;
    393     }
    394     m_state = Unpainted;
    395     m_dirty = true;
    396 }
    397 
    398 bool Tile::swapTexturesIfNeeded() {
    399     android::AutoMutex lock(m_atomicSync);
    400     if (m_state == ReadyToSwap) {
    401         // discard old texture and swap the new one in its place
    402         if (m_frontTexture)
    403             m_frontTexture->release(this);
    404 
    405         m_frontTexture = m_backTexture;
    406         m_backTexture = 0;
    407         m_state = UpToDate;
    408         ALOGV("display texture for %p at %d, %d front is now %p, back is %p",
    409               this, m_x, m_y, m_frontTexture, m_backTexture);
    410 
    411         return true;
    412     }
    413     return false;
    414 }
    415 
    416 void Tile::backTextureTransfer() {
    417     android::AutoMutex lock(m_atomicSync);
    418     if (m_state == PaintingStarted)
    419         m_state = TransferredUnvalidated;
    420     else if (m_state == ValidatedUntransferred)
    421         m_state = ReadyToSwap;
    422     else {
    423         // shouldn't have transferred a tile in any other state, log
    424         ALOGV("Note: transferred tile %p at %d %d, state wasn't paintingstarted or validated: %d",
    425               this, m_x, m_y, m_state);
    426     }
    427 }
    428 
    429 void Tile::backTextureTransferFail() {
    430     // transfer failed for some reason, mark dirty so it will (repaint and) be
    431     // retransferred.
    432     android::AutoMutex lock(m_atomicSync);
    433     m_state = Unpainted;
    434     m_dirty = true;
    435     // whether validatePaint is called before or after, it won't do anything
    436 }
    437 
    438 void Tile::onBlitUpdate()
    439 {
    440     // The front texture was directly updated with a blit, so mark this as clean
    441     android::AutoMutex lock(m_atomicSync);
    442     m_dirty = false;
    443     m_dirtyArea.setEmpty();
    444     m_state = Tile::UpToDate;
    445 }
    446 
    447 void Tile::validatePaint() {
    448     // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap())
    449 
    450     if (!m_dirty) {
    451         // since after the paint, the tile isn't dirty, 'validate' it - this
    452         // may happed before or after the transfer queue operation. Only
    453         // when both have happened, mark as 'ReadyToSwap'
    454         if (m_state == PaintingStarted)
    455             m_state = ValidatedUntransferred;
    456         else if (m_state == TransferredUnvalidated) {
    457             // When the backTexture has been marked pureColor, we will skip the
    458             // transfer and marked as ReadyToSwap, in this case, we don't want
    459             // to reset m_dirty bit to true.
    460             m_state = ReadyToSwap;
    461         } else {
    462             ALOGV("Note: validated tile %p at %d %d, state wasn't paintingstarted or transferred %d",
    463                   this, m_x, m_y, m_state);
    464             // failed transferring, in which case mark dirty (since
    465             // paintBitmap() may have cleared m_dirty)
    466             m_dirty = true;
    467         }
    468     } else {
    469         ALOGV("Note: paint was unsuccessful.");
    470         m_state = Unpainted;
    471     }
    472 
    473 }
    474 
    475 } // namespace WebCore
    476 
    477 #endif // USE(ACCELERATED_COMPOSITING)
    478