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 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 88 GLWebViewState::~GLWebViewState() 89 { 90 #ifdef DEBUG_COUNT 91 ClassTracker::instance()->decrement("GLWebViewState"); 92 #endif 93 94 } 95 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 122 void GLWebViewState::scrollLayer(int layerId, int x, int y) 123 { 124 m_surfaceCollectionManager.updateScrollableLayer(layerId, x, y); 125 } 126 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 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 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 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 201 void GLWebViewState::doFrameworkFullInval() 202 { 203 m_doFrameworkFullInval = true; 204 } 205 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 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. 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 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 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