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 "SurfaceCollectionManager"
     27 #define LOG_NDEBUG 1
     28 
     29 #include "config.h"
     30 #include "SurfaceCollectionManager.h"
     31 
     32 #include "AndroidLog.h"
     33 #include "private/hwui/DrawGlInfo.h"
     34 #include "TilesManager.h"
     35 #include "SurfaceCollection.h"
     36 
     37 namespace WebCore {
     38 
     39 using namespace android::uirenderer;
     40 
     41 // Tag used to display current number of SurfaceCollections.
     42 // Note: this will only work if one webview is actively drawing at a time.
     43 static const char* COLLECTION_COUNT_TAG = "CollectionCount";
     44 
     45 SurfaceCollectionManager::SurfaceCollectionManager()
     46     : m_drawingCollection(0)
     47     , m_paintingCollection(0)
     48     , m_queuedCollection(0)
     49     , m_fastSwapMode(false)
     50     , m_previouslyScrolling(false)
     51     , m_newPaintingCollection(false)
     52 {
     53 }
     54 
     55 SurfaceCollectionManager::~SurfaceCollectionManager()
     56 {
     57     clearCollections();
     58 }
     59 
     60 // the painting collection has finished painting:
     61 //   discard the drawing collection
     62 //   swap the painting collection in place of the drawing collection
     63 //   and start painting the queued collection
     64 void SurfaceCollectionManager::swap()
     65 {
     66     // swap can't be called unless painting just finished
     67     ASSERT(m_paintingCollection);
     68 
     69     ALOGV("SWAPPING, D %p, P %p, Q %p",
     70           m_drawingCollection, m_paintingCollection, m_queuedCollection);
     71 
     72     // if we have a drawing collection, discard it since the painting collection is done
     73     if (m_drawingCollection) {
     74         ALOGV("destroying drawing collection %p", m_drawingCollection);
     75         m_drawingCollection->addFrameworkInvals();
     76         m_drawingCollection->removePainterOperations();
     77         SkSafeUnref(m_drawingCollection);
     78     }
     79 
     80     // painting collection becomes the drawing collection
     81     ALOGV("drawing collection %p", m_paintingCollection);
     82     m_paintingCollection->setIsDrawing(); // initialize animations
     83     m_paintingCollection->addFrameworkInvals();
     84 
     85     if (m_queuedCollection) {
     86         // start painting with the queued collection
     87         ALOGV("now painting collection %p", m_queuedCollection);
     88         m_queuedCollection->setIsPainting(m_paintingCollection);
     89     }
     90     m_drawingCollection = m_paintingCollection;
     91     m_paintingCollection = m_queuedCollection;
     92     m_queuedCollection = 0;
     93 
     94     if (ATRACE_ENABLED()) {
     95         ATRACE_INT(COLLECTION_COUNT_TAG,
     96                    (m_drawingCollection ? 1 : 0)
     97                    + (m_paintingCollection ? 1 : 0));
     98     }
     99 
    100     ALOGV("SWAPPING COMPLETE, D %p, P %p, Q %p",
    101          m_drawingCollection, m_paintingCollection, m_queuedCollection);
    102 }
    103 
    104 // clear all of the content in the three collections held by the collection manager
    105 void SurfaceCollectionManager::clearCollections()
    106 {
    107     // remove all painting operations, since they're no longer relevant
    108     if (m_drawingCollection)
    109         m_drawingCollection->removePainterOperations();
    110     if (m_paintingCollection)
    111         m_paintingCollection->removePainterOperations();
    112 
    113     SkSafeUnref(m_drawingCollection);
    114     m_drawingCollection = 0;
    115     SkSafeUnref(m_paintingCollection);
    116     m_paintingCollection = 0;
    117     SkSafeUnref(m_queuedCollection);
    118     m_queuedCollection = 0;
    119 
    120     ATRACE_INT(COLLECTION_COUNT_TAG, 0);
    121 }
    122 
    123 void SurfaceCollectionManager::updatePaintingCollection(SurfaceCollection* newCollection)
    124 {
    125     m_paintingCollection = newCollection;
    126     m_paintingCollection->setIsPainting(m_drawingCollection);
    127     m_newPaintingCollection = true;
    128 }
    129 
    130 // a new layer collection has arrived, queue it if we're painting something already,
    131 // or start painting it if we aren't. Returns true if the manager has two collections
    132 // already queued.
    133 bool SurfaceCollectionManager::updateWithSurfaceCollection(SurfaceCollection* newCollection,
    134                                                            bool brandNew)
    135 {
    136     // can't have a queued collection unless have a painting collection too
    137     ASSERT(m_paintingCollection || !m_queuedCollection);
    138 
    139     if (!newCollection || brandNew) {
    140         clearCollections();
    141         if (brandNew) {
    142             updatePaintingCollection(newCollection);
    143             ATRACE_INT(COLLECTION_COUNT_TAG, 1);
    144         }
    145         return false;
    146     }
    147 
    148     ALOGV("updateWithSurfaceCollection - %p, has children %d, has animations %d",
    149           newCollection, newCollection->hasCompositedLayers(),
    150           newCollection->hasCompositedAnimations());
    151 
    152     if (m_queuedCollection || m_paintingCollection) {
    153         // currently painting, so defer this new collection
    154         if (m_queuedCollection) {
    155             // already have a queued collection, copy over invals so the regions are
    156             // eventually repainted and let the old queued collection be discarded
    157             m_queuedCollection->mergeInvalsInto(newCollection);
    158 
    159             if (!TilesManager::instance()->useDoubleBuffering()) {
    160                 // not double buffering, count discarded collection/webkit paint as an update
    161                 TilesManager::instance()->incContentUpdates();
    162             }
    163 
    164             ALOGV("DISCARDING collection - %p, has children %d, has animations %d",
    165                   newCollection, newCollection->hasCompositedLayers(),
    166                   newCollection->hasCompositedAnimations());
    167         }
    168         SkSafeUnref(m_queuedCollection);
    169         m_queuedCollection = newCollection;
    170     } else {
    171         // don't have painting collection, paint this one!
    172         updatePaintingCollection(newCollection);
    173     }
    174 
    175     if (ATRACE_ENABLED()) {
    176         ATRACE_INT(COLLECTION_COUNT_TAG,
    177                    (m_drawingCollection ? 1 : 0)
    178                    + (m_paintingCollection ? 1 : 0)
    179                    + (m_queuedCollection ? 1 : 0));
    180     }
    181     return m_drawingCollection && TilesManager::instance()->useDoubleBuffering();
    182 }
    183 
    184 void SurfaceCollectionManager::updateScrollableLayer(int layerId, int x, int y)
    185 {
    186     if (m_queuedCollection)
    187         m_queuedCollection->updateScrollableLayer(layerId, x, y);
    188     if (m_paintingCollection)
    189         m_paintingCollection->updateScrollableLayer(layerId, x, y);
    190     if (m_drawingCollection)
    191         m_drawingCollection->updateScrollableLayer(layerId, x, y);
    192 }
    193 
    194 
    195 int SurfaceCollectionManager::singleSurfaceModeInvalidation(bool hasRunningAnimation,
    196                                                             bool scrolling,
    197                                                             bool shouldDraw)
    198 {
    199     int returnFlags = 0;
    200     // In single surface mode, we need to dirty all the tiles when we are finishing
    201     // scrolling or have an incoming painting tree.
    202     bool requireDirtyAll = (m_previouslyScrolling && !scrolling)
    203                            || m_newPaintingCollection;
    204 
    205     // We also need to tell the framework to continue to invoke until
    206     // the base layer is ready.
    207     bool drawingBaseSurfaceReady = m_drawingCollection
    208                                    && m_drawingCollection->isReady();
    209 
    210     // When the base layer is ready, we can ask the framework to draw. And if
    211     // animation is running, dirty all the tiles, otherwise the animation will
    212     // be paused.
    213     if (drawingBaseSurfaceReady) {
    214         if (!shouldDraw)
    215             returnFlags |= DrawGlInfo::kStatusDraw;
    216         else
    217             requireDirtyAll |= hasRunningAnimation;
    218     }
    219     if (requireDirtyAll)
    220         TilesManager::instance()->dirtyAllTiles();
    221 
    222     bool requireInvoke = requireDirtyAll || !drawingBaseSurfaceReady;
    223     if (requireInvoke)
    224         returnFlags |= DrawGlInfo::kStatusInvoke;
    225 
    226     m_newPaintingCollection = false;
    227     m_previouslyScrolling = scrolling;
    228 
    229     return returnFlags;
    230 }
    231 
    232 int SurfaceCollectionManager::drawGL(double currentTime, IntRect& viewRect,
    233                             SkRect& visibleContentRect, float scale,
    234                             bool scrolling, bool singleSurfaceMode,
    235                             bool* collectionsSwappedPtr, bool* newCollectionHasAnimPtr,
    236                             TexturesResult* texturesResultPtr, bool shouldDraw)
    237 {
    238     m_fastSwapMode |= scrolling || singleSurfaceMode;
    239 
    240     ALOGV("drawGL, D %p, P %p, Q %p, fastSwap %d shouldDraw %d",
    241           m_drawingCollection, m_paintingCollection,
    242           m_queuedCollection, m_fastSwapMode, shouldDraw);
    243 
    244     // ask for kStatusInvoke while painting, kStatusDraw if we have content to be redrawn next frame
    245     // returning 0 indicates all painting complete, no framework inval needed.
    246     int returnFlags = 0;
    247 
    248     bool didCollectionSwap = false;
    249     bool tryFastBlit = !m_fastSwapMode;
    250     if (m_paintingCollection) {
    251         ALOGV("preparing painting collection %p", m_paintingCollection);
    252 
    253         m_paintingCollection->evaluateAnimations(currentTime);
    254 
    255         m_paintingCollection->prepareGL(visibleContentRect, tryFastBlit);
    256         m_paintingCollection->computeTexturesAmount(texturesResultPtr);
    257 
    258         if (!TilesManager::instance()->useDoubleBuffering() || m_paintingCollection->isReady()) {
    259             ALOGV("have painting collection %p ready, swapping!", m_paintingCollection);
    260             didCollectionSwap = true;
    261             m_fastSwapMode = false;
    262             TilesManager::instance()->incContentUpdates();
    263             if (collectionsSwappedPtr)
    264                 *collectionsSwappedPtr = true;
    265             if (newCollectionHasAnimPtr)
    266                 *newCollectionHasAnimPtr = m_paintingCollection->hasCompositedAnimations();
    267             swap();
    268             returnFlags |= uirenderer::DrawGlInfo::kStatusDraw;
    269         }
    270     } else if (m_drawingCollection) {
    271         ALOGV("preparing drawing collection %p", m_drawingCollection);
    272         m_drawingCollection->prepareGL(visibleContentRect);
    273         m_drawingCollection->computeTexturesAmount(texturesResultPtr);
    274     }
    275 
    276     if (m_paintingCollection)
    277         returnFlags |= DrawGlInfo::kStatusInvoke;
    278 
    279     if (!shouldDraw) {
    280         if (didCollectionSwap
    281             || (!m_paintingCollection
    282                 && m_drawingCollection
    283                 && m_drawingCollection->isReady())) {
    284             // either a swap just occurred, or there is no more work to be done: do a full draw
    285             m_drawingCollection->swapTiles();
    286 
    287             if (didCollectionSwap && m_paintingCollection)
    288                 m_paintingCollection->prepareGL(visibleContentRect, tryFastBlit);
    289             returnFlags |= DrawGlInfo::kStatusDraw;
    290         } else {
    291             // current collection not ready - invoke functor in process mode
    292             // until either drawing or painting collection is ready
    293             returnFlags |= DrawGlInfo::kStatusInvoke;
    294         }
    295 
    296         return returnFlags;
    297     }
    298 
    299     // ===========================================================================
    300     // Don't have a drawing collection, draw white background
    301     Color background = Color::white;
    302     bool drawBackground = true;
    303     bool hasRunningAnimation = false;
    304     if (m_drawingCollection) {
    305         bool drawingReady = didCollectionSwap || m_drawingCollection->isReady();
    306 
    307         // call the page swap callback if registration happened without more collections enqueued
    308         if (collectionsSwappedPtr && drawingReady && !m_paintingCollection)
    309             *collectionsSwappedPtr = true;
    310 
    311         if (didCollectionSwap || m_fastSwapMode || (drawingReady && !m_paintingCollection))
    312             m_drawingCollection->swapTiles();
    313 
    314         if (didCollectionSwap && m_paintingCollection)
    315             m_paintingCollection->prepareGL(visibleContentRect, tryFastBlit);
    316 
    317         if (drawingReady) {
    318             // exit fast swap mode, as content is up to date
    319             m_fastSwapMode = false;
    320         } else {
    321             // drawing isn't ready, must redraw
    322             returnFlags |= DrawGlInfo::kStatusInvoke;
    323         }
    324 
    325         hasRunningAnimation = m_drawingCollection->evaluateAnimations(currentTime);
    326 
    327         ALOGV("drawing collection %p", m_drawingCollection);
    328         background = m_drawingCollection->getBackgroundColor();
    329         drawBackground = m_drawingCollection->isMissingBackgroundContent();
    330     } else if (m_paintingCollection) {
    331         // Use paintingCollection background color while tiles are not done painting.
    332         background = m_paintingCollection->getBackgroundColor();
    333     }
    334 
    335     if (singleSurfaceMode)
    336         returnFlags |= singleSurfaceModeInvalidation(hasRunningAnimation,
    337                                                      scrolling, shouldDraw);
    338     // Start doing the actual GL drawing.
    339     if (drawBackground) {
    340         ALOGV("background is %x", background.rgb());
    341         // If background is opaque, we can safely and efficiently clear it here.
    342         // Otherwise, we have to calculate all the missing tiles and blend the background.
    343         GLUtils::clearBackgroundIfOpaque(&background);
    344     }
    345 
    346 #ifdef DEBUG
    347     ALOGV("Drawing %d / %d surfaces",
    348         m_drawingCollection ? m_drawingCollection->backedSize() : -1,
    349         m_drawingCollection ? m_drawingCollection->size() : -1);
    350 #endif
    351 
    352     if (m_drawingCollection && m_drawingCollection->drawGL(visibleContentRect))
    353         returnFlags |= DrawGlInfo::kStatusDraw;
    354 
    355     ALOGV("returnFlags %d,  m_paintingCollection %d ", returnFlags, m_paintingCollection);
    356     return returnFlags;
    357 }
    358 
    359 } // namespace WebCore
    360