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 #define LOG_TAG "GLWebViewState"
27 #define LOG_NDEBUG 1
28
29 #include "config.h"
30 #include "GLWebViewState.h"
31
32 #if USE(ACCELERATED_COMPOSITING)
33
34 #include "AndroidLog.h"
35 #include "BaseLayerAndroid.h"
36 #include "ClassTracker.h"
37 #include "GLUtils.h"
38 #include "ImagesManager.h"
39 #include "LayerAndroid.h"
40 #include "private/hwui/DrawGlInfo.h"
41 #include "ScrollableLayerAndroid.h"
42 #include "SkPath.h"
43 #include "TilesManager.h"
44 #include "TransferQueue.h"
45 #include "SurfaceCollection.h"
46 #include "SurfaceCollectionManager.h"
47 #include <pthread.h>
48 #include <wtf/CurrentTime.h>
49
50 // log warnings if scale goes outside this range
51 #define MIN_SCALE_WARNING 0.1
52 #define MAX_SCALE_WARNING 10
53
54 // fps indicator is FPS_INDICATOR_HEIGHT pixels high.
55 // The max width is equal to MAX_FPS_VALUE fps.
56 #define FPS_INDICATOR_HEIGHT 10
57 #define MAX_FPS_VALUE 60
58
59 #define COLLECTION_SWAPPED_COUNTER_MODULE 10
60
61 namespace WebCore {
62
63 using namespace android::uirenderer;
64
GLWebViewState()65 GLWebViewState::GLWebViewState()
66 : m_frameworkLayersInval(0, 0, 0, 0)
67 , m_doFrameworkFullInval(false)
68 , m_isScrolling(false)
69 , m_isVisibleContentRectScrolling(false)
70 , m_goingDown(true)
71 , m_goingLeft(false)
72 , m_scale(1)
73 , m_layersRenderingMode(kAllTextures)
74 , m_surfaceCollectionManager()
75 {
76 m_visibleContentRect.setEmpty();
77
78 #ifdef DEBUG_COUNT
79 ClassTracker::instance()->increment("GLWebViewState");
80 #endif
81 #ifdef MEASURES_PERF
82 m_timeCounter = 0;
83 m_totalTimeCounter = 0;
84 m_measurePerfs = false;
85 #endif
86 }
87
~GLWebViewState()88 GLWebViewState::~GLWebViewState()
89 {
90 #ifdef DEBUG_COUNT
91 ClassTracker::instance()->decrement("GLWebViewState");
92 #endif
93
94 }
95
setBaseLayer(BaseLayerAndroid * layer,bool showVisualIndicator,bool isPictureAfterFirstLayout)96 bool GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, bool showVisualIndicator,
97 bool isPictureAfterFirstLayout)
98 {
99 if (!layer || isPictureAfterFirstLayout)
100 m_layersRenderingMode = kAllTextures;
101
102 SurfaceCollection* collection = 0;
103 if (layer) {
104 ALOGV("layer tree %p, with child %p", layer, layer->getChild(0));
105 layer->setState(this);
106 collection = new SurfaceCollection(layer);
107 }
108 bool queueFull = m_surfaceCollectionManager.updateWithSurfaceCollection(
109 collection, isPictureAfterFirstLayout);
110 m_glExtras.setDrawExtra(0);
111
112 #ifdef MEASURES_PERF
113 if (m_measurePerfs && !showVisualIndicator)
114 dumpMeasures();
115 m_measurePerfs = showVisualIndicator;
116 #endif
117
118 TilesManager::instance()->setShowVisualIndicator(showVisualIndicator);
119 return queueFull;
120 }
121
scrollLayer(int layerId,int x,int y)122 void GLWebViewState::scrollLayer(int layerId, int x, int y)
123 {
124 m_surfaceCollectionManager.updateScrollableLayer(layerId, x, y);
125 }
126
setVisibleContentRect(const SkRect & visibleContentRect,float scale)127 void GLWebViewState::setVisibleContentRect(const SkRect& visibleContentRect, float scale)
128 {
129 // allocate max possible number of tiles visible with this visibleContentRect / expandedTileBounds
130 const float invTileContentWidth = scale / TilesManager::tileWidth();
131 const float invTileContentHeight = scale / TilesManager::tileHeight();
132
133 int viewMaxTileX =
134 static_cast<int>(ceilf((visibleContentRect.width()-1) * invTileContentWidth)) + 1;
135 int viewMaxTileY =
136 static_cast<int>(ceilf((visibleContentRect.height()-1) * invTileContentHeight)) + 1;
137
138 TilesManager* tilesManager = TilesManager::instance();
139 int maxTextureCount = viewMaxTileX * viewMaxTileY * (tilesManager->highEndGfx() ? 4 : 2);
140
141 tilesManager->setCurrentTextureCount(maxTextureCount);
142
143 // TODO: investigate whether we can move this return earlier.
144 if ((m_visibleContentRect == visibleContentRect)
145 && (m_scale == scale)) {
146 // everything below will stay the same, early return.
147 m_isVisibleContentRectScrolling = false;
148 return;
149 }
150 m_scale = scale;
151
152 m_goingDown = m_visibleContentRect.fTop - visibleContentRect.fTop <= 0;
153 m_goingLeft = m_visibleContentRect.fLeft - visibleContentRect.fLeft >= 0;
154
155 // detect visibleContentRect scrolling from short programmatic scrolls/jumps
156 m_isVisibleContentRectScrolling = m_visibleContentRect != visibleContentRect
157 && SkRect::Intersects(m_visibleContentRect, visibleContentRect);
158 m_visibleContentRect = visibleContentRect;
159
160 ALOGV("New visibleContentRect %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f )",
161 m_visibleContentRect.fLeft, m_visibleContentRect.fTop,
162 m_visibleContentRect.fRight, m_visibleContentRect.fBottom,
163 m_visibleContentRect.width(), m_visibleContentRect.height(), scale);
164 }
165
166 #ifdef MEASURES_PERF
dumpMeasures()167 void GLWebViewState::dumpMeasures()
168 {
169 for (int i = 0; i < m_timeCounter; i++) {
170 ALOGD("%d delay: %d ms", m_totalTimeCounter + i,
171 static_cast<int>(m_delayTimes[i]*1000));
172 m_delayTimes[i] = 0;
173 }
174 m_totalTimeCounter += m_timeCounter;
175 m_timeCounter = 0;
176 }
177 #endif // MEASURES_PERF
178
addDirtyArea(const IntRect & rect)179 void GLWebViewState::addDirtyArea(const IntRect& rect)
180 {
181 if (rect.isEmpty())
182 return;
183
184 IntRect inflatedRect = rect;
185 inflatedRect.inflate(8);
186 if (m_frameworkLayersInval.isEmpty())
187 m_frameworkLayersInval = inflatedRect;
188 else
189 m_frameworkLayersInval.unite(inflatedRect);
190 }
191
resetLayersDirtyArea()192 void GLWebViewState::resetLayersDirtyArea()
193 {
194 m_frameworkLayersInval.setX(0);
195 m_frameworkLayersInval.setY(0);
196 m_frameworkLayersInval.setWidth(0);
197 m_frameworkLayersInval.setHeight(0);
198 m_doFrameworkFullInval = false;
199 }
200
doFrameworkFullInval()201 void GLWebViewState::doFrameworkFullInval()
202 {
203 m_doFrameworkFullInval = true;
204 }
205
setupDrawing(const IntRect & invScreenRect,const SkRect & visibleContentRect,const IntRect & screenRect,int titleBarHeight,const IntRect & screenClip,float scale)206 double GLWebViewState::setupDrawing(const IntRect& invScreenRect,
207 const SkRect& visibleContentRect,
208 const IntRect& screenRect, int titleBarHeight,
209 const IntRect& screenClip, float scale)
210 {
211 TilesManager* tilesManager = TilesManager::instance();
212
213 // Make sure GL resources are created on the UI thread.
214 // They are created either for the first time, or after EGL context
215 // recreation caused by onTrimMemory in the framework.
216 ShaderProgram* shader = tilesManager->shader();
217 if (shader->needsInit()) {
218 ALOGD("Reinit shader");
219 shader->initGLResources();
220 }
221 TransferQueue* transferQueue = tilesManager->transferQueue();
222 if (transferQueue->needsInit()) {
223 ALOGD("Reinit transferQueue");
224 transferQueue->initGLResources(TilesManager::tileWidth(),
225 TilesManager::tileHeight());
226 }
227 shader->setupDrawing(invScreenRect, visibleContentRect, screenRect,
228 titleBarHeight, screenClip, scale);
229
230 double currentTime = WTF::currentTime();
231
232 setVisibleContentRect(visibleContentRect, scale);
233
234 return currentTime;
235 }
236
setLayersRenderingMode(TexturesResult & nbTexturesNeeded)237 bool GLWebViewState::setLayersRenderingMode(TexturesResult& nbTexturesNeeded)
238 {
239 bool invalBase = false;
240
241 if (!nbTexturesNeeded.full)
242 TilesManager::instance()->setCurrentLayerTextureCount(0);
243 else
244 TilesManager::instance()->setCurrentLayerTextureCount((2 * nbTexturesNeeded.full) + 1);
245
246 int maxTextures = TilesManager::instance()->currentLayerTextureCount();
247 LayersRenderingMode layersRenderingMode = m_layersRenderingMode;
248
249 if (m_layersRenderingMode == kSingleSurfaceRendering) {
250 // only switch out of SingleSurface mode, if we have 2x needed textures
251 // to avoid changing too often
252 maxTextures /= 2;
253 }
254
255 m_layersRenderingMode = kSingleSurfaceRendering;
256 if (nbTexturesNeeded.fixed < maxTextures)
257 m_layersRenderingMode = kFixedLayers;
258 if (nbTexturesNeeded.scrollable < maxTextures)
259 m_layersRenderingMode = kScrollableAndFixedLayers;
260 if (nbTexturesNeeded.clipped < maxTextures)
261 m_layersRenderingMode = kClippedTextures;
262 if (nbTexturesNeeded.full < maxTextures)
263 m_layersRenderingMode = kAllTextures;
264
265 if (!maxTextures && !nbTexturesNeeded.full)
266 m_layersRenderingMode = kAllTextures;
267
268 if (m_layersRenderingMode < layersRenderingMode
269 && m_layersRenderingMode != kAllTextures)
270 invalBase = true;
271
272 if (m_layersRenderingMode > layersRenderingMode
273 && m_layersRenderingMode != kClippedTextures)
274 invalBase = true;
275
276 #ifdef DEBUG
277 if (m_layersRenderingMode != layersRenderingMode) {
278 char* mode[] = { "kAllTextures", "kClippedTextures",
279 "kScrollableAndFixedLayers", "kFixedLayers", "kSingleSurfaceRendering" };
280 ALOGD("Change from mode %s to %s -- We need textures: fixed: %d,"
281 " scrollable: %d, clipped: %d, full: %d, max textures: %d",
282 static_cast<char*>(mode[layersRenderingMode]),
283 static_cast<char*>(mode[m_layersRenderingMode]),
284 nbTexturesNeeded.fixed,
285 nbTexturesNeeded.scrollable,
286 nbTexturesNeeded.clipped,
287 nbTexturesNeeded.full, maxTextures);
288 }
289 #endif
290
291 // For now, anything below kClippedTextures is equivalent
292 // to kSingleSurfaceRendering
293 // TODO: implement the other rendering modes
294 if (m_layersRenderingMode > kClippedTextures)
295 m_layersRenderingMode = kSingleSurfaceRendering;
296
297 // update the base surface if needed
298 // TODO: inval base layergroup when going into single surface mode
299 return (m_layersRenderingMode != layersRenderingMode && invalBase);
300 }
301
302 // -invScreenRect is the webView's rect with inverted Y screen coordinate.
303 // -visibleContentRect is the visible area in content coordinate.
304 // They are both based on webView's rect and calculated in Java side.
305 //
306 // -screenClip is in screen coordinate, so we need to invert the Y axis before
307 // passing into GL functions. Clip can be smaller than the webView's rect.
308 //
309 // TODO: Try to decrease the number of parameters as some info is redundant.
drawGL(IntRect & invScreenRect,SkRect & visibleContentRect,IntRect * invalRect,IntRect & screenRect,int titleBarHeight,IntRect & screenClip,float scale,bool * collectionsSwappedPtr,bool * newCollectionHasAnimPtr,bool shouldDraw)310 int GLWebViewState::drawGL(IntRect& invScreenRect, SkRect& visibleContentRect,
311 IntRect* invalRect, IntRect& screenRect, int titleBarHeight,
312 IntRect& screenClip, float scale,
313 bool* collectionsSwappedPtr, bool* newCollectionHasAnimPtr,
314 bool shouldDraw)
315 {
316 TilesManager* tilesManager = TilesManager::instance();
317 if (shouldDraw)
318 tilesManager->getProfiler()->nextFrame(visibleContentRect.fLeft,
319 visibleContentRect.fTop,
320 visibleContentRect.fRight,
321 visibleContentRect.fBottom,
322 scale);
323 tilesManager->incDrawGLCount();
324
325 ALOGV("drawGL, invScreenRect(%d, %d, %d, %d), visibleContentRect(%.2f, %.2f, %.2f, %.2f)",
326 invScreenRect.x(), invScreenRect.y(), invScreenRect.width(), invScreenRect.height(),
327 visibleContentRect.fLeft, visibleContentRect.fTop,
328 visibleContentRect.fRight, visibleContentRect.fBottom);
329
330 ALOGV("drawGL, invalRect(%d, %d, %d, %d), screenRect(%d, %d, %d, %d)"
331 "screenClip (%d, %d, %d, %d), scale %f titleBarHeight %d",
332 invalRect->x(), invalRect->y(), invalRect->width(), invalRect->height(),
333 screenRect.x(), screenRect.y(), screenRect.width(), screenRect.height(),
334 screenClip.x(), screenClip.y(), screenClip.width(), screenClip.height(), scale, titleBarHeight);
335
336 m_inUnclippedDraw = shouldDraw && (screenRect == screenClip);
337
338 resetLayersDirtyArea();
339
340 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
341 ALOGW("WARNING, scale seems corrupted before update: %e", scale);
342
343 tilesManager->updateTilesIfContextVerified();
344
345 // gather the textures we can use, make sure this happens before any
346 // texture preparation work.
347 tilesManager->gatherTextures();
348
349 // Upload any pending ImageTexture
350 // Return true if we still have some images to upload.
351 // TODO: upload as many textures as possible within a certain time limit
352 int returnFlags = 0;
353 if (ImagesManager::instance()->prepareTextures(this))
354 returnFlags |= DrawGlInfo::kStatusDraw;
355
356 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) {
357 ALOGW("WARNING, scale seems corrupted after update: %e", scale);
358 scale = 1.0f; // WORKAROUND for corrupted scale: use 1.0
359 }
360
361 double currentTime = setupDrawing(invScreenRect, visibleContentRect, screenRect,
362 titleBarHeight, screenClip, scale);
363
364 TexturesResult nbTexturesNeeded;
365 bool scrolling = isScrolling();
366 bool singleSurfaceMode = m_layersRenderingMode == kSingleSurfaceRendering;
367 m_glExtras.setVisibleContentRect(visibleContentRect);
368
369 returnFlags |= m_surfaceCollectionManager.drawGL(currentTime, invScreenRect,
370 visibleContentRect,
371 scale, scrolling,
372 singleSurfaceMode,
373 collectionsSwappedPtr,
374 newCollectionHasAnimPtr,
375 &nbTexturesNeeded, shouldDraw);
376
377 int nbTexturesForImages = ImagesManager::instance()->nbTextures();
378 ALOGV("*** We have %d textures for images, %d full, %d clipped, total %d / %d",
379 nbTexturesForImages, nbTexturesNeeded.full, nbTexturesNeeded.clipped,
380 nbTexturesNeeded.full + nbTexturesForImages,
381 nbTexturesNeeded.clipped + nbTexturesForImages);
382 nbTexturesNeeded.full += nbTexturesForImages;
383 nbTexturesNeeded.clipped += nbTexturesForImages;
384
385 if (setLayersRenderingMode(nbTexturesNeeded)) {
386 TilesManager::instance()->dirtyAllTiles();
387 returnFlags |= DrawGlInfo::kStatusDraw | DrawGlInfo::kStatusInvoke;
388 }
389
390 glBindBuffer(GL_ARRAY_BUFFER, 0);
391
392 if (returnFlags & DrawGlInfo::kStatusDraw) {
393 // returnFlags & kStatusDraw && empty inval region means we've inval'd everything,
394 // but don't have new content. Keep redrawing full view (0,0,0,0)
395 // until tile generation catches up and we swap pages.
396 bool fullScreenInval = m_frameworkLayersInval.isEmpty() || m_doFrameworkFullInval;
397
398 if (!fullScreenInval) {
399 m_frameworkLayersInval.inflate(1);
400
401 invalRect->setX(m_frameworkLayersInval.x());
402 invalRect->setY(m_frameworkLayersInval.y());
403 invalRect->setWidth(m_frameworkLayersInval.width());
404 invalRect->setHeight(m_frameworkLayersInval.height());
405
406 ALOGV("invalRect(%d, %d, %d, %d)", invalRect->x(),
407 invalRect->y(), invalRect->width(), invalRect->height());
408
409 if (!invalRect->intersects(invScreenRect)) {
410 // invalidate is occurring offscreen, do full inval to guarantee redraw
411 fullScreenInval = true;
412 }
413 }
414
415 if (fullScreenInval) {
416 invalRect->setX(0);
417 invalRect->setY(0);
418 invalRect->setWidth(0);
419 invalRect->setHeight(0);
420 }
421 }
422
423 if (shouldDraw)
424 showFrameInfo(invScreenRect, *collectionsSwappedPtr);
425
426 return returnFlags;
427 }
428
showFrameInfo(const IntRect & rect,bool collectionsSwapped)429 void GLWebViewState::showFrameInfo(const IntRect& rect, bool collectionsSwapped)
430 {
431 bool showVisualIndicator = TilesManager::instance()->getShowVisualIndicator();
432
433 bool drawOrDumpFrameInfo = showVisualIndicator;
434 #ifdef MEASURES_PERF
435 drawOrDumpFrameInfo |= m_measurePerfs;
436 #endif
437 if (!drawOrDumpFrameInfo)
438 return;
439
440 double currentDrawTime = WTF::currentTime();
441 double delta = currentDrawTime - m_prevDrawTime;
442 m_prevDrawTime = currentDrawTime;
443
444 #ifdef MEASURES_PERF
445 if (m_measurePerfs) {
446 m_delayTimes[m_timeCounter++] = delta;
447 if (m_timeCounter >= MAX_MEASURES_PERF)
448 dumpMeasures();
449 }
450 #endif
451
452 IntRect frameInfoRect = rect;
453 frameInfoRect.setHeight(FPS_INDICATOR_HEIGHT);
454 double ratio = (1.0 / delta) / MAX_FPS_VALUE;
455
456 clearRectWithColor(frameInfoRect, 1, 1, 1, 1);
457 frameInfoRect.setWidth(frameInfoRect.width() * ratio);
458 clearRectWithColor(frameInfoRect, 1, 0, 0, 1);
459
460 // Draw the collection swap counter as a circling progress bar.
461 // This will basically show how fast we are updating the collection.
462 static int swappedCounter = 0;
463 if (collectionsSwapped)
464 swappedCounter = (swappedCounter + 1) % COLLECTION_SWAPPED_COUNTER_MODULE;
465
466 frameInfoRect = rect;
467 frameInfoRect.setHeight(FPS_INDICATOR_HEIGHT);
468 frameInfoRect.move(0, FPS_INDICATOR_HEIGHT);
469
470 clearRectWithColor(frameInfoRect, 1, 1, 1, 1);
471 ratio = (swappedCounter + 1.0) / COLLECTION_SWAPPED_COUNTER_MODULE;
472
473 frameInfoRect.setWidth(frameInfoRect.width() * ratio);
474 clearRectWithColor(frameInfoRect, 0, 1, 0, 1);
475 }
476
clearRectWithColor(const IntRect & rect,float r,float g,float b,float a)477 void GLWebViewState::clearRectWithColor(const IntRect& rect, float r, float g,
478 float b, float a)
479 {
480 glScissor(rect.x(), rect.y(), rect.width(), rect.height());
481 glClearColor(r, g, b, a);
482 glClear(GL_COLOR_BUFFER_BIT);
483 }
484
485 } // namespace WebCore
486
487 #endif // USE(ACCELERATED_COMPOSITING)
488