OSDN Git Service

fix viewport lagging a drawGL call behind
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / android / GLWebViewState.cpp
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 #include "config.h"
27 #include "GLWebViewState.h"
28
29 #if USE(ACCELERATED_COMPOSITING)
30
31 #include "BaseLayerAndroid.h"
32 #include "ClassTracker.h"
33 #include "GLUtils.h"
34 #include "ImagesManager.h"
35 #include "LayerAndroid.h"
36 #include "ScrollableLayerAndroid.h"
37 #include "SkPath.h"
38 #include "TilesManager.h"
39 #include "TilesTracker.h"
40 #include "TreeManager.h"
41 #include <wtf/CurrentTime.h>
42
43 #include <pthread.h>
44
45 #include <cutils/log.h>
46 #include <wtf/text/CString.h>
47
48 #undef XLOGC
49 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
50
51 #ifdef DEBUG
52
53 #undef XLOG
54 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
55
56 #else
57
58 #undef XLOG
59 #define XLOG(...)
60
61 #endif // DEBUG
62
63 #define FIRST_TILED_PAGE_ID 1
64 #define SECOND_TILED_PAGE_ID 2
65
66 #define FRAMERATE_CAP 0.01666 // We cap at 60 fps
67
68 // log warnings if scale goes outside this range
69 #define MIN_SCALE_WARNING 0.1
70 #define MAX_SCALE_WARNING 10
71
72 namespace WebCore {
73
74 using namespace android;
75
76 GLWebViewState::GLWebViewState()
77     : m_zoomManager(this)
78     , m_currentPictureCounter(0)
79     , m_usePageA(true)
80     , m_frameworkInval(0, 0, 0, 0)
81     , m_frameworkLayersInval(0, 0, 0, 0)
82     , m_isScrolling(false)
83     , m_goingDown(true)
84     , m_goingLeft(false)
85     , m_expandedTileBoundsX(0)
86     , m_expandedTileBoundsY(0)
87     , m_highEndGfx(false)
88     , m_scale(1)
89     , m_layersRenderingMode(kAllTextures)
90 {
91     m_viewport.setEmpty();
92     m_futureViewportTileBounds.setEmpty();
93     m_viewportTileBounds.setEmpty();
94     m_preZoomBounds.setEmpty();
95
96     m_tiledPageA = new TiledPage(FIRST_TILED_PAGE_ID, this);
97     m_tiledPageB = new TiledPage(SECOND_TILED_PAGE_ID, this);
98
99 #ifdef DEBUG_COUNT
100     ClassTracker::instance()->increment("GLWebViewState");
101 #endif
102 #ifdef MEASURES_PERF
103     m_timeCounter = 0;
104     m_totalTimeCounter = 0;
105     m_measurePerfs = false;
106 #endif
107 }
108
109 GLWebViewState::~GLWebViewState()
110 {
111     // Take care of the transfer queue such that Tex Gen thread will not stuck
112     TilesManager::instance()->unregisterGLWebViewState(this);
113
114     // We have to destroy the two tiled pages first as their destructor
115     // may depend on the existence of this GLWebViewState and some of its
116     // instance variables in order to complete.
117     // Explicitely, currently we need to have the m_paintingBaseLayer around
118     // in order to complete any pending paint operations (the tiled pages
119     // will remove any pending operations, and wait if one is underway).
120     delete m_tiledPageA;
121     delete m_tiledPageB;
122 #ifdef DEBUG_COUNT
123     ClassTracker::instance()->decrement("GLWebViewState");
124 #endif
125
126 }
127
128 void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
129                                   bool showVisualIndicator, bool isPictureAfterFirstLayout)
130 {
131     if (!layer || isPictureAfterFirstLayout) {
132         // TODO: move this into TreeManager
133         m_tiledPageA->discardTextures();
134         m_tiledPageB->discardTextures();
135     }
136     if (layer) {
137         XLOG("new base layer %p, (inval region empty %d) with child %p", layer, inval.isEmpty(), layer->getChild(0));
138         layer->setState(this);
139         layer->markAsDirty(inval); // TODO: set in webview.cpp
140     }
141     m_treeManager.updateWithTree(layer, isPictureAfterFirstLayout);
142     m_glExtras.setDrawExtra(0);
143
144 #ifdef MEASURES_PERF
145     if (m_measurePerfs && !showVisualIndicator)
146         dumpMeasures();
147     m_measurePerfs = showVisualIndicator;
148 #endif
149
150     TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
151 }
152
153 void GLWebViewState::scrollLayer(int layerId, int x, int y)
154 {
155     m_treeManager.updateScrollableLayer(layerId, x, y);
156
157     // TODO: only inval the area of the scrolled layer instead of
158     // doing a fullInval()
159     if (m_layersRenderingMode == kSingleSurfaceRendering)
160         fullInval();
161 }
162
163 void GLWebViewState::invalRegion(const SkRegion& region)
164 {
165     if (m_layersRenderingMode == kSingleSurfaceRendering) {
166         // TODO: do the union of both layers tree to compute
167         //the minimum inval instead of doing a fullInval()
168         fullInval();
169         return;
170     }
171     SkRegion::Iterator iterator(region);
172     while (!iterator.done()) {
173         SkIRect r = iterator.rect();
174         IntRect ir(r.fLeft, r.fTop, r.width(), r.height());
175         inval(ir);
176         iterator.next();
177     }
178 }
179
180 void GLWebViewState::inval(const IntRect& rect)
181 {
182     m_currentPictureCounter++;
183     if (!rect.isEmpty()) {
184         // find which tiles fall within the invalRect and mark them as dirty
185         m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
186         m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
187         if (m_frameworkInval.isEmpty())
188             m_frameworkInval = rect;
189         else
190             m_frameworkInval.unite(rect);
191         XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
192              m_frameworkInval.y(), m_frameworkInval.width(), m_frameworkInval.height(),
193              rect.x(), rect.y(), rect.width(), rect.height());
194     }
195     TilesManager::instance()->getProfiler()->nextInval(rect, zoomManager()->currentScale());
196 }
197
198 unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
199 {
200     m_treeManager.drawCanvas(canvas, m_layersRenderingMode == kSingleSurfaceRendering);
201     return m_currentPictureCounter;
202 }
203
204 TiledPage* GLWebViewState::sibling(TiledPage* page)
205 {
206     return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA;
207 }
208
209 TiledPage* GLWebViewState::frontPage()
210 {
211     android::Mutex::Autolock lock(m_tiledPageLock);
212     return m_usePageA ? m_tiledPageA : m_tiledPageB;
213 }
214
215 TiledPage* GLWebViewState::backPage()
216 {
217     android::Mutex::Autolock lock(m_tiledPageLock);
218     return m_usePageA ? m_tiledPageB : m_tiledPageA;
219 }
220
221 void GLWebViewState::swapPages()
222 {
223     android::Mutex::Autolock lock(m_tiledPageLock);
224     m_usePageA ^= true;
225     TiledPage* oldPage = m_usePageA ? m_tiledPageB : m_tiledPageA;
226     zoomManager()->swapPages();
227     oldPage->discardTextures();
228 }
229
230 int GLWebViewState::baseContentWidth()
231 {
232     return m_treeManager.baseContentWidth();
233 }
234 int GLWebViewState::baseContentHeight()
235 {
236     return m_treeManager.baseContentHeight();
237 }
238
239 void GLWebViewState::setViewport(SkRect& viewport, float scale)
240 {
241     if ((m_viewport == viewport) &&
242         (zoomManager()->futureScale() == scale))
243         return;
244
245     m_goingDown = m_viewport.fTop - viewport.fTop <= 0;
246     m_goingLeft = m_viewport.fLeft - viewport.fLeft >= 0;
247     m_viewport = viewport;
248
249     XLOG("New VIEWPORT %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f currentScale: %.2f futureScale: %.2f)",
250          m_viewport.fLeft, m_viewport.fTop, m_viewport.fRight, m_viewport.fBottom,
251          m_viewport.width(), m_viewport.height(), scale,
252          zoomManager()->currentScale(), zoomManager()->futureScale());
253
254     const float invTileContentWidth = scale / TilesManager::tileWidth();
255     const float invTileContentHeight = scale / TilesManager::tileHeight();
256
257     m_viewportTileBounds.set(
258             static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)),
259             static_cast<int>(floorf(viewport.fTop * invTileContentHeight)),
260             static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)),
261             static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight)));
262
263     // allocate max possible number of tiles visible with this viewport
264     int viewMaxTileX = static_cast<int>(ceilf((viewport.width()-1) * invTileContentWidth)) + 1;
265     int viewMaxTileY = static_cast<int>(ceilf((viewport.height()-1) * invTileContentHeight)) + 1;
266
267     int maxTextureCount = (viewMaxTileX + m_expandedTileBoundsX * 2) *
268         (viewMaxTileY + m_expandedTileBoundsY * 2) * (m_highEndGfx ? 4 : 2);
269
270     TilesManager::instance()->setMaxTextureCount(maxTextureCount);
271     m_tiledPageA->updateBaseTileSize();
272     m_tiledPageB->updateBaseTileSize();
273 }
274
275 #ifdef MEASURES_PERF
276 void GLWebViewState::dumpMeasures()
277 {
278     for (int i = 0; i < m_timeCounter; i++) {
279         XLOGC("%d delay: %d ms", m_totalTimeCounter + i,
280              static_cast<int>(m_delayTimes[i]*1000));
281         m_delayTimes[i] = 0;
282     }
283     m_totalTimeCounter += m_timeCounter;
284     m_timeCounter = 0;
285 }
286 #endif // MEASURES_PERF
287
288 void GLWebViewState::resetFrameworkInval()
289 {
290     m_frameworkInval.setX(0);
291     m_frameworkInval.setY(0);
292     m_frameworkInval.setWidth(0);
293     m_frameworkInval.setHeight(0);
294 }
295
296 void GLWebViewState::addDirtyArea(const IntRect& rect)
297 {
298     if (rect.isEmpty())
299         return;
300
301     IntRect inflatedRect = rect;
302     inflatedRect.inflate(8);
303     if (m_frameworkLayersInval.isEmpty())
304         m_frameworkLayersInval = inflatedRect;
305     else
306         m_frameworkLayersInval.unite(inflatedRect);
307 }
308
309 void GLWebViewState::resetLayersDirtyArea()
310 {
311     m_frameworkLayersInval.setX(0);
312     m_frameworkLayersInval.setY(0);
313     m_frameworkLayersInval.setWidth(0);
314     m_frameworkLayersInval.setHeight(0);
315 }
316
317 void GLWebViewState::drawBackground(Color& backgroundColor)
318 {
319     if (TilesManager::instance()->invertedScreen()) {
320         float color = 1.0 - ((((float) backgroundColor.red() / 255.0) +
321                       ((float) backgroundColor.green() / 255.0) +
322                       ((float) backgroundColor.blue() / 255.0)) / 3.0);
323         glClearColor(color, color, color, 1);
324     } else {
325         glClearColor((float)backgroundColor.red() / 255.0,
326                      (float)backgroundColor.green() / 255.0,
327                      (float)backgroundColor.blue() / 255.0, 1);
328     }
329     glClear(GL_COLOR_BUFFER_BIT);
330 }
331
332 double GLWebViewState::setupDrawing(IntRect& viewRect, SkRect& visibleRect,
333                                     IntRect& webViewRect, int titleBarHeight,
334                                     IntRect& screenClip, float scale)
335 {
336     int left = viewRect.x();
337     int top = viewRect.y();
338     int width = viewRect.width();
339     int height = viewRect.height();
340
341     ShaderProgram* shader = TilesManager::instance()->shader();
342     if (shader->program() == -1) {
343         XLOG("Reinit shader");
344         shader->init();
345     }
346     shader->setViewport(visibleRect, scale);
347     shader->setViewRect(viewRect);
348     shader->setWebViewRect(webViewRect);
349     shader->setTitleBarHeight(titleBarHeight);
350     shader->setScreenClip(screenClip);
351     shader->resetBlending();
352
353     shader->calculateAnimationDelta();
354
355     glViewport(left + shader->getAnimationDeltaX(),
356                top - shader->getAnimationDeltaY(),
357                width, height);
358
359     double currentTime = WTF::currentTime();
360
361     setViewport(visibleRect, scale);
362     m_zoomManager.processNewScale(currentTime, scale);
363
364     return currentTime;
365 }
366
367 bool GLWebViewState::setLayersRenderingMode(TexturesResult& nbTexturesNeeded)
368 {
369     bool invalBase = false;
370
371     if (!nbTexturesNeeded.full)
372         TilesManager::instance()->setMaxLayerTextureCount(0);
373     else
374         TilesManager::instance()->setMaxLayerTextureCount((2*nbTexturesNeeded.full)+1);
375
376     int maxTextures = TilesManager::instance()->maxLayerTextureCount();
377     LayersRenderingMode layersRenderingMode = m_layersRenderingMode;
378
379     m_layersRenderingMode = kSingleSurfaceRendering;
380     if (nbTexturesNeeded.fixed < maxTextures)
381         m_layersRenderingMode = kFixedLayers;
382     if (nbTexturesNeeded.scrollable < maxTextures)
383         m_layersRenderingMode = kScrollableAndFixedLayers;
384     if (nbTexturesNeeded.clipped < maxTextures)
385         m_layersRenderingMode = kClippedTextures;
386     if (nbTexturesNeeded.full < maxTextures)
387         m_layersRenderingMode = kAllTextures;
388
389     if (!maxTextures && !nbTexturesNeeded.full)
390         m_layersRenderingMode = kAllTextures;
391
392     if (m_layersRenderingMode < layersRenderingMode
393         && m_layersRenderingMode != kAllTextures)
394         invalBase = true;
395
396     if (m_layersRenderingMode > layersRenderingMode
397         && m_layersRenderingMode != kClippedTextures)
398         invalBase = true;
399
400 #ifdef DEBUG
401     if (m_layersRenderingMode != layersRenderingMode) {
402         char* mode[] = { "kAllTextures", "kClippedTextures",
403             "kScrollableAndFixedLayers", "kFixedLayers", "kSingleSurfaceRendering" };
404         XLOGC("Change from mode %s to %s -- We need textures: fixed: %d,"
405               " scrollable: %d, clipped: %d, full: %d, max textures: %d",
406               static_cast<char*>(mode[layersRenderingMode]),
407               static_cast<char*>(mode[m_layersRenderingMode]),
408               nbTexturesNeeded.fixed,
409               nbTexturesNeeded.scrollable,
410               nbTexturesNeeded.clipped,
411               nbTexturesNeeded.full, maxTextures);
412     }
413 #endif
414
415     // For now, anything below kClippedTextures is equivalent
416     // to kSingleSurfaceRendering
417     // TODO: implement the other rendering modes
418     if (m_layersRenderingMode > kClippedTextures)
419         m_layersRenderingMode = kSingleSurfaceRendering;
420
421     // update the base surface if needed
422     if (m_layersRenderingMode != layersRenderingMode
423         && invalBase) {
424         m_tiledPageA->discardTextures();
425         m_tiledPageB->discardTextures();
426         fullInval();
427         return true;
428     }
429     return false;
430 }
431
432 void GLWebViewState::fullInval()
433 {
434     // TODO -- use base layer's size.
435     IntRect ir(0, 0, 1E6, 1E6);
436     inval(ir);
437 }
438
439 bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
440                             IntRect& webViewRect, int titleBarHeight,
441                             IntRect& clip, float scale,
442                             bool* treesSwappedPtr, bool* newTreeHasAnimPtr)
443 {
444     m_scale = scale;
445     TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft,
446                                                        viewport.fTop,
447                                                        viewport.fRight,
448                                                        viewport.fBottom,
449                                                        scale);
450     TilesManager::instance()->incDrawGLCount();
451
452 #ifdef DEBUG
453     TilesManager::instance()->getTilesTracker()->clear();
454 #endif
455
456     float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO;
457     float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO;
458     bool useMinimalMemory = TilesManager::instance()->useMinimalMemory();
459     bool useHorzPrefetch = useMinimalMemory ? 0 : viewWidth < baseContentWidth();
460     bool useVertPrefetch = useMinimalMemory ? 0 : viewHeight < baseContentHeight();
461     m_expandedTileBoundsX = (useHorzPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
462     m_expandedTileBoundsY = (useVertPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
463
464     XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
465          rect.x(), rect.y(), rect.width(), rect.height(),
466          viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);
467
468     resetLayersDirtyArea();
469
470     // when adding or removing layers, use the the paintingBaseLayer's tree so
471     // that content that moves to the base layer from a layer is synchronized
472
473     if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
474         XLOGC("WARNING, scale seems corrupted before update: %e", scale);
475
476     // Here before we draw, update the BaseTile which has updated content.
477     // Inside this function, just do GPU blits from the transfer queue into
478     // the BaseTiles' texture.
479     TilesManager::instance()->transferQueue()->updateDirtyBaseTiles();
480
481     // Upload any pending ImageTexture
482     // Return true if we still have some images to upload.
483     // TODO: upload as many textures as possible within a certain time limit
484     bool ret = ImagesManager::instance()->prepareTextures(this);
485
486     if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
487         XLOGC("WARNING, scale seems corrupted after update: %e", scale);
488
489     // gather the textures we can use
490     TilesManager::instance()->gatherLayerTextures();
491
492     double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale);
493
494
495     TexturesResult nbTexturesNeeded;
496     bool fastSwap = isScrolling() || m_layersRenderingMode == kSingleSurfaceRendering;
497     ret |= m_treeManager.drawGL(currentTime, rect, viewport,
498                                 scale, fastSwap,
499                                 treesSwappedPtr, newTreeHasAnimPtr,
500                                 &nbTexturesNeeded);
501     if (!ret)
502         resetFrameworkInval();
503
504     int nbTexturesForImages = ImagesManager::instance()->nbTextures();
505     XLOG("*** We have %d textures for images, %d full, %d clipped, total %d / %d",
506           nbTexturesForImages, nbTexturesNeeded.full, nbTexturesNeeded.clipped,
507           nbTexturesNeeded.full + nbTexturesForImages,
508           nbTexturesNeeded.clipped + nbTexturesForImages);
509     nbTexturesNeeded.full += nbTexturesForImages;
510     nbTexturesNeeded.clipped += nbTexturesForImages;
511     ret |= setLayersRenderingMode(nbTexturesNeeded);
512
513     FloatRect extrasclip(0, 0, rect.width(), rect.height());
514     TilesManager::instance()->shader()->clip(extrasclip);
515
516     m_glExtras.drawGL(webViewRect, viewport, titleBarHeight);
517
518     glBindBuffer(GL_ARRAY_BUFFER, 0);
519
520     // Clean up GL textures for video layer.
521     TilesManager::instance()->videoLayerManager()->deleteUnusedTextures();
522     ret |= TilesManager::instance()->invertedScreenSwitch();
523
524     if (ret) {
525         // ret==true && empty inval region means we've inval'd everything,
526         // but don't have new content. Keep redrawing full view (0,0,0,0)
527         // until tile generation catches up and we swap pages.
528         bool fullScreenInval = m_frameworkInval.isEmpty();
529
530         if (TilesManager::instance()->invertedScreenSwitch()) {
531             fullScreenInval = true;
532             TilesManager::instance()->setInvertedScreenSwitch(false);
533         }
534
535         if (!fullScreenInval) {
536             FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(
537                     m_frameworkInval);
538             // Inflate the invalidate rect to avoid precision lost.
539             frameworkInval.inflate(1);
540             IntRect inval(frameworkInval.x(), frameworkInval.y(),
541                     frameworkInval.width(), frameworkInval.height());
542
543             inval.unite(m_frameworkLayersInval);
544
545             invalRect->setX(inval.x());
546             invalRect->setY(inval.y());
547             invalRect->setWidth(inval.width());
548             invalRect->setHeight(inval.height());
549
550             XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
551                     inval.y(), inval.width(), inval.height());
552
553             if (!invalRect->intersects(rect)) {
554                 // invalidate is occurring offscreen, do full inval to guarantee redraw
555                 fullScreenInval = true;
556             }
557         }
558
559         if (fullScreenInval) {
560             invalRect->setX(0);
561             invalRect->setY(0);
562             invalRect->setWidth(0);
563             invalRect->setHeight(0);
564         }
565     } else {
566         resetFrameworkInval();
567     }
568
569 #ifdef MEASURES_PERF
570     if (m_measurePerfs) {
571         m_delayTimes[m_timeCounter++] = delta;
572         if (m_timeCounter >= MAX_MEASURES_PERF)
573             dumpMeasures();
574     }
575 #endif
576
577 #ifdef DEBUG
578     TilesManager::instance()->getTilesTracker()->showTrackTextures();
579     ImagesManager::instance()->showImages();
580 #endif
581
582     return ret;
583 }
584
585 } // namespace WebCore
586
587 #endif // USE(ACCELERATED_COMPOSITING)