Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright 2011, 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 "TileGrid"
     27 #define LOG_NDEBUG 1
     28 
     29 #include "config.h"
     30 #include "TileGrid.h"
     31 
     32 #include "AndroidLog.h"
     33 #include "DrawQuadData.h"
     34 #include "GLWebViewState.h"
     35 #include "PaintTileOperation.h"
     36 #include "Tile.h"
     37 #include "TileTexture.h"
     38 #include "TilesManager.h"
     39 
     40 #include <wtf/CurrentTime.h>
     41 
     42 #define EXPANDED_BOUNDS_INFLATE 1
     43 #define EXPANDED_PREFETCH_BOUNDS_Y_INFLATE 1
     44 
     45 namespace WebCore {
     46 
     47 TileGrid::TileGrid(bool isBaseSurface)
     48     : m_prevTileY(0)
     49     , m_scale(1)
     50     , m_isBaseSurface(isBaseSurface)
     51 {
     52     m_dirtyRegion.setEmpty();
     53 #ifdef DEBUG_COUNT
     54     ClassTracker::instance()->increment("TileGrid");
     55 #endif
     56 }
     57 
     58 TileGrid::~TileGrid()
     59 {
     60 #ifdef DEBUG_COUNT
     61     ClassTracker::instance()->decrement("TileGrid");
     62 #endif
     63     removeTiles();
     64 }
     65 
     66 bool TileGrid::isReady()
     67 {
     68     bool tilesAllReady = true;
     69     bool tilesVisible = false;
     70     for (unsigned int i = 0; i < m_tiles.size(); i++) {
     71         Tile* tile = m_tiles[i];
     72         if (tile->isTileVisible(m_area)) {
     73             tilesVisible = true;
     74             if (!tile->isTileReady()) {
     75                 tilesAllReady = false;
     76                 break;
     77             }
     78         }
     79     }
     80     // For now, if no textures are available, consider ourselves as ready
     81     // in order to unblock the zooming process.
     82     // FIXME: have a better system -- maybe keeping the last scale factor
     83     // able to fully render everything
     84     ALOGV("TG %p, ready %d, visible %d, texturesRemain %d",
     85           this, tilesAllReady, tilesVisible,
     86           TilesManager::instance()->layerTexturesRemain());
     87 
     88     return !TilesManager::instance()->layerTexturesRemain()
     89             || !tilesVisible || tilesAllReady;
     90 }
     91 
     92 bool TileGrid::isMissingContent()
     93 {
     94     for (unsigned int i = 0; i < m_tiles.size(); i++)
     95         if (m_tiles[i]->isTileVisible(m_area) && !m_tiles[i]->frontTexture())
     96             return true;
     97     return false;
     98 }
     99 
    100 bool TileGrid::swapTiles()
    101 {
    102     int swaps = 0;
    103     for (unsigned int i = 0; i < m_tiles.size(); i++)
    104         if (m_tiles[i]->swapTexturesIfNeeded())
    105             swaps++;
    106     ALOGV("TG %p swapping, swaps = %d", this, swaps);
    107     return swaps != 0;
    108 }
    109 
    110 IntRect TileGrid::computeTilesArea(const IntRect& contentArea, float scale)
    111 {
    112     IntRect computedArea;
    113     IntRect area(contentArea.x() * scale,
    114                  contentArea.y() * scale,
    115                  ceilf(contentArea.width() * scale),
    116                  ceilf(contentArea.height() * scale));
    117 
    118     ALOGV("TG prepare, scale %f, area %d x %d", scale, area.width(), area.height());
    119 
    120     if (area.width() == 0 && area.height() == 0) {
    121         computedArea.setWidth(0);
    122         computedArea.setHeight(0);
    123         return computedArea;
    124     }
    125 
    126     int tileWidth = TilesManager::tileWidth();
    127     int tileHeight = TilesManager::tileHeight();
    128 
    129     computedArea.setX(area.x() / tileWidth);
    130     computedArea.setY(area.y() / tileHeight);
    131     float right = (area.x() + area.width()) / (float) tileWidth;
    132     float bottom = (area.y() + area.height()) / (float) tileHeight;
    133     computedArea.setWidth(ceilf(right) - computedArea.x());
    134     computedArea.setHeight(ceilf(bottom) - computedArea.y());
    135     return computedArea;
    136 }
    137 
    138 void TileGrid::prepareGL(GLWebViewState* state, float scale,
    139                          const IntRect& prepareArea, const IntRect& fullContentArea,
    140                          TilePainter* painter, int regionFlags, bool isLowResPrefetch,
    141                          bool updateWithBlit)
    142 {
    143     // first, how many tiles do we need
    144     m_area = computeTilesArea(prepareArea, scale);
    145     if (m_area.isEmpty())
    146         return;
    147 
    148     ALOGV("prepare TileGrid %p with scale %.2f, prepareArea "
    149           " %d, %d - %d x %d, corresponding to %d, %d x - %d x %d tiles",
    150           this, scale,
    151           prepareArea.x(), prepareArea.y(),
    152           prepareArea.width(), prepareArea.height(),
    153           m_area.x(), m_area.y(),
    154           m_area.width(), m_area.height());
    155 
    156     bool goingDown = m_prevTileY < m_area.y();
    157     m_prevTileY = m_area.y();
    158 
    159     TilesManager* tilesManager = TilesManager::instance();
    160     if (scale != m_scale)
    161         tilesManager->removeOperationsForFilter(new ScaleFilter(painter, m_scale));
    162 
    163     m_scale = scale;
    164 
    165     // apply dirty region to affected tiles
    166     if (!m_dirtyRegion.isEmpty()) {
    167         for (unsigned int i = 0; i < m_tiles.size(); i++)
    168             m_tiles[i]->markAsDirty(m_dirtyRegion);
    169 
    170         // log inval region for the base surface
    171         if (m_isBaseSurface && tilesManager->getProfiler()->enabled()) {
    172             SkRegion::Iterator iterator(m_dirtyRegion);
    173             while (!iterator.done()) {
    174                 SkIRect r = iterator.rect();
    175                 tilesManager->getProfiler()->nextInval(r, scale);
    176                 iterator.next();
    177             }
    178         }
    179         m_dirtyRegion.setEmpty();
    180     }
    181 
    182     if (regionFlags & StandardRegion) {
    183         for (int i = 0; i < m_area.width(); i++) {
    184             if (goingDown) {
    185                 for (int j = 0; j < m_area.height(); j++)
    186                     prepareTile(m_area.x() + i, m_area.y() + j,
    187                                 painter, state, isLowResPrefetch, false, updateWithBlit);
    188             } else {
    189                 for (int j = m_area.height() - 1; j >= 0; j--)
    190                     prepareTile(m_area.x() + i, m_area.y() + j,
    191                                 painter, state, isLowResPrefetch, false, updateWithBlit);
    192             }
    193         }
    194     }
    195 
    196     if (regionFlags & ExpandedRegion) {
    197         IntRect fullArea = computeTilesArea(fullContentArea, scale);
    198         IntRect expandedArea = m_area;
    199 
    200         // on systems reporting highEndGfx=true and useMinimalMemory not set, use expanded bounds
    201         if (tilesManager->highEndGfx() && !tilesManager->useMinimalMemory())
    202             expandedArea.inflate(EXPANDED_BOUNDS_INFLATE);
    203 
    204         if (isLowResPrefetch)
    205             expandedArea.inflateY(EXPANDED_PREFETCH_BOUNDS_Y_INFLATE);
    206 
    207         // clip painting area to content
    208         expandedArea.intersect(fullArea);
    209 
    210         for (int i = expandedArea.x(); i < expandedArea.maxX(); i++)
    211             for (int j = expandedArea.y(); j < expandedArea.maxY(); j++)
    212                 if (!m_area.contains(i, j))
    213                     prepareTile(i, j, painter, state, isLowResPrefetch, true, updateWithBlit);
    214     }
    215 }
    216 
    217 void TileGrid::markAsDirty(const SkRegion& invalRegion)
    218 {
    219     ALOGV("TG %p markAsDirty, current region empty %d, new empty %d",
    220           this, m_dirtyRegion.isEmpty(), invalRegion.isEmpty());
    221     m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op);
    222 }
    223 
    224 void TileGrid::prepareTile(int x, int y, TilePainter* painter,
    225                            GLWebViewState* state, bool isLowResPrefetch,
    226                            bool isExpandPrefetch, bool shouldTryUpdateWithBlit)
    227 {
    228     Tile* tile = getTile(x, y);
    229     if (!tile) {
    230         bool isLayerTile = !m_isBaseSurface;
    231         tile = new Tile(isLayerTile);
    232         m_tiles.append(tile);
    233     }
    234 
    235     ALOGV("preparing tile %p at %d, %d, painter is %p", tile, x, y, painter);
    236 
    237     tile->setContents(x, y, m_scale, isExpandPrefetch);
    238 
    239     if (shouldTryUpdateWithBlit && tryBlitFromContents(tile, painter))
    240         return;
    241 
    242     if (tile->isDirty() || !tile->frontTexture())
    243         tile->reserveTexture();
    244 
    245     if (tile->backTexture() && tile->isDirty()) {
    246         TilesManager* tilesManager = TilesManager::instance();
    247 
    248         // if a scheduled repaint is still outstanding, update it with the new painter
    249         if (tile->isRepaintPending() && tilesManager->tryUpdateOperationWithPainter(tile, painter))
    250             return;
    251 
    252         ALOGV("painting TG %p's tile %d %d for LG %p, scale %f", this, x, y, painter, m_scale);
    253         PaintTileOperation *operation = new PaintTileOperation(tile, painter,
    254                                                                state, isLowResPrefetch);
    255         tilesManager->scheduleOperation(operation);
    256     }
    257 }
    258 
    259 bool TileGrid::tryBlitFromContents(Tile* tile, TilePainter* painter)
    260 {
    261     return tile->frontTexture()
    262            && !tile->frontTexture()->isPureColor()
    263            && tile->frontTexture()->m_ownTextureId
    264            && !tile->isRepaintPending()
    265            && painter->blitFromContents(tile);
    266 }
    267 
    268 Tile* TileGrid::getTile(int x, int y)
    269 {
    270     for (unsigned int i = 0; i <m_tiles.size(); i++) {
    271         Tile* tile = m_tiles[i];
    272         if (tile->x() == x && tile->y() == y)
    273             return tile;
    274     }
    275     return 0;
    276 }
    277 
    278 unsigned int TileGrid::getImageTextureId()
    279 {
    280     if (m_tiles.size() == 1) {
    281         if (m_tiles[0]->frontTexture())
    282             return m_tiles[0]->frontTexture()->m_ownTextureId;
    283     }
    284     return 0;
    285 }
    286 
    287 int TileGrid::nbTextures(const IntRect& area, float scale)
    288 {
    289     IntRect tileBounds = computeTilesArea(area, scale);
    290     int numberTextures = tileBounds.width() * tileBounds.height();
    291 
    292     // add the number of dirty tiles in the bounds, as they take up double
    293     // textures for double buffering
    294     for (unsigned int i = 0; i <m_tiles.size(); i++) {
    295         Tile* tile = m_tiles[i];
    296         if (tile->isDirty()
    297                 && tile->x() >= tileBounds.x() && tile->x() <= tileBounds.maxX()
    298                 && tile->y() >= tileBounds.y() && tile->y() <= tileBounds.maxY())
    299             numberTextures++;
    300     }
    301     return numberTextures;
    302 }
    303 
    304 void TileGrid::drawGL(const IntRect& visibleContentArea, float opacity,
    305                       const TransformationMatrix* transform,
    306                       const Color* background)
    307 {
    308     m_area = computeTilesArea(visibleContentArea, m_scale);
    309     if (m_area.width() == 0 || m_area.height() == 0)
    310         return;
    311 
    312     float invScale = 1.0 / m_scale;
    313     const float tileWidth = TilesManager::tileWidth() * invScale;
    314     const float tileHeight = TilesManager::tileHeight() * invScale;
    315 
    316     int drawn = 0;
    317 
    318     SkRegion missingRegion;
    319     bool semiOpaqueBaseSurface =
    320         background ? (background->hasAlpha() && background->alpha() > 0) : false;
    321     if (semiOpaqueBaseSurface) {
    322         SkIRect totalArea = SkIRect::MakeXYWH(m_area.x(), m_area.y(),
    323                                               m_area.width(), m_area.height());
    324         missingRegion = SkRegion(totalArea);
    325     }
    326 
    327     bool usePointSampling =
    328         TilesManager::instance()->shader()->usePointSampling(m_scale, transform);
    329 
    330     float minTileX =  visibleContentArea.x() / tileWidth;
    331     float minTileY =  visibleContentArea.y() / tileWidth;
    332     float maxTileWidth = visibleContentArea.maxX() / tileWidth;
    333     float maxTileHeight = visibleContentArea.maxY() / tileWidth;
    334     ALOGV("minTileX, minTileY, maxTileWidth, maxTileHeight %f, %f, %f %f",
    335           minTileX, minTileY, maxTileWidth, maxTileHeight);
    336     for (unsigned int i = 0; i < m_tiles.size(); i++) {
    337         Tile* tile = m_tiles[i];
    338 
    339         bool tileInView = tile->isTileVisible(m_area);
    340         if (tileInView) {
    341             SkRect rect;
    342             rect.fLeft = tile->x() * tileWidth;
    343             rect.fTop = tile->y() * tileHeight;
    344             rect.fRight = rect.fLeft + tileWidth;
    345             rect.fBottom = rect.fTop + tileHeight;
    346             ALOGV("tile %p (layer tile: %d) %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d",
    347                   tile, tile->isLayerTile(), tile->x(), tile->y(),
    348                   tile->scale(), m_scale, tile->isTileReady(), tile->isDirty());
    349 
    350             bool forceBaseBlending = background ? background->hasAlpha() : false;
    351 
    352             float left = std::max(minTileX - tile->x(), 0.0f);
    353             float top = std::max(minTileY - tile->y(), 0.0f);
    354             float right = std::min(maxTileWidth - tile->x(), 1.0f);
    355             float bottom = std::min(maxTileHeight - tile->y(), 1.0f);
    356             if (left > 1.0f || top > 1.0f || right < 0.0f || bottom < 0.0f) {
    357                 ALOGE("Unexpected portion:left, top, right, bottom %f %f %f %f",
    358                       left, top, right, bottom);
    359                 left = 0.0f;
    360                 top = 0.0f;
    361                 right = 1.0f;
    362                 bottom = 1.0f;
    363             }
    364             FloatRect fillPortion(left, top, right - left, bottom - top);
    365 
    366             bool success = tile->drawGL(opacity, rect, m_scale, transform,
    367                                         forceBaseBlending, usePointSampling, fillPortion);
    368             if (semiOpaqueBaseSurface && success) {
    369                 // Cut the successful drawn tile area from the missing region.
    370                 missingRegion.op(SkIRect::MakeXYWH(tile->x(), tile->y(), 1, 1),
    371                                  SkRegion::kDifference_Op);
    372             }
    373             if (tile->frontTexture())
    374                 drawn++;
    375         }
    376 
    377         // log tile information for base, high res tiles
    378         if (m_isBaseSurface && background)
    379             TilesManager::instance()->getProfiler()->nextTile(tile, invScale, tileInView);
    380     }
    381 
    382     // Draw missing Regions with blend turned on
    383     if (semiOpaqueBaseSurface)
    384         drawMissingRegion(missingRegion, opacity, background);
    385 
    386     ALOGV("TG %p drew %d tiles, scale %f",
    387           this, drawn, m_scale);
    388 }
    389 
    390 void TileGrid::drawMissingRegion(const SkRegion& region, float opacity,
    391                                      const Color* background)
    392 {
    393     SkRegion::Iterator iterator(region);
    394     const float tileWidth = TilesManager::tileWidth() / m_scale;
    395     const float tileHeight = TilesManager::tileHeight() / m_scale;
    396     while (!iterator.done()) {
    397         SkIRect r = iterator.rect();
    398         SkRect rect;
    399         rect.fLeft = r.x() * tileWidth;
    400         rect.fTop =  r.y() * tileHeight;
    401         rect.fRight = rect.fLeft + tileWidth * r.width();
    402         rect.fBottom = rect.fTop + tileHeight * r.height();
    403         ALOGV("draw tile x y, %d %d (%d %d) opacity %f", r.x(), r.y(),
    404               r.width(), r.height(), opacity);
    405         // Skia is using pre-multiplied color.
    406         Color postAlpha = Color(background->red() * background->alpha() / 255,
    407                                 background->green() * background->alpha() / 255,
    408                                 background->blue() * background->alpha() / 255,
    409                                 background->alpha() );
    410 
    411         PureColorQuadData backGroundData(postAlpha, BaseQuad, 0, &rect, opacity);
    412         TilesManager::instance()->shader()->drawQuad(&backGroundData);
    413         iterator.next();
    414     }
    415 }
    416 
    417 void TileGrid::removeTiles()
    418 {
    419     for (unsigned int i = 0; i < m_tiles.size(); i++) {
    420         delete m_tiles[i];
    421     }
    422     m_tiles.clear();
    423 }
    424 
    425 void TileGrid::discardTextures()
    426 {
    427     ALOGV("TG %p discarding textures", this);
    428     for (unsigned int i = 0; i < m_tiles.size(); i++)
    429         m_tiles[i]->discardTextures();
    430 }
    431 
    432 } // namespace WebCore
    433