Home | History | Annotate | Download | only in android
      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 "GLWebViewState"
     27 #define LOG_NDEBUG 1
     28 
     29 #include "config.h"
     30 #include "GLWebViewState.h"
     31 
     32 #if USE(ACCELERATED_COMPOSITING)
     33 
     34 #include "AndroidLog.h"
     35 #include "BaseLayerAndroid.h"
     36 #include "ClassTracker.h"
     37 #include "GLUtils.h"
     38 #include "ImagesManager.h"
     39 #include "LayerAndroid.h"
     40 #include "private/hwui/DrawGlInfo.h"
     41 #include "ScrollableLayerAndroid.h"
     42 #include "SkPath.h"
     43 #include "TilesManager.h"
     44 #include "TransferQueue.h"
     45 #include "SurfaceCollection.h"
     46 #include "SurfaceCollectionManager.h"
     47 #include <pthread.h>
     48 #include <wtf/CurrentTime.h>
     49 
     50 // log warnings if scale goes outside this range
     51 #define MIN_SCALE_WARNING 0.1
     52 #define MAX_SCALE_WARNING 10
     53 
     54 // fps indicator is FPS_INDICATOR_HEIGHT pixels high.
     55 // The max width is equal to MAX_FPS_VALUE fps.
     56 #define FPS_INDICATOR_HEIGHT 10
     57 #define MAX_FPS_VALUE 60
     58 
     59 #define COLLECTION_SWAPPED_COUNTER_MODULE 10
     60 
     61 namespace WebCore {
     62 
     63 using namespace android::uirenderer;
     64 
     65 GLWebViewState::GLWebViewState()
     66     : m_frameworkLayersInval(0, 0, 0, 0)
     67     , m_doFrameworkFullInval(false)
     68     , m_isScrolling(false)
     69     , m_isVisibleContentRectScrolling(false)
     70     , m_goingDown(true)
     71     , m_goingLeft(false)
     72     , m_scale(1)
     73     , m_layersRenderingMode(kAllTextures)
     74     , m_surfaceCollectionManager()
     75 {
     76     m_visibleContentRect.setEmpty();
     77 
     78 #ifdef DEBUG_COUNT
     79     ClassTracker::instance()->increment("GLWebViewState");
     80 #endif
     81 #ifdef MEASURES_PERF
     82     m_timeCounter = 0;
     83     m_totalTimeCounter = 0;
     84     m_measurePerfs = false;
     85 #endif
     86 }
     87 
     88 GLWebViewState::~GLWebViewState()
     89 {
     90 #ifdef DEBUG_COUNT
     91     ClassTracker::instance()->decrement("GLWebViewState");
     92 #endif
     93 
     94 }
     95 
     96 bool GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, bool showVisualIndicator,
     97                                   bool isPictureAfterFirstLayout)
     98 {
     99     if (!layer || isPictureAfterFirstLayout)
    100         m_layersRenderingMode = kAllTextures;
    101 
    102     SurfaceCollection* collection = 0;
    103     if (layer) {
    104         ALOGV("layer tree %p, with child %p", layer, layer->getChild(0));
    105         layer->setState(this);
    106         collection = new SurfaceCollection(layer);
    107     }
    108     bool queueFull = m_surfaceCollectionManager.updateWithSurfaceCollection(
    109         collection, isPictureAfterFirstLayout);
    110     m_glExtras.setDrawExtra(0);
    111 
    112 #ifdef MEASURES_PERF
    113     if (m_measurePerfs && !showVisualIndicator)
    114         dumpMeasures();
    115     m_measurePerfs = showVisualIndicator;
    116 #endif
    117 
    118     TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
    119     return queueFull;
    120 }
    121 
    122 void GLWebViewState::scrollLayer(int layerId, int x, int y)
    123 {
    124     m_surfaceCollectionManager.updateScrollableLayer(layerId, x, y);
    125 }
    126 
    127 void GLWebViewState::setVisibleContentRect(const SkRect& visibleContentRect, float scale)
    128 {
    129     // allocate max possible number of tiles visible with this visibleContentRect / expandedTileBounds
    130     const float invTileContentWidth = scale / TilesManager::tileWidth();
    131     const float invTileContentHeight = scale / TilesManager::tileHeight();
    132 
    133     int viewMaxTileX =
    134         static_cast<int>(ceilf((visibleContentRect.width()-1) * invTileContentWidth)) + 1;
    135     int viewMaxTileY =
    136         static_cast<int>(ceilf((visibleContentRect.height()-1) * invTileContentHeight)) + 1;
    137 
    138     TilesManager* tilesManager = TilesManager::instance();
    139     int maxTextureCount = viewMaxTileX * viewMaxTileY * (tilesManager->highEndGfx() ? 4 : 2);
    140 
    141     tilesManager->setCurrentTextureCount(maxTextureCount);
    142 
    143     // TODO: investigate whether we can move this return earlier.
    144     if ((m_visibleContentRect == visibleContentRect)
    145         && (m_scale == scale)) {
    146         // everything below will stay the same, early return.
    147         m_isVisibleContentRectScrolling = false;
    148         return;
    149     }
    150     m_scale = scale;
    151 
    152     m_goingDown = m_visibleContentRect.fTop - visibleContentRect.fTop <= 0;
    153     m_goingLeft = m_visibleContentRect.fLeft - visibleContentRect.fLeft >= 0;
    154 
    155     // detect visibleContentRect scrolling from short programmatic scrolls/jumps
    156     m_isVisibleContentRectScrolling = m_visibleContentRect != visibleContentRect
    157         && SkRect::Intersects(m_visibleContentRect, visibleContentRect);
    158     m_visibleContentRect = visibleContentRect;
    159 
    160     ALOGV("New visibleContentRect %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f )",
    161           m_visibleContentRect.fLeft, m_visibleContentRect.fTop,
    162           m_visibleContentRect.fRight, m_visibleContentRect.fBottom,
    163           m_visibleContentRect.width(), m_visibleContentRect.height(), scale);
    164 }
    165 
    166 #ifdef MEASURES_PERF
    167 void GLWebViewState::dumpMeasures()
    168 {
    169     for (int i = 0; i < m_timeCounter; i++) {
    170         ALOGD("%d delay: %d ms", m_totalTimeCounter + i,
    171              static_cast<int>(m_delayTimes[i]*1000));
    172         m_delayTimes[i] = 0;
    173     }
    174     m_totalTimeCounter += m_timeCounter;
    175     m_timeCounter = 0;
    176 }
    177 #endif // MEASURES_PERF
    178 
    179 void GLWebViewState::addDirtyArea(const IntRect& rect)
    180 {
    181     if (rect.isEmpty())
    182         return;
    183 
    184     IntRect inflatedRect = rect;
    185     inflatedRect.inflate(8);
    186     if (m_frameworkLayersInval.isEmpty())
    187         m_frameworkLayersInval = inflatedRect;
    188     else
    189         m_frameworkLayersInval.unite(inflatedRect);
    190 }
    191 
    192 void GLWebViewState::resetLayersDirtyArea()
    193 {
    194     m_frameworkLayersInval.setX(0);
    195     m_frameworkLayersInval.setY(0);
    196     m_frameworkLayersInval.setWidth(0);
    197     m_frameworkLayersInval.setHeight(0);
    198     m_doFrameworkFullInval = false;
    199 }
    200 
    201 void GLWebViewState::doFrameworkFullInval()
    202 {
    203     m_doFrameworkFullInval = true;
    204 }
    205 
    206 double GLWebViewState::setupDrawing(const IntRect& invScreenRect,
    207                                     const SkRect& visibleContentRect,
    208                                     const IntRect& screenRect, int titleBarHeight,
    209                                     const IntRect& screenClip, float scale)
    210 {
    211     TilesManager* tilesManager = TilesManager::instance();
    212 
    213     // Make sure GL resources are created on the UI thread.
    214     // They are created either for the first time, or after EGL context
    215     // recreation caused by onTrimMemory in the framework.
    216     ShaderProgram* shader = tilesManager->shader();
    217     if (shader->needsInit()) {
    218         ALOGD("Reinit shader");
    219         shader->initGLResources();
    220     }
    221     TransferQueue* transferQueue = tilesManager->transferQueue();
    222     if (transferQueue->needsInit()) {
    223         ALOGD("Reinit transferQueue");
    224         transferQueue->initGLResources(TilesManager::tileWidth(),
    225                                        TilesManager::tileHeight());
    226     }
    227     shader->setupDrawing(invScreenRect, visibleContentRect, screenRect,
    228                          titleBarHeight, screenClip, scale);
    229 
    230     double currentTime = WTF::currentTime();
    231 
    232     setVisibleContentRect(visibleContentRect, scale);
    233 
    234     return currentTime;
    235 }
    236 
    237 bool GLWebViewState::setLayersRenderingMode(TexturesResult& nbTexturesNeeded)
    238 {
    239     bool invalBase = false;
    240 
    241     if (!nbTexturesNeeded.full)
    242         TilesManager::instance()->setCurrentLayerTextureCount(0);
    243     else
    244         TilesManager::instance()->setCurrentLayerTextureCount((2 * nbTexturesNeeded.full) + 1);
    245 
    246     int maxTextures = TilesManager::instance()->currentLayerTextureCount();
    247     LayersRenderingMode layersRenderingMode = m_layersRenderingMode;
    248 
    249     if (m_layersRenderingMode == kSingleSurfaceRendering) {
    250         // only switch out of SingleSurface mode, if we have 2x needed textures
    251         // to avoid changing too often
    252         maxTextures /= 2;
    253     }
    254 
    255     m_layersRenderingMode = kSingleSurfaceRendering;
    256     if (nbTexturesNeeded.fixed < maxTextures)
    257         m_layersRenderingMode = kFixedLayers;
    258     if (nbTexturesNeeded.scrollable < maxTextures)
    259         m_layersRenderingMode = kScrollableAndFixedLayers;
    260     if (nbTexturesNeeded.clipped < maxTextures)
    261         m_layersRenderingMode = kClippedTextures;
    262     if (nbTexturesNeeded.full < maxTextures)
    263         m_layersRenderingMode = kAllTextures;
    264 
    265     if (!maxTextures && !nbTexturesNeeded.full)
    266         m_layersRenderingMode = kAllTextures;
    267 
    268     if (m_layersRenderingMode < layersRenderingMode
    269         && m_layersRenderingMode != kAllTextures)
    270         invalBase = true;
    271 
    272     if (m_layersRenderingMode > layersRenderingMode
    273         && m_layersRenderingMode != kClippedTextures)
    274         invalBase = true;
    275 
    276 #ifdef DEBUG
    277     if (m_layersRenderingMode != layersRenderingMode) {
    278         char* mode[] = { "kAllTextures", "kClippedTextures",
    279             "kScrollableAndFixedLayers", "kFixedLayers", "kSingleSurfaceRendering" };
    280         ALOGD("Change from mode %s to %s -- We need textures: fixed: %d,"
    281               " scrollable: %d, clipped: %d, full: %d, max textures: %d",
    282               static_cast<char*>(mode[layersRenderingMode]),
    283               static_cast<char*>(mode[m_layersRenderingMode]),
    284               nbTexturesNeeded.fixed,
    285               nbTexturesNeeded.scrollable,
    286               nbTexturesNeeded.clipped,
    287               nbTexturesNeeded.full, maxTextures);
    288     }
    289 #endif
    290 
    291     // For now, anything below kClippedTextures is equivalent
    292     // to kSingleSurfaceRendering
    293     // TODO: implement the other rendering modes
    294     if (m_layersRenderingMode > kClippedTextures)
    295         m_layersRenderingMode = kSingleSurfaceRendering;
    296 
    297     // update the base surface if needed
    298     // TODO: inval base layergroup when going into single surface mode
    299     return (m_layersRenderingMode != layersRenderingMode && invalBase);
    300 }
    301 
    302 // -invScreenRect is the webView's rect with inverted Y screen coordinate.
    303 // -visibleContentRect is the visible area in content coordinate.
    304 // They are both based on  webView's rect and calculated in Java side.
    305 //
    306 // -screenClip is in screen coordinate, so we need to invert the Y axis before
    307 // passing into GL functions. Clip can be smaller than the webView's rect.
    308 //
    309 // TODO: Try to decrease the number of parameters as some info is redundant.
    310 int GLWebViewState::drawGL(IntRect& invScreenRect, SkRect& visibleContentRect,
    311                            IntRect* invalRect, IntRect& screenRect, int titleBarHeight,
    312                            IntRect& screenClip, float scale,
    313                            bool* collectionsSwappedPtr, bool* newCollectionHasAnimPtr,
    314                            bool shouldDraw)
    315 {
    316     TilesManager* tilesManager = TilesManager::instance();
    317     if (shouldDraw)
    318         tilesManager->getProfiler()->nextFrame(visibleContentRect.fLeft,
    319                                                visibleContentRect.fTop,
    320                                                visibleContentRect.fRight,
    321                                                visibleContentRect.fBottom,
    322                                                scale);
    323     tilesManager->incDrawGLCount();
    324 
    325     ALOGV("drawGL, invScreenRect(%d, %d, %d, %d), visibleContentRect(%.2f, %.2f, %.2f, %.2f)",
    326           invScreenRect.x(), invScreenRect.y(), invScreenRect.width(), invScreenRect.height(),
    327           visibleContentRect.fLeft, visibleContentRect.fTop,
    328           visibleContentRect.fRight, visibleContentRect.fBottom);
    329 
    330     ALOGV("drawGL, invalRect(%d, %d, %d, %d), screenRect(%d, %d, %d, %d)"
    331           "screenClip (%d, %d, %d, %d), scale %f titleBarHeight %d",
    332           invalRect->x(), invalRect->y(), invalRect->width(), invalRect->height(),
    333           screenRect.x(), screenRect.y(), screenRect.width(), screenRect.height(),
    334           screenClip.x(), screenClip.y(), screenClip.width(), screenClip.height(), scale, titleBarHeight);
    335 
    336     m_inUnclippedDraw = shouldDraw && (screenRect == screenClip);
    337 
    338     resetLayersDirtyArea();
    339 
    340     if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
    341         ALOGW("WARNING, scale seems corrupted before update: %e", scale);
    342 
    343     tilesManager->updateTilesIfContextVerified();
    344 
    345     // gather the textures we can use, make sure this happens before any
    346     // texture preparation work.
    347     tilesManager->gatherTextures();
    348 
    349     // Upload any pending ImageTexture
    350     // Return true if we still have some images to upload.
    351     // TODO: upload as many textures as possible within a certain time limit
    352     int returnFlags = 0;
    353     if (ImagesManager::instance()->prepareTextures(this))
    354         returnFlags |= DrawGlInfo::kStatusDraw;
    355 
    356     if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
    357         ALOGW("WARNING, scale seems corrupted after update: %e", scale);
    358 
    359     double currentTime = setupDrawing(invScreenRect, visibleContentRect, screenRect,
    360                                       titleBarHeight, screenClip, scale);
    361 
    362     TexturesResult nbTexturesNeeded;
    363     bool scrolling = isScrolling();
    364     bool singleSurfaceMode = m_layersRenderingMode == kSingleSurfaceRendering;
    365     m_glExtras.setVisibleContentRect(visibleContentRect);
    366 
    367     returnFlags |= m_surfaceCollectionManager.drawGL(currentTime, invScreenRect,
    368                                                      visibleContentRect,
    369                                                      scale, scrolling,
    370                                                      singleSurfaceMode,
    371                                                      collectionsSwappedPtr,
    372                                                      newCollectionHasAnimPtr,
    373                                                      &nbTexturesNeeded, shouldDraw);
    374 
    375     int nbTexturesForImages = ImagesManager::instance()->nbTextures();
    376     ALOGV("*** We have %d textures for images, %d full, %d clipped, total %d / %d",
    377           nbTexturesForImages, nbTexturesNeeded.full, nbTexturesNeeded.clipped,
    378           nbTexturesNeeded.full + nbTexturesForImages,
    379           nbTexturesNeeded.clipped + nbTexturesForImages);
    380     nbTexturesNeeded.full += nbTexturesForImages;
    381     nbTexturesNeeded.clipped += nbTexturesForImages;
    382 
    383     if (setLayersRenderingMode(nbTexturesNeeded)) {
    384         TilesManager::instance()->dirtyAllTiles();
    385         returnFlags |= DrawGlInfo::kStatusDraw | DrawGlInfo::kStatusInvoke;
    386     }
    387 
    388     glBindBuffer(GL_ARRAY_BUFFER, 0);
    389 
    390     if (returnFlags & DrawGlInfo::kStatusDraw) {
    391         // returnFlags & kStatusDraw && empty inval region means we've inval'd everything,
    392         // but don't have new content. Keep redrawing full view (0,0,0,0)
    393         // until tile generation catches up and we swap pages.
    394         bool fullScreenInval = m_frameworkLayersInval.isEmpty() || m_doFrameworkFullInval;
    395 
    396         if (!fullScreenInval) {
    397             m_frameworkLayersInval.inflate(1);
    398 
    399             invalRect->setX(m_frameworkLayersInval.x());
    400             invalRect->setY(m_frameworkLayersInval.y());
    401             invalRect->setWidth(m_frameworkLayersInval.width());
    402             invalRect->setHeight(m_frameworkLayersInval.height());
    403 
    404             ALOGV("invalRect(%d, %d, %d, %d)", invalRect->x(),
    405                   invalRect->y(), invalRect->width(), invalRect->height());
    406 
    407             if (!invalRect->intersects(invScreenRect)) {
    408                 // invalidate is occurring offscreen, do full inval to guarantee redraw
    409                 fullScreenInval = true;
    410             }
    411         }
    412 
    413         if (fullScreenInval) {
    414             invalRect->setX(0);
    415             invalRect->setY(0);
    416             invalRect->setWidth(0);
    417             invalRect->setHeight(0);
    418         }
    419     }
    420 
    421     if (shouldDraw)
    422         showFrameInfo(invScreenRect, *collectionsSwappedPtr);
    423 
    424     return returnFlags;
    425 }
    426 
    427 void GLWebViewState::showFrameInfo(const IntRect& rect, bool collectionsSwapped)
    428 {
    429     bool showVisualIndicator = TilesManager::instance()->getShowVisualIndicator();
    430 
    431     bool drawOrDumpFrameInfo = showVisualIndicator;
    432 #ifdef MEASURES_PERF
    433     drawOrDumpFrameInfo |= m_measurePerfs;
    434 #endif
    435     if (!drawOrDumpFrameInfo)
    436         return;
    437 
    438     double currentDrawTime = WTF::currentTime();
    439     double delta = currentDrawTime - m_prevDrawTime;
    440     m_prevDrawTime = currentDrawTime;
    441 
    442 #ifdef MEASURES_PERF
    443     if (m_measurePerfs) {
    444         m_delayTimes[m_timeCounter++] = delta;
    445         if (m_timeCounter >= MAX_MEASURES_PERF)
    446             dumpMeasures();
    447     }
    448 #endif
    449 
    450     IntRect frameInfoRect = rect;
    451     frameInfoRect.setHeight(FPS_INDICATOR_HEIGHT);
    452     double ratio = (1.0 / delta) / MAX_FPS_VALUE;
    453 
    454     clearRectWithColor(frameInfoRect, 1, 1, 1, 1);
    455     frameInfoRect.setWidth(frameInfoRect.width() * ratio);
    456     clearRectWithColor(frameInfoRect, 1, 0, 0, 1);
    457 
    458     // Draw the collection swap counter as a circling progress bar.
    459     // This will basically show how fast we are updating the collection.
    460     static int swappedCounter = 0;
    461     if (collectionsSwapped)
    462         swappedCounter = (swappedCounter + 1) % COLLECTION_SWAPPED_COUNTER_MODULE;
    463 
    464     frameInfoRect = rect;
    465     frameInfoRect.setHeight(FPS_INDICATOR_HEIGHT);
    466     frameInfoRect.move(0, FPS_INDICATOR_HEIGHT);
    467 
    468     clearRectWithColor(frameInfoRect, 1, 1, 1, 1);
    469     ratio = (swappedCounter + 1.0) / COLLECTION_SWAPPED_COUNTER_MODULE;
    470 
    471     frameInfoRect.setWidth(frameInfoRect.width() * ratio);
    472     clearRectWithColor(frameInfoRect, 0, 1, 0, 1);
    473 }
    474 
    475 void GLWebViewState::clearRectWithColor(const IntRect& rect, float r, float g,
    476                                       float b, float a)
    477 {
    478     glScissor(rect.x(), rect.y(), rect.width(), rect.height());
    479     glClearColor(r, g, b, a);
    480     glClear(GL_COLOR_BUFFER_BIT);
    481 }
    482 
    483 } // namespace WebCore
    484 
    485 #endif // USE(ACCELERATED_COMPOSITING)
    486