Surface.cpp revision 25c25a826222bf0fa1df56f6cfd8582296357b49
1/* 2 * Copyright 2012, 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 "Surface" 27#define LOG_NDEBUG 1 28 29#include "config.h" 30#include "Surface.h" 31 32#include "AndroidLog.h" 33#include "BaseLayerAndroid.h" 34#include "ClassTracker.h" 35#include "LayerAndroid.h" 36#include "LayerContent.h" 37#include "GLWebViewState.h" 38#include "PrerenderedInval.h" 39#include "SkCanvas.h" 40#include "SurfaceBacking.h" 41#include "Tile.h" 42#include "TileTexture.h" 43#include "TilesManager.h" 44 45#include <wtf/text/CString.h> 46 47// Surfaces with an area larger than 2048*2048 should never be unclipped 48#define MAX_FULL_CONTENT_AREA 4194304 49 50namespace WebCore { 51 52Surface::Surface() 53 : m_surfaceBacking(0) 54 , m_needsTexture(false) 55 , m_hasText(false) 56{ 57#ifdef DEBUG_COUNT 58 ClassTracker::instance()->increment("Surface"); 59#endif 60} 61 62Surface::~Surface() 63{ 64 for (unsigned int i = 0; i < m_layers.size(); i++) 65 SkSafeUnref(m_layers[i]); 66 if (m_surfaceBacking) 67 SkSafeUnref(m_surfaceBacking); 68#ifdef DEBUG_COUNT 69 ClassTracker::instance()->decrement("Surface"); 70#endif 71} 72 73bool Surface::tryUpdateSurface(Surface* oldSurface) 74{ 75 if (!needsTexture() || !oldSurface->needsTexture()) 76 return false; 77 78 // merge surfaces based on first layer ID 79 if (getFirstLayer()->uniqueId() != oldSurface->getFirstLayer()->uniqueId()) 80 return false; 81 82 m_surfaceBacking = oldSurface->m_surfaceBacking; 83 SkSafeRef(m_surfaceBacking); 84 85 ALOGV("%p taking old SurfBack %p from surface %p, nt %d", 86 this, m_surfaceBacking, oldSurface, oldSurface->needsTexture()); 87 88 if (!m_surfaceBacking) { 89 // no SurfBack to inval, so don't worry about it. 90 return true; 91 } 92 93 if (singleLayer() && oldSurface->singleLayer()) { 94 // both are single matching layers, simply apply inval 95 SkRegion* layerInval = getFirstLayer()->getInvalRegion(); 96 m_surfaceBacking->markAsDirty(*layerInval); 97 } else { 98 SkRegion invalRegion; 99 bool fullInval = m_layers.size() != oldSurface->m_layers.size(); 100 if (!fullInval) { 101 for (unsigned int i = 0; i < m_layers.size(); i++) { 102 if ((m_layers[i]->uniqueId() != oldSurface->m_layers[i]->uniqueId()) 103 || (m_layers[i]->fullContentAreaMapped() != oldSurface->m_layers[i]->fullContentAreaMapped())) { 104 // layer list has changed, fully invalidate 105 // TODO: partially invalidate based on layer size/position 106 fullInval = true; 107 break; 108 } else if (!m_layers[i]->getInvalRegion()->isEmpty()) { 109 // merge layer inval - translate the layer's inval region into surface coordinates 110 // TODO: handle scale/3d transform mapping 111 FloatRect layerPos = m_layers[i]->fullContentAreaMapped(); 112 m_layers[i]->getInvalRegion()->translate(layerPos.x(), layerPos.y()); 113 invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op); 114 break; 115 } 116 } 117 } 118 119 if (fullInval) 120 invalRegion.setRect(-1e8, -1e8, 2e8, 2e8); 121 122 m_surfaceBacking->markAsDirty(invalRegion); 123 } 124 return true; 125} 126 127void Surface::addLayer(LayerAndroid* layer, const TransformationMatrix& transform) 128{ 129 m_layers.append(layer); 130 SkSafeRef(layer); 131 132 m_needsTexture |= layer->needsTexture(); 133 m_hasText |= layer->hasText(); 134 135 // add this layer's size to the surface's area 136 // TODO: handle scale/3d transform mapping 137 IntRect rect = enclosingIntRect(layer->fullContentAreaMapped()); 138 139 if (layer->needsTexture()) { 140 if (m_fullContentArea.isEmpty()) { 141 m_drawTransform = transform; 142 m_drawTransform.translate3d(-rect.x(), -rect.y(), 0); 143 m_fullContentArea = rect; 144 } else 145 m_fullContentArea.unite(rect); 146 ALOGV("Surf %p adding LA %p, size " INT_RECT_FORMAT 147 " now fullContentArea " INT_RECT_FORMAT, 148 this, layer, INT_RECT_ARGS(rect), INT_RECT_ARGS(m_fullContentArea)); 149 } 150 151 if (isBase()) 152 m_background = static_cast<BaseLayerAndroid*>(layer)->getBackgroundColor(); 153} 154 155IntRect Surface::visibleContentArea(bool force3dContentVisible) const 156{ 157 if (singleLayer()) 158 return getFirstLayer()->visibleContentArea(force3dContentVisible); 159 160 IntRect rect = m_fullContentArea; 161 162 // clip with the viewport in content coordinate 163 IntRect contentViewport(TilesManager::instance()->shader()->contentViewport()); 164 rect.intersect(contentViewport); 165 166 // TODO: handle recursive layer clip 167 168 return rect; 169} 170 171IntRect Surface::fullContentArea() 172{ 173 if (singleLayer()) 174 return getFirstLayer()->fullContentArea(); 175 return m_fullContentArea; 176} 177 178bool Surface::useAggressiveRendering() 179{ 180 // When the background is semi-opaque, 0 < alpha < 255, we had to turn off 181 // low res to avoid artifacts from double drawing. 182 // TODO: avoid double drawing for low res tiles. 183 return isBase() 184 && (!m_background.alpha() 185 || !m_background.hasAlpha()); 186} 187 188void Surface::prepareGL(bool layerTilesDisabled, bool updateWithBlit) 189{ 190 bool tilesDisabled = layerTilesDisabled && !isBase(); 191 if (!m_surfaceBacking) { 192 ALOGV("prepareGL on Surf %p, no SurfBack, needsTexture? %d", 193 this, m_surfaceBacking, needsTexture()); 194 195 if (needsTexture() || (isBase() && layerTilesDisabled)) 196 m_surfaceBacking = new SurfaceBacking(isBase()); 197 else 198 return; 199 } 200 201 if (tilesDisabled) { 202 m_surfaceBacking->discardTextures(); 203 } else { 204 bool allowZoom = hasText(); // only allow for scale > 1 if painting vectors 205 IntRect prepareArea = computePrepareArea(); 206 IntRect fullArea = fullContentArea(); 207 208 ALOGV("prepareGL on Surf %p with SurfBack %p, %d layers, first layer %s (%d) " 209 "prepareArea(%d, %d - %d x %d) fullArea(%d, %d - %d x %d)", 210 this, m_surfaceBacking, m_layers.size(), 211 getFirstLayer()->subclassName().ascii().data(), 212 getFirstLayer()->uniqueId(), 213 prepareArea.x(), prepareArea.y(), prepareArea.width(), prepareArea.height(), 214 fullArea.x(), fullArea.y(), fullArea.width(), fullArea.height()); 215 216 m_surfaceBacking->prepareGL(getFirstLayer()->state(), allowZoom, 217 prepareArea, fullArea, 218 this, useAggressiveRendering(), updateWithBlit); 219 } 220 for (size_t i = 0; i < m_layers.size(); i++) { 221 LayerContent* content = m_layers[i]->content(); 222 if (content) 223 content->clearPrerenders(); 224 } 225} 226 227bool Surface::drawGL(bool layerTilesDisabled) 228{ 229 bool tilesDisabled = layerTilesDisabled && !isBase(); 230 if (singleLayer() && !getFirstLayer()->visible()) 231 return false; 232 233 if (!isBase()) { 234 FloatRect drawClip = getFirstLayer()->drawClip(); 235 if (!singleLayer()) { 236 for (unsigned int i = 1; i < m_layers.size(); i++) 237 drawClip.unite(m_layers[i]->drawClip()); 238 } 239 FloatRect clippingRect = TilesManager::instance()->shader()->rectInInvViewCoord(drawClip); 240 TilesManager::instance()->shader()->clip(clippingRect); 241 } 242 243 bool askRedraw = false; 244 if (m_surfaceBacking && !tilesDisabled) { 245 ALOGV("drawGL on Surf %p with SurfBack %p, first layer %s (%d)", this, m_surfaceBacking, 246 getFirstLayer()->subclassName().ascii().data(), getFirstLayer()->uniqueId()); 247 248 bool force3dContentVisible = true; 249 IntRect drawArea = visibleContentArea(force3dContentVisible); 250 m_surfaceBacking->drawGL(drawArea, opacity(), drawTransform(), 251 useAggressiveRendering(), background()); 252 } 253 254 // draw member layers (draws image textures, glextras) 255 for (unsigned int i = 0; i < m_layers.size(); i++) { 256 if (m_layers[i]->drawGL(tilesDisabled)) { 257 m_layers[i]->addDirtyArea(); 258 askRedraw = true; 259 } 260 } 261 262 return askRedraw; 263} 264 265void Surface::swapTiles(bool calculateFrameworkInvals) 266{ 267 if (!m_surfaceBacking) 268 return; 269 270 if (m_surfaceBacking->swapTiles() && calculateFrameworkInvals) 271 addFrameworkInvals(); 272} 273 274void Surface::addFrameworkInvals() 275{ 276 // Let's return an inval area to framework that will 277 // contain all of our layers' areas 278 for (unsigned int i = 0; i < m_layers.size(); i++) 279 m_layers[i]->addDirtyArea(); 280} 281 282bool Surface::isReady() 283{ 284 if (!m_surfaceBacking) 285 return true; 286 287 return m_surfaceBacking->isReady(); 288} 289 290bool Surface::isMissingContent() 291{ 292 if (!m_surfaceBacking) 293 return true; 294 295 return m_surfaceBacking->isMissingContent(); 296} 297 298bool Surface::canUpdateWithBlit() 299{ 300 // If we don't have a texture, we have nothing to update and thus can take 301 // the fast path 302 if (!needsTexture()) 303 return true; 304 // If we have a surface backing that isn't ready, we can't update with a blit 305 // If it is ready, then check to see if it is dirty. We can only call isDirty() 306 // if isReady() returns true 307 if (!m_surfaceBacking) 308 return false; 309 if (!m_surfaceBacking->isReady()) 310 return false; 311 if (!m_surfaceBacking->isDirty()) 312 return true; 313 if (!singleLayer()) 314 return false; 315 return getFirstLayer()->canUpdateWithBlit(); 316} 317 318IntRect Surface::computePrepareArea() 319{ 320 IntRect area; 321 322 if (!getFirstLayer()->contentIsScrollable() 323 && !isBase() 324 && getFirstLayer()->state()->layersRenderingMode() == GLWebViewState::kAllTextures) { 325 326 area = fullContentArea(); 327 328 double total = ((double) area.width()) * ((double) area.height()); 329 if (total > MAX_FULL_CONTENT_AREA) 330 area = visibleContentArea(); 331 } else 332 area = visibleContentArea(); 333 334 return area; 335} 336 337void Surface::computeTexturesAmount(TexturesResult* result) 338{ 339 if (!m_surfaceBacking || isBase()) 340 return; 341 342 343 LayerAndroid* layer = 0; 344 if (singleLayer()) 345 layer = getFirstLayer(); 346 347 m_surfaceBacking->computeTexturesAmount(result, visibleContentArea(), 348 fullContentArea(), layer); 349} 350 351bool Surface::isBase() 352{ 353 // base layer surface 354 // - doesn't use layer tiles (disables blending, doesn't compute textures amount) 355 // - ignores clip rects 356 // - only prepares clippedArea 357 return getFirstLayer()->subclassType() == LayerAndroid::BaseLayer; 358} 359 360bool Surface::paint(SkCanvas* canvas) 361{ 362 if (singleLayer()) { 363 getFirstLayer()->contentDraw(canvas, Layer::UnmergedLayers); 364 365 // TODO: double buffer by disabling SurfaceCollection swaps and position 366 // updates until painting complete 367 368 // In single surface mode, draw layer content onto the base layer 369 if (isBase() 370 && getFirstLayer()->countChildren() 371 && getFirstLayer()->state()->isSingleSurfaceRenderingMode()) { 372 for (int i = 0; i < getFirstLayer()->countChildren(); i++) 373 getFirstLayer()->getChild(i)->drawCanvas(canvas, true, Layer::FlattenedLayers); 374 } 375 } else { 376 SkAutoCanvasRestore acr(canvas, true); 377 SkMatrix matrix; 378 GLUtils::toSkMatrix(matrix, m_drawTransform); 379 380 SkMatrix inverse; 381 inverse.reset(); 382 matrix.invert(&inverse); 383 384 SkMatrix canvasMatrix = canvas->getTotalMatrix(); 385 inverse.postConcat(canvasMatrix); 386 canvas->setMatrix(inverse); 387 388 for (unsigned int i=0; i<m_layers.size(); i++) 389 m_layers[i]->drawCanvas(canvas, false, Layer::MergedLayers); 390 } 391 return true; 392} 393 394float Surface::opacity() 395{ 396 if (singleLayer()) 397 return getFirstLayer()->drawOpacity(); 398 return 1.0; 399} 400 401Color* Surface::background() 402{ 403 if (!isBase() || !m_background.isValid()) 404 return 0; 405 return &m_background; 406} 407 408bool Surface::blitFromContents(Tile* tile) 409{ 410 if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content()) 411 return false; 412 413 LayerContent* content = getFirstLayer()->content(); 414 // Extract the dirty rect from the region. Note that this is *NOT* constrained 415 // to this tile 416 IntRect dirtyRect = tile->dirtyArea().getBounds(); 417 IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(), 418 tile->y() * TilesManager::tileHeight(), 419 TilesManager::tileWidth(), 420 TilesManager::tileHeight()); 421 FloatRect tileRectInDoc = tileRect; 422 tileRectInDoc.scale(1 / tile->scale()); 423 dirtyRect.intersect(enclosingIntRect(tileRectInDoc)); 424 PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect); 425 if (!prerenderedInval || prerenderedInval->bitmap.isNull()) 426 return false; 427 SkBitmap sourceBitmap = prerenderedInval->bitmap; 428 // Calculate the screen rect that is dirty, then intersect it with the 429 // tile's screen rect so that we end up with the pixels we need to blit 430 FloatRect screenDirty = dirtyRect; 431 screenDirty.scale(tile->scale()); 432 IntRect enclosingScreenDirty = enclosingIntRect(screenDirty); 433 enclosingScreenDirty.intersect(tileRect); 434 if (enclosingScreenDirty.isEmpty()) 435 return false; 436 // Make sure the screen area we want to blit is contained by the 437 // prerendered screen area 438 if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) { 439 ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain " 440 "enclosingScreenDirty " INT_RECT_FORMAT, 441 INT_RECT_ARGS(prerenderedInval->screenArea), 442 INT_RECT_ARGS(enclosingScreenDirty)); 443 return false; 444 } 445 IntPoint origin = prerenderedInval->screenArea.location(); 446 SkBitmap subset; 447 subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(), 448 enclosingScreenDirty.height()); 449 subset.allocPixels(); 450 451 int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y(); 452 int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x(); 453 if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset)) 454 return false; 455 // Now upload 456 SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(), 457 enclosingScreenDirty.y() - tileRect.y(), 458 enclosingScreenDirty.width(), 459 enclosingScreenDirty.height()); 460 GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId, 461 subset, textureInval); 462 tile->onBlitUpdate(); 463 return true; 464} 465 466const TransformationMatrix* Surface::drawTransform() 467{ 468 // single layer surfaces query the layer's draw transform, while multi-layer 469 // surfaces copy the draw transform once, during initialization 470 // TODO: support fixed multi-layer surfaces by querying the changing drawTransform 471 if (singleLayer()) 472 return getFirstLayer()->drawTransform(); 473 474 return &m_drawTransform; 475} 476 477} // namespace WebCore 478