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
61namespace WebCore {
62
63using namespace android::uirenderer;
64
65GLWebViewState::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
88GLWebViewState::~GLWebViewState()
89{
90#ifdef DEBUG_COUNT
91    ClassTracker::instance()->decrement("GLWebViewState");
92#endif
93
94}
95
96bool 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
122void GLWebViewState::scrollLayer(int layerId, int x, int y)
123{
124    m_surfaceCollectionManager.updateScrollableLayer(layerId, x, y);
125}
126
127void 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
167void 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
179void 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
192void 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
201void GLWebViewState::doFrameworkFullInval()
202{
203    m_doFrameworkFullInval = true;
204}
205
206double 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
237bool 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.
310int 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
427void 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
475void 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