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 "Tile" 27 #define LOG_NDEBUG 1 28 29 #include "config.h" 30 #include "Tile.h" 31 32 #if USE(ACCELERATED_COMPOSITING) 33 34 #include "AndroidLog.h" 35 #include "GLUtils.h" 36 #include "RasterRenderer.h" 37 #include "TextureInfo.h" 38 #include "TileTexture.h" 39 #include "TilesManager.h" 40 41 // If the dirty portion of a tile exceeds this ratio, fully repaint. 42 // Lower values give fewer partial repaints, thus fewer front-to-back 43 // texture copies (cost will vary by device). It's a tradeoff between 44 // the rasterization cost and the FBO texture recopy cost when using 45 // GPU for the transfer queue. 46 #define MAX_INVAL_AREA 0.6 47 48 namespace WebCore { 49 50 Tile::Tile(bool isLayerTile) 51 : m_x(-1) 52 , m_y(-1) 53 , m_frontTexture(0) 54 , m_backTexture(0) 55 , m_lastDrawnTexture(0) 56 , m_scale(1) 57 , m_dirty(true) 58 , m_repaintsPending(0) 59 , m_fullRepaint(true) 60 , m_isLayerTile(isLayerTile) 61 , m_drawCount(0) 62 , m_state(Unpainted) 63 { 64 #ifdef DEBUG_COUNT 65 ClassTracker::instance()->increment("Tile"); 66 #endif 67 m_renderer = BaseRenderer::createRenderer(); 68 } 69 70 Tile::~Tile() 71 { 72 if (m_backTexture) 73 m_backTexture->release(this); 74 if (m_frontTexture) 75 m_frontTexture->release(this); 76 77 delete m_renderer; 78 79 #ifdef DEBUG_COUNT 80 ClassTracker::instance()->decrement("Tile"); 81 #endif 82 } 83 84 // All the following functions must be called from the main GL thread. 85 86 void Tile::setContents(int x, int y, float scale, bool isExpandedPrefetchTile) 87 { 88 // TODO: investigate whether below check/discard is necessary 89 if ((m_x != x) 90 || (m_y != y) 91 || (m_scale != scale)) { 92 // neither texture is relevant 93 discardTextures(); 94 } 95 96 android::AutoMutex lock(m_atomicSync); 97 m_x = x; 98 m_y = y; 99 m_scale = scale; 100 m_drawCount = TilesManager::instance()->getDrawGLCount(); 101 if (isExpandedPrefetchTile) 102 m_drawCount--; // deprioritize expanded painting region 103 } 104 105 void Tile::reserveTexture() 106 { 107 TileTexture* texture = TilesManager::instance()->getAvailableTexture(this); 108 109 android::AutoMutex lock(m_atomicSync); 110 if (texture && m_backTexture != texture) { 111 ALOGV("tile %p reserving texture %p, back was %p (front %p)", 112 this, texture, m_backTexture, m_frontTexture); 113 m_state = Unpainted; 114 m_backTexture = texture; 115 } 116 117 if (m_state == UpToDate) { 118 ALOGV("moving tile %p to unpainted, since it reserved while up to date", this); 119 m_dirty = true; 120 m_state = Unpainted; 121 } 122 } 123 124 bool Tile::removeTexture(TileTexture* texture) 125 { 126 ALOGV("%p removeTexture %p, back %p front %p", 127 this, texture, m_backTexture, m_frontTexture); 128 // We update atomically, so paintBitmap() can see the correct value 129 android::AutoMutex lock(m_atomicSync); 130 if (m_frontTexture == texture) { 131 if (m_state == UpToDate) { 132 ALOGV("front texture removed, state was UpToDate, now becoming unpainted, bt is %p", m_backTexture); 133 m_state = Unpainted; 134 } 135 136 m_frontTexture = 0; 137 } 138 if (m_backTexture == texture) { 139 m_state = Unpainted; 140 m_backTexture = 0; 141 } 142 143 // mark dirty regardless of which texture was taken - the back texture may 144 // have been ready to swap 145 m_dirty = true; 146 147 return true; 148 } 149 150 void Tile::markAsDirty() 151 { 152 android::AutoMutex lock(m_atomicSync); 153 m_dirtyArea.setEmpty(); // empty dirty rect prevents fast blit path 154 markAsDirtyInternal(); 155 } 156 157 void Tile::markAsDirty(const SkRegion& dirtyArea) 158 { 159 if (dirtyArea.isEmpty()) 160 return; 161 android::AutoMutex lock(m_atomicSync); 162 m_dirtyArea.op(dirtyArea, SkRegion::kUnion_Op); 163 164 // Check if we actually intersect with the area 165 bool intersect = false; 166 SkRegion::Iterator cliperator(dirtyArea); 167 SkRect realTileRect; 168 SkRect dirtyRect; 169 while (!cliperator.done()) { 170 dirtyRect.set(cliperator.rect()); 171 if (intersectWithRect(m_x, m_y, TilesManager::tileWidth(), TilesManager::tileHeight(), 172 m_scale, dirtyRect, realTileRect)) { 173 intersect = true; 174 break; 175 } 176 cliperator.next(); 177 } 178 179 if (!intersect) 180 return; 181 182 markAsDirtyInternal(); 183 } 184 185 void Tile::markAsDirtyInternal() 186 { 187 // NOTE: callers must hold lock on m_atomicSync 188 189 m_dirty = true; 190 if (m_state == UpToDate) { 191 // We only mark a tile as unpainted in 'markAsDirty' if its status is 192 // UpToDate: marking dirty means we need to repaint, but don't stop the 193 // current paint 194 m_state = Unpainted; 195 } else if (m_state != Unpainted) { 196 // TODO: fix it so that they can paint while deferring the markAsDirty 197 // call (or block updates) 198 ALOGV("Warning: tried to mark tile %p at %d, %d islayertile %d as dirty, state %d", 199 this, m_x, m_y, isLayerTile(), m_state); 200 201 // prefetch tiles can be marked dirty while in the process of painting, 202 // due to not using an update lock. force them to fail validate step. 203 m_state = Unpainted; 204 } 205 } 206 207 bool Tile::isDirty() 208 { 209 android::AutoMutex lock(m_atomicSync); 210 return m_dirty; 211 } 212 213 bool Tile::isRepaintPending() 214 { 215 android::AutoMutex lock(m_atomicSync); 216 return m_repaintsPending != 0; 217 } 218 219 void Tile::setRepaintPending(bool pending) 220 { 221 android::AutoMutex lock(m_atomicSync); 222 m_repaintsPending += pending ? 1 : -1; 223 } 224 225 bool Tile::drawGL(float opacity, const SkRect& rect, float scale, 226 const TransformationMatrix* transform, 227 bool forceBlending, bool usePointSampling, 228 const FloatRect& fillPortion) 229 { 230 if (m_x < 0 || m_y < 0 || m_scale != scale) 231 return false; 232 233 // No need to mutex protect reads of m_backTexture as it is only written to by 234 // the consumer thread. 235 if (!m_frontTexture) 236 return false; 237 238 if (fillPortion.maxX() < 1.0f || fillPortion.maxY() < 1.0f 239 || fillPortion.x() > 0.0f || fillPortion.y() > 0.0f) 240 ALOGV("drawing tile %p (%d, %d with fill portions %f %f->%f, %f", 241 this, m_x, m_y, fillPortion.x(), fillPortion.y(), 242 fillPortion.maxX(), fillPortion.maxY()); 243 244 m_frontTexture->drawGL(isLayerTile(), rect, opacity, transform, 245 forceBlending, usePointSampling, fillPortion); 246 m_lastDrawnTexture = m_frontTexture; 247 return true; 248 } 249 250 bool Tile::isTileReady() 251 { 252 // Return true if the tile's most recently drawn texture is up to date 253 android::AutoMutex lock(m_atomicSync); 254 TileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture; 255 256 if (!texture) 257 return false; 258 259 if (texture->owner() != this) 260 return false; 261 262 if (m_dirty) 263 return false; 264 265 if (m_state != ReadyToSwap && m_state != UpToDate) 266 return false; 267 268 return true; 269 } 270 271 bool Tile::intersectWithRect(int x, int y, int tileWidth, int tileHeight, 272 float scale, const SkRect& dirtyRect, 273 SkRect& realTileRect) 274 { 275 // compute the rect to corresponds to pixels 276 realTileRect.fLeft = x * tileWidth; 277 realTileRect.fTop = y * tileHeight; 278 realTileRect.fRight = realTileRect.fLeft + tileWidth; 279 realTileRect.fBottom = realTileRect.fTop + tileHeight; 280 281 // scale the dirtyRect for intersect computation. 282 SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale, 283 dirtyRect.height() * scale); 284 realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale); 285 286 if (!realTileRect.intersect(realDirtyRect)) 287 return false; 288 return true; 289 } 290 291 bool Tile::isTileVisible(const IntRect& viewTileBounds) 292 { 293 return (m_x >= viewTileBounds.x() 294 && m_x < viewTileBounds.x() + viewTileBounds.width() 295 && m_y >= viewTileBounds.y() 296 && m_y < viewTileBounds.y() + viewTileBounds.height()); 297 } 298 299 // This is called from the texture generation thread 300 void Tile::paintBitmap(TilePainter* painter) 301 { 302 // We acquire the values below atomically. This ensures that we are reading 303 // values correctly across cores. Further, once we have these values they 304 // can be updated by other threads without consequence. 305 m_atomicSync.lock(); 306 bool dirty = m_dirty; 307 TileTexture* texture = m_backTexture; 308 SkRegion dirtyArea = m_dirtyArea; 309 float scale = m_scale; 310 const int x = m_x; 311 const int y = m_y; 312 313 if (!dirty || !texture) { 314 m_atomicSync.unlock(); 315 return; 316 } 317 if (m_state != Unpainted) { 318 ALOGV("Warning: started painting tile %p, but was at state %d, ft %p bt %p", 319 this, m_state, m_frontTexture, m_backTexture); 320 } 321 m_state = PaintingStarted; 322 TextureInfo* textureInfo = texture->getTextureInfo(); 323 m_atomicSync.unlock(); 324 325 // at this point we can safely check the ownership (if the texture got 326 // transferred to another Tile under us) 327 if (texture->owner() != this) { 328 return; 329 } 330 331 // swap out the renderer if necessary 332 BaseRenderer::swapRendererIfNeeded(m_renderer); 333 // setup the common renderInfo fields; 334 TileRenderInfo renderInfo; 335 renderInfo.x = x; 336 renderInfo.y = y; 337 renderInfo.scale = scale; 338 renderInfo.tileSize = texture->getSize(); 339 renderInfo.tilePainter = painter; 340 renderInfo.baseTile = this; 341 renderInfo.textureInfo = textureInfo; 342 343 const float tileWidth = renderInfo.tileSize.width(); 344 const float tileHeight = renderInfo.tileSize.height(); 345 346 m_renderer->renderTiledContent(renderInfo); 347 348 m_atomicSync.lock(); 349 350 if (texture == m_backTexture) { 351 // set the fullrepaint flags 352 m_fullRepaint = false; 353 354 // The various checks to see if we are still dirty... 355 356 m_dirty = false; 357 358 if (m_scale != scale) 359 m_dirty = true; 360 361 m_dirtyArea.setEmpty(); 362 363 ALOGV("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty); 364 365 validatePaint(); 366 } else { 367 ALOGV("tile %p no longer owns texture %p, m_state %d. ft %p bt %p", 368 this, texture, m_state, m_frontTexture, m_backTexture); 369 } 370 371 m_atomicSync.unlock(); 372 } 373 374 void Tile::discardTextures() { 375 android::AutoMutex lock(m_atomicSync); 376 ALOGV("%p discarding bt %p, ft %p", 377 this, m_backTexture, m_frontTexture); 378 if (m_frontTexture) { 379 m_frontTexture->release(this); 380 m_frontTexture = 0; 381 } 382 if (m_backTexture) { 383 m_backTexture->release(this); 384 m_backTexture = 0; 385 } 386 m_dirtyArea.setEmpty(); 387 m_fullRepaint = true; 388 389 m_dirty = true; 390 m_state = Unpainted; 391 } 392 393 void Tile::discardBackTexture() { 394 android::AutoMutex lock(m_atomicSync); 395 if (m_backTexture) { 396 m_backTexture->release(this); 397 m_backTexture = 0; 398 } 399 m_state = Unpainted; 400 m_dirty = true; 401 } 402 403 bool Tile::swapTexturesIfNeeded() { 404 android::AutoMutex lock(m_atomicSync); 405 if (m_state == ReadyToSwap) { 406 // discard old texture and swap the new one in its place 407 if (m_frontTexture) 408 m_frontTexture->release(this); 409 410 m_frontTexture = m_backTexture; 411 m_backTexture = 0; 412 m_state = UpToDate; 413 ALOGV("display texture for %p at %d, %d front is now %p, back is %p", 414 this, m_x, m_y, m_frontTexture, m_backTexture); 415 416 return true; 417 } 418 return false; 419 } 420 421 void Tile::backTextureTransfer() { 422 android::AutoMutex lock(m_atomicSync); 423 if (m_state == PaintingStarted) 424 m_state = TransferredUnvalidated; 425 else if (m_state == ValidatedUntransferred) 426 m_state = ReadyToSwap; 427 else { 428 // shouldn't have transferred a tile in any other state, log 429 ALOGV("Note: transferred tile %p at %d %d, state wasn't paintingstarted or validated: %d", 430 this, m_x, m_y, m_state); 431 } 432 } 433 434 void Tile::backTextureTransferFail() { 435 // transfer failed for some reason, mark dirty so it will (repaint and) be 436 // retransferred. 437 android::AutoMutex lock(m_atomicSync); 438 m_state = Unpainted; 439 m_dirty = true; 440 // whether validatePaint is called before or after, it won't do anything 441 } 442 443 void Tile::onBlitUpdate() 444 { 445 // The front texture was directly updated with a blit, so mark this as clean 446 android::AutoMutex lock(m_atomicSync); 447 m_dirty = false; 448 m_dirtyArea.setEmpty(); 449 m_state = Tile::UpToDate; 450 } 451 452 void Tile::validatePaint() { 453 // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap()) 454 455 if (!m_dirty) { 456 // since after the paint, the tile isn't dirty, 'validate' it - this 457 // may happed before or after the transfer queue operation. Only 458 // when both have happened, mark as 'ReadyToSwap' 459 if (m_state == PaintingStarted) 460 m_state = ValidatedUntransferred; 461 else if (m_state == TransferredUnvalidated) { 462 // When the backTexture has been marked pureColor, we will skip the 463 // transfer and marked as ReadyToSwap, in this case, we don't want 464 // to reset m_dirty bit to true. 465 m_state = ReadyToSwap; 466 } else { 467 ALOGV("Note: validated tile %p at %d %d, state wasn't paintingstarted or transferred %d", 468 this, m_x, m_y, m_state); 469 // failed transferring, in which case mark dirty (since 470 // paintBitmap() may have cleared m_dirty) 471 m_dirty = true; 472 } 473 } else { 474 ALOGV("Note: paint was unsuccessful."); 475 m_state = Unpainted; 476 } 477 478 } 479 480 } // namespace WebCore 481 482 #endif // USE(ACCELERATED_COMPOSITING) 483