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 "LayerAndroid.h"
36 #include "TilesManager.h"
37 #include "TilesTracker.h"
38 #include <wtf/CurrentTime.h>
40 #include <cutils/log.h>
41 #include <wtf/text/CString.h>
44 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
49 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
58 #define FIRST_TILED_PAGE_ID 1
59 #define SECOND_TILED_PAGE_ID 2
61 #define FRAMERATE_CAP 0.01666 // We cap at 60 fps
63 // Touch ring border width. This is doubled if the ring is not pressed
64 #define RING_BORDER_WIDTH 1
65 // Color of the ring is 0x6633b5e5 (copied from framework)
66 #define RING_COLOR_ALPHA 0.4
67 #define RING_COLOR_R 0x33
68 #define RING_COLOR_G 0xb5
69 #define RING_COLOR_B 0xe5
71 // log warnings if scale goes outside this range
72 #define MIN_SCALE_WARNING 0.1
73 #define MAX_SCALE_WARNING 10
77 using namespace android;
79 GLWebViewState::GLWebViewState(android::Mutex* buttonMutex)
82 , m_currentBaseLayer(0)
83 , m_previouslyUsedRoot(0)
84 , m_currentPictureCounter(0)
86 , m_frameworkInval(0, 0, 0, 0)
87 , m_frameworkLayersInval(0, 0, 0, 0)
88 , m_globalButtonMutex(buttonMutex)
89 , m_baseLayerUpdate(true)
90 , m_backgroundColor(SK_ColorWHITE)
91 , m_displayRings(false)
92 , m_focusRingTexture(-1)
93 , m_isScrolling(false)
96 , m_expandedTileBoundsX(0)
97 , m_expandedTileBoundsY(0)
99 m_viewport.setEmpty();
100 m_futureViewportTileBounds.setEmpty();
101 m_viewportTileBounds.setEmpty();
102 m_preZoomBounds.setEmpty();
104 m_tiledPageA = new TiledPage(FIRST_TILED_PAGE_ID, this);
105 m_tiledPageB = new TiledPage(SECOND_TILED_PAGE_ID, this);
108 ClassTracker::instance()->increment("GLWebViewState");
112 m_totalTimeCounter = 0;
113 m_measurePerfs = false;
117 GLWebViewState::~GLWebViewState()
119 // Unref the existing tree/PaintedSurfaces
120 if (m_previouslyUsedRoot)
121 TilesManager::instance()->swapLayersTextures(m_previouslyUsedRoot, 0);
123 // Take care of the transfer queue such that Tex Gen thread will not stuck
124 TilesManager::instance()->unregisterGLWebViewState(this);
126 // We have to destroy the two tiled pages first as their destructor
127 // may depend on the existence of this GLWebViewState and some of its
128 // instance variables in order to complete.
129 // Explicitely, currently we need to have the m_currentBaseLayer around
130 // in order to complete any pending paint operations (the tiled pages
131 // will remove any pending operations, and wait if one is underway).
134 SkSafeUnref(m_previouslyUsedRoot);
135 SkSafeUnref(m_currentBaseLayer);
136 SkSafeUnref(m_baseLayer);
137 m_previouslyUsedRoot = 0;
139 m_currentBaseLayer = 0;
141 ClassTracker::instance()->decrement("GLWebViewState");
146 void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
147 bool showVisualIndicator, bool isPictureAfterFirstLayout)
149 android::Mutex::Autolock lock(m_baseLayerLock);
150 if (!layer || isPictureAfterFirstLayout) {
151 m_tiledPageA->discardTextures();
152 m_tiledPageB->discardTextures();
154 if (isPictureAfterFirstLayout) {
155 m_baseLayerUpdate = true;
156 m_invalidateRegion.setEmpty();
158 if (m_baseLayer && layer)
159 m_baseLayer->swapExtra(layer);
162 SkSafeUnref(m_baseLayer);
165 m_baseLayer->setGLWebViewState(this);
167 // We only update the layers if we are not currently
168 // waiting for a tiledPage to be painted
169 if (m_baseLayerUpdate) {
171 SkSafeUnref(m_currentBaseLayer);
172 m_currentBaseLayer = layer;
174 m_displayRings = false;
178 if (m_measurePerfs && !showVisualIndicator)
180 m_measurePerfs = showVisualIndicator;
183 TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
186 void GLWebViewState::setRings(Vector<IntRect>& rings, bool isPressed, bool isButton)
188 android::Mutex::Autolock lock(m_baseLayerLock);
189 m_displayRings = true;
191 for (size_t i = 0; i < rings.size(); i++) {
193 m_rings.setRect(rings.at(i));
195 m_rings.op(rings.at(i), SkRegion::kUnion_Op);
197 m_ringsIsPressed = isPressed;
198 m_ringsIsButton = isButton;
201 void GLWebViewState::invalRegion(const SkRegion& region)
203 SkRegion::Iterator iterator(region);
204 while (!iterator.done()) {
205 SkIRect r = iterator.rect();
206 IntRect ir(r.fLeft, r.fTop, r.width(), r.height());
212 void GLWebViewState::unlockBaseLayerUpdate() {
213 if (m_baseLayerUpdate)
216 m_baseLayerUpdate = true;
217 android::Mutex::Autolock lock(m_baseLayerLock);
218 SkSafeRef(m_baseLayer);
219 SkSafeUnref(m_currentBaseLayer);
220 m_currentBaseLayer = m_baseLayer;
222 invalRegion(m_invalidateRegion);
223 m_invalidateRegion.setEmpty();
226 void GLWebViewState::setExtra(BaseLayerAndroid* layer, SkPicture& picture,
227 const IntRect& rect, bool allowSame)
229 android::Mutex::Autolock lock(m_baseLayerLock);
230 if (!m_baseLayerUpdate)
233 layer->setExtra(picture);
235 if (!allowSame && m_lastInval == rect)
240 if (!m_lastInval.isEmpty())
243 m_displayRings = false;
246 void GLWebViewState::inval(const IntRect& rect)
248 if (m_baseLayerUpdate) {
249 // base layer isn't locked, so go ahead and issue the inval to both tiled pages
250 m_currentPictureCounter++;
251 if (!rect.isEmpty()) {
252 // find which tiles fall within the invalRect and mark them as dirty
253 frontPage()->invalidateRect(rect, m_currentPictureCounter);
254 if (m_frameworkInval.isEmpty())
255 m_frameworkInval = rect;
257 m_frameworkInval.unite(rect);
258 XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
259 m_frameworkInval.y(), m_frameworkInval.width(), m_frameworkInval.height(),
260 rect.x(), rect.y(), rect.width(), rect.height());
263 // base layer is locked, so defer invalidation until unlockBaseLayerUpdate()
264 m_invalidateRegion.op(rect.x(), rect.y(), rect.maxX(), rect.maxY(), SkRegion::kUnion_Op);
266 TilesManager::instance()->getProfiler()->nextInval(rect, zoomManager()->currentScale());
269 void GLWebViewState::resetRings()
271 m_displayRings = false;
274 void GLWebViewState::drawFocusRing(SkRect& srcRect)
276 if (m_focusRingTexture == -1)
277 m_focusRingTexture = GLUtils::createSampleColorTexture(RING_COLOR_R,
281 TilesManager::instance()->shader()->drawQuad(srcRect, m_focusRingTexture,
285 void GLWebViewState::paintExtras()
287 if (m_displayRings && !m_rings.isEmpty()) {
288 if (m_ringsIsPressed) {
289 SkRegion::Iterator rgnIter(m_rings);
290 while (!rgnIter.done()) {
291 SkIRect ir = rgnIter.rect();
293 r.set(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom);
298 if (m_ringsIsButton && m_ringsIsPressed)
301 if (!m_rings.getBoundaryPath(&path))
303 SkPath::Iter iter(path, true);
308 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
309 if (verb == SkPath::kLine_Verb) {
313 int borderWidth = RING_BORDER_WIDTH;
314 if (!m_ringsIsPressed)
316 line.fLeft = r.fLeft - borderWidth;
317 line.fRight = r.fRight + borderWidth;
318 line.fTop = r.fTop - borderWidth;
319 line.fBottom = r.fBottom + borderWidth;
320 if (clip.intersects(line)) {
321 clip.op(line, SkRegion::kReverseDifference_Op);
323 continue; // Nothing to draw, continue
324 line = clip.getBounds();
325 if (SkIRect::Intersects(startRect, line)) {
326 clip.op(startRect, SkRegion::kDifference_Op);
328 continue; // Nothing to draw, continue
329 line = clip.getBounds();
334 r.set(line.fLeft, line.fTop, line.fRight, line.fBottom);
336 if (!m_ringsIsPressed) {
337 r.fLeft += RING_BORDER_WIDTH;
338 r.fRight -= RING_BORDER_WIDTH;
339 r.fTop += RING_BORDER_WIDTH;
340 r.fBottom -= RING_BORDER_WIDTH;
343 if (startRect.isEmpty()) {
344 startRect.set(line.fLeft, line.fTop, line.fRight, line.fBottom);
347 if (verb == SkPath::kMove_Verb) {
348 startRect.setEmpty();
354 unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
356 android::Mutex::Autolock lock(m_baseLayerLock);
357 if (m_currentBaseLayer) {
358 m_globalButtonMutex->lock();
359 m_currentBaseLayer->drawCanvas(canvas);
360 m_globalButtonMutex->unlock();
362 return m_currentPictureCounter;
365 TiledPage* GLWebViewState::sibling(TiledPage* page)
367 return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA;
370 TiledPage* GLWebViewState::frontPage()
372 android::Mutex::Autolock lock(m_tiledPageLock);
373 return m_usePageA ? m_tiledPageA : m_tiledPageB;
376 TiledPage* GLWebViewState::backPage()
378 android::Mutex::Autolock lock(m_tiledPageLock);
379 return m_usePageA ? m_tiledPageB : m_tiledPageA;
382 void GLWebViewState::swapPages()
384 android::Mutex::Autolock lock(m_tiledPageLock);
386 TiledPage* oldPage = m_usePageA ? m_tiledPageB : m_tiledPageA;
387 zoomManager()->swapPages();
388 oldPage->discardTextures();
391 int GLWebViewState::baseContentWidth()
393 return m_currentBaseLayer ? m_currentBaseLayer->content()->width() : 0;
395 int GLWebViewState::baseContentHeight()
397 return m_currentBaseLayer ? m_currentBaseLayer->content()->height() : 0;
400 void GLWebViewState::setViewport(SkRect& viewport, float scale)
402 if ((m_viewport == viewport) &&
403 (zoomManager()->futureScale() == scale))
406 m_goingDown = m_viewport.fTop - viewport.fTop <= 0;
407 m_goingLeft = m_viewport.fLeft - viewport.fLeft >= 0;
408 m_viewport = viewport;
410 XLOG("New VIEWPORT %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f currentScale: %.2f futureScale: %.2f)",
411 m_viewport.fLeft, m_viewport.fTop, m_viewport.fRight, m_viewport.fBottom,
412 m_viewport.width(), m_viewport.height(), scale,
413 zoomManager()->currentScale(), zoomManager()->futureScale());
415 const float invTileContentWidth = scale / TilesManager::tileWidth();
416 const float invTileContentHeight = scale / TilesManager::tileHeight();
418 m_viewportTileBounds.set(
419 static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)),
420 static_cast<int>(floorf(viewport.fTop * invTileContentHeight)),
421 static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)),
422 static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight)));
424 // allocate max possible number of tiles visible with this viewport
425 int viewMaxTileX = static_cast<int>(ceilf((viewport.width()-1) * invTileContentWidth)) + 1;
426 int viewMaxTileY = static_cast<int>(ceilf((viewport.height()-1) * invTileContentHeight)) + 1;
427 int maxTextureCount = (viewMaxTileX + TILE_PREFETCH_DISTANCE * 2) *
428 (viewMaxTileY + TILE_PREFETCH_DISTANCE * 2) * 2;
429 TilesManager::instance()->setMaxTextureCount(maxTextureCount);
430 m_tiledPageA->updateBaseTileSize();
431 m_tiledPageB->updateBaseTileSize();
435 void GLWebViewState::dumpMeasures()
437 for (int i = 0; i < m_timeCounter; i++) {
438 XLOGC("%d delay: %d ms", m_totalTimeCounter + i,
439 static_cast<int>(m_delayTimes[i]*1000));
442 m_totalTimeCounter += m_timeCounter;
445 #endif // MEASURES_PERF
447 void GLWebViewState::resetFrameworkInval()
449 m_frameworkInval.setX(0);
450 m_frameworkInval.setY(0);
451 m_frameworkInval.setWidth(0);
452 m_frameworkInval.setHeight(0);
455 void GLWebViewState::addDirtyArea(const IntRect& rect)
460 IntRect inflatedRect = rect;
461 inflatedRect.inflate(8);
462 if (m_frameworkLayersInval.isEmpty())
463 m_frameworkLayersInval = inflatedRect;
465 m_frameworkLayersInval.unite(inflatedRect);
468 void GLWebViewState::resetLayersDirtyArea()
470 m_frameworkLayersInval.setX(0);
471 m_frameworkLayersInval.setY(0);
472 m_frameworkLayersInval.setWidth(0);
473 m_frameworkLayersInval.setHeight(0);
476 double GLWebViewState::setupDrawing(IntRect& viewRect, SkRect& visibleRect,
477 IntRect& webViewRect, int titleBarHeight,
478 IntRect& screenClip, float scale)
480 int left = viewRect.x();
481 int top = viewRect.y();
482 int width = viewRect.width();
483 int height = viewRect.height();
485 if (TilesManager::instance()->invertedScreen()) {
486 float color = 1.0 - ((((float) m_backgroundColor.red() / 255.0) +
487 ((float) m_backgroundColor.green() / 255.0) +
488 ((float) m_backgroundColor.blue() / 255.0)) / 3.0);
489 glClearColor(color, color, color, 1);
491 glClearColor((float)m_backgroundColor.red() / 255.0,
492 (float)m_backgroundColor.green() / 255.0,
493 (float)m_backgroundColor.blue() / 255.0, 1);
495 glClear(GL_COLOR_BUFFER_BIT);
497 glViewport(left, top, width, height);
499 ShaderProgram* shader = TilesManager::instance()->shader();
500 if (shader->program() == -1) {
501 XLOG("Reinit shader");
504 glUseProgram(shader->program());
505 glUniform1i(shader->textureSampler(), 0);
506 shader->setViewRect(viewRect);
507 shader->setViewport(visibleRect);
508 shader->setWebViewRect(webViewRect);
509 shader->setTitleBarHeight(titleBarHeight);
510 shader->setScreenClip(screenClip);
511 shader->resetBlending();
513 double currentTime = WTF::currentTime();
515 setViewport(visibleRect, scale);
516 m_zoomManager.processNewScale(currentTime, scale);
521 bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
522 IntRect& webViewRect, int titleBarHeight,
523 IntRect& clip, float scale, bool* buffersSwappedPtr)
525 TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft,
530 TilesManager::instance()->incDrawGLCount();
533 TilesManager::instance()->getTilesTracker()->clear();
536 m_baseLayerLock.lock();
537 BaseLayerAndroid* baseLayer = m_currentBaseLayer;
538 SkSafeRef(baseLayer);
539 BaseLayerAndroid* baseForComposited = m_baseLayer;
540 SkSafeRef(baseForComposited);
541 m_baseLayerLock.unlock();
543 SkSafeUnref(baseForComposited);
547 float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO;
548 float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO;
549 bool useHorzPrefetch = viewWidth < baseContentWidth();
550 bool useVertPrefetch = viewHeight < baseContentHeight();
551 m_expandedTileBoundsX = (useHorzPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
552 m_expandedTileBoundsY = (useVertPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
554 XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
555 rect.x(), rect.y(), rect.width(), rect.height(),
556 viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);
558 resetLayersDirtyArea();
560 if (!baseForComposited ||
561 (baseForComposited && !baseForComposited->countChildren())) {
562 SkSafeRef(baseLayer);
563 SkSafeUnref(baseForComposited);
564 baseForComposited = baseLayer;
567 LayerAndroid* compositedRoot = 0;
568 if (baseForComposited && baseForComposited->countChildren() >= 1)
569 compositedRoot = static_cast<LayerAndroid*>(baseForComposited->getChild(0));
571 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
572 XLOGC("WARNING, scale seems corrupted before update: %e", scale);
574 // Here before we draw, update the BaseTile which has updated content.
575 // Inside this function, just do GPU blits from the transfer queue into
576 // the BaseTiles' texture.
577 TilesManager::instance()->transferQueue()->updateDirtyBaseTiles();
579 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) {
580 XLOGC("WARNING, scale seems corrupted after update: %e", scale);
584 // gather the textures we can use
585 TilesManager::instance()->gatherLayerTextures();
587 if (compositedRoot != m_previouslyUsedRoot)
588 TilesManager::instance()->swapLayersTextures(m_previouslyUsedRoot, compositedRoot);
590 // set up zoom manager, shaders, etc.
591 m_backgroundColor = baseLayer->getBackgroundColor();
592 double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale);
593 bool ret = baseLayer->drawGL(currentTime, compositedRoot, rect,
594 viewport, scale, buffersSwappedPtr);
595 // Reset the clip to make sure we can draw the rings. If this isn't done, the
596 // current clip will be the clip of whatever layer was last drawn
597 TilesManager::instance()->shader()->clip(clip);
600 glBindBuffer(GL_ARRAY_BUFFER, 0);
602 SkSafeRef(compositedRoot);
603 SkSafeUnref(m_previouslyUsedRoot);
604 m_previouslyUsedRoot = compositedRoot;
606 ret |= TilesManager::instance()->invertedScreenSwitch();
609 // ret==true && empty inval region means we've inval'd everything,
610 // but don't have new content. Keep redrawing full view (0,0,0,0)
611 // until tile generation catches up and we swap pages.
612 bool fullScreenInval = m_frameworkInval.isEmpty();
614 if (TilesManager::instance()->invertedScreenSwitch()) {
615 fullScreenInval = true;
616 TilesManager::instance()->setInvertedScreenSwitch(false);
619 if (!fullScreenInval) {
620 FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(
622 // Inflate the invalidate rect to avoid precision lost.
623 frameworkInval.inflate(1);
624 IntRect inval(frameworkInval.x(), frameworkInval.y(),
625 frameworkInval.width(), frameworkInval.height());
627 inval.unite(m_frameworkLayersInval);
629 invalRect->setX(inval.x());
630 invalRect->setY(inval.y());
631 invalRect->setWidth(inval.width());
632 invalRect->setHeight(inval.height());
634 XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
635 inval.y(), inval.width(), inval.height());
637 if (!invalRect->intersects(rect)) {
638 // invalidate is occurring offscreen, do full inval to guarantee redraw
639 fullScreenInval = true;
643 if (fullScreenInval) {
646 invalRect->setWidth(0);
647 invalRect->setHeight(0);
650 resetFrameworkInval();
654 if (m_measurePerfs) {
655 m_delayTimes[m_timeCounter++] = delta;
656 if (m_timeCounter >= MAX_MEASURES_PERF)
661 SkSafeUnref(baseForComposited);
662 SkSafeUnref(baseLayer);
664 TilesManager::instance()->getTilesTracker()->showTrackTextures();
665 TilesManager::instance()->showImages();
670 } // namespace WebCore
672 #endif // USE(ACCELERATED_COMPOSITING)