Modified: trunk/Source/WebCore/platform/graphics/chromium/TiledLayerChromium.cpp (125497 => 125498)
--- trunk/Source/WebCore/platform/graphics/chromium/TiledLayerChromium.cpp 2012-08-14 02:10:43 UTC (rev 125497)
+++ trunk/Source/WebCore/platform/graphics/chromium/TiledLayerChromium.cpp 2012-08-14 02:14:16 UTC (rev 125498)
@@ -402,7 +402,7 @@
// FIXME: This should not ever be null.
if (!tile)
continue;
- ASSERT(!tile->occluded); // Did resetUpdateState get skipped? Are we doing more than one occluded pass?
+ 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;
@@ -560,31 +560,77 @@
setTexturePrioritiesInRect(priorityCalc, visibleContentRect());
}
+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::setTexturePrioritiesInRect(const CCPriorityCalculator& priorityCalc, const IntRect& visibleContentRect)
{
updateBounds();
resetUpdateState();
- IntRect prepaintRect = idlePaintRect(visibleContentRect);
+ if (m_tiler->hasEmptyBounds())
+ return;
+
bool drawsToRoot = !renderTarget()->parent();
+ bool smallAnimatedLayer = isSmallAnimatedLayer(this);
// Minimally create the tiles in the desired pre-paint rect.
- if (!prepaintRect.isEmpty()) {
+ IntRect createTilesRect = idlePaintRect(visibleContentRect);
+ if (!createTilesRect.isEmpty()) {
int left, top, right, bottom;
- m_tiler->contentRectToTileIndices(prepaintRect, left, top, right, bottom);
- for (int j = top; j <= bottom; ++j)
- for (int i = left; i <= right; ++i)
+ 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 (!visibleContentRect.isEmpty()) {
+ if (!doubleBufferedRect.isEmpty()) {
int left, top, right, bottom;
- m_tiler->contentRectToTileIndices(visibleContentRect, 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);
@@ -600,9 +646,10 @@
continue;
}
- tile->dirtyRect = m_tiler->tileRect(tile);
+ IntRect tileRect = m_tiler->tileRect(tile);
+ tile->dirtyRect = tileRect;
LayerTextureUpdater::Texture* backBuffer = tile->texture();
- backBuffer->texture()->setRequestPriority(priorityCalc.priorityFromVisibility(true, drawsToRoot));
+ setPriorityForTexture(priorityCalc, visibleContentRect, tile->dirtyRect, drawsToRoot, smallAnimatedLayer, backBuffer->texture());
OwnPtr<CCPrioritizedTexture> frontBuffer = CCPrioritizedTexture::create(backBuffer->texture()->textureManager(),
backBuffer->texture()->size(),
backBuffer->texture()->format());
@@ -613,18 +660,14 @@
}
}
- // Now set priorities on all tiles.
+ // 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);
- // FIXME: This indicates the "small animated layer" case. This special case
- // can be removed soon with better priorities, but for now paint these layers after
- // 512 pixels of pre-painting. Later we can just pass an animating flag etc. to the
- // calculator and it can take care of this special case if we still need it.
- if (visibleContentRect.isEmpty() && !prepaintRect.isEmpty())
- tile->managedTexture()->setRequestPriority(priorityCalc.priorityFromDistance(512, drawsToRoot));
- else if (!visibleContentRect.isEmpty())
- tile->managedTexture()->setRequestPriority(priorityCalc.priorityFromDistance(visibleContentRect, tileRect, drawsToRoot));
+ setPriorityForTexture(priorityCalc, visibleContentRect, tileRect, drawsToRoot, smallAnimatedLayer, tile->managedTexture());
}
}
@@ -660,45 +703,45 @@
bool didPaint = false;
- // Visible painting. Only paint visible tiles if the visible rect isn't empty.
- if (!contentRect.isEmpty()) {
+ // 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(contentRect, 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)
+ 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 (contentRect.isEmpty())
+ return;
+
+ // Visible painting. First occlude visible tiles and paint the non-occluded tiles.
+ int left, top, right, bottom;
+ m_tiler->contentRectToTileIndices(contentRect, 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(contentRect);
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);
- // If the layer is not visible, we have nothing to expand from, so instead we prepaint the outer-most set of tiles.
- if (contentRect.isEmpty()) {
- if (!updateTiles(prepaintLeft, prepaintTop, prepaintRight, prepaintTop, queue, 0, stats, didPaint) || didPaint)
- return;
- if (!updateTiles(prepaintLeft, prepaintBottom, prepaintRight, prepaintBottom, queue, 0, stats, didPaint) || didPaint)
- return;
- if (!updateTiles(prepaintLeft, prepaintTop, prepaintLeft, prepaintBottom, queue, 0, stats, didPaint) || didPaint)
- return;
- updateTiles(prepaintRight, prepaintTop, prepaintRight, prepaintBottom, queue, 0, stats, didPaint);
- return;
- }
-
- int left, top, right, bottom;
- m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
-
- // Otherwise, prepaint anything that was occluded but inside the layer's visible region.
- if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
- return;
-
// 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) {
@@ -727,12 +770,9 @@
bool TiledLayerChromium::needsIdlePaint(const IntRect& visibleContentRect)
{
// Don't trigger more paints if we failed (as we'll just fail again).
- if (m_failedUpdate)
+ if (m_failedUpdate || visibleContentRect.isEmpty() || m_tiler->hasEmptyBounds())
return false;
- if (m_tiler->hasEmptyBounds())
- return false;
-
IntRect idlePaintContentRect = idlePaintRect(visibleContentRect);
if (idlePaintContentRect.isEmpty())
return false;
@@ -742,9 +782,6 @@
for (int j = top; j <= bottom; ++j) {
for (int i = left; i <= right; ++i) {
- // If the visibleContentRect is empty, then we are painting the outer-most set of tiles only.
- if (visibleContentRect.isEmpty() && i != left && i != right && j != top && j != bottom)
- continue;
UpdatableTile* tile = tileAt(i, j);
ASSERT(tile); // Did setTexturePriorities get skipped?
if (!tile)
@@ -762,23 +799,16 @@
IntRect TiledLayerChromium::idlePaintRect(const IntRect& visibleContentRect)
{
- IntRect contentRect(IntPoint::zero(), contentBounds());
+ // Don't inflate an empty rect.
+ if (visibleContentRect.isEmpty())
+ return visibleContentRect;
- // For layers that are animating transforms but not visible at all, we don't know what part
- // of them is going to become visible. For small layers we return the entire layer, for larger
- // ones we avoid prepainting the layer at all.
- if (visibleContentRect.isEmpty()) {
- bool isSmallLayer = m_tiler->numTilesX() <= 9 && m_tiler->numTilesY() <= 9 && m_tiler->numTilesX() * m_tiler->numTilesY() <= 9;
- if ((drawTransformIsAnimating() || screenSpaceTransformIsAnimating()) && isSmallLayer)
- return contentRect;
- 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;
Modified: trunk/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp (125497 => 125498)
--- trunk/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp 2012-08-14 02:10:43 UTC (rev 125497)
+++ trunk/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp 2012-08-14 02:14:16 UTC (rev 125498)
@@ -497,6 +497,72 @@
EXPECT_TRUE(layer2Impl->hasTileAt(0, 1));
}
+TEST_F(TiledLayerChromiumTest, paintSmallAnimatedLayersImmediately)
+{
+ // Create a CCLayerTreeHost that has the right viewportsize,
+ // so the layer is considered small enough.
+ WebKit::WebCompositor::initialize(0);
+ FakeCCLayerTreeHostClient fakeCCLayerTreeHostClient;
+ OwnPtr<CCLayerTreeHost> ccLayerTreeHost = CCLayerTreeHost::create(&fakeCCLayerTreeHostClient, CCLayerTreeSettings());
+
+ bool runOutOfMemory[2] = {false, true};
+ for (int i = 0; i < 2; i++) {
+ // Create a layer with 4x4 tiles.
+ int layerWidth = 4 * FakeTiledLayerChromium::tileSize().width();
+ int layerHeight = 4 * FakeTiledLayerChromium::tileSize().height();
+ int memoryForLayer = layerWidth * layerHeight * 4;
+ IntSize viewportSize = IntSize(layerWidth, layerHeight);
+ ccLayerTreeHost->setViewportSize(viewportSize, viewportSize);
+
+ // Use 8x4 tiles to run out of memory.
+ if (runOutOfMemory[i])
+ layerWidth *= 2;
+
+ OwnPtr<CCPrioritizedTextureManager> textureManager = CCPrioritizedTextureManager::create(memoryForLayer, 1024, CCRenderer::ContentPool);
+ DebugScopedSetImplThread implThread;
+
+ RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get()));
+ OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(1)));
+
+ // Full size layer with half being visible.
+ IntSize contentBounds(layerWidth, layerHeight);
+ IntRect contentRect(IntPoint::zero(), contentBounds);
+ IntRect visibleRect(IntPoint::zero(), IntSize(layerWidth / 2, layerHeight));
+
+ // Pretend the layer is animating.
+ layer->setDrawTransformIsAnimating(true);
+ layer->setBounds(contentBounds);
+ layer->setVisibleContentRect(visibleRect);
+ layer->invalidateContentRect(contentRect);
+ layer->setLayerTreeHost(ccLayerTreeHost.get());
+
+ // The layer should paint it's entire contents on the first paint
+ // if it is close to the viewport size and has the available memory.
+ layer->setTexturePriorities(m_priorityCalculator);
+ textureManager->prioritizeTextures();
+ layer->updateContentRect(m_queue, visibleRect, 0, m_stats);
+ updateTextures();
+ layer->pushPropertiesTo(layerImpl.get());
+
+ // We should have all the tiles for the small animated layer.
+ // We should still have the visible tiles when we didn't
+ // have enough memory for all the tiles.
+ if (!runOutOfMemory[i]) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j)
+ EXPECT_TRUE(layerImpl->hasTileAt(i, j));
+ }
+ } else {
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 4; ++j)
+ EXPECT_EQ(layerImpl->hasTileAt(i, j), i < 4);
+ }
+ }
+ }
+ ccLayerTreeHost.clear();
+ WebKit::WebCompositor::shutdown();
+}
+
TEST_F(TiledLayerChromiumTest, idlePaintOutOfMemory)
{
// The tile size is 100x100. Setup 3x3 tiles with one 1x1 visible tile in the center.
@@ -581,108 +647,47 @@
IntSize contentBounds(100, 100);
IntRect contentRect(IntPoint::zero(), contentBounds);
- OwnPtr<CCPrioritizedTextureManager> textureManager = CCPrioritizedTextureManager::create(20000, 1024, CCRenderer::ContentPool);
+ OwnPtr<CCPrioritizedTextureManager> textureManager = CCPrioritizedTextureManager::create(4*1024*1024, 1024, CCRenderer::ContentPool);
RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get()));
DebugScopedSetImplThread implThread;
OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(1)));
+ layer->setBounds(contentBounds);
- // Invalidate the layer but make none of it visible, so nothing paints.
- IntRect visibleRect;
+ // Alternate between not visible and visible.
+ IntRect v(0, 0, 100, 100);
+ IntRect nv(0, 0, 0, 0);
+ IntRect visibleRect[10] = {nv, nv, v, v, nv, nv, v, v, nv, nv};
+ bool invalidate[10] = {true, true, true, true, true, true, true, true, false, false };
- layer->setBounds(contentBounds);
- layer->setVisibleContentRect(visibleRect);
- layer->invalidateContentRect(contentRect);
+ // We should not have any tiles except for when the layer was visible
+ // or after the layer was visible and we didn't invalidate.
+ bool haveTile[10] = { false, false, true, true, false, false, true, true, true, true };
- for (int i = 0; i < 2; i++) {
+ for (int i = 0; i < 10; i++) {
+ layer->setVisibleContentRect(visibleRect[i]);
+
+ // Skip invalidation once to insure the tile stays in memory while not visible.
+ if (invalidate[i])
+ layer->invalidateContentRect(contentRect);
+
// Paint / idle-paint.
+ textureManager->clearPriorities();
layer->setTexturePriorities(m_priorityCalculator);
textureManager->prioritizeTextures();
- layer->updateContentRect(m_queue, visibleRect, 0, m_stats);
+ layer->updateContentRect(m_queue, visibleRect[i], 0, m_stats);
- // Non-visible layers don't need idle paint.
- EXPECT_FALSE(layer->needsIdlePaint(visibleRect));
+ updateTextures();
+ // We should never signal idle paint, as we painted the entire layer
+ // or the layer was not visible.
+ EXPECT_FALSE(layer->needsIdlePaint(visibleRect[i]));
+
layer->pushPropertiesTo(layerImpl.get());
-
- // We should not have any tiles pushed since the layer is not visible.
- EXPECT_FALSE(layerImpl->hasTileAt(0, 0));
+ EXPECT_EQ(layerImpl->hasTileAt(0, 0), haveTile[i]);
}
}
-static void testHaveOuterTiles(FakeCCTiledLayerImpl* layerImpl, int width, int height, int have)
-{
- for (int i = 0; i < width; ++i) {
- for (int j = 0; j < height; ++j) {
- bool hasTile = i < have || j < have || i >= width - have || j >= height - have;
- EXPECT_EQ(hasTile, layerImpl->hasTileAt(i, j));
- }
- }
-}
-TEST_F(TiledLayerChromiumTest, idlePaintNonVisibleAnimatingLayers)
-{
- OwnPtr<CCPrioritizedTextureManager> textureManager = CCPrioritizedTextureManager::create(8000*8000*8, 1024, CCRenderer::ContentPool);
- DebugScopedSetImplThread implThread;
-
- int tileWidth = FakeTiledLayerChromium::tileSize().width();
- int tileHeight = FakeTiledLayerChromium::tileSize().height();
- int width[] = { 1, 2, 3, 4, 9, 10, 0 };
- int height[] = { 1, 2, 3, 4, 9, 10, 0 };
-
- for (int j = 0; height[j]; ++j) {
- for (int i = 0; width[i]; ++i) {
- RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get()));
- OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(1)));
-
- // Pretend the layer is animating.
- layer->setDrawTransformIsAnimating(true);
-
- IntSize contentBounds(width[i] * tileWidth, height[j] * tileHeight);
- IntRect contentRect(IntPoint::zero(), contentBounds);
- IntRect visibleRect;
-
- layer->setBounds(contentBounds);
- layer->setVisibleContentRect(visibleRect);
- layer->invalidateContentRect(contentRect);
-
- layer->setTexturePriorities(m_priorityCalculator);
- textureManager->prioritizeTextures();
-
- // If idlePaintRect gives back a non-empty result then we should paint it. Otherwise,
- // we shoud paint nothing.
- bool shouldPrepaint = !layer->idlePaintRect(visibleRect).isEmpty();
-
- // Normally we don't allow non-visible layers to pre-paint, but if they are animating then we should.
- EXPECT_EQ(shouldPrepaint, layer->needsIdlePaint(visibleRect));
-
- // If the layer is to be prepainted at all, then after four updates we should have the outer row/columns painted.
- for (int k = 0; k < 4; ++k) {
- layer->setTexturePriorities(m_priorityCalculator);
- textureManager->prioritizeTextures();
-
- layer->updateContentRect(m_queue, visibleRect, 0, m_stats);
- updateTextures();
- layer->pushPropertiesTo(layerImpl.get());
- }
-
- testHaveOuterTiles(layerImpl.get(), width[i], height[j], shouldPrepaint ? 1 : 0);
-
- // We don't currently idle paint past the outermost tiles.
- EXPECT_FALSE(layer->needsIdlePaint(visibleRect));
- for (int k = 0; k < 4; ++k) {
- layer->setTexturePriorities(m_priorityCalculator);
- textureManager->prioritizeTextures();
-
- layer->updateContentRect(m_queue, visibleRect, 0, m_stats);
- updateTextures();
- layer->pushPropertiesTo(layerImpl.get());
- }
-
- testHaveOuterTiles(layerImpl.get(), width[i], height[j], shouldPrepaint ? 1 : 0);
- }
- }
-}
-
TEST_F(TiledLayerChromiumTest, invalidateFromPrepare)
{
OwnPtr<CCPrioritizedTextureManager> textureManager = CCPrioritizedTextureManager::create(4*1024*1024, 1024, CCRenderer::ContentPool);