2 * Copyright 2010, The Android Open Source Project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "GLWebViewState.h"
29 #if USE(ACCELERATED_COMPOSITING)
31 #include "BaseLayerAndroid.h"
32 #include "ClassTracker.h"
34 #include "ImagesManager.h"
35 #include "LayerAndroid.h"
36 #include "ScrollableLayerAndroid.h"
38 #include "TilesManager.h"
39 #include "TilesTracker.h"
40 #include "TreeManager.h"
41 #include <wtf/CurrentTime.h>
45 #include <cutils/log.h>
46 #include <wtf/text/CString.h>
49 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
54 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
63 #define FIRST_TILED_PAGE_ID 1
64 #define SECOND_TILED_PAGE_ID 2
66 #define FRAMERATE_CAP 0.01666 // We cap at 60 fps
68 // log warnings if scale goes outside this range
69 #define MIN_SCALE_WARNING 0.1
70 #define MAX_SCALE_WARNING 10
74 using namespace android;
76 GLWebViewState::GLWebViewState()
78 , m_currentPictureCounter(0)
80 , m_frameworkInval(0, 0, 0, 0)
81 , m_frameworkLayersInval(0, 0, 0, 0)
82 , m_isScrolling(false)
85 , m_expandedTileBoundsX(0)
86 , m_expandedTileBoundsY(0)
89 , m_layersRenderingMode(kAllTextures)
91 m_viewport.setEmpty();
92 m_futureViewportTileBounds.setEmpty();
93 m_viewportTileBounds.setEmpty();
94 m_preZoomBounds.setEmpty();
96 m_tiledPageA = new TiledPage(FIRST_TILED_PAGE_ID, this);
97 m_tiledPageB = new TiledPage(SECOND_TILED_PAGE_ID, this);
100 ClassTracker::instance()->increment("GLWebViewState");
104 m_totalTimeCounter = 0;
105 m_measurePerfs = false;
109 GLWebViewState::~GLWebViewState()
111 // Take care of the transfer queue such that Tex Gen thread will not stuck
112 TilesManager::instance()->unregisterGLWebViewState(this);
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).
123 ClassTracker::instance()->decrement("GLWebViewState");
128 void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
129 bool showVisualIndicator, bool isPictureAfterFirstLayout)
131 if (!layer || isPictureAfterFirstLayout) {
132 // TODO: move this into TreeManager
133 m_zoomManager.swapPages(); // reset zoom state
134 m_tiledPageA->discardTextures();
135 m_tiledPageB->discardTextures();
136 m_layersRenderingMode = kAllTextures;
139 XLOG("new base layer %p, (inval region empty %d) with child %p", layer, inval.isEmpty(), layer->getChild(0));
140 layer->setState(this);
141 layer->markAsDirty(inval); // TODO: set in webview.cpp
143 m_treeManager.updateWithTree(layer, isPictureAfterFirstLayout);
144 m_glExtras.setDrawExtra(0);
147 if (m_measurePerfs && !showVisualIndicator)
149 m_measurePerfs = showVisualIndicator;
152 TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
155 void GLWebViewState::scrollLayer(int layerId, int x, int y)
157 m_treeManager.updateScrollableLayer(layerId, x, y);
159 // TODO: only inval the area of the scrolled layer instead of
160 // doing a fullInval()
161 if (m_layersRenderingMode == kSingleSurfaceRendering)
165 void GLWebViewState::invalRegion(const SkRegion& region)
167 if (m_layersRenderingMode == kSingleSurfaceRendering) {
168 // TODO: do the union of both layers tree to compute
169 //the minimum inval instead of doing a fullInval()
173 SkRegion::Iterator iterator(region);
174 while (!iterator.done()) {
175 SkIRect r = iterator.rect();
176 IntRect ir(r.fLeft, r.fTop, r.width(), r.height());
182 void GLWebViewState::inval(const IntRect& rect)
184 m_currentPictureCounter++;
185 if (!rect.isEmpty()) {
186 // find which tiles fall within the invalRect and mark them as dirty
187 m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
188 m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
189 if (m_frameworkInval.isEmpty())
190 m_frameworkInval = rect;
192 m_frameworkInval.unite(rect);
193 XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
194 m_frameworkInval.y(), m_frameworkInval.width(), m_frameworkInval.height(),
195 rect.x(), rect.y(), rect.width(), rect.height());
197 TilesManager::instance()->getProfiler()->nextInval(rect, zoomManager()->currentScale());
200 unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
202 m_treeManager.drawCanvas(canvas, m_layersRenderingMode == kSingleSurfaceRendering);
203 return m_currentPictureCounter;
206 TiledPage* GLWebViewState::sibling(TiledPage* page)
208 return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA;
211 TiledPage* GLWebViewState::frontPage()
213 android::Mutex::Autolock lock(m_tiledPageLock);
214 return m_usePageA ? m_tiledPageA : m_tiledPageB;
217 TiledPage* GLWebViewState::backPage()
219 android::Mutex::Autolock lock(m_tiledPageLock);
220 return m_usePageA ? m_tiledPageB : m_tiledPageA;
223 void GLWebViewState::swapPages()
225 android::Mutex::Autolock lock(m_tiledPageLock);
227 TiledPage* oldPage = m_usePageA ? m_tiledPageB : m_tiledPageA;
228 zoomManager()->swapPages();
229 oldPage->discardTextures();
232 int GLWebViewState::baseContentWidth()
234 return m_treeManager.baseContentWidth();
236 int GLWebViewState::baseContentHeight()
238 return m_treeManager.baseContentHeight();
241 void GLWebViewState::setViewport(SkRect& viewport, float scale)
243 if ((m_viewport == viewport) &&
244 (zoomManager()->futureScale() == scale))
247 m_goingDown = m_viewport.fTop - viewport.fTop <= 0;
248 m_goingLeft = m_viewport.fLeft - viewport.fLeft >= 0;
249 m_viewport = viewport;
251 XLOG("New VIEWPORT %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f currentScale: %.2f futureScale: %.2f)",
252 m_viewport.fLeft, m_viewport.fTop, m_viewport.fRight, m_viewport.fBottom,
253 m_viewport.width(), m_viewport.height(), scale,
254 zoomManager()->currentScale(), zoomManager()->futureScale());
256 const float invTileContentWidth = scale / TilesManager::tileWidth();
257 const float invTileContentHeight = scale / TilesManager::tileHeight();
259 m_viewportTileBounds.set(
260 static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)),
261 static_cast<int>(floorf(viewport.fTop * invTileContentHeight)),
262 static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)),
263 static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight)));
265 // allocate max possible number of tiles visible with this viewport
266 int viewMaxTileX = static_cast<int>(ceilf((viewport.width()-1) * invTileContentWidth)) + 1;
267 int viewMaxTileY = static_cast<int>(ceilf((viewport.height()-1) * invTileContentHeight)) + 1;
269 int maxTextureCount = (viewMaxTileX + m_expandedTileBoundsX * 2) *
270 (viewMaxTileY + m_expandedTileBoundsY * 2) * (m_highEndGfx ? 4 : 2);
272 TilesManager::instance()->setMaxTextureCount(maxTextureCount);
273 m_tiledPageA->updateBaseTileSize();
274 m_tiledPageB->updateBaseTileSize();
278 void GLWebViewState::dumpMeasures()
280 for (int i = 0; i < m_timeCounter; i++) {
281 XLOGC("%d delay: %d ms", m_totalTimeCounter + i,
282 static_cast<int>(m_delayTimes[i]*1000));
285 m_totalTimeCounter += m_timeCounter;
288 #endif // MEASURES_PERF
290 void GLWebViewState::resetFrameworkInval()
292 m_frameworkInval.setX(0);
293 m_frameworkInval.setY(0);
294 m_frameworkInval.setWidth(0);
295 m_frameworkInval.setHeight(0);
298 void GLWebViewState::addDirtyArea(const IntRect& rect)
303 IntRect inflatedRect = rect;
304 inflatedRect.inflate(8);
305 if (m_frameworkLayersInval.isEmpty())
306 m_frameworkLayersInval = inflatedRect;
308 m_frameworkLayersInval.unite(inflatedRect);
311 void GLWebViewState::resetLayersDirtyArea()
313 m_frameworkLayersInval.setX(0);
314 m_frameworkLayersInval.setY(0);
315 m_frameworkLayersInval.setWidth(0);
316 m_frameworkLayersInval.setHeight(0);
319 void GLWebViewState::drawBackground(Color& backgroundColor)
321 if (TilesManager::instance()->invertedScreen()) {
322 float color = 1.0 - ((((float) backgroundColor.red() / 255.0) +
323 ((float) backgroundColor.green() / 255.0) +
324 ((float) backgroundColor.blue() / 255.0)) / 3.0);
325 glClearColor(color, color, color, 1);
327 glClearColor((float)backgroundColor.red() / 255.0,
328 (float)backgroundColor.green() / 255.0,
329 (float)backgroundColor.blue() / 255.0, 1);
331 glClear(GL_COLOR_BUFFER_BIT);
334 double GLWebViewState::setupDrawing(IntRect& viewRect, SkRect& visibleRect,
335 IntRect& webViewRect, int titleBarHeight,
336 IntRect& screenClip, float scale)
338 int left = viewRect.x();
339 int top = viewRect.y();
340 int width = viewRect.width();
341 int height = viewRect.height();
343 ShaderProgram* shader = TilesManager::instance()->shader();
344 if (shader->program() == -1) {
345 XLOG("Reinit shader");
348 shader->setViewport(visibleRect, scale);
349 shader->setViewRect(viewRect);
350 shader->setWebViewRect(webViewRect);
351 shader->setTitleBarHeight(titleBarHeight);
352 shader->setScreenClip(screenClip);
353 shader->resetBlending();
355 shader->calculateAnimationDelta();
357 glViewport(left + shader->getAnimationDeltaX(),
358 top - shader->getAnimationDeltaY(),
361 double currentTime = WTF::currentTime();
363 setViewport(visibleRect, scale);
364 m_zoomManager.processNewScale(currentTime, scale);
369 bool GLWebViewState::setLayersRenderingMode(TexturesResult& nbTexturesNeeded)
371 bool invalBase = false;
373 if (!nbTexturesNeeded.full)
374 TilesManager::instance()->setMaxLayerTextureCount(0);
376 TilesManager::instance()->setMaxLayerTextureCount((2*nbTexturesNeeded.full)+1);
378 int maxTextures = TilesManager::instance()->maxLayerTextureCount();
379 LayersRenderingMode layersRenderingMode = m_layersRenderingMode;
381 if (m_layersRenderingMode == kSingleSurfaceRendering) {
382 // only switch out of SingleSurface mode, if we have 2x needed textures
383 // to avoid changing too often
387 m_layersRenderingMode = kSingleSurfaceRendering;
388 if (nbTexturesNeeded.fixed < maxTextures)
389 m_layersRenderingMode = kFixedLayers;
390 if (nbTexturesNeeded.scrollable < maxTextures)
391 m_layersRenderingMode = kScrollableAndFixedLayers;
392 if (nbTexturesNeeded.clipped < maxTextures)
393 m_layersRenderingMode = kClippedTextures;
394 if (nbTexturesNeeded.full < maxTextures)
395 m_layersRenderingMode = kAllTextures;
397 if (!maxTextures && !nbTexturesNeeded.full)
398 m_layersRenderingMode = kAllTextures;
400 if (m_layersRenderingMode < layersRenderingMode
401 && m_layersRenderingMode != kAllTextures)
404 if (m_layersRenderingMode > layersRenderingMode
405 && m_layersRenderingMode != kClippedTextures)
409 if (m_layersRenderingMode != layersRenderingMode) {
410 char* mode[] = { "kAllTextures", "kClippedTextures",
411 "kScrollableAndFixedLayers", "kFixedLayers", "kSingleSurfaceRendering" };
412 XLOGC("Change from mode %s to %s -- We need textures: fixed: %d,"
413 " scrollable: %d, clipped: %d, full: %d, max textures: %d",
414 static_cast<char*>(mode[layersRenderingMode]),
415 static_cast<char*>(mode[m_layersRenderingMode]),
416 nbTexturesNeeded.fixed,
417 nbTexturesNeeded.scrollable,
418 nbTexturesNeeded.clipped,
419 nbTexturesNeeded.full, maxTextures);
423 // For now, anything below kClippedTextures is equivalent
424 // to kSingleSurfaceRendering
425 // TODO: implement the other rendering modes
426 if (m_layersRenderingMode > kClippedTextures)
427 m_layersRenderingMode = kSingleSurfaceRendering;
429 // update the base surface if needed
430 if (m_layersRenderingMode != layersRenderingMode
432 m_tiledPageA->discardTextures();
433 m_tiledPageB->discardTextures();
440 void GLWebViewState::fullInval()
442 // TODO -- use base layer's size.
443 IntRect ir(0, 0, 1E6, 1E6);
447 bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
448 IntRect& webViewRect, int titleBarHeight,
449 IntRect& clip, float scale,
450 bool* treesSwappedPtr, bool* newTreeHasAnimPtr)
453 TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft,
458 TilesManager::instance()->incDrawGLCount();
461 TilesManager::instance()->getTilesTracker()->clear();
464 float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO;
465 float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO;
466 bool useMinimalMemory = TilesManager::instance()->useMinimalMemory();
467 bool useHorzPrefetch = useMinimalMemory ? 0 : viewWidth < baseContentWidth();
468 bool useVertPrefetch = useMinimalMemory ? 0 : viewHeight < baseContentHeight();
469 m_expandedTileBoundsX = (useHorzPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
470 m_expandedTileBoundsY = (useVertPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
472 XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
473 rect.x(), rect.y(), rect.width(), rect.height(),
474 viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);
476 resetLayersDirtyArea();
478 // when adding or removing layers, use the the paintingBaseLayer's tree so
479 // that content that moves to the base layer from a layer is synchronized
481 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
482 XLOGC("WARNING, scale seems corrupted before update: %e", scale);
484 // Here before we draw, update the BaseTile which has updated content.
485 // Inside this function, just do GPU blits from the transfer queue into
486 // the BaseTiles' texture.
487 TilesManager::instance()->transferQueue()->updateDirtyBaseTiles();
489 // Upload any pending ImageTexture
490 // Return true if we still have some images to upload.
491 // TODO: upload as many textures as possible within a certain time limit
492 bool ret = ImagesManager::instance()->prepareTextures(this);
494 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
495 XLOGC("WARNING, scale seems corrupted after update: %e", scale);
497 // gather the textures we can use
498 TilesManager::instance()->gatherLayerTextures();
500 double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale);
503 TexturesResult nbTexturesNeeded;
504 bool fastSwap = isScrolling() || m_layersRenderingMode == kSingleSurfaceRendering;
505 ret |= m_treeManager.drawGL(currentTime, rect, viewport,
507 treesSwappedPtr, newTreeHasAnimPtr,
510 resetFrameworkInval();
512 int nbTexturesForImages = ImagesManager::instance()->nbTextures();
513 XLOG("*** We have %d textures for images, %d full, %d clipped, total %d / %d",
514 nbTexturesForImages, nbTexturesNeeded.full, nbTexturesNeeded.clipped,
515 nbTexturesNeeded.full + nbTexturesForImages,
516 nbTexturesNeeded.clipped + nbTexturesForImages);
517 nbTexturesNeeded.full += nbTexturesForImages;
518 nbTexturesNeeded.clipped += nbTexturesForImages;
519 ret |= setLayersRenderingMode(nbTexturesNeeded);
521 FloatRect extrasclip(0, 0, rect.width(), rect.height());
522 TilesManager::instance()->shader()->clip(extrasclip);
524 m_glExtras.drawGL(webViewRect, viewport, titleBarHeight);
526 glBindBuffer(GL_ARRAY_BUFFER, 0);
528 // Clean up GL textures for video layer.
529 TilesManager::instance()->videoLayerManager()->deleteUnusedTextures();
530 ret |= TilesManager::instance()->invertedScreenSwitch();
533 // ret==true && empty inval region means we've inval'd everything,
534 // but don't have new content. Keep redrawing full view (0,0,0,0)
535 // until tile generation catches up and we swap pages.
536 bool fullScreenInval = m_frameworkInval.isEmpty();
538 if (TilesManager::instance()->invertedScreenSwitch()) {
539 fullScreenInval = true;
540 TilesManager::instance()->setInvertedScreenSwitch(false);
543 if (!fullScreenInval) {
544 FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(
546 // Inflate the invalidate rect to avoid precision lost.
547 frameworkInval.inflate(1);
548 IntRect inval(frameworkInval.x(), frameworkInval.y(),
549 frameworkInval.width(), frameworkInval.height());
551 inval.unite(m_frameworkLayersInval);
553 invalRect->setX(inval.x());
554 invalRect->setY(inval.y());
555 invalRect->setWidth(inval.width());
556 invalRect->setHeight(inval.height());
558 XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
559 inval.y(), inval.width(), inval.height());
561 if (!invalRect->intersects(rect)) {
562 // invalidate is occurring offscreen, do full inval to guarantee redraw
563 fullScreenInval = true;
567 if (fullScreenInval) {
570 invalRect->setWidth(0);
571 invalRect->setHeight(0);
574 resetFrameworkInval();
578 if (m_measurePerfs) {
579 m_delayTimes[m_timeCounter++] = delta;
580 if (m_timeCounter >= MAX_MEASURES_PERF)
586 TilesManager::instance()->getTilesTracker()->showTrackTextures();
587 ImagesManager::instance()->showImages();
593 } // namespace WebCore
595 #endif // USE(ACCELERATED_COMPOSITING)