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"
37 #include "TilesManager.h"
38 #include "TilesTracker.h"
39 #include <wtf/CurrentTime.h>
41 #include <cutils/log.h>
42 #include <wtf/text/CString.h>
45 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
50 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
59 #define FIRST_TILED_PAGE_ID 1
60 #define SECOND_TILED_PAGE_ID 2
62 #define FRAMERATE_CAP 0.01666 // We cap at 60 fps
64 // log warnings if scale goes outside this range
65 #define MIN_SCALE_WARNING 0.1
66 #define MAX_SCALE_WARNING 10
70 using namespace android;
72 GLWebViewState::GLWebViewState(android::Mutex* buttonMutex)
75 , m_currentBaseLayer(0)
76 , m_previouslyUsedRoot(0)
77 , m_currentPictureCounter(0)
79 , m_frameworkInval(0, 0, 0, 0)
80 , m_frameworkLayersInval(0, 0, 0, 0)
81 , m_globalButtonMutex(buttonMutex)
82 , m_baseLayerUpdate(true)
83 , m_backgroundColor(SK_ColorWHITE)
84 , m_isScrolling(false)
87 , m_expandedTileBoundsX(0)
88 , m_expandedTileBoundsY(0)
90 m_viewport.setEmpty();
91 m_futureViewportTileBounds.setEmpty();
92 m_viewportTileBounds.setEmpty();
93 m_preZoomBounds.setEmpty();
95 m_tiledPageA = new TiledPage(FIRST_TILED_PAGE_ID, this);
96 m_tiledPageB = new TiledPage(SECOND_TILED_PAGE_ID, this);
99 ClassTracker::instance()->increment("GLWebViewState");
103 m_totalTimeCounter = 0;
104 m_measurePerfs = false;
108 GLWebViewState::~GLWebViewState()
110 // Unref the existing tree/PaintedSurfaces
111 if (m_previouslyUsedRoot)
112 TilesManager::instance()->swapLayersTextures(m_previouslyUsedRoot, 0);
114 // Take care of the transfer queue such that Tex Gen thread will not stuck
115 TilesManager::instance()->unregisterGLWebViewState(this);
117 // We have to destroy the two tiled pages first as their destructor
118 // may depend on the existence of this GLWebViewState and some of its
119 // instance variables in order to complete.
120 // Explicitely, currently we need to have the m_currentBaseLayer around
121 // in order to complete any pending paint operations (the tiled pages
122 // will remove any pending operations, and wait if one is underway).
125 SkSafeUnref(m_previouslyUsedRoot);
126 SkSafeUnref(m_currentBaseLayer);
127 SkSafeUnref(m_baseLayer);
128 m_previouslyUsedRoot = 0;
130 m_currentBaseLayer = 0;
132 ClassTracker::instance()->decrement("GLWebViewState");
137 void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
138 bool showVisualIndicator, bool isPictureAfterFirstLayout)
140 android::Mutex::Autolock lock(m_baseLayerLock);
141 if (!layer || isPictureAfterFirstLayout) {
142 m_tiledPageA->discardTextures();
143 m_tiledPageB->discardTextures();
145 if (isPictureAfterFirstLayout) {
146 m_baseLayerUpdate = true;
147 m_invalidateRegion.setEmpty();
149 if (m_baseLayer && layer)
150 m_baseLayer->swapExtra(layer);
153 SkSafeUnref(m_baseLayer);
156 m_baseLayer->setGLWebViewState(this);
158 // We only update the layers if we are not currently
159 // waiting for a tiledPage to be painted
160 if (m_baseLayerUpdate) {
162 SkSafeUnref(m_currentBaseLayer);
163 m_currentBaseLayer = layer;
165 m_glExtras.setDrawExtra(0);
169 if (m_measurePerfs && !showVisualIndicator)
171 m_measurePerfs = showVisualIndicator;
174 TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
177 void GLWebViewState::invalRegion(const SkRegion& region)
179 SkRegion::Iterator iterator(region);
180 while (!iterator.done()) {
181 SkIRect r = iterator.rect();
182 IntRect ir(r.fLeft, r.fTop, r.width(), r.height());
188 void GLWebViewState::unlockBaseLayerUpdate() {
189 if (m_baseLayerUpdate)
192 m_baseLayerUpdate = true;
193 android::Mutex::Autolock lock(m_baseLayerLock);
194 SkSafeRef(m_baseLayer);
195 SkSafeUnref(m_currentBaseLayer);
196 m_currentBaseLayer = m_baseLayer;
198 invalRegion(m_invalidateRegion);
199 m_invalidateRegion.setEmpty();
202 void GLWebViewState::inval(const IntRect& rect)
204 if (m_baseLayerUpdate) {
205 // base layer isn't locked, so go ahead and issue the inval to both tiled pages
206 m_currentPictureCounter++;
207 if (!rect.isEmpty()) {
208 // find which tiles fall within the invalRect and mark them as dirty
209 m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
210 m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
211 if (m_frameworkInval.isEmpty())
212 m_frameworkInval = rect;
214 m_frameworkInval.unite(rect);
215 XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
216 m_frameworkInval.y(), m_frameworkInval.width(), m_frameworkInval.height(),
217 rect.x(), rect.y(), rect.width(), rect.height());
220 // base layer is locked, so defer invalidation until unlockBaseLayerUpdate()
221 m_invalidateRegion.op(rect.x(), rect.y(), rect.maxX(), rect.maxY(), SkRegion::kUnion_Op);
223 TilesManager::instance()->getProfiler()->nextInval(rect, zoomManager()->currentScale());
226 unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
228 android::Mutex::Autolock lock(m_baseLayerLock);
229 if (m_currentBaseLayer) {
230 m_globalButtonMutex->lock();
231 m_currentBaseLayer->drawCanvas(canvas);
232 m_globalButtonMutex->unlock();
234 return m_currentPictureCounter;
237 TiledPage* GLWebViewState::sibling(TiledPage* page)
239 return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA;
242 TiledPage* GLWebViewState::frontPage()
244 android::Mutex::Autolock lock(m_tiledPageLock);
245 return m_usePageA ? m_tiledPageA : m_tiledPageB;
248 TiledPage* GLWebViewState::backPage()
250 android::Mutex::Autolock lock(m_tiledPageLock);
251 return m_usePageA ? m_tiledPageB : m_tiledPageA;
254 void GLWebViewState::swapPages()
256 android::Mutex::Autolock lock(m_tiledPageLock);
258 TiledPage* oldPage = m_usePageA ? m_tiledPageB : m_tiledPageA;
259 zoomManager()->swapPages();
260 oldPage->discardTextures();
263 int GLWebViewState::baseContentWidth()
265 return m_currentBaseLayer ? m_currentBaseLayer->content()->width() : 0;
267 int GLWebViewState::baseContentHeight()
269 return m_currentBaseLayer ? m_currentBaseLayer->content()->height() : 0;
272 void GLWebViewState::setViewport(SkRect& viewport, float scale)
274 if ((m_viewport == viewport) &&
275 (zoomManager()->futureScale() == scale))
278 m_goingDown = m_viewport.fTop - viewport.fTop <= 0;
279 m_goingLeft = m_viewport.fLeft - viewport.fLeft >= 0;
280 m_viewport = viewport;
282 XLOG("New VIEWPORT %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f currentScale: %.2f futureScale: %.2f)",
283 m_viewport.fLeft, m_viewport.fTop, m_viewport.fRight, m_viewport.fBottom,
284 m_viewport.width(), m_viewport.height(), scale,
285 zoomManager()->currentScale(), zoomManager()->futureScale());
287 const float invTileContentWidth = scale / TilesManager::tileWidth();
288 const float invTileContentHeight = scale / TilesManager::tileHeight();
290 m_viewportTileBounds.set(
291 static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)),
292 static_cast<int>(floorf(viewport.fTop * invTileContentHeight)),
293 static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)),
294 static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight)));
296 // allocate max possible number of tiles visible with this viewport
297 int viewMaxTileX = static_cast<int>(ceilf((viewport.width()-1) * invTileContentWidth)) + 1;
298 int viewMaxTileY = static_cast<int>(ceilf((viewport.height()-1) * invTileContentHeight)) + 1;
299 int maxTextureCount = (viewMaxTileX + TILE_PREFETCH_DISTANCE * 2) *
300 (viewMaxTileY + TILE_PREFETCH_DISTANCE * 2) * 2;
301 TilesManager::instance()->setMaxTextureCount(maxTextureCount);
302 m_tiledPageA->updateBaseTileSize();
303 m_tiledPageB->updateBaseTileSize();
307 void GLWebViewState::dumpMeasures()
309 for (int i = 0; i < m_timeCounter; i++) {
310 XLOGC("%d delay: %d ms", m_totalTimeCounter + i,
311 static_cast<int>(m_delayTimes[i]*1000));
314 m_totalTimeCounter += m_timeCounter;
317 #endif // MEASURES_PERF
319 void GLWebViewState::resetFrameworkInval()
321 m_frameworkInval.setX(0);
322 m_frameworkInval.setY(0);
323 m_frameworkInval.setWidth(0);
324 m_frameworkInval.setHeight(0);
327 void GLWebViewState::addDirtyArea(const IntRect& rect)
332 IntRect inflatedRect = rect;
333 inflatedRect.inflate(8);
334 if (m_frameworkLayersInval.isEmpty())
335 m_frameworkLayersInval = inflatedRect;
337 m_frameworkLayersInval.unite(inflatedRect);
340 void GLWebViewState::resetLayersDirtyArea()
342 m_frameworkLayersInval.setX(0);
343 m_frameworkLayersInval.setY(0);
344 m_frameworkLayersInval.setWidth(0);
345 m_frameworkLayersInval.setHeight(0);
348 double GLWebViewState::setupDrawing(IntRect& viewRect, SkRect& visibleRect,
349 IntRect& webViewRect, int titleBarHeight,
350 IntRect& screenClip, float scale)
352 int left = viewRect.x();
353 int top = viewRect.y();
354 int width = viewRect.width();
355 int height = viewRect.height();
357 if (TilesManager::instance()->invertedScreen()) {
358 float color = 1.0 - ((((float) m_backgroundColor.red() / 255.0) +
359 ((float) m_backgroundColor.green() / 255.0) +
360 ((float) m_backgroundColor.blue() / 255.0)) / 3.0);
361 glClearColor(color, color, color, 1);
363 glClearColor((float)m_backgroundColor.red() / 255.0,
364 (float)m_backgroundColor.green() / 255.0,
365 (float)m_backgroundColor.blue() / 255.0, 1);
367 glClear(GL_COLOR_BUFFER_BIT);
369 glViewport(left, top, width, height);
371 ShaderProgram* shader = TilesManager::instance()->shader();
372 if (shader->program() == -1) {
373 XLOG("Reinit shader");
376 shader->setViewRect(viewRect);
377 shader->setViewport(visibleRect);
378 shader->setWebViewRect(webViewRect);
379 shader->setTitleBarHeight(titleBarHeight);
380 shader->setScreenClip(screenClip);
381 shader->resetBlending();
383 double currentTime = WTF::currentTime();
385 setViewport(visibleRect, scale);
386 m_zoomManager.processNewScale(currentTime, scale);
391 bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
392 IntRect& webViewRect, int titleBarHeight,
393 IntRect& clip, float scale, bool* buffersSwappedPtr)
395 TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft,
400 TilesManager::instance()->incDrawGLCount();
403 TilesManager::instance()->getTilesTracker()->clear();
406 m_baseLayerLock.lock();
407 BaseLayerAndroid* baseLayer = m_currentBaseLayer;
408 SkSafeRef(baseLayer);
409 BaseLayerAndroid* baseForComposited = m_baseLayer;
410 SkSafeRef(baseForComposited);
411 m_baseLayerLock.unlock();
413 SkSafeUnref(baseForComposited);
417 float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO;
418 float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO;
419 bool useHorzPrefetch = viewWidth < baseContentWidth();
420 bool useVertPrefetch = viewHeight < baseContentHeight();
421 m_expandedTileBoundsX = (useHorzPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
422 m_expandedTileBoundsY = (useVertPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
424 XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
425 rect.x(), rect.y(), rect.width(), rect.height(),
426 viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);
428 resetLayersDirtyArea();
430 if (!baseForComposited ||
431 (baseForComposited && !baseForComposited->countChildren())) {
432 SkSafeRef(baseLayer);
433 SkSafeUnref(baseForComposited);
434 baseForComposited = baseLayer;
437 LayerAndroid* compositedRoot = 0;
438 if (baseForComposited && baseForComposited->countChildren() >= 1)
439 compositedRoot = static_cast<LayerAndroid*>(baseForComposited->getChild(0));
441 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
442 XLOGC("WARNING, scale seems corrupted before update: %e", scale);
444 // Here before we draw, update the BaseTile which has updated content.
445 // Inside this function, just do GPU blits from the transfer queue into
446 // the BaseTiles' texture.
447 TilesManager::instance()->transferQueue()->updateDirtyBaseTiles();
449 // Upload any pending ImageTexture
450 // Return true if we still have some images to upload.
451 // TODO: upload as many textures as possible within a certain time limit
452 bool ret = ImagesManager::instance()->uploadTextures();
454 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) {
455 XLOGC("WARNING, scale seems corrupted after update: %e", scale);
459 // gather the textures we can use
460 TilesManager::instance()->gatherLayerTextures();
462 if (compositedRoot != m_previouslyUsedRoot)
463 TilesManager::instance()->swapLayersTextures(m_previouslyUsedRoot, compositedRoot);
465 // set up zoom manager, shaders, etc.
466 m_backgroundColor = baseLayer->getBackgroundColor();
467 double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale);
468 ret |= baseLayer->drawGL(currentTime, compositedRoot, rect,
469 viewport, scale, buffersSwappedPtr);
470 m_glExtras.drawGL(webViewRect, viewport, titleBarHeight);
472 glBindBuffer(GL_ARRAY_BUFFER, 0);
474 SkSafeRef(compositedRoot);
475 SkSafeUnref(m_previouslyUsedRoot);
476 m_previouslyUsedRoot = compositedRoot;
478 ret |= TilesManager::instance()->invertedScreenSwitch();
481 // ret==true && empty inval region means we've inval'd everything,
482 // but don't have new content. Keep redrawing full view (0,0,0,0)
483 // until tile generation catches up and we swap pages.
484 bool fullScreenInval = m_frameworkInval.isEmpty();
486 if (TilesManager::instance()->invertedScreenSwitch()) {
487 fullScreenInval = true;
488 TilesManager::instance()->setInvertedScreenSwitch(false);
491 if (!fullScreenInval) {
492 FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(
494 // Inflate the invalidate rect to avoid precision lost.
495 frameworkInval.inflate(1);
496 IntRect inval(frameworkInval.x(), frameworkInval.y(),
497 frameworkInval.width(), frameworkInval.height());
499 inval.unite(m_frameworkLayersInval);
501 invalRect->setX(inval.x());
502 invalRect->setY(inval.y());
503 invalRect->setWidth(inval.width());
504 invalRect->setHeight(inval.height());
506 XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
507 inval.y(), inval.width(), inval.height());
509 if (!invalRect->intersects(rect)) {
510 // invalidate is occurring offscreen, do full inval to guarantee redraw
511 fullScreenInval = true;
515 if (fullScreenInval) {
518 invalRect->setWidth(0);
519 invalRect->setHeight(0);
522 resetFrameworkInval();
526 if (m_measurePerfs) {
527 m_delayTimes[m_timeCounter++] = delta;
528 if (m_timeCounter >= MAX_MEASURES_PERF)
533 SkSafeUnref(baseForComposited);
534 SkSafeUnref(baseLayer);
536 TilesManager::instance()->getTilesTracker()->showTrackTextures();
537 ImagesManager::instance()->showImages();
542 } // namespace WebCore
544 #endif // USE(ACCELERATED_COMPOSITING)