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 "BaseTile.h" 28 29 #if USE(ACCELERATED_COMPOSITING) 30 31 #include "GLUtils.h" 32 #include "RasterRenderer.h" 33 #include "TextureInfo.h" 34 #include "TilesManager.h" 35 36 #include <cutils/atomic.h> 37 38 #include <cutils/log.h> 39 #include <wtf/CurrentTime.h> 40 #include <wtf/text/CString.h> 41 42 #undef XLOGC 43 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTile", __VA_ARGS__) 44 45 #ifdef DEBUG 46 47 #undef XLOG 48 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTile", __VA_ARGS__) 49 50 #else 51 52 #undef XLOG 53 #define XLOG(...) 54 55 #endif // DEBUG 56 57 namespace WebCore { 58 59 BaseTile::BaseTile(bool isLayerTile) 60 : m_glWebViewState(0) 61 , m_painter(0) 62 , m_x(-1) 63 , m_y(-1) 64 , m_page(0) 65 , m_frontTexture(0) 66 , m_backTexture(0) 67 , m_scale(1) 68 , m_dirty(true) 69 , m_repaintPending(false) 70 , m_lastDirtyPicture(0) 71 , m_isTexturePainted(false) 72 , m_isLayerTile(isLayerTile) 73 , m_drawCount(0) 74 , m_state(Unpainted) 75 { 76 #ifdef DEBUG_COUNT 77 ClassTracker::instance()->increment("BaseTile"); 78 #endif 79 m_currentDirtyAreaIndex = 0; 80 81 // For EglImage Mode, the internal buffer should be 2. 82 // For Surface Texture mode, we only need one. 83 if (TilesManager::instance()->getSharedTextureMode() == EglImageMode) 84 m_maxBufferNumber = 2; 85 else 86 m_maxBufferNumber = 1; 87 88 m_dirtyArea = new SkRegion[m_maxBufferNumber]; 89 m_fullRepaint = new bool[m_maxBufferNumber]; 90 for (int i = 0; i < m_maxBufferNumber; i++) 91 m_fullRepaint[i] = true; 92 93 m_renderer = BaseRenderer::createRenderer(); 94 } 95 96 BaseTile::~BaseTile() 97 { 98 if (m_backTexture) 99 m_backTexture->release(this); 100 if (m_frontTexture) 101 m_frontTexture->release(this); 102 103 delete m_renderer; 104 delete[] m_dirtyArea; 105 delete[] m_fullRepaint; 106 107 #ifdef DEBUG_COUNT 108 ClassTracker::instance()->decrement("BaseTile"); 109 #endif 110 } 111 112 // All the following functions must be called from the main GL thread. 113 114 void BaseTile::setContents(TilePainter* painter, int x, int y, float scale) 115 { 116 if ((m_painter != painter) 117 || (m_x != x) 118 || (m_y != y) 119 || (m_scale != scale)) { 120 // neither texture is relevant 121 discardTextures(); 122 } 123 124 android::AutoMutex lock(m_atomicSync); 125 m_painter = painter; 126 m_x = x; 127 m_y = y; 128 m_scale = scale; 129 m_drawCount = TilesManager::instance()->getDrawGLCount(); 130 } 131 132 void BaseTile::reserveTexture() 133 { 134 BaseTileTexture* texture = TilesManager::instance()->getAvailableTexture(this); 135 136 android::AutoMutex lock(m_atomicSync); 137 if (texture && m_backTexture != texture) { 138 XLOG("tile %p reserving texture %p, back was %p (front %p)", 139 this, texture, m_backTexture, m_frontTexture); 140 m_state = Unpainted; 141 m_backTexture = texture; 142 } 143 144 if (m_state == UpToDate) { 145 XLOG("moving tile %p to unpainted, since it reserved while up to date", this); 146 m_dirty = true; 147 m_state = Unpainted; 148 } 149 } 150 151 bool BaseTile::removeTexture(BaseTileTexture* texture) 152 { 153 XLOG("%p removeTexture %p, back %p front %p... page %p", 154 this, texture, m_backTexture, m_frontTexture, m_page); 155 // We update atomically, so paintBitmap() can see the correct value 156 android::AutoMutex lock(m_atomicSync); 157 if (m_frontTexture == texture) { 158 if (m_state == UpToDate) { 159 XLOG("front texture removed, state was UpToDate, now becoming unpainted, bt is %p", m_backTexture); 160 m_state = Unpainted; 161 } 162 163 m_frontTexture = 0; 164 } 165 if (m_backTexture == texture) { 166 m_state = Unpainted; 167 m_backTexture = 0; 168 } 169 170 // mark dirty regardless of which texture was taken - the back texture may 171 // have been ready to swap 172 m_dirty = true; 173 174 return true; 175 } 176 177 void BaseTile::markAsDirty(int unsigned pictureCount, 178 const SkRegion& dirtyArea) 179 { 180 if (dirtyArea.isEmpty()) 181 return; 182 android::AutoMutex lock(m_atomicSync); 183 m_lastDirtyPicture = pictureCount; 184 for (int i = 0; i < m_maxBufferNumber; i++) 185 m_dirtyArea[i].op(dirtyArea, SkRegion::kUnion_Op); 186 187 // Check if we actually intersect with the area 188 bool intersect = false; 189 SkRegion::Iterator cliperator(dirtyArea); 190 int tileWidth = TilesManager::instance()->tileWidth(); 191 int tileHeight = TilesManager::instance()->tileHeight(); 192 if (m_isLayerTile) { 193 tileWidth = TilesManager::instance()->layerTileWidth(); 194 tileHeight = TilesManager::instance()->layerTileHeight(); 195 } 196 SkRect realTileRect; 197 SkRect dirtyRect; 198 while (!cliperator.done()) { 199 dirtyRect.set(cliperator.rect()); 200 if (intersectWithRect(m_x, m_y, tileWidth, tileHeight, 201 m_scale, dirtyRect, realTileRect)) { 202 intersect = true; 203 break; 204 } 205 cliperator.next(); 206 } 207 208 if (!intersect) 209 return; 210 211 m_dirty = true; 212 if (m_state == UpToDate) { 213 // We only mark a tile as unpainted in 'markAsDirty' if its status is 214 // UpToDate: marking dirty means we need to repaint, but don't stop the 215 // current paint 216 m_state = Unpainted; 217 } else if (m_state != Unpainted) { 218 // TODO: fix it so that they can paint while deferring the markAsDirty 219 // call (or block updates) 220 XLOG("Warning: tried to mark tile %p at %d, %d islayertile %d as dirty, state %d, page %p", 221 this, m_x, m_y, isLayerTile(), m_state, m_page); 222 223 // prefetch tiles can be marked dirty while in the process of painting, 224 // due to not using an update lock. force them to fail validate step. 225 m_state = Unpainted; 226 } 227 } 228 229 bool BaseTile::isDirty() 230 { 231 android::AutoMutex lock(m_atomicSync); 232 return m_dirty; 233 } 234 235 bool BaseTile::isRepaintPending() 236 { 237 android::AutoMutex lock(m_atomicSync); 238 return m_repaintPending; 239 } 240 241 void BaseTile::setRepaintPending(bool pending) 242 { 243 android::AutoMutex lock(m_atomicSync); 244 m_repaintPending = pending; 245 } 246 247 void BaseTile::draw(float transparency, SkRect& rect, float scale) 248 { 249 if (m_x < 0 || m_y < 0 || m_scale != scale) 250 return; 251 252 // No need to mutex protect reads of m_backTexture as it is only written to by 253 // the consumer thread. 254 if (!m_frontTexture) 255 return; 256 257 // Early return if set to un-usable in purpose! 258 m_atomicSync.lock(); 259 bool isTexturePainted = m_isTexturePainted; 260 m_atomicSync.unlock(); 261 262 if (!isTexturePainted) 263 return; 264 265 TextureInfo* textureInfo = m_frontTexture->consumerLock(); 266 if (!textureInfo) { 267 m_frontTexture->consumerRelease(); 268 return; 269 } 270 271 if (m_frontTexture->readyFor(this)) { 272 if (isLayerTile() && m_painter && m_painter->transform()) 273 TilesManager::instance()->shader()->drawLayerQuad(*m_painter->transform(), 274 rect, m_frontTexture->m_ownTextureId, 275 transparency, true); 276 else 277 TilesManager::instance()->shader()->drawQuad(rect, m_frontTexture->m_ownTextureId, 278 transparency); 279 } else { 280 XLOG("tile %p at %d, %d not readyfor (at draw),", this, m_x, m_y); 281 } 282 283 m_frontTexture->consumerRelease(); 284 } 285 286 bool BaseTile::isTileReady() 287 { 288 // Return true if the tile's most recently drawn texture is up to date 289 android::AutoMutex lock(m_atomicSync); 290 BaseTileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture; 291 292 if (!texture) 293 return false; 294 295 if (texture->owner() != this) 296 return false; 297 298 if (m_dirty) 299 return false; 300 301 if (m_state != ReadyToSwap && m_state != UpToDate) 302 return false; 303 304 texture->consumerLock(); 305 bool ready = texture->readyFor(this); 306 texture->consumerRelease(); 307 308 if (ready) 309 return true; 310 311 XLOG("tile %p at %d, %d not readyfor (at isTileReady)", this, m_x, m_y); 312 313 return false; 314 } 315 316 bool BaseTile::intersectWithRect(int x, int y, int tileWidth, int tileHeight, 317 float scale, const SkRect& dirtyRect, 318 SkRect& realTileRect) 319 { 320 // compute the rect to corresponds to pixels 321 realTileRect.fLeft = x * tileWidth; 322 realTileRect.fTop = y * tileHeight; 323 realTileRect.fRight = realTileRect.fLeft + tileWidth; 324 realTileRect.fBottom = realTileRect.fTop + tileHeight; 325 326 // scale the dirtyRect for intersect computation. 327 SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale, 328 dirtyRect.height() * scale); 329 realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale); 330 331 if (!realTileRect.intersect(realDirtyRect)) 332 return false; 333 return true; 334 } 335 336 bool BaseTile::isTileVisible(const IntRect& viewTileBounds) 337 { 338 return (m_x >= viewTileBounds.x() 339 && m_x < viewTileBounds.x() + viewTileBounds.width() 340 && m_y >= viewTileBounds.y() 341 && m_y < viewTileBounds.y() + viewTileBounds.height()); 342 } 343 344 // This is called from the texture generation thread 345 void BaseTile::paintBitmap() 346 { 347 // We acquire the values below atomically. This ensures that we are reading 348 // values correctly across cores. Further, once we have these values they 349 // can be updated by other threads without consequence. 350 m_atomicSync.lock(); 351 bool dirty = m_dirty; 352 BaseTileTexture* texture = m_backTexture; 353 SkRegion dirtyArea = m_dirtyArea[m_currentDirtyAreaIndex]; 354 float scale = m_scale; 355 const int x = m_x; 356 const int y = m_y; 357 TilePainter* painter = m_painter; 358 359 if (!dirty || !texture) { 360 m_atomicSync.unlock(); 361 return; 362 } 363 if (m_state != Unpainted) { 364 XLOG("Warning: started painting tile %p, but was at state %d, ft %p bt %p", 365 this, m_state, m_frontTexture, m_backTexture); 366 } 367 m_state = PaintingStarted; 368 369 texture->producerAcquireContext(); 370 TextureInfo* textureInfo = texture->producerLock(); 371 m_atomicSync.unlock(); 372 373 // at this point we can safely check the ownership (if the texture got 374 // transferred to another BaseTile under us) 375 if (texture->owner() != this) { 376 texture->producerRelease(); 377 return; 378 } 379 380 unsigned int pictureCount = 0; 381 382 // swap out the renderer if necessary 383 BaseRenderer::swapRendererIfNeeded(m_renderer); 384 385 // setup the common renderInfo fields; 386 TileRenderInfo renderInfo; 387 renderInfo.x = x; 388 renderInfo.y = y; 389 renderInfo.scale = scale; 390 renderInfo.tileSize = texture->getSize(); 391 renderInfo.tilePainter = painter; 392 renderInfo.baseTile = this; 393 renderInfo.textureInfo = textureInfo; 394 395 const float tileWidth = renderInfo.tileSize.width(); 396 const float tileHeight = renderInfo.tileSize.height(); 397 398 SkRegion::Iterator cliperator(dirtyArea); 399 400 bool fullRepaint = false; 401 402 if (m_fullRepaint[m_currentDirtyAreaIndex] 403 || textureInfo->m_width != tileWidth 404 || textureInfo->m_height != tileHeight) { 405 fullRepaint = true; 406 } 407 408 bool surfaceTextureMode = textureInfo->getSharedTextureMode() == SurfaceTextureMode; 409 410 if (surfaceTextureMode) 411 fullRepaint = true; 412 413 while (!fullRepaint && !cliperator.done()) { 414 SkRect realTileRect; 415 SkRect dirtyRect; 416 dirtyRect.set(cliperator.rect()); 417 bool intersect = intersectWithRect(x, y, tileWidth, tileHeight, 418 scale, dirtyRect, realTileRect); 419 420 // With SurfaceTexture, just repaint the entire tile if we intersect 421 // TODO: Implement the partial invalidate in Surface Texture Mode 422 if (intersect && surfaceTextureMode) { 423 fullRepaint = true; 424 break; 425 } 426 427 if (intersect && !surfaceTextureMode) { 428 // initialize finalRealRect to the rounded values of realTileRect 429 SkIRect finalRealRect; 430 realTileRect.roundOut(&finalRealRect); 431 432 // stash the int values of the current width and height 433 const int iWidth = finalRealRect.width(); 434 const int iHeight = finalRealRect.height(); 435 436 if (iWidth == tileWidth || iHeight == tileHeight) { 437 fullRepaint = true; 438 break; 439 } 440 441 // translate the rect into tile space coordinates 442 finalRealRect.fLeft = finalRealRect.fLeft % static_cast<int>(tileWidth); 443 finalRealRect.fTop = finalRealRect.fTop % static_cast<int>(tileHeight); 444 finalRealRect.fRight = finalRealRect.fLeft + iWidth; 445 finalRealRect.fBottom = finalRealRect.fTop + iHeight; 446 447 renderInfo.invalRect = &finalRealRect; 448 renderInfo.measurePerf = false; 449 450 pictureCount = m_renderer->renderTiledContent(renderInfo); 451 } 452 453 cliperator.next(); 454 } 455 456 // Do a full repaint if needed 457 if (fullRepaint) { 458 SkIRect rect; 459 rect.set(0, 0, tileWidth, tileHeight); 460 461 renderInfo.invalRect = ▭ 462 renderInfo.measurePerf = TilesManager::instance()->getShowVisualIndicator(); 463 464 pictureCount = m_renderer->renderTiledContent(renderInfo); 465 } 466 467 m_atomicSync.lock(); 468 469 #if DEPRECATED_SURFACE_TEXTURE_MODE 470 texture->setTile(textureInfo, x, y, scale, painter, pictureCount); 471 #endif 472 texture->producerReleaseAndSwap(); 473 if (texture == m_backTexture) { 474 m_isTexturePainted = true; 475 476 // set the fullrepaint flags 477 m_fullRepaint[m_currentDirtyAreaIndex] = false; 478 479 // The various checks to see if we are still dirty... 480 481 m_dirty = false; 482 483 if (m_scale != scale) 484 m_dirty = true; 485 486 if (fullRepaint) 487 m_dirtyArea[m_currentDirtyAreaIndex].setEmpty(); 488 else 489 m_dirtyArea[m_currentDirtyAreaIndex].op(dirtyArea, SkRegion::kDifference_Op); 490 491 if (!m_dirtyArea[m_currentDirtyAreaIndex].isEmpty()) 492 m_dirty = true; 493 494 // Now we can swap the dirty areas 495 // TODO: For surface texture in Async mode, the index will be updated 496 // according to the current buffer just dequeued. 497 m_currentDirtyAreaIndex = (m_currentDirtyAreaIndex+1) % m_maxBufferNumber; 498 499 if (!m_dirtyArea[m_currentDirtyAreaIndex].isEmpty()) 500 m_dirty = true; 501 502 XLOG("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty); 503 504 validatePaint(); 505 } else { 506 XLOG("tile %p no longer owns texture %p, m_state %d. ft %p bt %p", 507 this, texture, m_state, m_frontTexture, m_backTexture); 508 } 509 510 m_atomicSync.unlock(); 511 } 512 513 void BaseTile::discardTextures() { 514 android::AutoMutex lock(m_atomicSync); 515 XLOG("%p discarding bt %p, ft %p", 516 this, m_backTexture, m_frontTexture); 517 if (m_frontTexture) { 518 m_frontTexture->release(this); 519 m_frontTexture = 0; 520 } 521 if (m_backTexture) { 522 m_backTexture->release(this); 523 m_backTexture = 0; 524 } 525 for (int i = 0; i < m_maxBufferNumber; i++) { 526 m_dirtyArea[i].setEmpty(); 527 m_fullRepaint[i] = true; 528 } 529 m_dirty = true; 530 m_state = Unpainted; 531 } 532 533 void BaseTile::discardBackTexture() { 534 android::AutoMutex lock(m_atomicSync); 535 if (m_backTexture) { 536 m_backTexture->release(this); 537 m_backTexture = 0; 538 } 539 m_state = Unpainted; 540 m_dirty = true; 541 } 542 543 bool BaseTile::swapTexturesIfNeeded() { 544 android::AutoMutex lock(m_atomicSync); 545 if (m_state == ReadyToSwap) { 546 // discard old texture and swap the new one in its place 547 if (m_frontTexture) 548 m_frontTexture->release(this); 549 550 m_frontTexture = m_backTexture; 551 m_backTexture = 0; 552 m_state = UpToDate; 553 XLOG("display texture for %p at %d, %d front is now %p, back is %p", 554 this, m_x, m_y, m_frontTexture, m_backTexture); 555 556 return true; 557 } 558 return false; 559 } 560 561 void BaseTile::backTextureTransfer() { 562 android::AutoMutex lock(m_atomicSync); 563 if (m_state == PaintingStarted) 564 m_state = TransferredUnvalidated; 565 else if (m_state == ValidatedUntransferred) 566 m_state = ReadyToSwap; 567 else { 568 // shouldn't have transferred a tile in any other state, log 569 XLOG("Note: transferred tile %p at %d %d, state wasn't paintingstarted or validated: %d", 570 this, m_x, m_y, m_state); 571 } 572 } 573 574 void BaseTile::backTextureTransferFail() { 575 // transfer failed for some reason, mark dirty so it will (repaint and) be 576 // retransferred. 577 android::AutoMutex lock(m_atomicSync); 578 m_state = Unpainted; 579 m_dirty = true; 580 // whether validatePaint is called before or after, it won't do anything 581 } 582 583 void BaseTile::validatePaint() { 584 // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap()) 585 586 if (!m_dirty) { 587 // since after the paint, the tile isn't dirty, 'validate' it - this 588 // may happed before or after the transfer queue operation. Only 589 // when both have happened, mark as 'ReadyToSwap' 590 if (m_state == PaintingStarted) 591 m_state = ValidatedUntransferred; 592 else if (m_state == TransferredUnvalidated) 593 m_state = ReadyToSwap; 594 else { 595 XLOG("Note: validated tile %p at %d %d, state wasn't paintingstarted or transferred %d", 596 this, m_x, m_y, m_state); 597 // failed transferring, in which case mark dirty (since 598 // paintBitmap() may have cleared m_dirty) 599 m_dirty = true; 600 } 601 602 if (m_deferredDirty) { 603 XLOG("Note: deferred dirty flag set, possibly a missed paint on tile %p", this); 604 m_deferredDirty = false; 605 } 606 } else { 607 XLOG("Note: paint was unsuccessful."); 608 m_state = Unpainted; 609 } 610 611 } 612 613 } // namespace WebCore 614 615 #endif // USE(ACCELERATED_COMPOSITING) 616