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 "SkPath.h"
37 #include "TilesManager.h"
38 #include "TilesTracker.h"
39 #include <wtf/CurrentTime.h>
40
41 #include <cutils/log.h>
42 #include <wtf/text/CString.h>
43
44 #undef XLOGC
45 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
46
47 #ifdef DEBUG
48
49 #undef XLOG
50 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__)
51
52 #else
53
54 #undef XLOG
55 #define XLOG(...)
56
57 #endif // DEBUG
58
59 #define FIRST_TILED_PAGE_ID 1
60 #define SECOND_TILED_PAGE_ID 2
61
62 #define FRAMERATE_CAP 0.01666 // We cap at 60 fps
63
64 // log warnings if scale goes outside this range
65 #define MIN_SCALE_WARNING 0.1
66 #define MAX_SCALE_WARNING 10
67
68 namespace WebCore {
69
70 using namespace android;
71
GLWebViewState(android::Mutex * buttonMutex)72 GLWebViewState::GLWebViewState(android::Mutex* buttonMutex)
73 : m_zoomManager(this)
74 , m_paintingBaseLayer(0)
75 , m_currentBaseLayer(0)
76 , m_currentBaseLayerRoot(0)
77 , m_currentPictureCounter(0)
78 , m_usePageA(true)
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)
85 , m_goingDown(true)
86 , m_goingLeft(false)
87 , m_expandedTileBoundsX(0)
88 , m_expandedTileBoundsY(0)
89 , m_scale(1)
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
~GLWebViewState()109 GLWebViewState::~GLWebViewState()
110 {
111 // Unref the existing tree/PaintedSurfaces
112 if (m_currentBaseLayerRoot)
113 TilesManager::instance()->swapLayersTextures(m_currentBaseLayerRoot, 0);
114
115 // Take care of the transfer queue such that Tex Gen thread will not stuck
116 TilesManager::instance()->unregisterGLWebViewState(this);
117
118 // We have to destroy the two tiled pages first as their destructor
119 // may depend on the existence of this GLWebViewState and some of its
120 // instance variables in order to complete.
121 // Explicitely, currently we need to have the m_paintingBaseLayer around
122 // in order to complete any pending paint operations (the tiled pages
123 // will remove any pending operations, and wait if one is underway).
124 delete m_tiledPageA;
125 delete m_tiledPageB;
126 SkSafeUnref(m_paintingBaseLayer);
127 SkSafeUnref(m_currentBaseLayer);
128 SkSafeUnref(m_currentBaseLayerRoot);
129 m_paintingBaseLayer = 0;
130 m_currentBaseLayer = 0;
131 m_currentBaseLayerRoot = 0;
132 #ifdef DEBUG_COUNT
133 ClassTracker::instance()->decrement("GLWebViewState");
134 #endif
135
136 }
137
setBaseLayer(BaseLayerAndroid * layer,const SkRegion & inval,bool showVisualIndicator,bool isPictureAfterFirstLayout)138 void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
139 bool showVisualIndicator, bool isPictureAfterFirstLayout)
140 {
141 android::Mutex::Autolock lock(m_baseLayerLock);
142 if (!layer || isPictureAfterFirstLayout) {
143 m_tiledPageA->discardTextures();
144 m_tiledPageB->discardTextures();
145 }
146 if (isPictureAfterFirstLayout) {
147 m_baseLayerUpdate = true;
148 m_invalidateRegion.setEmpty();
149 }
150 if (m_currentBaseLayer && layer)
151 m_currentBaseLayer->swapExtra(layer);
152
153 SkSafeRef(layer);
154 SkSafeUnref(m_currentBaseLayer);
155 m_currentBaseLayer = layer;
156
157
158 // copy content from old composited root to new
159 LayerAndroid* oldRoot = m_currentBaseLayerRoot;
160 if (layer) {
161 layer->setGLWebViewState(this);
162 m_currentBaseLayerRoot = static_cast<LayerAndroid*>(layer->getChild(0));
163 SkSafeRef(m_currentBaseLayerRoot);
164 } else {
165 m_currentBaseLayerRoot = 0;
166 }
167 if (oldRoot != m_currentBaseLayerRoot)
168 TilesManager::instance()->swapLayersTextures(oldRoot, m_currentBaseLayerRoot);
169 SkSafeUnref(oldRoot);
170
171 // We only update the base layer if we are not currently
172 // waiting for a tiledPage to be painted
173 if (m_baseLayerUpdate) {
174 SkSafeRef(layer);
175 SkSafeUnref(m_paintingBaseLayer);
176 m_paintingBaseLayer = layer;
177 }
178 m_glExtras.setDrawExtra(0);
179 invalRegion(inval);
180
181 #ifdef MEASURES_PERF
182 if (m_measurePerfs && !showVisualIndicator)
183 dumpMeasures();
184 m_measurePerfs = showVisualIndicator;
185 #endif
186
187 TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
188 }
189
invalRegion(const SkRegion & region)190 void GLWebViewState::invalRegion(const SkRegion& region)
191 {
192 SkRegion::Iterator iterator(region);
193 while (!iterator.done()) {
194 SkIRect r = iterator.rect();
195 IntRect ir(r.fLeft, r.fTop, r.width(), r.height());
196 inval(ir);
197 iterator.next();
198 }
199 }
200
unlockBaseLayerUpdate()201 void GLWebViewState::unlockBaseLayerUpdate() {
202 if (m_baseLayerUpdate)
203 return;
204
205 m_baseLayerUpdate = true;
206 android::Mutex::Autolock lock(m_baseLayerLock);
207 SkSafeRef(m_currentBaseLayer);
208 SkSafeUnref(m_paintingBaseLayer);
209 m_paintingBaseLayer = m_currentBaseLayer;
210
211 invalRegion(m_invalidateRegion);
212 m_invalidateRegion.setEmpty();
213 }
214
inval(const IntRect & rect)215 void GLWebViewState::inval(const IntRect& rect)
216 {
217 if (m_baseLayerUpdate) {
218 // base layer isn't locked, so go ahead and issue the inval to both tiled pages
219 m_currentPictureCounter++;
220 if (!rect.isEmpty()) {
221 // find which tiles fall within the invalRect and mark them as dirty
222 m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
223 m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
224 if (m_frameworkInval.isEmpty())
225 m_frameworkInval = rect;
226 else
227 m_frameworkInval.unite(rect);
228 XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
229 m_frameworkInval.y(), m_frameworkInval.width(), m_frameworkInval.height(),
230 rect.x(), rect.y(), rect.width(), rect.height());
231 }
232 } else {
233 // base layer is locked, so defer invalidation until unlockBaseLayerUpdate()
234 m_invalidateRegion.op(rect.x(), rect.y(), rect.maxX(), rect.maxY(), SkRegion::kUnion_Op);
235 }
236 TilesManager::instance()->getProfiler()->nextInval(rect, zoomManager()->currentScale());
237 }
238
paintBaseLayerContent(SkCanvas * canvas)239 unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
240 {
241 android::Mutex::Autolock lock(m_baseLayerLock);
242 if (m_paintingBaseLayer) {
243 m_globalButtonMutex->lock();
244 m_paintingBaseLayer->drawCanvas(canvas);
245 m_globalButtonMutex->unlock();
246 }
247 return m_currentPictureCounter;
248 }
249
sibling(TiledPage * page)250 TiledPage* GLWebViewState::sibling(TiledPage* page)
251 {
252 return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA;
253 }
254
frontPage()255 TiledPage* GLWebViewState::frontPage()
256 {
257 android::Mutex::Autolock lock(m_tiledPageLock);
258 return m_usePageA ? m_tiledPageA : m_tiledPageB;
259 }
260
backPage()261 TiledPage* GLWebViewState::backPage()
262 {
263 android::Mutex::Autolock lock(m_tiledPageLock);
264 return m_usePageA ? m_tiledPageB : m_tiledPageA;
265 }
266
swapPages()267 void GLWebViewState::swapPages()
268 {
269 android::Mutex::Autolock lock(m_tiledPageLock);
270 m_usePageA ^= true;
271 TiledPage* oldPage = m_usePageA ? m_tiledPageB : m_tiledPageA;
272 zoomManager()->swapPages();
273 oldPage->discardTextures();
274 }
275
baseContentWidth()276 int GLWebViewState::baseContentWidth()
277 {
278 return m_paintingBaseLayer ? m_paintingBaseLayer->content()->width() : 0;
279 }
baseContentHeight()280 int GLWebViewState::baseContentHeight()
281 {
282 return m_paintingBaseLayer ? m_paintingBaseLayer->content()->height() : 0;
283 }
284
setViewport(SkRect & viewport,float scale)285 void GLWebViewState::setViewport(SkRect& viewport, float scale)
286 {
287 if ((m_viewport == viewport) &&
288 (zoomManager()->futureScale() == scale))
289 return;
290
291 m_goingDown = m_viewport.fTop - viewport.fTop <= 0;
292 m_goingLeft = m_viewport.fLeft - viewport.fLeft >= 0;
293 m_viewport = viewport;
294
295 XLOG("New VIEWPORT %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f currentScale: %.2f futureScale: %.2f)",
296 m_viewport.fLeft, m_viewport.fTop, m_viewport.fRight, m_viewport.fBottom,
297 m_viewport.width(), m_viewport.height(), scale,
298 zoomManager()->currentScale(), zoomManager()->futureScale());
299
300 const float invTileContentWidth = scale / TilesManager::tileWidth();
301 const float invTileContentHeight = scale / TilesManager::tileHeight();
302
303 m_viewportTileBounds.set(
304 static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)),
305 static_cast<int>(floorf(viewport.fTop * invTileContentHeight)),
306 static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)),
307 static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight)));
308
309 // allocate max possible number of tiles visible with this viewport
310 int viewMaxTileX = static_cast<int>(ceilf((viewport.width()-1) * invTileContentWidth)) + 1;
311 int viewMaxTileY = static_cast<int>(ceilf((viewport.height()-1) * invTileContentHeight)) + 1;
312
313 // NOTE: fetching 4 viewports worth, may need to be adjusted per-device
314 int maxTextureCount = (viewMaxTileX + TILE_PREFETCH_DISTANCE * 2) *
315 (viewMaxTileY + TILE_PREFETCH_DISTANCE * 2) * 4;
316 TilesManager::instance()->setMaxTextureCount(maxTextureCount);
317 m_tiledPageA->updateBaseTileSize();
318 m_tiledPageB->updateBaseTileSize();
319 }
320
321 #ifdef MEASURES_PERF
dumpMeasures()322 void GLWebViewState::dumpMeasures()
323 {
324 for (int i = 0; i < m_timeCounter; i++) {
325 XLOGC("%d delay: %d ms", m_totalTimeCounter + i,
326 static_cast<int>(m_delayTimes[i]*1000));
327 m_delayTimes[i] = 0;
328 }
329 m_totalTimeCounter += m_timeCounter;
330 m_timeCounter = 0;
331 }
332 #endif // MEASURES_PERF
333
resetFrameworkInval()334 void GLWebViewState::resetFrameworkInval()
335 {
336 m_frameworkInval.setX(0);
337 m_frameworkInval.setY(0);
338 m_frameworkInval.setWidth(0);
339 m_frameworkInval.setHeight(0);
340 }
341
addDirtyArea(const IntRect & rect)342 void GLWebViewState::addDirtyArea(const IntRect& rect)
343 {
344 if (rect.isEmpty())
345 return;
346
347 IntRect inflatedRect = rect;
348 inflatedRect.inflate(8);
349 if (m_frameworkLayersInval.isEmpty())
350 m_frameworkLayersInval = inflatedRect;
351 else
352 m_frameworkLayersInval.unite(inflatedRect);
353 }
354
resetLayersDirtyArea()355 void GLWebViewState::resetLayersDirtyArea()
356 {
357 m_frameworkLayersInval.setX(0);
358 m_frameworkLayersInval.setY(0);
359 m_frameworkLayersInval.setWidth(0);
360 m_frameworkLayersInval.setHeight(0);
361 }
362
setupDrawing(IntRect & viewRect,SkRect & visibleRect,IntRect & webViewRect,int titleBarHeight,IntRect & screenClip,float scale)363 double GLWebViewState::setupDrawing(IntRect& viewRect, SkRect& visibleRect,
364 IntRect& webViewRect, int titleBarHeight,
365 IntRect& screenClip, float scale)
366 {
367 int left = viewRect.x();
368 int top = viewRect.y();
369 int width = viewRect.width();
370 int height = viewRect.height();
371
372 if (TilesManager::instance()->invertedScreen()) {
373 float color = 1.0 - ((((float) m_backgroundColor.red() / 255.0) +
374 ((float) m_backgroundColor.green() / 255.0) +
375 ((float) m_backgroundColor.blue() / 255.0)) / 3.0);
376 glClearColor(color, color, color, 1);
377 } else {
378 glClearColor((float)m_backgroundColor.red() / 255.0,
379 (float)m_backgroundColor.green() / 255.0,
380 (float)m_backgroundColor.blue() / 255.0, 1);
381 }
382 glClear(GL_COLOR_BUFFER_BIT);
383
384 glViewport(left, top, width, height);
385
386 ShaderProgram* shader = TilesManager::instance()->shader();
387 if (shader->program() == -1) {
388 XLOG("Reinit shader");
389 shader->init();
390 }
391 shader->setViewRect(viewRect);
392 shader->setViewport(visibleRect);
393 shader->setWebViewRect(webViewRect);
394 shader->setTitleBarHeight(titleBarHeight);
395 shader->setScreenClip(screenClip);
396 shader->resetBlending();
397
398 double currentTime = WTF::currentTime();
399
400 setViewport(visibleRect, scale);
401 m_zoomManager.processNewScale(currentTime, scale);
402
403 return currentTime;
404 }
405
drawGL(IntRect & rect,SkRect & viewport,IntRect * invalRect,IntRect & webViewRect,int titleBarHeight,IntRect & clip,float scale,bool * buffersSwappedPtr)406 bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
407 IntRect& webViewRect, int titleBarHeight,
408 IntRect& clip, float scale, bool* buffersSwappedPtr)
409 {
410 m_scale = scale;
411 TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft,
412 viewport.fTop,
413 viewport.fRight,
414 viewport.fBottom,
415 scale);
416 TilesManager::instance()->incDrawGLCount();
417
418 #ifdef DEBUG
419 TilesManager::instance()->getTilesTracker()->clear();
420 #endif
421
422 m_baseLayerLock.lock();
423 BaseLayerAndroid* baseLayer = m_paintingBaseLayer;
424 SkSafeRef(baseLayer);
425 m_baseLayerLock.unlock();
426 if (!baseLayer)
427 return false;
428
429 float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO;
430 float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO;
431 bool useHorzPrefetch = viewWidth < baseContentWidth();
432 bool useVertPrefetch = viewHeight < baseContentHeight();
433 m_expandedTileBoundsX = (useHorzPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
434 m_expandedTileBoundsY = (useVertPrefetch) ? TILE_PREFETCH_DISTANCE : 0;
435
436 XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)",
437 rect.x(), rect.y(), rect.width(), rect.height(),
438 viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom);
439
440 resetLayersDirtyArea();
441
442 LayerAndroid* compositedRoot = m_currentBaseLayerRoot;
443 LayerAndroid* paintingBaseLayerRoot = 0;
444 if (baseLayer && baseLayer->countChildren() >= 1)
445 paintingBaseLayerRoot = static_cast<LayerAndroid*>(baseLayer->getChild(0));
446
447 // when adding or removing layers, use the the paintingBaseLayer's tree so
448 // that content that moves to the base layer from a layer is synchronized
449 bool paintingHasLayers = (paintingBaseLayerRoot == 0);
450 bool currentHasLayers = (m_currentBaseLayerRoot == 0);
451 if (paintingHasLayers != currentHasLayers)
452 compositedRoot = paintingBaseLayerRoot;
453
454 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
455 XLOGC("WARNING, scale seems corrupted before update: %e", scale);
456
457 // Here before we draw, update the BaseTile which has updated content.
458 // Inside this function, just do GPU blits from the transfer queue into
459 // the BaseTiles' texture.
460 TilesManager::instance()->transferQueue()->updateDirtyBaseTiles();
461
462 // Upload any pending ImageTexture
463 // Return true if we still have some images to upload.
464 // TODO: upload as many textures as possible within a certain time limit
465 bool ret = ImagesManager::instance()->uploadTextures();
466
467 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
468 XLOGC("WARNING, scale seems corrupted after update: %e", scale);
469
470 // gather the textures we can use
471 TilesManager::instance()->gatherLayerTextures();
472
473 // set up zoom manager, shaders, etc.
474 m_backgroundColor = baseLayer->getBackgroundColor();
475 double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale);
476 ret |= baseLayer->drawGL(currentTime, compositedRoot, rect,
477 viewport, scale, buffersSwappedPtr);
478 m_glExtras.drawGL(webViewRect, viewport, titleBarHeight);
479
480 glBindBuffer(GL_ARRAY_BUFFER, 0);
481
482 ret |= TilesManager::instance()->invertedScreenSwitch();
483
484 if (ret) {
485 // ret==true && empty inval region means we've inval'd everything,
486 // but don't have new content. Keep redrawing full view (0,0,0,0)
487 // until tile generation catches up and we swap pages.
488 bool fullScreenInval = m_frameworkInval.isEmpty();
489
490 if (TilesManager::instance()->invertedScreenSwitch()) {
491 fullScreenInval = true;
492 TilesManager::instance()->setInvertedScreenSwitch(false);
493 }
494
495 if (!fullScreenInval) {
496 FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(
497 m_frameworkInval);
498 // Inflate the invalidate rect to avoid precision lost.
499 frameworkInval.inflate(1);
500 IntRect inval(frameworkInval.x(), frameworkInval.y(),
501 frameworkInval.width(), frameworkInval.height());
502
503 inval.unite(m_frameworkLayersInval);
504
505 invalRect->setX(inval.x());
506 invalRect->setY(inval.y());
507 invalRect->setWidth(inval.width());
508 invalRect->setHeight(inval.height());
509
510 XLOG("invalRect(%d, %d, %d, %d)", inval.x(),
511 inval.y(), inval.width(), inval.height());
512
513 if (!invalRect->intersects(rect)) {
514 // invalidate is occurring offscreen, do full inval to guarantee redraw
515 fullScreenInval = true;
516 }
517 }
518
519 if (fullScreenInval) {
520 invalRect->setX(0);
521 invalRect->setY(0);
522 invalRect->setWidth(0);
523 invalRect->setHeight(0);
524 }
525 } else {
526 resetFrameworkInval();
527 }
528
529 #ifdef MEASURES_PERF
530 if (m_measurePerfs) {
531 m_delayTimes[m_timeCounter++] = delta;
532 if (m_timeCounter >= MAX_MEASURES_PERF)
533 dumpMeasures();
534 }
535 #endif
536
537 SkSafeUnref(baseLayer);
538 #ifdef DEBUG
539 TilesManager::instance()->getTilesTracker()->showTrackTextures();
540 ImagesManager::instance()->showImages();
541 #endif
542 return ret;
543 }
544
545 } // namespace WebCore
546
547 #endif // USE(ACCELERATED_COMPOSITING)
548