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"
35 #include "TilesManager.h"
36 #include <wtf/CurrentTime.h>
38 #include <cutils/log.h>
39 #include <wtf/text/CString.h>
42 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
47 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
56 #define FIRST_TILED_PAGE_ID 1
57 #define SECOND_TILED_PAGE_ID 2
59 #define FRAMERATE_CAP 0.01666 // We cap at 60 fps
63 using namespace android;
65 GLWebViewState::GLWebViewState(android::Mutex* buttonMutex)
66 : m_scaleRequestState(kNoScaleRequest)
70 , m_transitionTime(-1)
72 , m_currentBaseLayer(0)
73 , m_previouslyUsedRoot(0)
74 , m_currentPictureCounter(0)
76 , m_frameworkInval(0, 0, 0, 0)
77 , m_frameworkLayersInval(0, 0, 0, 0)
78 , m_globalButtonMutex(buttonMutex)
79 , m_baseLayerUpdate(true)
80 , m_backgroundColor(SK_ColorWHITE)
82 , m_displayRings(false)
83 , m_focusRingTexture(-1)
85 m_viewport.setEmpty();
86 m_previousViewport.setEmpty();
87 m_futureViewportTileBounds.setEmpty();
88 m_viewportTileBounds.setEmpty();
89 m_preZoomBounds.setEmpty();
91 m_tiledPageA = new TiledPage(FIRST_TILED_PAGE_ID, this);
92 m_tiledPageB = new TiledPage(SECOND_TILED_PAGE_ID, this);
94 ClassTracker::instance()->increment("GLWebViewState");
98 m_totalTimeCounter = 0;
99 m_measurePerfs = false;
103 GLWebViewState::~GLWebViewState()
105 // We have to destroy the two tiled pages first as their destructor
106 // may depend on the existence of this GLWebViewState and some of its
107 // instance variables in order to complete.
108 // Explicitely, currently we need to have the m_currentBaseLayer around
109 // in order to complete any pending paint operations (the tiled pages
110 // will remove any pending operations, and wait if one is underway).
113 SkSafeUnref(m_currentBaseLayer);
114 SkSafeUnref(m_baseLayer);
116 m_currentBaseLayer = 0;
118 ClassTracker::instance()->decrement("GLWebViewState");
122 void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
123 bool showVisualIndicator, bool isPictureAfterFirstLayout)
125 android::Mutex::Autolock lock(m_baseLayerLock);
126 if (!layer || isPictureAfterFirstLayout) {
127 m_tiledPageA->setUsable(false);
128 m_tiledPageB->setUsable(false);
130 if (isPictureAfterFirstLayout) {
131 m_baseLayerUpdate = true;
132 m_invalidateRegion.setEmpty();
134 if (m_baseLayer && layer)
135 m_baseLayer->swapExtra(layer);
138 SkSafeUnref(m_baseLayer);
141 m_baseLayer->setGLWebViewState(this);
143 // We only update the layers if we are not currently
144 // waiting for a tiledPage to be painted
145 if (m_baseLayerUpdate) {
147 SkSafeUnref(m_currentBaseLayer);
148 m_currentBaseLayer = layer;
150 m_displayRings = false;
154 if (m_measurePerfs && !showVisualIndicator)
156 m_measurePerfs = showVisualIndicator;
159 TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
162 void GLWebViewState::setRings(Vector<IntRect>& rings)
164 android::Mutex::Autolock lock(m_baseLayerLock);
165 m_displayRings = true;
169 void GLWebViewState::invalRegion(const SkRegion& region)
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());
180 void GLWebViewState::unlockBaseLayerUpdate() {
181 if (m_baseLayerUpdate)
184 m_baseLayerUpdate = true;
185 android::Mutex::Autolock lock(m_baseLayerLock);
186 SkSafeRef(m_baseLayer);
187 SkSafeUnref(m_currentBaseLayer);
188 m_currentBaseLayer = m_baseLayer;
190 invalRegion(m_invalidateRegion);
191 m_invalidateRegion.setEmpty();
194 void GLWebViewState::setExtra(BaseLayerAndroid* layer, SkPicture& picture,
195 const IntRect& rect, bool allowSame)
197 android::Mutex::Autolock lock(m_baseLayerLock);
198 if (!m_baseLayerUpdate)
201 layer->setExtra(picture);
203 if (!allowSame && m_lastInval == rect)
208 if (!m_lastInval.isEmpty())
211 m_displayRings = false;
214 void GLWebViewState::inval(const IntRect& rect)
216 if (m_baseLayerUpdate) {
217 m_currentPictureCounter++;
218 if (!rect.isEmpty()) {
219 // find which tiles fall within the invalRect and mark them as dirty
220 m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
221 m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
222 if (m_frameworkInval.isEmpty())
223 m_frameworkInval = rect;
225 m_frameworkInval.unite(rect);
226 XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
227 m_frameworkInval.y(), m_frameworkInval.right(), m_frameworkInval.bottom(),
228 rect.x(), rect.y(), rect.right(), rect.bottom());
231 m_invalidateRegion.op(rect.x(), rect.y(), rect.right(), rect.bottom(), SkRegion::kUnion_Op);
235 void GLWebViewState::resetRings()
237 m_displayRings = false;
240 void GLWebViewState::drawFocusRing(IntRect& srcRect)
242 // TODO: use a 9-patch texture to draw the focus ring
243 // instead of plain colors
244 const int border = 1;
245 const int fuzzyBorder = border * 2;
246 const int padding = 4;
247 const float alpha = 0.3;
248 const float borderAlpha = 0.40;
254 if (m_focusRingTexture == -1)
255 m_focusRingTexture = GLUtils::createSampleColorTexture(r, g, b);
257 SkRect rLeft, rTop, rRight, rBottom, rOverlay;
259 IntRect rect(srcRect.x() - padding, srcRect.y() - padding,
260 srcRect.width() + (padding * 2), srcRect.height() + (padding * 2));
261 rLeft.set(rect.x() - border, rect.y(),
262 rect.x(), rect.y() + rect.height());
263 rTop.set(rect.x() - border, rect.y() - border,
264 rect.x() + rect.width() + border, rect.y());
265 rRight.set(rect.x() + rect.width(), rect.y(),
266 rect.x() + rect.width() + border,
267 rect.y() + rect.height());
268 rBottom.set(rect.x() - border, rect.y() + rect.height(),
269 rect.x() + rect.width() + border,
270 rect.y() + rect.height() + border);
271 rOverlay.set(rect.x() - fuzzyBorder, rect.y() - fuzzyBorder,
272 rect.x() + rect.width() + fuzzyBorder,
273 rect.y() + rect.height() + fuzzyBorder);
275 TilesManager::instance()->shader()->drawQuad(rLeft, m_focusRingTexture, borderAlpha);
276 TilesManager::instance()->shader()->drawQuad(rTop, m_focusRingTexture, borderAlpha);
277 TilesManager::instance()->shader()->drawQuad(rRight, m_focusRingTexture, borderAlpha);
278 TilesManager::instance()->shader()->drawQuad(rBottom, m_focusRingTexture, borderAlpha);
279 TilesManager::instance()->shader()->drawQuad(rOverlay, m_focusRingTexture, alpha);
282 void GLWebViewState::paintExtras()
284 if (m_displayRings) {
285 // TODO: handles correctly the multi-rings case
286 for (int i=0; i<m_rings.size(); i++) {
287 IntRect rect = m_rings.at(i);
293 unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
295 android::Mutex::Autolock lock(m_baseLayerLock);
296 if (m_currentBaseLayer) {
297 m_globalButtonMutex->lock();
298 m_currentBaseLayer->drawCanvas(canvas);
299 m_globalButtonMutex->unlock();
301 return m_currentPictureCounter;
304 void GLWebViewState::scheduleUpdate(const double& currentTime,
305 const SkIRect& viewport, float scale)
307 // if no update time, set it
308 if (updateTime() == -1) {
309 m_scaleRequestState = kWillScheduleRequest;
310 setUpdateTime(currentTime + s_updateInitialDelay);
311 setFutureScale(scale);
312 setFutureViewport(viewport);
316 if (currentTime < updateTime())
319 // we reached the scheduled update time, check if we can update
320 if (futureScale() == scale) {
321 // we are still with the previous scale, let's go
323 m_scaleRequestState = kRequestNewScale;
326 // we reached the update time, but the planned update was for
327 // a different scale factor -- meaning the user is still probably
328 // in the process of zooming. Let's push the update time a bit.
329 setUpdateTime(currentTime + s_updateDelay);
330 setFutureScale(scale);
331 setFutureViewport(viewport);
335 double GLWebViewState::zoomInTransitionTime(double currentTime)
337 if (m_transitionTime == -1)
338 m_transitionTime = currentTime + s_zoomInTransitionDelay;
339 return m_transitionTime;
342 double GLWebViewState::zoomOutTransitionTime(double currentTime)
344 if (m_transitionTime == -1)
345 m_transitionTime = currentTime + s_zoomOutTransitionDelay;
346 return m_transitionTime;
350 float GLWebViewState::zoomInTransparency(double currentTime)
352 float t = zoomInTransitionTime(currentTime) - currentTime;
353 t *= s_invZoomInTransitionDelay;
354 return fmin(1, fmax(0, t));
357 float GLWebViewState::zoomOutTransparency(double currentTime)
359 float t = zoomOutTransitionTime(currentTime) - currentTime;
360 t *= s_invZoomOutTransitionDelay;
361 return fmin(1, fmax(0, t));
364 TiledPage* GLWebViewState::sibling(TiledPage* page)
366 return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA;
369 TiledPage* GLWebViewState::frontPage()
371 android::Mutex::Autolock lock(m_tiledPageLock);
372 return m_usePageA ? m_tiledPageA : m_tiledPageB;
375 TiledPage* GLWebViewState::backPage()
377 android::Mutex::Autolock lock(m_tiledPageLock);
378 return m_usePageA ? m_tiledPageB : m_tiledPageA;
381 void GLWebViewState::swapPages()
383 android::Mutex::Autolock lock(m_tiledPageLock);
385 TiledPage* working = m_usePageA ? m_tiledPageB : m_tiledPageA;
386 if (m_scaleRequestState != kNoScaleRequest)
387 TilesManager::instance()->resetTextureUsage(working);
389 m_scaleRequestState = kNoScaleRequest;
392 int GLWebViewState::baseContentWidth()
394 return m_currentBaseLayer ? m_currentBaseLayer->getWidth() : 0;
397 int GLWebViewState::baseContentHeight()
399 return m_currentBaseLayer ? m_currentBaseLayer->getHeight() : 0;
402 void GLWebViewState::setViewport(SkRect& viewport, float scale)
404 m_previousViewport = m_viewport;
405 if ((m_viewport == viewport) &&
406 (m_futureScale == scale))
409 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, m_currentScale, m_futureScale);
414 const float invTileContentWidth = scale / TilesManager::tileWidth();
415 const float invTileContentHeight = scale / TilesManager::tileHeight();
417 m_viewportTileBounds.set(
418 static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)),
419 static_cast<int>(floorf(viewport.fTop * invTileContentHeight)),
420 static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)),
421 static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight)));
423 int maxTextureCount = (m_viewportTileBounds.width() + TilesManager::instance()->expandedTileBoundsX() * 2 + 1) *
424 (m_viewportTileBounds.height() + TilesManager::instance()->expandedTileBoundsY() * 2 + 1) * 2;
425 TilesManager::instance()->setMaxTextureCount(maxTextureCount);
426 m_tiledPageA->updateBaseTileSize();
427 m_tiledPageB->updateBaseTileSize();
431 void GLWebViewState::dumpMeasures()
433 for (int i = 0; i < m_timeCounter; i++) {
434 XLOGC("%d delay: %d ms", m_totalTimeCounter + i,
435 static_cast<int>(m_delayTimes[i]*1000));
438 m_totalTimeCounter += m_timeCounter;
441 #endif // MEASURES_PERF
443 void GLWebViewState::resetFrameworkInval()
445 m_frameworkInval.setX(0);
446 m_frameworkInval.setY(0);
447 m_frameworkInval.setWidth(0);
448 m_frameworkInval.setHeight(0);
451 void GLWebViewState::addDirtyArea(const IntRect& rect)
456 IntRect inflatedRect = rect;
457 inflatedRect.inflate(8);
458 if (m_frameworkLayersInval.isEmpty())
459 m_frameworkLayersInval = inflatedRect;
461 m_frameworkLayersInval.unite(inflatedRect);
464 void GLWebViewState::resetLayersDirtyArea()
466 m_frameworkLayersInval.setX(0);
467 m_frameworkLayersInval.setY(0);
468 m_frameworkLayersInval.setWidth(0);
469 m_frameworkLayersInval.setHeight(0);
472 bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
473 IntRect& webViewRect, int titleBarHeight,
474 IntRect& clip, float scale, SkColor color)
478 double currentTime = WTF::currentTime();
479 double delta = currentTime - m_prevDrawTime;
481 if (delta < FRAMERATE_CAP) {
482 unsigned int usecs = (FRAMERATE_CAP - delta) * 1E6;
486 m_prevDrawTime = currentTime;
488 m_baseLayerLock.lock();
489 BaseLayerAndroid* baseLayer = m_currentBaseLayer;
490 SkSafeRef(baseLayer);
491 BaseLayerAndroid* baseForComposited = m_baseLayer;
492 SkSafeRef(baseForComposited);
493 m_baseLayerLock.unlock();
495 SkSafeUnref(baseForComposited);
499 XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
500 rect.x(), rect.y(), rect.right(), rect.bottom(),
501 viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);
503 resetLayersDirtyArea();
505 if (!baseForComposited ||
506 (baseForComposited && !baseForComposited->countChildren())) {
507 SkSafeRef(baseLayer);
508 SkSafeUnref(baseForComposited);
509 baseForComposited = baseLayer;
512 LayerAndroid* compositedRoot = 0;
513 if (baseForComposited && baseForComposited->countChildren() >= 1)
514 compositedRoot = static_cast<LayerAndroid*>(baseForComposited->getChild(0));
516 bool ret = baseLayer->drawGL(compositedRoot, rect, viewport, webViewRect, titleBarHeight, clip, scale, color);
517 m_previouslyUsedRoot = compositedRoot;
519 FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(m_frameworkInval);
520 IntRect inval(frameworkInval.x(), frameworkInval.y(), frameworkInval.width(), frameworkInval.height());
522 inval.unite(m_frameworkLayersInval);
524 invalRect->setX(inval.x());
525 invalRect->setY(inval.y());
526 invalRect->setWidth(inval.width());
527 invalRect->setHeight(inval.height());
529 XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
530 inval.y(), inval.right(), inval.bottom());
532 resetFrameworkInval();
536 if (m_measurePerfs) {
537 m_delayTimes[m_timeCounter++] = delta;
538 if (m_timeCounter >= MAX_MEASURES_PERF)
543 SkSafeUnref(baseForComposited);
544 SkSafeUnref(baseLayer);
548 } // namespace WebCore
550 #endif // USE(ACCELERATED_COMPOSITING)