Surface.cpp revision 418e065ccd82593c3f5d49942b0aaee6fac95615
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 // layer list has changed, fully invalidate 104 // TODO: partially invalidate based on layer size/position 105 fullInval = true; 106 break; 107 } else if (!m_layers[i]->getInvalRegion()->isEmpty()) { 108 // merge layer inval - translate the layer's inval region into surface coordinates 109 SkPoint pos = m_layers[i]->getPosition(); 110 m_layers[i]->getInvalRegion()->translate(pos.fX, pos.fY); 111 invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op); 112 break; 113 } 114 } 115 } 116 117 if (fullInval) 118 invalRegion.setRect(-1e8, -1e8, 2e8, 2e8); 119 120 m_surfaceBacking->markAsDirty(invalRegion); 121 } 122 return true; 123} 124 125void Surface::addLayer(LayerAndroid* layer, const TransformationMatrix& transform) 126{ 127 m_layers.append(layer); 128 SkSafeRef(layer); 129 130 m_needsTexture |= layer->needsTexture(); 131 m_hasText |= layer->hasText(); 132 133 // calculate area size for comparison later 134 IntRect rect = layer->fullContentArea(); 135 SkPoint pos = layer->getPosition(); 136 rect.setLocation(IntPoint(pos.fX, pos.fY)); 137 138 if (layer->needsTexture()) { 139 if (m_fullContentArea.isEmpty()) { 140 m_drawTransform = transform; 141 m_drawTransform.translate3d(-pos.fX, -pos.fY, 0); 142 m_fullContentArea = rect; 143 } else 144 m_fullContentArea.unite(rect); 145 ALOGV("Surf %p adding LA %p, size %d, %d %dx%d, now Surf size %d,%d %dx%d", 146 this, layer, rect.x(), rect.y(), rect.width(), rect.height(), 147 m_fullContentArea.x(), m_fullContentArea.y(), 148 m_fullContentArea.width(), m_fullContentArea.height()); 149 } 150 151 if (isBase()) 152 m_background = static_cast<BaseLayerAndroid*>(layer)->getBackgroundColor(); 153} 154 155IntRect Surface::visibleContentArea(bool force3dContentVisible) 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 (!getFirstLayer()->visible()) 231 return false; 232 233 bool isBaseLayer = isBase() 234 || getFirstLayer()->subclassType() == LayerAndroid::FixedBackgroundBaseLayer 235 || getFirstLayer()->subclassType() == LayerAndroid::ForegroundBaseLayer; 236 237 if (!isBaseLayer) { 238 // TODO: why are clipping regions wrong for base layer? 239 FloatRect drawClip = getFirstLayer()->drawClip(); 240 FloatRect clippingRect = TilesManager::instance()->shader()->rectInInvViewCoord(drawClip); 241 TilesManager::instance()->shader()->clip(clippingRect); 242 } 243 244 bool askRedraw = false; 245 if (m_surfaceBacking && !tilesDisabled) { 246 ALOGV("drawGL on Surf %p with SurfBack %p, first layer %s (%d)", this, m_surfaceBacking, 247 getFirstLayer()->subclassName().ascii().data(), getFirstLayer()->uniqueId()); 248 249 bool force3dContentVisible = true; 250 IntRect drawArea = visibleContentArea(force3dContentVisible); 251 m_surfaceBacking->drawGL(drawArea, opacity(), drawTransform(), 252 useAggressiveRendering(), background()); 253 } 254 255 // draw member layers (draws image textures, glextras) 256 for (unsigned int i = 0; i < m_layers.size(); i++) 257 askRedraw |= m_layers[i]->drawGL(tilesDisabled); 258 259 return askRedraw; 260} 261 262void Surface::swapTiles() 263{ 264 if (!m_surfaceBacking) 265 return; 266 267 m_surfaceBacking->swapTiles(); 268} 269 270bool Surface::isReady() 271{ 272 if (!m_surfaceBacking) 273 return true; 274 275 return m_surfaceBacking->isReady(); 276} 277 278bool Surface::isMissingContent() 279{ 280 if (!m_surfaceBacking) 281 return true; 282 283 return m_surfaceBacking->isMissingContent(); 284} 285 286bool Surface::canUpdateWithBlit() 287{ 288 // If we don't have a texture, we have nothing to update and thus can take 289 // the fast path 290 if (!needsTexture()) 291 return true; 292 // If we have a surface backing that isn't ready, we can't update with a blit 293 // If it is ready, then check to see if it is dirty. We can only call isDirty() 294 // if isReady() returns true 295 if (!m_surfaceBacking) 296 return false; 297 if (!m_surfaceBacking->isReady()) 298 return false; 299 if (!m_surfaceBacking->isDirty()) 300 return true; 301 if (!singleLayer()) 302 return false; 303 return getFirstLayer()->canUpdateWithBlit(); 304} 305 306IntRect Surface::computePrepareArea() 307{ 308 IntRect area; 309 310 if (!getFirstLayer()->contentIsScrollable() 311 && !isBase() 312 && getFirstLayer()->state()->layersRenderingMode() == GLWebViewState::kAllTextures) { 313 314 area = fullContentArea(); 315 316 double total = ((double) area.width()) * ((double) area.height()); 317 if (total > MAX_FULL_CONTENT_AREA) 318 area = visibleContentArea(); 319 } else 320 area = visibleContentArea(); 321 322 return area; 323} 324 325void Surface::computeTexturesAmount(TexturesResult* result) 326{ 327 if (!m_surfaceBacking || isBase()) 328 return; 329 330 m_surfaceBacking->computeTexturesAmount(result, getFirstLayer()); 331} 332 333bool Surface::isBase() 334{ 335 // base layer surface 336 // - doesn't use layer tiles (disables blending, doesn't compute textures amount) 337 // - ignores clip rects 338 // - only prepares clippedArea 339 return getFirstLayer()->subclassType() == LayerAndroid::BaseLayer; 340} 341 342bool Surface::paint(SkCanvas* canvas) 343{ 344 if (singleLayer()) { 345 getFirstLayer()->contentDraw(canvas, Layer::UnmergedLayers); 346 347 // TODO: double buffer by disabling SurfaceCollection swaps and position 348 // updates until painting complete 349 350 // In single surface mode, draw layer content onto the base layer 351 if (isBase() 352 && getFirstLayer()->countChildren() 353 && getFirstLayer()->state()->layersRenderingMode() > GLWebViewState::kClippedTextures) { 354 for (unsigned int i = 0; i < getFirstLayer()->countChildren(); i++) 355 getFirstLayer()->getChild(i)->drawCanvas(canvas, true, Layer::FlattenedLayers); 356 } 357 } else { 358 SkAutoCanvasRestore acr(canvas, true); 359 SkMatrix matrix; 360 GLUtils::toSkMatrix(matrix, m_drawTransform); 361 362 SkMatrix inverse; 363 inverse.reset(); 364 matrix.invert(&inverse); 365 366 SkMatrix canvasMatrix = canvas->getTotalMatrix(); 367 inverse.postConcat(canvasMatrix); 368 canvas->setMatrix(inverse); 369 370 for (unsigned int i=0; i<m_layers.size(); i++) 371 m_layers[i]->drawCanvas(canvas, false, Layer::MergedLayers); 372 } 373 return true; 374} 375 376float Surface::opacity() 377{ 378 if (singleLayer()) 379 return getFirstLayer()->drawOpacity(); 380 return 1.0; 381} 382 383Color* Surface::background() 384{ 385 if (!isBase() || !m_background.isValid()) 386 return 0; 387 return &m_background; 388} 389 390bool Surface::blitFromContents(Tile* tile) 391{ 392 if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content()) 393 return false; 394 395 LayerContent* content = getFirstLayer()->content(); 396 // Extract the dirty rect from the region. Note that this is *NOT* constrained 397 // to this tile 398 IntRect dirtyRect = tile->dirtyArea().getBounds(); 399 IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(), 400 tile->y() * TilesManager::tileHeight(), 401 TilesManager::tileWidth(), 402 TilesManager::tileHeight()); 403 FloatRect tileRectInDoc = tileRect; 404 tileRectInDoc.scale(1 / tile->scale()); 405 dirtyRect.intersect(enclosingIntRect(tileRectInDoc)); 406 PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect); 407 if (!prerenderedInval || prerenderedInval->bitmap.isNull()) 408 return false; 409 SkBitmap sourceBitmap = prerenderedInval->bitmap; 410 // Calculate the screen rect that is dirty, then intersect it with the 411 // tile's screen rect so that we end up with the pixels we need to blit 412 FloatRect screenDirty = dirtyRect; 413 screenDirty.scale(tile->scale()); 414 IntRect enclosingScreenDirty = enclosingIntRect(screenDirty); 415 enclosingScreenDirty.intersect(tileRect); 416 if (enclosingScreenDirty.isEmpty()) 417 return false; 418 // Make sure the screen area we want to blit is contained by the 419 // prerendered screen area 420 if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) { 421 ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain " 422 "enclosingScreenDirty " INT_RECT_FORMAT, 423 INT_RECT_ARGS(prerenderedInval->screenArea), 424 INT_RECT_ARGS(enclosingScreenDirty)); 425 return false; 426 } 427 IntPoint origin = prerenderedInval->screenArea.location(); 428 SkBitmap subset; 429 subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(), 430 enclosingScreenDirty.height()); 431 subset.allocPixels(); 432 433 int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y(); 434 int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x(); 435 if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset)) 436 return false; 437 // Now upload 438 SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(), 439 enclosingScreenDirty.y() - tileRect.y(), 440 enclosingScreenDirty.width(), 441 enclosingScreenDirty.height()); 442 GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId, 443 subset, textureInval); 444 tile->onBlitUpdate(); 445 return true; 446} 447 448const TransformationMatrix* Surface::drawTransform() 449{ 450 // single layer surfaces query the layer's draw transform, while multi-layer 451 // surfaces copy the draw transform once, during initialization 452 // TODO: support fixed multi-layer surfaces by querying the changing drawTransform 453 if (singleLayer()) 454 return getFirstLayer()->drawTransform(); 455 456 return &m_drawTransform; 457} 458 459} // namespace WebCore 460