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 "BaseLayerAndroid.h"
28
29 #if USE(ACCELERATED_COMPOSITING)
30 #include "ClassTracker.h"
31 #include "GLUtils.h"
32 #include "ShaderProgram.h"
33 #include "SkCanvas.h"
34 #include "TilesManager.h"
35 #include <GLES2/gl2.h>
36 #include <wtf/CurrentTime.h>
37 #endif // USE(ACCELERATED_COMPOSITING)
38
39 #include <cutils/log.h>
40 #include <wtf/text/CString.h>
41
42 #undef XLOGC
43 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "BaseLayerAndroid", __VA_ARGS__)
44
45 #ifdef DEBUG
46
47 #undef XLOG
48 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "BaseLayerAndroid", __VA_ARGS__)
49
50 #else
51
52 #undef XLOG
53 #define XLOG(...)
54
55 #endif // DEBUG
56
57 // TODO: dynamically determine based on DPI
58 #define PREFETCH_SCALE_MODIFIER 0.3
59 #define PREFETCH_OPACITY 1
60 #define PREFETCH_X_DIST 0
61 #define PREFETCH_Y_DIST 1
62
63 namespace WebCore {
64
65 using namespace android;
66
BaseLayerAndroid()67 BaseLayerAndroid::BaseLayerAndroid()
68 #if USE(ACCELERATED_COMPOSITING)
69 : m_glWebViewState(0)
70 , m_color(Color::white)
71 , m_scrollState(NotScrolling)
72 #endif
73 {
74 #ifdef DEBUG_COUNT
75 ClassTracker::instance()->increment("BaseLayerAndroid");
76 #endif
77 }
78
~BaseLayerAndroid()79 BaseLayerAndroid::~BaseLayerAndroid()
80 {
81 m_content.clear();
82 #ifdef DEBUG_COUNT
83 ClassTracker::instance()->decrement("BaseLayerAndroid");
84 #endif
85 }
86
setContent(const PictureSet & src)87 void BaseLayerAndroid::setContent(const PictureSet& src)
88 {
89 #if USE(ACCELERATED_COMPOSITING)
90 // FIXME: We lock here because we do not want
91 // to paint and change the m_content concurrently.
92 // We should instead refactor PictureSet to use
93 // an atomic refcounting scheme and use atomic operations
94 // to swap PictureSets.
95 android::Mutex::Autolock lock(m_drawLock);
96 #endif
97 m_content.set(src);
98 // FIXME: We cannot set the size of the base layer because it will screw up
99 // the matrix used. We need to fix matrix computation for the base layer
100 // and then we can set the size.
101 // setSize(src.width(), src.height());
102 }
103
setExtra(SkPicture & src)104 void BaseLayerAndroid::setExtra(SkPicture& src)
105 {
106 #if USE(ACCELERATED_COMPOSITING)
107 android::Mutex::Autolock lock(m_drawLock);
108 #endif
109 m_extra.swap(src);
110 }
111
drawCanvas(SkCanvas * canvas)112 void BaseLayerAndroid::drawCanvas(SkCanvas* canvas)
113 {
114 #if USE(ACCELERATED_COMPOSITING)
115 android::Mutex::Autolock lock(m_drawLock);
116 #endif
117 if (!m_content.isEmpty())
118 m_content.draw(canvas);
119 // TODO : replace with !m_extra.isEmpty() once such a call exists
120 if (m_extra.width() > 0)
121 m_extra.draw(canvas);
122 }
123
124 #if USE(ACCELERATED_COMPOSITING)
125
prefetchBasePicture(SkRect & viewport,float currentScale,TiledPage * prefetchTiledPage)126 void BaseLayerAndroid::prefetchBasePicture(SkRect& viewport, float currentScale,
127 TiledPage* prefetchTiledPage)
128 {
129 SkIRect bounds;
130 float prefetchScale = currentScale * PREFETCH_SCALE_MODIFIER;
131
132 float invTileWidth = (prefetchScale)
133 / TilesManager::instance()->tileWidth();
134 float invTileHeight = (prefetchScale)
135 / TilesManager::instance()->tileHeight();
136 bool goingDown = m_glWebViewState->goingDown();
137 bool goingLeft = m_glWebViewState->goingLeft();
138
139
140 XLOG("fetch rect %f %f %f %f, scale %f",
141 viewport.fLeft,
142 viewport.fTop,
143 viewport.fRight,
144 viewport.fBottom,
145 scale);
146
147 bounds.fLeft = static_cast<int>(floorf(viewport.fLeft * invTileWidth)) - PREFETCH_X_DIST;
148 bounds.fTop = static_cast<int>(floorf(viewport.fTop * invTileHeight)) - PREFETCH_Y_DIST;
149 bounds.fRight = static_cast<int>(ceilf(viewport.fRight * invTileWidth)) + PREFETCH_X_DIST;
150 bounds.fBottom = static_cast<int>(ceilf(viewport.fBottom * invTileHeight)) + PREFETCH_Y_DIST;
151
152 XLOG("prefetch rect %d %d %d %d, scale %f, preparing page %p",
153 bounds.fLeft, bounds.fTop,
154 bounds.fRight, bounds.fBottom,
155 scale * PREFETCH_SCALE,
156 prefetchTiledPage);
157
158 prefetchTiledPage->setScale(prefetchScale);
159 prefetchTiledPage->updateTileDirtiness(bounds);
160 prefetchTiledPage->prepare(goingDown, goingLeft, bounds,
161 TiledPage::ExpandedBounds);
162 prefetchTiledPage->swapBuffersIfReady(bounds,
163 prefetchScale,
164 TiledPage::SwapWhateverIsReady);
165 prefetchTiledPage->draw(PREFETCH_OPACITY, bounds);
166 }
167
drawBasePictureInGL(SkRect & viewport,float scale,double currentTime,bool * buffersSwappedPtr)168 bool BaseLayerAndroid::drawBasePictureInGL(SkRect& viewport, float scale,
169 double currentTime, bool* buffersSwappedPtr)
170 {
171 ZoomManager* zoomManager = m_glWebViewState->zoomManager();
172
173 bool goingDown = m_glWebViewState->goingDown();
174 bool goingLeft = m_glWebViewState->goingLeft();
175
176 const SkIRect& viewportTileBounds = m_glWebViewState->viewportTileBounds();
177 XLOG("drawBasePicture, TX: %d, TY: %d scale %.2f", viewportTileBounds.fLeft,
178 viewportTileBounds.fTop, scale);
179
180 // Query the resulting state from the zoom manager
181 bool prepareNextTiledPage = zoomManager->needPrepareNextTiledPage();
182
183 // Display the current page
184 TiledPage* tiledPage = m_glWebViewState->frontPage();
185 TiledPage* nextTiledPage = m_glWebViewState->backPage();
186 tiledPage->setScale(zoomManager->currentScale());
187
188 // Let's prepare the page if needed so that it will start painting
189 if (prepareNextTiledPage) {
190 nextTiledPage->setScale(scale);
191 m_glWebViewState->setFutureViewport(viewportTileBounds);
192 m_glWebViewState->lockBaseLayerUpdate();
193
194 // ignore dirtiness return value since while zooming we repaint regardless
195 nextTiledPage->updateTileDirtiness(viewportTileBounds);
196
197 nextTiledPage->prepare(goingDown, goingLeft, viewportTileBounds,
198 TiledPage::VisibleBounds);
199 // Cancel pending paints for the foreground page
200 TilesManager::instance()->removePaintOperationsForPage(tiledPage, false);
201 }
202
203 // If we fired a request, let's check if it's ready to use
204 if (zoomManager->didFireRequest()) {
205 if (nextTiledPage->swapBuffersIfReady(viewportTileBounds,
206 zoomManager->futureScale(),
207 TiledPage::SwapWholePage))
208 zoomManager->setReceivedRequest(); // transition to received request state
209 }
210
211 float transparency = 1;
212 bool doZoomPageSwap = false;
213
214 // If the page is ready, display it. We do a short transition between
215 // the two pages (current one and future one with the new scale factor)
216 if (zoomManager->didReceivedRequest()) {
217 float nextTiledPageTransparency = 1;
218 zoomManager->processTransition(currentTime, scale, &doZoomPageSwap,
219 &nextTiledPageTransparency, &transparency);
220 nextTiledPage->draw(nextTiledPageTransparency, viewportTileBounds);
221 }
222
223 const SkIRect& preZoomBounds = m_glWebViewState->preZoomBounds();
224
225 // update scrolling state machine by querying glwebviewstate - note that the
226 // NotScrolling state is only set below
227 if (m_glWebViewState->isScrolling())
228 m_scrollState = Scrolling;
229 else if (m_scrollState == Scrolling)
230 m_scrollState = ScrollingFinishPaint;
231
232 bool scrolling = m_scrollState != NotScrolling;
233 bool zooming = ZoomManager::kNoScaleRequest != zoomManager->scaleRequestState();
234
235 // When we aren't zooming, we should TRY and swap tile buffers if they're
236 // ready. When scrolling, we swap whatever's ready. Otherwise, buffer until
237 // the entire page is ready and then swap.
238 bool buffersSwapped = false;
239 if (!zooming) {
240 TiledPage::SwapMethod swapMethod;
241 if (scrolling)
242 swapMethod = TiledPage::SwapWhateverIsReady;
243 else
244 swapMethod = TiledPage::SwapWholePage;
245
246 buffersSwapped = tiledPage->swapBuffersIfReady(preZoomBounds,
247 zoomManager->currentScale(),
248 swapMethod);
249
250 if (buffersSwappedPtr && buffersSwapped)
251 *buffersSwappedPtr = true;
252 if (buffersSwapped) {
253 if (m_scrollState == ScrollingFinishPaint) {
254 m_scrollState = NotScrolling;
255 scrolling = false;
256 }
257 }
258 }
259
260 if (doZoomPageSwap) {
261 zoomManager->setCurrentScale(scale);
262 m_glWebViewState->swapPages();
263 if (buffersSwappedPtr)
264 *buffersSwappedPtr = true;
265 }
266
267
268 bool needsRedraw = scrolling || zooming || !buffersSwapped;
269
270 // if we don't expect to redraw, unlock the invals
271 if (!needsRedraw)
272 m_glWebViewState->unlockBaseLayerUpdate();
273
274 // if applied invals mark tiles dirty, need to redraw
275 needsRedraw |= tiledPage->updateTileDirtiness(preZoomBounds);
276
277 if (needsRedraw) {
278 // lock and paint what's needed unless we're zooming, since the new
279 // tiles won't be relevant soon anyway
280 m_glWebViewState->lockBaseLayerUpdate();
281 if (!zooming)
282 tiledPage->prepare(goingDown, goingLeft, preZoomBounds,
283 TiledPage::ExpandedBounds);
284 }
285
286 XLOG("scrolling %d, zooming %d, buffersSwapped %d, needsRedraw %d",
287 scrolling, zooming, buffersSwapped, needsRedraw);
288
289 // prefetch in the nextTiledPage if unused by zooming (even if not scrolling
290 // since we want the tiles to be ready before they're needed)
291 bool usePrefetchPage = !zooming;
292 nextTiledPage->setIsPrefetchPage(usePrefetchPage);
293 if (usePrefetchPage)
294 prefetchBasePicture(viewport, scale, nextTiledPage);
295
296 tiledPage->draw(transparency, preZoomBounds);
297
298 return needsRedraw;
299 }
300 #endif // USE(ACCELERATED_COMPOSITING)
301
drawGL(double currentTime,LayerAndroid * compositedRoot,IntRect & viewRect,SkRect & visibleRect,float scale,bool * buffersSwappedPtr)302 bool BaseLayerAndroid::drawGL(double currentTime, LayerAndroid* compositedRoot,
303 IntRect& viewRect, SkRect& visibleRect, float scale,
304 bool* buffersSwappedPtr)
305 {
306 bool needsRedraw = false;
307 #if USE(ACCELERATED_COMPOSITING)
308
309 needsRedraw = drawBasePictureInGL(visibleRect, scale, currentTime,
310 buffersSwappedPtr);
311
312 if (!needsRedraw)
313 m_glWebViewState->resetFrameworkInval();
314
315 if (compositedRoot) {
316 TransformationMatrix ident;
317
318 bool animsRunning = compositedRoot->evaluateAnimations();
319 if (animsRunning)
320 needsRedraw = true;
321
322 compositedRoot->updateFixedLayersPositions(visibleRect);
323 FloatRect clip(0, 0, viewRect.width(), viewRect.height());
324 compositedRoot->updateGLPositionsAndScale(
325 ident, clip, 1, m_glWebViewState->zoomManager()->layersScale());
326 SkMatrix matrix;
327 matrix.setTranslate(viewRect.x(), viewRect.y());
328
329 #ifdef DEBUG
330 compositedRoot->showLayer(0);
331 XLOG("We have %d layers, %d textured",
332 compositedRoot->nbLayers(),
333 compositedRoot->nbTexturedLayers());
334 #endif
335
336 // Clean up GL textures for video layer.
337 TilesManager::instance()->videoLayerManager()->deleteUnusedTextures();
338
339 compositedRoot->prepare(m_glWebViewState);
340 if (compositedRoot->drawGL(m_glWebViewState, matrix)) {
341 if (TilesManager::instance()->layerTexturesRemain()) {
342 // only try redrawing for layers if layer textures remain,
343 // otherwise we'll repaint without getting anything done
344 needsRedraw = true;
345 }
346 } else if (!animsRunning)
347 m_glWebViewState->resetLayersDirtyArea();
348
349 }
350
351 m_previousVisible = visibleRect;
352
353 #endif // USE(ACCELERATED_COMPOSITING)
354 #ifdef DEBUG
355 ClassTracker::instance()->show();
356 #endif
357 return needsRedraw;
358 }
359
360 } // namespace WebCore
361