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