diff options
author | jamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-25 00:09:14 +0000 |
---|---|---|
committer | jamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-25 00:09:14 +0000 |
commit | 94f206c1c75eb8cc4df2225a1c5c9c7b6fc96679 (patch) | |
tree | 530f51d5c75459999e4adf2a6895884ce1c15ce0 /cc/TiledLayerChromium.cpp | |
parent | 56235947f2b023fc63cfad692c56df4e92199848 (diff) | |
download | chromium_src-94f206c1c75eb8cc4df2225a1c5c9c7b6fc96679.zip chromium_src-94f206c1c75eb8cc4df2225a1c5c9c7b6fc96679.tar.gz chromium_src-94f206c1c75eb8cc4df2225a1c5c9c7b6fc96679.tar.bz2 |
Here are gyp targets and stubs for compiling libcc and the webkit_compositor bindings in chromium. Everything is guarded behind the off-by-default use_libcc_for_compositor gyp variable. I haven't included the actual code here, but there are scripts to sync. I plan to land + manually sync the code into place until we're ready to flip the gyp switch.
Snapshot from r126652
BUG=
Review URL: https://chromiumcodereview.appspot.com/10828381
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153354 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc/TiledLayerChromium.cpp')
-rw-r--r-- | cc/TiledLayerChromium.cpp | 791 |
1 files changed, 791 insertions, 0 deletions
diff --git a/cc/TiledLayerChromium.cpp b/cc/TiledLayerChromium.cpp new file mode 100644 index 0000000..8a6e4b9 --- /dev/null +++ b/cc/TiledLayerChromium.cpp @@ -0,0 +1,791 @@ +// Copyright 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "TiledLayerChromium.h" + +#include "CCLayerImpl.h" +#include "CCLayerTreeHost.h" +#include "CCOverdrawMetrics.h" +#include "CCTextureUpdateQueue.h" +#include "CCTiledLayerImpl.h" +#include "GraphicsContext3D.h" +#include "Region.h" +#include "TextStream.h" +#include <wtf/CurrentTime.h> +#include <wtf/MathExtras.h> + +using namespace std; +using WebKit::WebTransformationMatrix; + +namespace WebCore { + +class UpdatableTile : public CCLayerTilingData::Tile { + WTF_MAKE_NONCOPYABLE(UpdatableTile); +public: + static PassOwnPtr<UpdatableTile> create(PassOwnPtr<LayerTextureUpdater::Texture> texture) + { + return adoptPtr(new UpdatableTile(texture)); + } + + LayerTextureUpdater::Texture* texture() { return m_texture.get(); } + CCPrioritizedTexture* managedTexture() { return m_texture->texture(); } + + bool isDirty() const { return !dirtyRect.isEmpty(); } + + // Reset update state for the current frame. This should occur before painting + // for all layers. Since painting one layer can invalidate another layer + // after it has already painted, mark all non-dirty tiles as valid before painting + // such that invalidations during painting won't prevent them from being pushed. + void resetUpdateState() + { + updateRect = IntRect(); + occluded = false; + partialUpdate = false; + validForFrame = !isDirty(); + } + + // This promises to update the tile and therefore also guarantees the tile + // will be valid for this frame. dirtyRect is copied into updateRect so + // we can continue to track re-entrant invalidations that occur during painting. + void markForUpdate() + { + validForFrame = true; + updateRect = dirtyRect; + dirtyRect = IntRect(); + } + + IntRect dirtyRect; + IntRect updateRect; + bool partialUpdate; + bool validForFrame; + bool occluded; + bool isInUseOnImpl; +private: + explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture) + : partialUpdate(false) + , validForFrame(false) + , occluded(false) + , isInUseOnImpl(false) + , m_texture(texture) + { + } + + OwnPtr<LayerTextureUpdater::Texture> m_texture; +}; + +TiledLayerChromium::TiledLayerChromium() + : LayerChromium() + , m_textureFormat(GraphicsContext3D::INVALID_ENUM) + , m_skipsDraw(false) + , m_failedUpdate(false) + , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid) + , m_tilingOption(AutoTile) +{ + m_tiler = CCLayerTilingData::create(IntSize(), CCLayerTilingData::HasBorderTexels); +} + +TiledLayerChromium::~TiledLayerChromium() +{ +} + +PassOwnPtr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl() +{ + return CCTiledLayerImpl::create(id()); +} + +void TiledLayerChromium::updateTileSizeAndTilingOption() +{ + ASSERT(layerTreeHost()); + + const IntSize& defaultTileSize = layerTreeHost()->settings().defaultTileSize; + const IntSize& maxUntiledLayerSize = layerTreeHost()->settings().maxUntiledLayerSize; + int layerWidth = contentBounds().width(); + int layerHeight = contentBounds().height(); + + const IntSize tileSize(min(defaultTileSize.width(), layerWidth), min(defaultTileSize.height(), layerHeight)); + + // Tile if both dimensions large, or any one dimension large and the other + // extends into a second tile but the total layer area isn't larger than that + // of the largest possible untiled layer. This heuristic allows for long skinny layers + // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space but still avoids + // creating very large tiles. + const bool anyDimensionLarge = layerWidth > maxUntiledLayerSize.width() || layerHeight > maxUntiledLayerSize.height(); + const bool anyDimensionOneTile = (layerWidth <= defaultTileSize.width() || layerHeight <= defaultTileSize.height()) + && (layerWidth * layerHeight) <= (maxUntiledLayerSize.width() * maxUntiledLayerSize.height()); + const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile; + + bool isTiled; + if (m_tilingOption == AlwaysTile) + isTiled = true; + else if (m_tilingOption == NeverTile) + isTiled = false; + else + isTiled = autoTiled; + + IntSize requestedSize = isTiled ? tileSize : contentBounds(); + const int maxSize = layerTreeHost()->rendererCapabilities().maxTextureSize; + IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize)); + setTileSize(clampedSize); +} + +void TiledLayerChromium::updateBounds() +{ + IntSize oldBounds = m_tiler->bounds(); + IntSize newBounds = contentBounds(); + if (oldBounds == newBounds) + return; + m_tiler->setBounds(newBounds); + + // Invalidate any areas that the new bounds exposes. + Region oldRegion(IntRect(IntPoint(), oldBounds)); + Region newRegion(IntRect(IntPoint(), newBounds)); + newRegion.subtract(oldRegion); + Vector<IntRect> rects = newRegion.rects(); + for (size_t i = 0; i < rects.size(); ++i) + invalidateContentRect(rects[i]); +} + +void TiledLayerChromium::setTileSize(const IntSize& size) +{ + m_tiler->setTileSize(size); +} + +void TiledLayerChromium::setBorderTexelOption(CCLayerTilingData::BorderTexelOption borderTexelOption) +{ + m_tiler->setBorderTexelOption(borderTexelOption); +} + +bool TiledLayerChromium::drawsContent() const +{ + if (!LayerChromium::drawsContent()) + return false; + + bool hasMoreThanOneTile = m_tiler->numTilesX() > 1 || m_tiler->numTilesY() > 1; + if (m_tilingOption == NeverTile && hasMoreThanOneTile) + return false; + + return true; +} + +bool TiledLayerChromium::needsContentsScale() const +{ + return true; +} + +IntSize TiledLayerChromium::contentBounds() const +{ + return IntSize(lroundf(bounds().width() * contentsScale()), lroundf(bounds().height() * contentsScale())); +} + +void TiledLayerChromium::setTilingOption(TilingOption tilingOption) +{ + m_tilingOption = tilingOption; +} + +void TiledLayerChromium::setIsMask(bool isMask) +{ + setTilingOption(isMask ? NeverTile : AutoTile); +} + +void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer) +{ + LayerChromium::pushPropertiesTo(layer); + + CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer); + + tiledLayer->setSkipsDraw(m_skipsDraw); + tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater::SampledTexelFormatRGBA); + tiledLayer->setTilingData(*m_tiler); + Vector<UpdatableTile*> invalidTiles; + + for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) { + int i = iter->first.first; + int j = iter->first.second; + UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); + // FIXME: This should not ever be null. + if (!tile) + continue; + tile->isInUseOnImpl = false; + if (!tile->managedTexture()->haveBackingTexture()) { + invalidTiles.append(tile); + continue; + } + if (!tile->validForFrame) + continue; + + tiledLayer->pushTileProperties(i, j, tile->managedTexture()->resourceId(), tile->opaqueRect()); + tile->isInUseOnImpl = true; + } + for (Vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); iter != invalidTiles.end(); ++iter) + m_tiler->takeTile((*iter)->i(), (*iter)->j()); +} + +CCPrioritizedTextureManager* TiledLayerChromium::textureManager() const +{ + if (!layerTreeHost()) + return 0; + return layerTreeHost()->contentsTextureManager(); +} + +void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host) +{ + if (host && host != layerTreeHost()) { + for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) { + UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); + // FIXME: This should not ever be null. + if (!tile) + continue; + tile->managedTexture()->setTextureManager(host->contentsTextureManager()); + } + } + LayerChromium::setLayerTreeHost(host); +} + +UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const +{ + return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j)); +} + +UpdatableTile* TiledLayerChromium::createTile(int i, int j) +{ + createTextureUpdaterIfNeeded(); + + OwnPtr<UpdatableTile> tile(UpdatableTile::create(textureUpdater()->createTexture(textureManager()))); + tile->managedTexture()->setDimensions(m_tiler->tileSize(), m_textureFormat); + + UpdatableTile* addedTile = tile.get(); + m_tiler->addTile(tile.release(), i, j); + + addedTile->dirtyRect = m_tiler->tileRect(addedTile); + + // Temporary diagnostic crash. + if (!addedTile) + CRASH(); + if (!tileAt(i, j)) + CRASH(); + + return addedTile; +} + +void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect) +{ + float contentsWidthScale = static_cast<float>(contentBounds().width()) / bounds().width(); + float contentsHeightScale = static_cast<float>(contentBounds().height()) / bounds().height(); + FloatRect scaledDirtyRect(dirtyRect); + scaledDirtyRect.scale(contentsWidthScale, contentsHeightScale); + IntRect dirty = enclosingIntRect(scaledDirtyRect); + invalidateContentRect(dirty); + LayerChromium::setNeedsDisplayRect(dirtyRect); +} + +void TiledLayerChromium::setUseLCDText(bool useLCDText) +{ + LayerChromium::setUseLCDText(useLCDText); + + CCLayerTilingData::BorderTexelOption borderTexelOption; +#if OS(ANDROID) + // Always want border texels and GL_LINEAR due to pinch zoom. + borderTexelOption = CCLayerTilingData::HasBorderTexels; +#else + borderTexelOption = useLCDText ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels; +#endif + setBorderTexelOption(borderTexelOption); +} + +void TiledLayerChromium::invalidateContentRect(const IntRect& contentRect) +{ + updateBounds(); + if (m_tiler->isEmpty() || contentRect.isEmpty() || m_skipsDraw) + return; + + for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) { + UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); + ASSERT(tile); + // FIXME: This should not ever be null. + if (!tile) + continue; + IntRect bound = m_tiler->tileRect(tile); + bound.intersect(contentRect); + tile->dirtyRect.unite(bound); + } +} + +// Returns true if tile is dirty and only part of it needs to be updated. +bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile) +{ + return !tile->dirtyRect.contains(m_tiler->tileRect(tile)); +} + +// Dirty tiles with valid textures needs buffered update to guarantee that +// we don't modify textures currently used for drawing by the impl thread. +bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile) +{ + if (!tile->managedTexture()->haveBackingTexture()) + return false; + + if (!tile->isDirty()) + return false; + + if (!tile->isInUseOnImpl) + return false; + + return true; +} + + +bool TiledLayerChromium::updateTiles(int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats, bool& didPaint) +{ + didPaint = false; + createTextureUpdaterIfNeeded(); + + bool ignoreOcclusions = !occlusion; + if (!haveTexturesForTiles(left, top, right, bottom, ignoreOcclusions)) { + m_failedUpdate = true; + return false; + } + + IntRect paintRect = markTilesForUpdate(left, top, right, bottom, ignoreOcclusions); + + if (occlusion) + occlusion->overdrawMetrics().didPaint(paintRect); + + if (paintRect.isEmpty()) + return true; + + didPaint = true; + updateTileTextures(paintRect, left, top, right, bottom, queue, occlusion, stats); + return true; +} + +void TiledLayerChromium::markOcclusionsAndRequestTextures(int left, int top, int right, int bottom, const CCOcclusionTracker* occlusion) +{ + // There is some difficult dependancies between occlusions, recording occlusion metrics + // and requesting memory so those are encapsulated in this function: + // - We only want to call requestLate on unoccluded textures (to preserve + // memory for other layers when near OOM). + // - We only want to record occlusion metrics if all memory requests succeed. + + int occludedTileCount = 0; + bool succeeded = true; + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + UpdatableTile* tile = tileAt(i, j); + ASSERT(tile); // Did setTexturePriorities get skipped? + // FIXME: This should not ever be null. + if (!tile) + continue; + ASSERT(!tile->occluded); // Did resetUpdateState get skipped? Are we doing more than one occlusion pass? + IntRect visibleTileRect = intersection(m_tiler->tileBounds(i, j), visibleContentRect()); + if (occlusion && occlusion->occluded(this, visibleTileRect)) { + tile->occluded = true; + occludedTileCount++; + } else { + succeeded &= tile->managedTexture()->requestLate(); + } + } + } + + if (!succeeded) + return; + + // FIXME: Remove the loop and just pass the count! + for (int i = 0; i < occludedTileCount; i++) + occlusion->overdrawMetrics().didCullTileForUpload(); +} + +bool TiledLayerChromium::haveTexturesForTiles(int left, int top, int right, int bottom, bool ignoreOcclusions) +{ + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + UpdatableTile* tile = tileAt(i, j); + ASSERT(tile); // Did setTexturePriorites get skipped? + // FIXME: This should not ever be null. + if (!tile) + continue; + + // Ensure the entire tile is dirty if we don't have the texture. + if (!tile->managedTexture()->haveBackingTexture()) + tile->dirtyRect = m_tiler->tileRect(tile); + + // If using occlusion and the visible region of the tile is occluded, + // don't reserve a texture or update the tile. + if (tile->occluded && !ignoreOcclusions) + continue; + + if (!tile->managedTexture()->canAcquireBackingTexture()) + return false; + } + } + return true; +} + +IntRect TiledLayerChromium::markTilesForUpdate(int left, int top, int right, int bottom, bool ignoreOcclusions) +{ + IntRect paintRect; + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + UpdatableTile* tile = tileAt(i, j); + ASSERT(tile); // Did setTexturePriorites get skipped? + // FIXME: This should not ever be null. + if (!tile) + continue; + if (tile->occluded && !ignoreOcclusions) + continue; + paintRect.unite(tile->dirtyRect); + tile->markForUpdate(); + } + } + return paintRect; +} + +void TiledLayerChromium::updateTileTextures(const IntRect& paintRect, int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats) +{ + // The updateRect should be in layer space. So we have to convert the paintRect from content space to layer space. + m_updateRect = FloatRect(paintRect); + float widthScale = bounds().width() / static_cast<float>(contentBounds().width()); + float heightScale = bounds().height() / static_cast<float>(contentBounds().height()); + m_updateRect.scale(widthScale, heightScale); + + // Calling prepareToUpdate() calls into WebKit to paint, which may have the side + // effect of disabling compositing, which causes our reference to the texture updater to be deleted. + // However, we can't free the memory backing the SkCanvas until the paint finishes, + // so we grab a local reference here to hold the updater alive until the paint completes. + RefPtr<LayerTextureUpdater> protector(textureUpdater()); + IntRect paintedOpaqueRect; + textureUpdater()->prepareToUpdate(paintRect, m_tiler->tileSize(), 1 / widthScale, 1 / heightScale, paintedOpaqueRect, stats); + + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + UpdatableTile* tile = tileAt(i, j); + ASSERT(tile); // Did setTexturePriorites get skipped? + // FIXME: This should not ever be null. + if (!tile) + continue; + + IntRect tileRect = m_tiler->tileBounds(i, j); + + // Use updateRect as the above loop copied the dirty rect for this frame to updateRect. + const IntRect& dirtyRect = tile->updateRect; + if (dirtyRect.isEmpty()) + continue; + + // Save what was painted opaque in the tile. Keep the old area if the paint didn't touch it, and didn't paint some + // other part of the tile opaque. + IntRect tilePaintedRect = intersection(tileRect, paintRect); + IntRect tilePaintedOpaqueRect = intersection(tileRect, paintedOpaqueRect); + if (!tilePaintedRect.isEmpty()) { + IntRect paintInsideTileOpaqueRect = intersection(tile->opaqueRect(), tilePaintedRect); + bool paintInsideTileOpaqueRectIsNonOpaque = !tilePaintedOpaqueRect.contains(paintInsideTileOpaqueRect); + bool opaquePaintNotInsideTileOpaqueRect = !tilePaintedOpaqueRect.isEmpty() && !tile->opaqueRect().contains(tilePaintedOpaqueRect); + + if (paintInsideTileOpaqueRectIsNonOpaque || opaquePaintNotInsideTileOpaqueRect) + tile->setOpaqueRect(tilePaintedOpaqueRect); + } + + // sourceRect starts as a full-sized tile with border texels included. + IntRect sourceRect = m_tiler->tileRect(tile); + sourceRect.intersect(dirtyRect); + // Paint rect not guaranteed to line up on tile boundaries, so + // make sure that sourceRect doesn't extend outside of it. + sourceRect.intersect(paintRect); + + tile->updateRect = sourceRect; + + if (sourceRect.isEmpty()) + continue; + + tile->texture()->prepareRect(sourceRect, stats); + if (occlusion) + occlusion->overdrawMetrics().didUpload(WebTransformationMatrix(), sourceRect, tile->opaqueRect()); + + const IntPoint anchor = m_tiler->tileRect(tile).location(); + + // Calculate tile-space rectangle to upload into. + IntSize destOffset(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()); + if (destOffset.width() < 0) + CRASH(); + if (destOffset.height() < 0) + CRASH(); + + // Offset from paint rectangle to this tile's dirty rectangle. + IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y() - paintRect.y()); + if (paintOffset.x() < 0) + CRASH(); + if (paintOffset.y() < 0) + CRASH(); + if (paintOffset.x() + sourceRect.width() > paintRect.width()) + CRASH(); + if (paintOffset.y() + sourceRect.height() > paintRect.height()) + CRASH(); + + TextureUploader::Parameters upload = { tile->texture(), sourceRect, destOffset }; + if (tile->partialUpdate) + queue.appendPartialUpload(upload); + else + queue.appendFullUpload(upload); + } + } +} + +namespace { +// This picks a small animated layer to be anything less than one viewport. This +// is specifically for page transitions which are viewport-sized layers. The extra +// 64 pixels is due to these layers being slightly larger than the viewport in some cases. +bool isSmallAnimatedLayer(TiledLayerChromium* layer) +{ + if (!layer->drawTransformIsAnimating() && !layer->screenSpaceTransformIsAnimating()) + return false; + IntSize viewportSize = layer->layerTreeHost() ? layer->layerTreeHost()->deviceViewportSize() : IntSize(); + IntRect contentRect(IntPoint::zero(), layer->contentBounds()); + return contentRect.width() <= viewportSize.width() + 64 + && contentRect.height() <= viewportSize.height() + 64; +} + +// FIXME: Remove this and make this based on distance once distance can be calculated +// for offscreen layers. For now, prioritize all small animated layers after 512 +// pixels of pre-painting. +void setPriorityForTexture(const CCPriorityCalculator& priorityCalc, + const IntRect& visibleRect, + const IntRect& tileRect, + bool drawsToRoot, + bool isSmallAnimatedLayer, + CCPrioritizedTexture* texture) +{ + int priority = CCPriorityCalculator::lowestPriority(); + if (!visibleRect.isEmpty()) + priority = priorityCalc.priorityFromDistance(visibleRect, tileRect, drawsToRoot); + if (isSmallAnimatedLayer) + priority = CCPriorityCalculator::maxPriority(priority, priorityCalc.priorityFromDistance(512, drawsToRoot)); + if (priority != CCPriorityCalculator::lowestPriority()) + texture->setRequestPriority(priority); +} +} + +void TiledLayerChromium::setTexturePriorities(const CCPriorityCalculator& priorityCalc) +{ + updateBounds(); + resetUpdateState(); + + if (m_tiler->hasEmptyBounds()) + return; + + bool drawsToRoot = !renderTarget()->parent(); + bool smallAnimatedLayer = isSmallAnimatedLayer(this); + + // Minimally create the tiles in the desired pre-paint rect. + IntRect createTilesRect = idlePaintRect(); + if (!createTilesRect.isEmpty()) { + int left, top, right, bottom; + m_tiler->contentRectToTileIndices(createTilesRect, left, top, right, bottom); + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + if (!tileAt(i, j)) + createTile(i, j); + } + } + } + + // Also, minimally create all tiles for small animated layers and also + // double-buffer them since we have limited their size to be reasonable. + IntRect doubleBufferedRect = visibleContentRect(); + if (smallAnimatedLayer) + doubleBufferedRect = IntRect(IntPoint::zero(), contentBounds()); + + // Create additional textures for double-buffered updates when needed. + // These textures must stay alive while the updated textures are incrementally + // uploaded, swapped atomically via pushProperties, and finally deleted + // after the commit is complete, after which they can be recycled. + if (!doubleBufferedRect.isEmpty()) { + int left, top, right, bottom; + m_tiler->contentRectToTileIndices(doubleBufferedRect, left, top, right, bottom); + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + UpdatableTile* tile = tileAt(i, j); + if (!tile) + tile = createTile(i, j); + // We need an additional texture if the tile needs a buffered-update and it's not a partial update. + // FIXME: Decide if partial update should be allowed based on cost + // of update. https://bugs.webkit.org/show_bug.cgi?id=77376 + if (!layerTreeHost() || !layerTreeHost()->bufferedUpdates() || !tileNeedsBufferedUpdate(tile)) + continue; + if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->requestPartialTextureUpdate()) { + tile->partialUpdate = true; + continue; + } + + IntRect tileRect = m_tiler->tileRect(tile); + tile->dirtyRect = tileRect; + LayerTextureUpdater::Texture* backBuffer = tile->texture(); + setPriorityForTexture(priorityCalc, visibleContentRect(), tile->dirtyRect, drawsToRoot, smallAnimatedLayer, backBuffer->texture()); + OwnPtr<CCPrioritizedTexture> frontBuffer = CCPrioritizedTexture::create(backBuffer->texture()->textureManager(), + backBuffer->texture()->size(), + backBuffer->texture()->format()); + // Swap backBuffer into frontBuffer and add it to delete after commit queue. + backBuffer->swapTextureWith(frontBuffer); + layerTreeHost()->deleteTextureAfterCommit(frontBuffer.release()); + } + } + } + + // Now update priorities on all tiles we have in the layer, no matter where they are. + for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) { + UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); + // FIXME: This should not ever be null. + if (!tile) + continue; + IntRect tileRect = m_tiler->tileRect(tile); + setPriorityForTexture(priorityCalc, visibleContentRect(), tileRect, drawsToRoot, smallAnimatedLayer, tile->managedTexture()); + } +} + +Region TiledLayerChromium::visibleContentOpaqueRegion() const +{ + if (m_skipsDraw) + return Region(); + if (opaque()) + return visibleContentRect(); + return m_tiler->opaqueRegionInContentRect(visibleContentRect()); +} + +void TiledLayerChromium::resetUpdateState() +{ + m_skipsDraw = false; + m_failedUpdate = false; + + CCLayerTilingData::TileMap::const_iterator end = m_tiler->tiles().end(); + for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != end; ++iter) { + UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); + // FIXME: This should not ever be null. + if (!tile) + continue; + tile->resetUpdateState(); + } +} + +void TiledLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats) +{ + ASSERT(!m_skipsDraw && !m_failedUpdate); // Did resetUpdateState get skipped? + updateBounds(); + if (m_tiler->hasEmptyBounds() || !drawsContent()) + return; + + bool didPaint = false; + + // Animation pre-paint. If the layer is small, try to paint it all + // immediately whether or not it is occluded, to avoid paint/upload + // hiccups while it is animating. + if (isSmallAnimatedLayer(this)) { + int left, top, right, bottom; + m_tiler->contentRectToTileIndices(IntRect(IntPoint::zero(), contentBounds()), left, top, right, bottom); + updateTiles(left, top, right, bottom, queue, 0, stats, didPaint); + if (didPaint) + return; + // This was an attempt to paint the entire layer so if we fail it's okay, + // just fallback on painting visible etc. below. + m_failedUpdate = false; + } + + if (visibleContentRect().isEmpty()) + return; + + // Visible painting. First occlude visible tiles and paint the non-occluded tiles. + int left, top, right, bottom; + m_tiler->contentRectToTileIndices(visibleContentRect(), left, top, right, bottom); + markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion); + m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats, didPaint); + if (m_skipsDraw) + m_tiler->reset(); + if (m_skipsDraw || didPaint) + return; + + // If we have already painting everything visible. Do some pre-painting while idle. + IntRect idlePaintContentRect = idlePaintRect(); + if (idlePaintContentRect.isEmpty()) + return; + + // Prepaint anything that was occluded but inside the layer's visible region. + if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || didPaint) + return; + + int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom; + m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom); + + // Then expand outwards from the visible area until we find a dirty row or column to update. + while (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom) { + if (bottom < prepaintBottom) { + ++bottom; + if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint) + return; + } + if (top > prepaintTop) { + --top; + if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint) + return; + } + if (left > prepaintLeft) { + --left; + if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint) + return; + } + if (right < prepaintRight) { + ++right; + if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPaint) || didPaint) + return; + } + } +} + +bool TiledLayerChromium::needsIdlePaint() +{ + // Don't trigger more paints if we failed (as we'll just fail again). + if (m_failedUpdate || visibleContentRect().isEmpty() || m_tiler->hasEmptyBounds() || !drawsContent()) + return false; + + IntRect idlePaintContentRect = idlePaintRect(); + if (idlePaintContentRect.isEmpty()) + return false; + + int left, top, right, bottom; + m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bottom); + + for (int j = top; j <= bottom; ++j) { + for (int i = left; i <= right; ++i) { + UpdatableTile* tile = tileAt(i, j); + ASSERT(tile); // Did setTexturePriorities get skipped? + if (!tile) + continue; + + bool updated = !tile->updateRect.isEmpty(); + bool canAcquire = tile->managedTexture()->canAcquireBackingTexture(); + bool dirty = tile->isDirty() || !tile->managedTexture()->haveBackingTexture(); + if (!updated && canAcquire && dirty) + return true; + } + } + return false; +} + +IntRect TiledLayerChromium::idlePaintRect() +{ + // Don't inflate an empty rect. + if (visibleContentRect().isEmpty()) + return IntRect(); + + // FIXME: This can be made a lot larger now! We should increase + // this slowly while insuring it doesn't cause any perf issues. + IntRect prepaintRect = visibleContentRect(); + prepaintRect.inflateX(m_tiler->tileSize().width()); + prepaintRect.inflateY(m_tiler->tileSize().height() * 2); + IntRect contentRect(IntPoint::zero(), contentBounds()); + prepaintRect.intersect(contentRect); + + return prepaintRect; +} + +} +#endif // USE(ACCELERATED_COMPOSITING) |