Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright 2012, 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 "Surface"
     27 #define LOG_NDEBUG 1
     28 
     29 #include "config.h"
     30 #include "Surface.h"
     31 
     32 #include "AndroidLog.h"
     33 #include "BaseLayerAndroid.h"
     34 #include "ClassTracker.h"
     35 #include "LayerAndroid.h"
     36 #include "LayerContent.h"
     37 #include "GLWebViewState.h"
     38 #include "PrerenderedInval.h"
     39 #include "SkCanvas.h"
     40 #include "SurfaceBacking.h"
     41 #include "Tile.h"
     42 #include "TileTexture.h"
     43 #include "TilesManager.h"
     44 
     45 #include <wtf/text/CString.h>
     46 
     47 // Surfaces with an area larger than 2048*2048 should never be unclipped
     48 #define MAX_FULL_CONTENT_AREA 4194304
     49 
     50 namespace WebCore {
     51 
     52 Surface::Surface()
     53     : m_surfaceBacking(0)
     54     , m_needsTexture(false)
     55     , m_hasText(false)
     56 {
     57 #ifdef DEBUG_COUNT
     58     ClassTracker::instance()->increment("Surface");
     59 #endif
     60 }
     61 
     62 Surface::~Surface()
     63 {
     64     for (unsigned int i = 0; i < m_layers.size(); i++)
     65         SkSafeUnref(m_layers[i]);
     66     if (m_surfaceBacking)
     67         SkSafeUnref(m_surfaceBacking);
     68 #ifdef DEBUG_COUNT
     69     ClassTracker::instance()->decrement("Surface");
     70 #endif
     71 }
     72 
     73 bool Surface::tryUpdateSurface(Surface* oldSurface)
     74 {
     75     if (!needsTexture() || !oldSurface->needsTexture())
     76         return false;
     77 
     78     // merge surfaces based on first layer ID
     79     if (getFirstLayer()->uniqueId() != oldSurface->getFirstLayer()->uniqueId())
     80         return false;
     81 
     82     m_surfaceBacking = oldSurface->m_surfaceBacking;
     83     SkSafeRef(m_surfaceBacking);
     84 
     85     ALOGV("%p taking old SurfBack %p from surface %p, nt %d",
     86           this, m_surfaceBacking, oldSurface, oldSurface->needsTexture());
     87 
     88     if (!m_surfaceBacking) {
     89         // no SurfBack to inval, so don't worry about it.
     90         return true;
     91     }
     92 
     93     SkRegion invalRegion;
     94     bool fullInval = false;
     95     if (singleLayer() && oldSurface->singleLayer()) {
     96         // both are single matching layers, simply apply inval
     97         SkRegion* layerInval = getFirstLayer()->getInvalRegion();
     98         invalRegion = *layerInval;
     99 
    100         if (isBase()) {
    101             // the base layer paints outside it's content area to ensure the
    102             // viewport is convered, so fully invalidate all tiles if its size
    103             // changes to ensure no stale content remains
    104             LayerContent* newContent = getFirstLayer()->content();
    105             LayerContent* oldContent = oldSurface->getFirstLayer()->content();
    106             fullInval = newContent->width() != oldContent->width()
    107                 || newContent->height() != oldContent->height();
    108         }
    109     } else {
    110         fullInval = m_layers.size() != oldSurface->m_layers.size();
    111         if (!fullInval) {
    112             for (unsigned int i = 0; i < m_layers.size(); i++) {
    113                 if ((m_layers[i]->uniqueId() != oldSurface->m_layers[i]->uniqueId())
    114                     || (m_layers[i]->fullContentAreaMapped() != oldSurface->m_layers[i]->fullContentAreaMapped())) {
    115                     // layer list has changed, fully invalidate
    116                     // TODO: partially invalidate based on layer size/position
    117                     fullInval = true;
    118                     break;
    119                 } else if (!m_layers[i]->getInvalRegion()->isEmpty()) {
    120                     // merge layer inval - translate the layer's inval region into surface coordinates
    121                     // TODO: handle scale/3d transform mapping
    122                     FloatRect layerPos = m_layers[i]->fullContentAreaMapped();
    123                     m_layers[i]->getInvalRegion()->translate(layerPos.x(), layerPos.y());
    124                     invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op);
    125                 }
    126             }
    127         }
    128     }
    129 
    130     if (fullInval)
    131         invalRegion.setRect(-1e8, -1e8, 2e8, 2e8);
    132 
    133     m_surfaceBacking->markAsDirty(invalRegion);
    134     return true;
    135 }
    136 
    137 void Surface::addLayer(LayerAndroid* layer, const TransformationMatrix& transform)
    138 {
    139     m_layers.append(layer);
    140     SkSafeRef(layer);
    141 
    142     m_needsTexture |= layer->needsTexture();
    143     m_hasText |= layer->hasText();
    144 
    145     // add this layer's size to the surface's area
    146     // TODO: handle scale/3d transform mapping
    147     IntRect rect = enclosingIntRect(layer->fullContentAreaMapped());
    148 
    149     if (layer->needsTexture()) {
    150         if (m_fullContentArea.isEmpty()) {
    151             m_drawTransform = transform;
    152             m_drawTransform.translate3d(-rect.x(), -rect.y(), 0);
    153             m_fullContentArea = rect;
    154         } else
    155             m_fullContentArea.unite(rect);
    156         ALOGV("Surf %p adding LA %p, size " INT_RECT_FORMAT
    157               " now fullContentArea " INT_RECT_FORMAT,
    158               this, layer, INT_RECT_ARGS(rect), INT_RECT_ARGS(m_fullContentArea));
    159     }
    160 
    161     if (isBase())
    162         m_background = static_cast<BaseLayerAndroid*>(layer)->getBackgroundColor();
    163 }
    164 
    165 IntRect Surface::visibleContentArea(bool force3dContentVisible) const
    166 {
    167     if (singleLayer())
    168         return getFirstLayer()->visibleContentArea(force3dContentVisible);
    169 
    170     IntRect rect = m_fullContentArea;
    171 
    172     // clip with the viewport in content coordinate
    173     IntRect contentViewport(TilesManager::instance()->shader()->contentViewport());
    174     rect.intersect(contentViewport);
    175 
    176     // TODO: handle recursive layer clip
    177 
    178     return rect;
    179 }
    180 
    181 IntRect Surface::fullContentArea()
    182 {
    183     if (singleLayer())
    184         return getFirstLayer()->fullContentArea();
    185     return m_fullContentArea;
    186 }
    187 
    188 bool Surface::useAggressiveRendering()
    189 {
    190     // When the background is semi-opaque, 0 < alpha < 255, we had to turn off
    191     // low res to avoid artifacts from double drawing.
    192     // TODO: avoid double drawing for low res tiles.
    193     return isBase()
    194            && (!m_background.alpha()
    195            || !m_background.hasAlpha());
    196 }
    197 
    198 void Surface::prepareGL(bool layerTilesDisabled, bool updateWithBlit)
    199 {
    200     bool tilesDisabled = layerTilesDisabled && !isBase();
    201     if (!m_surfaceBacking) {
    202         ALOGV("prepareGL on Surf %p, no SurfBack, needsTexture? %d",
    203               this, m_surfaceBacking, needsTexture());
    204 
    205         if (needsTexture() || (isBase() && layerTilesDisabled))
    206             m_surfaceBacking = new SurfaceBacking(isBase());
    207         else
    208             return;
    209     }
    210 
    211     if (tilesDisabled) {
    212         m_surfaceBacking->discardTextures();
    213     } else {
    214         bool allowZoom = hasText(); // only allow for scale > 1 if painting vectors
    215         IntRect prepareArea = computePrepareArea();
    216         IntRect fullArea = fullContentArea();
    217 
    218         ALOGV("prepareGL on Surf %p with SurfBack %p, %d layers, first layer %s (%d) "
    219               "prepareArea(%d, %d - %d x %d) fullArea(%d, %d - %d x %d)",
    220               this, m_surfaceBacking, m_layers.size(),
    221               getFirstLayer()->subclassName().ascii().data(),
    222               getFirstLayer()->uniqueId(),
    223               prepareArea.x(), prepareArea.y(), prepareArea.width(), prepareArea.height(),
    224               fullArea.x(), fullArea.y(), fullArea.width(), fullArea.height());
    225 
    226         m_surfaceBacking->prepareGL(getFirstLayer()->state(), allowZoom,
    227                                     prepareArea, fullArea,
    228                                     this, useAggressiveRendering(), updateWithBlit);
    229     }
    230     for (size_t i = 0; i < m_layers.size(); i++) {
    231         LayerContent* content = m_layers[i]->content();
    232         if (content)
    233             content->clearPrerenders();
    234     }
    235 }
    236 
    237 bool Surface::drawGL(bool layerTilesDisabled)
    238 {
    239     bool tilesDisabled = layerTilesDisabled && !isBase();
    240     if (singleLayer() && !getFirstLayer()->visible())
    241         return false;
    242 
    243     if (!isBase()) {
    244         FloatRect drawClip = getFirstLayer()->drawClip();
    245         if (!singleLayer()) {
    246             for (unsigned int i = 1; i < m_layers.size(); i++)
    247                 drawClip.unite(m_layers[i]->drawClip());
    248         }
    249         FloatRect clippingRect = TilesManager::instance()->shader()->rectInInvViewCoord(drawClip);
    250         TilesManager::instance()->shader()->clip(clippingRect);
    251     }
    252 
    253     bool askRedraw = false;
    254     if (m_surfaceBacking && !tilesDisabled) {
    255         ALOGV("drawGL on Surf %p with SurfBack %p, first layer %s (%d)", this, m_surfaceBacking,
    256               getFirstLayer()->subclassName().ascii().data(), getFirstLayer()->uniqueId());
    257 
    258         bool force3dContentVisible = true;
    259         IntRect drawArea = visibleContentArea(force3dContentVisible);
    260         m_surfaceBacking->drawGL(drawArea, opacity(), drawTransform(),
    261                                  useAggressiveRendering(), background());
    262     }
    263 
    264     // draw member layers (draws image textures, glextras)
    265     for (unsigned int i = 0; i < m_layers.size(); i++) {
    266         if (m_layers[i]->drawGL(tilesDisabled)) {
    267            m_layers[i]->addDirtyArea();
    268            askRedraw = true;
    269         }
    270     }
    271 
    272     return askRedraw;
    273 }
    274 
    275 void Surface::swapTiles(bool calculateFrameworkInvals)
    276 {
    277     if (!m_surfaceBacking)
    278         return;
    279 
    280     if (m_surfaceBacking->swapTiles() && calculateFrameworkInvals)
    281         addFrameworkInvals();
    282 }
    283 
    284 void Surface::addFrameworkInvals()
    285 {
    286     // Let's return an inval area to framework that will
    287     // contain all of our layers' areas
    288     for (unsigned int i = 0; i < m_layers.size(); i++)
    289         m_layers[i]->addDirtyArea();
    290 }
    291 
    292 bool Surface::isReady()
    293 {
    294     if (!m_surfaceBacking)
    295         return true;
    296 
    297     return m_surfaceBacking->isReady();
    298 }
    299 
    300 bool Surface::isMissingContent()
    301 {
    302     if (!m_surfaceBacking)
    303         return true;
    304 
    305     return m_surfaceBacking->isMissingContent();
    306 }
    307 
    308 bool Surface::canUpdateWithBlit()
    309 {
    310     // If we don't have a texture, we have nothing to update and thus can take
    311     // the fast path
    312     if (!needsTexture())
    313         return true;
    314     // If we have a surface backing that isn't ready, we can't update with a blit
    315     // If it is ready, then check to see if it is dirty. We can only call isDirty()
    316     // if isReady() returns true
    317     if (!m_surfaceBacking)
    318         return false;
    319     if (!m_surfaceBacking->isReady())
    320         return false;
    321     if (!m_surfaceBacking->isDirty())
    322         return true;
    323     if (!singleLayer())
    324         return false;
    325     return getFirstLayer()->canUpdateWithBlit();
    326 }
    327 
    328 IntRect Surface::computePrepareArea()
    329 {
    330     IntRect area;
    331 
    332     if (!getFirstLayer()->contentIsScrollable()
    333         && !isBase()
    334         && getFirstLayer()->state()->layersRenderingMode() == GLWebViewState::kAllTextures) {
    335 
    336         area = fullContentArea();
    337 
    338         double total = ((double) area.width()) * ((double) area.height());
    339         if (total > MAX_FULL_CONTENT_AREA)
    340             area = visibleContentArea();
    341     } else
    342         area = visibleContentArea();
    343 
    344     return area;
    345 }
    346 
    347 void Surface::computeTexturesAmount(TexturesResult* result)
    348 {
    349     if (!m_surfaceBacking || isBase())
    350         return;
    351 
    352 
    353     LayerAndroid* layer = 0;
    354     if (singleLayer())
    355         layer = getFirstLayer();
    356 
    357     m_surfaceBacking->computeTexturesAmount(result, visibleContentArea(),
    358                                             fullContentArea(), layer);
    359 }
    360 
    361 bool Surface::isBase()
    362 {
    363     // base layer surface
    364     // - doesn't use layer tiles (disables blending, doesn't compute textures amount)
    365     // - ignores clip rects
    366     // - only prepares clippedArea
    367     return getFirstLayer()->subclassType() == LayerAndroid::BaseLayer;
    368 }
    369 
    370 bool Surface::paint(SkCanvas* canvas)
    371 {
    372     if (singleLayer()) {
    373         getFirstLayer()->contentDraw(canvas, Layer::UnmergedLayers);
    374 
    375         // TODO: double buffer by disabling SurfaceCollection swaps and position
    376         // updates until painting complete
    377 
    378         // In single surface mode, draw layer content onto the base layer
    379         if (isBase()
    380             && getFirstLayer()->countChildren()
    381             && getFirstLayer()->state()->isSingleSurfaceRenderingMode()) {
    382             for (int i = 0; i < getFirstLayer()->countChildren(); i++)
    383                 getFirstLayer()->getChild(i)->drawCanvas(canvas, true, Layer::FlattenedLayers);
    384         }
    385     } else {
    386         SkAutoCanvasRestore acr(canvas, true);
    387         SkMatrix matrix;
    388         GLUtils::toSkMatrix(matrix, m_drawTransform);
    389 
    390         SkMatrix inverse;
    391         inverse.reset();
    392         matrix.invert(&inverse);
    393 
    394         SkMatrix canvasMatrix = canvas->getTotalMatrix();
    395         inverse.postConcat(canvasMatrix);
    396         canvas->setMatrix(inverse);
    397 
    398         for (unsigned int i=0; i<m_layers.size(); i++)
    399             m_layers[i]->drawCanvas(canvas, false, Layer::MergedLayers);
    400     }
    401     return true;
    402 }
    403 
    404 float Surface::opacity()
    405 {
    406     if (singleLayer())
    407         return getFirstLayer()->drawOpacity();
    408     return 1.0;
    409 }
    410 
    411 Color* Surface::background()
    412 {
    413     if (!isBase() || !m_background.isValid())
    414         return 0;
    415     return &m_background;
    416 }
    417 
    418 bool Surface::blitFromContents(Tile* tile)
    419 {
    420     if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content())
    421         return false;
    422 
    423     if (tile->frontTexture() != tile->lastDrawnTexture()) {
    424         // the below works around an issue where glTexSubImage2d can't update a
    425         // texture that hasn't drawn yet by drawing it off screen.
    426         // glFlush() and glFinish() work also, but are likely more wasteful.
    427         SkRect rect = SkRect::MakeXYWH(-100, -100, 0, 0);
    428         FloatRect fillPortion(0, 0, 0, 0);
    429         tile->frontTexture()->drawGL(false, rect, 1.0f, 0, false, true, fillPortion);
    430     }
    431     LayerContent* content = getFirstLayer()->content();
    432     // Extract the dirty rect from the region. Note that this is *NOT* constrained
    433     // to this tile
    434     IntRect dirtyRect = tile->dirtyArea().getBounds();
    435     IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(),
    436                                tile->y() * TilesManager::tileHeight(),
    437                                TilesManager::tileWidth(),
    438                                TilesManager::tileHeight());
    439     FloatRect tileRectInDoc = tileRect;
    440     tileRectInDoc.scale(1 / tile->scale());
    441     dirtyRect.intersect(enclosingIntRect(tileRectInDoc));
    442     PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect);
    443     if (!prerenderedInval || prerenderedInval->bitmap.isNull())
    444         return false;
    445     SkBitmap sourceBitmap = prerenderedInval->bitmap;
    446     // Calculate the screen rect that is dirty, then intersect it with the
    447     // tile's screen rect so that we end up with the pixels we need to blit
    448     FloatRect screenDirty = dirtyRect;
    449     screenDirty.scale(tile->scale());
    450     IntRect enclosingScreenDirty = enclosingIntRect(screenDirty);
    451     enclosingScreenDirty.intersect(tileRect);
    452     if (enclosingScreenDirty.isEmpty())
    453         return false;
    454     // Make sure the screen area we want to blit is contained by the
    455     // prerendered screen area
    456     if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) {
    457         ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain "
    458                 "enclosingScreenDirty " INT_RECT_FORMAT,
    459                 INT_RECT_ARGS(prerenderedInval->screenArea),
    460                 INT_RECT_ARGS(enclosingScreenDirty));
    461         return false;
    462     }
    463     IntPoint origin = prerenderedInval->screenArea.location();
    464     SkBitmap subset;
    465     subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(),
    466             enclosingScreenDirty.height());
    467     subset.allocPixels();
    468 
    469     int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y();
    470     int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x();
    471     if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset))
    472         return false;
    473     // Now upload
    474     SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(),
    475                                              enclosingScreenDirty.y() - tileRect.y(),
    476                                              enclosingScreenDirty.width(),
    477                                              enclosingScreenDirty.height());
    478     GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId,
    479                                      subset, textureInval);
    480     tile->onBlitUpdate();
    481     return true;
    482 }
    483 
    484 const TransformationMatrix* Surface::drawTransform()
    485 {
    486     // single layer surfaces query the layer's draw transform, while multi-layer
    487     // surfaces copy the draw transform once, during initialization
    488     // TODO: support fixed multi-layer surfaces by querying the changing drawTransform
    489     if (singleLayer())
    490         return getFirstLayer()->drawTransform();
    491 
    492     return &m_drawTransform;
    493 }
    494 
    495 } // namespace WebCore
    496