| // 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 "cc/tiled_layer.h" |
| |
| #include "cc/bitmap_content_layer_updater.h" |
| #include "cc/layer_painter.h" |
| #include "cc/overdraw_metrics.h" |
| #include "cc/rendering_stats.h" |
| #include "cc/resource_update_controller.h" |
| #include "cc/single_thread_proxy.h" // For DebugScopedSetImplThread |
| #include "cc/test/animation_test_common.h" |
| #include "cc/test/fake_graphics_context.h" |
| #include "cc/test/fake_layer_tree_host_client.h" |
| #include "cc/test/fake_proxy.h" |
| #include "cc/test/geometry_test_utils.h" |
| #include "cc/test/tiled_layer_test_common.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/rect_conversions.h" |
| #include "ui/gfx/transform.h" |
| |
| using namespace WebKitTests; |
| |
| namespace cc { |
| namespace { |
| |
| class TestOcclusionTracker : public OcclusionTracker { |
| public: |
| TestOcclusionTracker() |
| : OcclusionTracker(gfx::Rect(0, 0, 1000, 1000), true) |
| , m_layerClipRectInTarget(gfx::Rect(0, 0, 1000, 1000)) |
| { |
| // Pretend we have visited a render surface. |
| m_stack.push_back(StackObject()); |
| } |
| |
| void setOcclusion(const Region& occlusion) { m_stack.back().occlusionInTarget = occlusion; } |
| |
| protected: |
| virtual gfx::Rect layerClipRectInTarget(const Layer* layer) const OVERRIDE { return m_layerClipRectInTarget; } |
| |
| private: |
| gfx::Rect m_layerClipRectInTarget; |
| }; |
| |
| class TiledLayerTest : public testing::Test { |
| public: |
| TiledLayerTest() |
| : m_proxy(NULL) |
| , m_context(WebKit::createFakeGraphicsContext()) |
| , m_queue(make_scoped_ptr(new ResourceUpdateQueue)) |
| , m_occlusion(0) |
| { |
| } |
| |
| virtual void SetUp() |
| { |
| m_layerTreeHost = LayerTreeHost::create(&m_fakeLayerImplTreeHostClient, m_settings, scoped_ptr<Thread>(NULL)); |
| m_proxy = m_layerTreeHost->proxy(); |
| m_resourceManager = PrioritizedResourceManager::create(Renderer::ContentPool, m_proxy); |
| m_layerTreeHost->initializeRendererIfNeeded(); |
| DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy); |
| m_resourceProvider = ResourceProvider::create(m_context.get()); |
| } |
| |
| virtual ~TiledLayerTest() |
| { |
| resourceManagerClearAllMemory(m_resourceManager.get(), m_resourceProvider.get()); |
| DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy); |
| m_resourceProvider.reset(); |
| } |
| |
| class ScopedFakeTiledLayerImpl { |
| public: |
| ScopedFakeTiledLayerImpl(int id) |
| { |
| m_layerImpl = new FakeTiledLayerImpl(id); |
| } |
| ~ScopedFakeTiledLayerImpl() |
| { |
| delete m_layerImpl; |
| } |
| FakeTiledLayerImpl* get() |
| { |
| return m_layerImpl; |
| } |
| FakeTiledLayerImpl* operator->() |
| { |
| return m_layerImpl; |
| } |
| private: |
| FakeTiledLayerImpl* m_layerImpl; |
| }; |
| void resourceManagerClearAllMemory(PrioritizedResourceManager* resourceManager, ResourceProvider* resourceProvider) |
| { |
| DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy); |
| resourceManager->clearAllMemory(resourceProvider); |
| resourceManager->reduceMemory(resourceProvider); |
| } |
| void updateTextures() |
| { |
| DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy); |
| DCHECK(m_queue); |
| scoped_ptr<ResourceUpdateController> updateController = |
| ResourceUpdateController::create( |
| NULL, |
| m_proxy->implThread(), |
| m_queue.Pass(), |
| m_resourceProvider.get(), |
| m_proxy->hasImplThread()); |
| updateController->finalize(); |
| m_queue = make_scoped_ptr(new ResourceUpdateQueue); |
| } |
| void layerPushPropertiesTo(FakeTiledLayer* layer, FakeTiledLayerImpl* layerImpl) |
| { |
| DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy); |
| layer->pushPropertiesTo(layerImpl); |
| } |
| void layerUpdate(FakeTiledLayer* layer, TestOcclusionTracker* occluded) |
| { |
| DebugScopedSetMainThread mainThread(m_proxy); |
| layer->update(*m_queue.get(), occluded, m_stats); |
| } |
| |
| bool updateAndPush(FakeTiledLayer* layer1, |
| FakeTiledLayerImpl* layerImpl1, |
| FakeTiledLayer* layer2 = 0, |
| FakeTiledLayerImpl* layerImpl2 = 0) |
| { |
| // Get textures |
| m_resourceManager->clearPriorities(); |
| if (layer1) |
| layer1->setTexturePriorities(m_priorityCalculator); |
| if (layer2) |
| layer2->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| // Update content |
| if (layer1) |
| layer1->update(*m_queue.get(), m_occlusion, m_stats); |
| if (layer2) |
| layer2->update(*m_queue.get(), m_occlusion, m_stats); |
| |
| bool needsUpdate = false; |
| if (layer1) |
| needsUpdate |= layer1->needsIdlePaint(); |
| if (layer2) |
| needsUpdate |= layer2->needsIdlePaint(); |
| |
| // Update textures and push. |
| updateTextures(); |
| if (layer1) |
| layerPushPropertiesTo(layer1, layerImpl1); |
| if (layer2) |
| layerPushPropertiesTo(layer2, layerImpl2); |
| |
| return needsUpdate; |
| } |
| |
| public: |
| Proxy* m_proxy; |
| LayerTreeSettings m_settings; |
| scoped_ptr<GraphicsContext> m_context; |
| scoped_ptr<ResourceProvider> m_resourceProvider; |
| scoped_ptr<ResourceUpdateQueue> m_queue; |
| RenderingStats m_stats; |
| PriorityCalculator m_priorityCalculator; |
| FakeLayerImplTreeHostClient m_fakeLayerImplTreeHostClient; |
| scoped_ptr<LayerTreeHost> m_layerTreeHost; |
| scoped_ptr<PrioritizedResourceManager> m_resourceManager; |
| TestOcclusionTracker* m_occlusion; |
| }; |
| |
| TEST_F(TiledLayerTest, pushDirtyTiles) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles. |
| layer->setBounds(gfx::Size(100, 200)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 100, 200)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should have both tiles on the impl side. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| |
| // Invalidates both tiles, but then only update one of them. |
| layer->setBounds(gfx::Size(100, 200)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 100)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 100, 200)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should only have the first tile since the other tile was invalidated but not painted. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| } |
| |
| TEST_F(TiledLayerTest, pushOccludedDirtyTiles) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| TestOcclusionTracker occluded; |
| m_occlusion = &occluded; |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles. |
| layer->setBounds(gfx::Size(100, 200)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| layer->setDrawableContentRect(gfx::Rect(0, 0, 100, 200)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 100, 200)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| // We should have both tiles on the impl side. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| |
| // Invalidates part of the top tile... |
| layer->invalidateContentRect(gfx::Rect(0, 0, 50, 50)); |
| // ....but the area is occluded. |
| occluded.setOcclusion(gfx::Rect(0, 0, 50, 50)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 2500, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| // We should still have both tiles, as part of the top tile is still unoccluded. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| } |
| |
| TEST_F(TiledLayerTest, pushDeletedTiles) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles. |
| layer->setBounds(gfx::Size(100, 200)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 100, 200)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should have both tiles on the impl side. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| |
| m_resourceManager->clearPriorities(); |
| resourceManagerClearAllMemory(m_resourceManager.get(), m_resourceProvider.get()); |
| m_resourceManager->setMaxMemoryLimitBytes(4*1024*1024); |
| |
| // This should drop the tiles on the impl thread. |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| |
| // We should now have no textures on the impl thread. |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| |
| // This should recreate and update one of the deleted textures. |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 100)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should have one tiles on the impl side. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| } |
| |
| TEST_F(TiledLayerTest, pushIdlePaintTiles) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // The tile size is 100x100. Setup 5x5 tiles with one visible tile in the center. |
| // This paints 1 visible of the 25 invalid tiles. |
| layer->setBounds(gfx::Size(500, 500)); |
| layer->setVisibleContentRect(gfx::Rect(200, 200, 100, 100)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 500, 500)); |
| bool needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| // We should need idle-painting for surrounding tiles. |
| EXPECT_TRUE(needsUpdate); |
| |
| // We should have one tile on the impl side. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(2, 2)); |
| |
| // For the next four updates, we should detect we still need idle painting. |
| for (int i = 0; i < 4; i++) { |
| needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| EXPECT_TRUE(needsUpdate); |
| } |
| |
| // We should always finish painting eventually. |
| for (int i = 0; i < 20; i++) |
| needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should have pre-painted all of the surrounding tiles. |
| for (int i = 0; i < 5; i++) { |
| for (int j = 0; j < 5; j++) |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(i, j)); |
| } |
| |
| EXPECT_FALSE(needsUpdate); |
| } |
| |
| TEST_F(TiledLayerTest, predictivePainting) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // Prepainting should occur in the scroll direction first, and the |
| // visible rect should be extruded only along the dominant axis. |
| gfx::Vector2d directions[6] = { gfx::Vector2d(-10, 0), |
| gfx::Vector2d(10, 0), |
| gfx::Vector2d(0, -10), |
| gfx::Vector2d(0, 10), |
| gfx::Vector2d(10, 20), |
| gfx::Vector2d(-20, 10) }; |
| // We should push all tiles that touch the extruded visible rect. |
| gfx::Rect pushedVisibleTiles[6] = { gfx::Rect(2, 2, 2, 1), |
| gfx::Rect(1, 2, 2, 1), |
| gfx::Rect(2, 2, 1, 2), |
| gfx::Rect(2, 1, 1, 2), |
| gfx::Rect(2, 1, 1, 2), |
| gfx::Rect(2, 2, 2, 1) }; |
| // The first pre-paint should also paint first in the scroll |
| // direction so we should find one additional tile in the scroll direction. |
| gfx::Rect pushedPrepaintTiles[6] = { gfx::Rect(2, 2, 3, 1), |
| gfx::Rect(0, 2, 3, 1), |
| gfx::Rect(2, 2, 1, 3), |
| gfx::Rect(2, 0, 1, 3), |
| gfx::Rect(2, 0, 1, 3), |
| gfx::Rect(2, 2, 3, 1) }; |
| for(int k = 0; k < 6; k++) { |
| // The tile size is 100x100. Setup 5x5 tiles with one visible tile |
| // in the center. |
| gfx::Size contentBounds = gfx::Size(500, 500); |
| gfx::Rect contentRect = gfx::Rect(0, 0, 500, 500); |
| gfx::Rect visibleRect = gfx::Rect(200, 200, 100, 100); |
| gfx::Rect previousVisibleRect = gfx::Rect(visibleRect.origin() + directions[k], visibleRect.size()); |
| gfx::Rect nextVisibleRect = gfx::Rect(visibleRect.origin() - directions[k], visibleRect.size()); |
| |
| // Setup. Use the previousVisibleRect to setup the prediction for next frame. |
| layer->setBounds(contentBounds); |
| layer->setVisibleContentRect(previousVisibleRect); |
| layer->invalidateContentRect(contentRect); |
| bool needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| |
| // Invalidate and move the visibleRect in the scroll direction. |
| // Check that the correct tiles have been painted in the visible pass. |
| layer->invalidateContentRect(contentRect); |
| layer->setVisibleContentRect(visibleRect); |
| needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| for (int i = 0; i < 5; i++) { |
| for (int j = 0; j < 5; j++) |
| EXPECT_EQ(layerImpl->hasResourceIdForTileAt(i, j), pushedVisibleTiles[k].Contains(i, j)); |
| } |
| |
| // Move the transform in the same direction without invalidating. |
| // Check that non-visible pre-painting occured in the correct direction. |
| // Ignore diagonal scrolls here (k > 3) as these have new visible content now. |
| if (k <= 3) { |
| layer->setVisibleContentRect(nextVisibleRect); |
| needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| for (int i = 0; i < 5; i++) { |
| for (int j = 0; j < 5; j++) |
| EXPECT_EQ(layerImpl->hasResourceIdForTileAt(i, j), pushedPrepaintTiles[k].Contains(i, j)); |
| } |
| } |
| |
| // We should always finish painting eventually. |
| for (int i = 0; i < 20; i++) |
| needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| EXPECT_FALSE(needsUpdate); |
| } |
| } |
| |
| TEST_F(TiledLayerTest, pushTilesAfterIdlePaintFailed) |
| { |
| // Start with 2mb of memory, but the test is going to try to use just more than 1mb, so we reduce to 1mb later. |
| m_resourceManager->setMaxMemoryLimitBytes(2 * 1024 * 1024); |
| scoped_refptr<FakeTiledLayer> layer1 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl1(1); |
| scoped_refptr<FakeTiledLayer> layer2 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl2(2); |
| |
| // For this test we have two layers. layer1 exhausts most texture memory, leaving room for 2 more tiles from |
| // layer2, but not all three tiles. First we paint layer1, and one tile from layer2. Then when we idle paint |
| // layer2, we will fail on the third tile of layer2, and this should not leave the second tile in a bad state. |
| |
| // This uses 960000 bytes, leaving 88576 bytes of memory left, which is enough for 2 tiles only in the other layer. |
| gfx::Rect layer1Rect(0, 0, 100, 2400); |
| |
| // This requires 4*30000 bytes of memory. |
| gfx::Rect layer2Rect(0, 0, 100, 300); |
| |
| // Paint a single tile in layer2 so that it will idle paint. |
| layer1->setBounds(layer1Rect.size()); |
| layer1->setVisibleContentRect(layer1Rect); |
| layer2->setBounds(layer2Rect.size()); |
| layer2->setVisibleContentRect(gfx::Rect(0, 0, 100, 100)); |
| bool needsUpdate = updateAndPush(layer1.get(), layerImpl1.get(), |
| layer2.get(), layerImpl2.get()); |
| // We should need idle-painting for both remaining tiles in layer2. |
| EXPECT_TRUE(needsUpdate); |
| |
| // Reduce our memory limits to 1mb. |
| m_resourceManager->setMaxMemoryLimitBytes(1024 * 1024); |
| |
| // Now idle paint layer2. We are going to run out of memory though! |
| // Oh well, commit the frame and push. |
| for (int i = 0; i < 4; i++) { |
| needsUpdate = updateAndPush(layer1.get(), layerImpl1.get(), |
| layer2.get(), layerImpl2.get()); |
| } |
| |
| // Sanity check, we should have textures for the big layer. |
| EXPECT_TRUE(layerImpl1->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl1->hasResourceIdForTileAt(0, 23)); |
| |
| // We should only have the first two tiles from layer2 since |
| // it failed to idle update the last tile. |
| EXPECT_TRUE(layerImpl2->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl2->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl2->hasResourceIdForTileAt(0, 1)); |
| EXPECT_TRUE(layerImpl2->hasResourceIdForTileAt(0, 1)); |
| |
| EXPECT_FALSE(needsUpdate); |
| EXPECT_FALSE(layerImpl2->hasResourceIdForTileAt(0, 2)); |
| } |
| |
| TEST_F(TiledLayerTest, pushIdlePaintedOccludedTiles) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| TestOcclusionTracker occluded; |
| m_occlusion = &occluded; |
| |
| // The tile size is 100x100, so this invalidates one occluded tile, culls it during paint, but prepaints it. |
| occluded.setOcclusion(gfx::Rect(0, 0, 100, 100)); |
| |
| layer->setBounds(gfx::Size(100, 100)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 100)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should have the prepainted tile on the impl side, but culled it during paint. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_EQ(1, occluded.overdrawMetrics().tilesCulledForUpload()); |
| } |
| |
| TEST_F(TiledLayerTest, pushTilesMarkedDirtyDuringPaint) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles. |
| // However, during the paint, we invalidate one of the tiles. This should |
| // not prevent the tile from being pushed. |
| layer->fakeLayerUpdater()->setRectToInvalidate(gfx::Rect(0, 50, 100, 50), layer.get()); |
| layer->setBounds(gfx::Size(100, 200)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should have both tiles on the impl side. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| } |
| |
| TEST_F(TiledLayerTest, pushTilesLayerMarkedDirtyDuringPaintOnNextLayer) |
| { |
| scoped_refptr<FakeTiledLayer> layer1 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| scoped_refptr<FakeTiledLayer> layer2 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layer1Impl(1); |
| ScopedFakeTiledLayerImpl layer2Impl(2); |
| |
| // Invalidate a tile on layer1, during update of layer 2. |
| layer2->fakeLayerUpdater()->setRectToInvalidate(gfx::Rect(0, 50, 100, 50), layer1.get()); |
| layer1->setBounds(gfx::Size(100, 200)); |
| layer1->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| layer2->setBounds(gfx::Size(100, 200)); |
| layer2->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| updateAndPush(layer1.get(), layer1Impl.get(), |
| layer2.get(), layer2Impl.get()); |
| |
| // We should have both tiles on the impl side for all layers. |
| EXPECT_TRUE(layer1Impl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layer1Impl->hasResourceIdForTileAt(0, 1)); |
| EXPECT_TRUE(layer2Impl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layer2Impl->hasResourceIdForTileAt(0, 1)); |
| } |
| |
| TEST_F(TiledLayerTest, pushTilesLayerMarkedDirtyDuringPaintOnPreviousLayer) |
| { |
| scoped_refptr<FakeTiledLayer> layer1 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| scoped_refptr<FakeTiledLayer> layer2 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layer1Impl(1); |
| ScopedFakeTiledLayerImpl layer2Impl(2); |
| |
| layer1->fakeLayerUpdater()->setRectToInvalidate(gfx::Rect(0, 50, 100, 50), layer2.get()); |
| layer1->setBounds(gfx::Size(100, 200)); |
| layer1->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| layer2->setBounds(gfx::Size(100, 200)); |
| layer2->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| updateAndPush(layer1.get(), layer1Impl.get(), |
| layer2.get(), layer2Impl.get()); |
| |
| // We should have both tiles on the impl side for all layers. |
| EXPECT_TRUE(layer1Impl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layer1Impl->hasResourceIdForTileAt(0, 1)); |
| EXPECT_TRUE(layer2Impl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layer2Impl->hasResourceIdForTileAt(0, 1)); |
| } |
| |
| TEST_F(TiledLayerTest, paintSmallAnimatedLayersImmediately) |
| { |
| // Create a LayerTreeHost that has the right viewportsize, |
| // so the layer is considered small enough. |
| FakeLayerImplTreeHostClient fakeLayerImplTreeHostClient; |
| scoped_ptr<LayerTreeHost> layerTreeHost = LayerTreeHost::create(&fakeLayerImplTreeHostClient, LayerTreeSettings(), scoped_ptr<Thread>(NULL)); |
| |
| bool runOutOfMemory[2] = {false, true}; |
| for (int i = 0; i < 2; i++) { |
| // Create a layer with 5x5 tiles, with 4x4 size viewport. |
| int viewportWidth = 4 * FakeTiledLayer::tileSize().width(); |
| int viewportHeight = 4 * FakeTiledLayer::tileSize().width(); |
| int layerWidth = 5 * FakeTiledLayer::tileSize().width(); |
| int layerHeight = 5 * FakeTiledLayer::tileSize().height(); |
| int memoryForLayer = layerWidth * layerHeight * 4; |
| layerTreeHost->setViewportSize(gfx::Size(layerWidth, layerHeight), gfx::Size(layerWidth, layerHeight)); |
| |
| // Use 10x5 tiles to run out of memory. |
| if (runOutOfMemory[i]) |
| layerWidth *= 2; |
| |
| m_resourceManager->setMaxMemoryLimitBytes(memoryForLayer); |
| |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // Full size layer with half being visible. |
| gfx::Size contentBounds(layerWidth, layerHeight); |
| gfx::Rect contentRect(gfx::Point(), contentBounds); |
| gfx::Rect visibleRect(gfx::Point(), gfx::Size(layerWidth / 2, layerHeight)); |
| |
| // Pretend the layer is animating. |
| layer->setDrawTransformIsAnimating(true); |
| layer->setBounds(contentBounds); |
| layer->setVisibleContentRect(visibleRect); |
| layer->invalidateContentRect(contentRect); |
| layer->setLayerTreeHost(layerTreeHost.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); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), 0, m_stats); |
| updateTextures(); |
| layerPushPropertiesTo(layer.get(), 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 < 5; ++i) { |
| for (int j = 0; j < 5; ++j) |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(i, j)); |
| } |
| } else { |
| for (int i = 0; i < 10; ++i) { |
| for (int j = 0; j < 5; ++j) |
| EXPECT_EQ(layerImpl->hasResourceIdForTileAt(i, j), i < 5); |
| } |
| } |
| } |
| } |
| |
| TEST_F(TiledLayerTest, idlePaintOutOfMemory) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // We have enough memory for only the visible rect, so we will run out of memory in first idle paint. |
| int memoryLimit = 4 * 100 * 100; // 1 tiles, 4 bytes per pixel. |
| m_resourceManager->setMaxMemoryLimitBytes(memoryLimit); |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles. |
| bool needsUpdate = false; |
| layer->setBounds(gfx::Size(300, 300)); |
| layer->setVisibleContentRect(gfx::Rect(100, 100, 100, 100)); |
| for (int i = 0; i < 2; i++) |
| needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| |
| // Idle-painting should see no more priority tiles for painting. |
| EXPECT_FALSE(needsUpdate); |
| |
| // We should have one tile on the impl side. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(1, 1)); |
| } |
| |
| TEST_F(TiledLayerTest, idlePaintZeroSizedLayer) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| bool animating[2] = {false, true}; |
| for (int i = 0; i < 2; i++) { |
| // Pretend the layer is animating. |
| layer->setDrawTransformIsAnimating(animating[i]); |
| |
| // The layer's bounds are empty. |
| // Empty layers don't paint or idle-paint. |
| layer->setBounds(gfx::Size()); |
| layer->setVisibleContentRect(gfx::Rect()); |
| bool needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| |
| // Empty layers don't have tiles. |
| EXPECT_EQ(0u, layer->numPaintedTiles()); |
| |
| // Empty layers don't need prepaint. |
| EXPECT_FALSE(needsUpdate); |
| |
| // Empty layers don't have tiles. |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| } |
| } |
| |
| TEST_F(TiledLayerTest, idlePaintNonVisibleLayers) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // Alternate between not visible and visible. |
| gfx::Rect v(0, 0, 100, 100); |
| gfx::Rect nv(0, 0, 0, 0); |
| gfx::Rect 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 }; |
| |
| // 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 < 10; i++) { |
| layer->setBounds(gfx::Size(100, 100)); |
| layer->setVisibleContentRect(visibleRect[i]); |
| |
| if (invalidate[i]) |
| layer->invalidateContentRect(gfx::Rect(0, 0, 100, 100)); |
| bool needsUpdate = updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should never signal idle paint, as we painted the entire layer |
| // or the layer was not visible. |
| EXPECT_FALSE(needsUpdate); |
| EXPECT_EQ(layerImpl->hasResourceIdForTileAt(0, 0), haveTile[i]); |
| } |
| } |
| |
| TEST_F(TiledLayerTest, invalidateFromPrepare) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles. |
| layer->setBounds(gfx::Size(100, 200)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| |
| // We should have both tiles on the impl side. |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| |
| layer->fakeLayerUpdater()->clearPrepareCount(); |
| // Invoke update again. As the layer is valid update shouldn't be invoked on |
| // the LayerUpdater. |
| updateAndPush(layer.get(), layerImpl.get()); |
| EXPECT_EQ(0, layer->fakeLayerUpdater()->prepareCount()); |
| |
| // setRectToInvalidate triggers invalidateContentRect() being invoked from update. |
| layer->fakeLayerUpdater()->setRectToInvalidate(gfx::Rect(25, 25, 50, 50), layer.get()); |
| layer->fakeLayerUpdater()->clearPrepareCount(); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 50, 50)); |
| updateAndPush(layer.get(), layerImpl.get()); |
| EXPECT_EQ(1, layer->fakeLayerUpdater()->prepareCount()); |
| layer->fakeLayerUpdater()->clearPrepareCount(); |
| |
| // The layer should still be invalid as update invoked invalidate. |
| updateAndPush(layer.get(), layerImpl.get()); // visible |
| EXPECT_EQ(1, layer->fakeLayerUpdater()->prepareCount()); |
| } |
| |
| TEST_F(TiledLayerTest, verifyUpdateRectWhenContentBoundsAreScaled) |
| { |
| // The updateRect (that indicates what was actually painted) should be in |
| // layer space, not the content space. |
| scoped_refptr<FakeTiledLayerWithScaledBounds> layer = make_scoped_refptr(new FakeTiledLayerWithScaledBounds(m_resourceManager.get())); |
| |
| gfx::Rect layerBounds(0, 0, 300, 200); |
| gfx::Rect contentBounds(0, 0, 200, 250); |
| |
| layer->setBounds(layerBounds.size()); |
| layer->setContentBounds(contentBounds.size()); |
| layer->setVisibleContentRect(contentBounds); |
| |
| // On first update, the updateRect includes all tiles, even beyond the boundaries of the layer. |
| // However, it should still be in layer space, not content space. |
| layer->invalidateContentRect(contentBounds); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), 0, m_stats); |
| EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 300, 300 * 0.8), layer->updateRect()); |
| updateTextures(); |
| |
| // After the tiles are updated once, another invalidate only needs to update the bounds of the layer. |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->invalidateContentRect(contentBounds); |
| layer->update(*m_queue.get(), 0, m_stats); |
| EXPECT_FLOAT_RECT_EQ(gfx::RectF(layerBounds), layer->updateRect()); |
| updateTextures(); |
| |
| // Partial re-paint should also be represented by the updateRect in layer space, not content space. |
| gfx::Rect partialDamage(30, 100, 10, 10); |
| layer->invalidateContentRect(partialDamage); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), 0, m_stats); |
| EXPECT_FLOAT_RECT_EQ(gfx::RectF(45, 80, 15, 8), layer->updateRect()); |
| } |
| |
| TEST_F(TiledLayerTest, verifyInvalidationWhenContentsScaleChanges) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| |
| // Create a layer with one tile. |
| layer->setBounds(gfx::Size(100, 100)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 100)); |
| |
| // Invalidate the entire layer. |
| layer->setNeedsDisplay(); |
| EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 100, 100), layer->lastNeedsDisplayRect()); |
| |
| // Push the tiles to the impl side and check that there is exactly one. |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), 0, m_stats); |
| updateTextures(); |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(1, 0)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(1, 1)); |
| |
| // Change the contents scale and verify that the content rectangle requiring painting |
| // is not scaled. |
| layer->setContentsScale(2); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 200, 200)); |
| EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 100, 100), layer->lastNeedsDisplayRect()); |
| |
| // The impl side should get 2x2 tiles now. |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), 0, m_stats); |
| updateTextures(); |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(1, 0)); |
| EXPECT_TRUE(layerImpl->hasResourceIdForTileAt(1, 1)); |
| |
| // Invalidate the entire layer again, but do not paint. All tiles should be gone now from the |
| // impl side. |
| layer->setNeedsDisplay(); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(0, 0)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(0, 1)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(1, 0)); |
| EXPECT_FALSE(layerImpl->hasResourceIdForTileAt(1, 1)); |
| } |
| |
| TEST_F(TiledLayerTest, skipsDrawGetsReset) |
| { |
| // Create two 300 x 300 tiled layers. |
| gfx::Size contentBounds(300, 300); |
| gfx::Rect contentRect(gfx::Point(), contentBounds); |
| |
| // We have enough memory for only one of the two layers. |
| int memoryLimit = 4 * 300 * 300; // 4 bytes per pixel. |
| |
| scoped_refptr<FakeTiledLayer> rootLayer = make_scoped_refptr(new FakeTiledLayer(m_layerTreeHost->contentsTextureManager())); |
| scoped_refptr<FakeTiledLayer> childLayer = make_scoped_refptr(new FakeTiledLayer(m_layerTreeHost->contentsTextureManager())); |
| rootLayer->addChild(childLayer); |
| |
| rootLayer->setBounds(contentBounds); |
| rootLayer->setVisibleContentRect(contentRect); |
| rootLayer->setPosition(gfx::PointF(0, 0)); |
| childLayer->setBounds(contentBounds); |
| childLayer->setVisibleContentRect(contentRect); |
| childLayer->setPosition(gfx::PointF(0, 0)); |
| rootLayer->invalidateContentRect(contentRect); |
| childLayer->invalidateContentRect(contentRect); |
| |
| m_layerTreeHost->setRootLayer(rootLayer); |
| m_layerTreeHost->setViewportSize(gfx::Size(300, 300), gfx::Size(300, 300)); |
| |
| m_layerTreeHost->updateLayers(*m_queue.get(), memoryLimit); |
| |
| // We'll skip the root layer. |
| EXPECT_TRUE(rootLayer->skipsDraw()); |
| EXPECT_FALSE(childLayer->skipsDraw()); |
| |
| m_layerTreeHost->commitComplete(); |
| |
| // Remove the child layer. |
| rootLayer->removeAllChildren(); |
| |
| m_layerTreeHost->updateLayers(*m_queue.get(), memoryLimit); |
| EXPECT_FALSE(rootLayer->skipsDraw()); |
| |
| resourceManagerClearAllMemory(m_layerTreeHost->contentsTextureManager(), m_resourceProvider.get()); |
| m_layerTreeHost->setRootLayer(0); |
| } |
| |
| TEST_F(TiledLayerTest, resizeToSmaller) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| |
| layer->setBounds(gfx::Size(700, 700)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 700, 700)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 700, 700)); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), 0, m_stats); |
| |
| layer->setBounds(gfx::Size(200, 200)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 200, 200)); |
| } |
| |
| TEST_F(TiledLayerTest, hugeLayerUpdateCrash) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| |
| int size = 1 << 30; |
| layer->setBounds(gfx::Size(size, size)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 700, 700)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, size, size)); |
| |
| // Ensure no crash for bounds where size * size would overflow an int. |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), 0, m_stats); |
| } |
| |
| class TiledLayerPartialUpdateTest : public TiledLayerTest { |
| public: |
| TiledLayerPartialUpdateTest() |
| { |
| m_settings.maxPartialTextureUpdates = 4; |
| } |
| }; |
| |
| TEST_F(TiledLayerPartialUpdateTest, partialUpdates) |
| { |
| // Create one 300 x 200 tiled layer with 3 x 2 tiles. |
| gfx::Size contentBounds(300, 200); |
| gfx::Rect contentRect(gfx::Point(), contentBounds); |
| |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_layerTreeHost->contentsTextureManager())); |
| layer->setBounds(contentBounds); |
| layer->setPosition(gfx::PointF(0, 0)); |
| layer->setVisibleContentRect(contentRect); |
| layer->invalidateContentRect(contentRect); |
| |
| m_layerTreeHost->setRootLayer(layer); |
| m_layerTreeHost->setViewportSize(gfx::Size(300, 200), gfx::Size(300, 200)); |
| |
| // Full update of all 6 tiles. |
| m_layerTreeHost->updateLayers( |
| *m_queue.get(), std::numeric_limits<size_t>::max()); |
| { |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| EXPECT_EQ(6, m_queue->fullUploadSize()); |
| EXPECT_EQ(0, m_queue->partialUploadSize()); |
| updateTextures(); |
| EXPECT_EQ(6, layer->fakeLayerUpdater()->updateCount()); |
| EXPECT_FALSE(m_queue->hasMoreUpdates()); |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| // Full update of 3 tiles and partial update of 3 tiles. |
| layer->invalidateContentRect(gfx::Rect(0, 0, 300, 150)); |
| m_layerTreeHost->updateLayers(*m_queue.get(), std::numeric_limits<size_t>::max()); |
| { |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| EXPECT_EQ(3, m_queue->fullUploadSize()); |
| EXPECT_EQ(3, m_queue->partialUploadSize()); |
| updateTextures(); |
| EXPECT_EQ(6, layer->fakeLayerUpdater()->updateCount()); |
| EXPECT_FALSE(m_queue->hasMoreUpdates()); |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| // Partial update of 6 tiles. |
| layer->invalidateContentRect(gfx::Rect(50, 50, 200, 100)); |
| { |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| m_layerTreeHost->updateLayers(*m_queue.get(), std::numeric_limits<size_t>::max()); |
| EXPECT_EQ(2, m_queue->fullUploadSize()); |
| EXPECT_EQ(4, m_queue->partialUploadSize()); |
| updateTextures(); |
| EXPECT_EQ(6, layer->fakeLayerUpdater()->updateCount()); |
| EXPECT_FALSE(m_queue->hasMoreUpdates()); |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| // Checkerboard all tiles. |
| layer->invalidateContentRect(gfx::Rect(0, 0, 300, 200)); |
| { |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| // Partial update of 6 checkerboard tiles. |
| layer->invalidateContentRect(gfx::Rect(50, 50, 200, 100)); |
| { |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| m_layerTreeHost->updateLayers(*m_queue.get(), std::numeric_limits<size_t>::max()); |
| EXPECT_EQ(6, m_queue->fullUploadSize()); |
| EXPECT_EQ(0, m_queue->partialUploadSize()); |
| updateTextures(); |
| EXPECT_EQ(6, layer->fakeLayerUpdater()->updateCount()); |
| EXPECT_FALSE(m_queue->hasMoreUpdates()); |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| // Partial update of 4 tiles. |
| layer->invalidateContentRect(gfx::Rect(50, 50, 100, 100)); |
| { |
| ScopedFakeTiledLayerImpl layerImpl(1); |
| m_layerTreeHost->updateLayers(*m_queue.get(), std::numeric_limits<size_t>::max()); |
| EXPECT_EQ(0, m_queue->fullUploadSize()); |
| EXPECT_EQ(4, m_queue->partialUploadSize()); |
| updateTextures(); |
| EXPECT_EQ(4, layer->fakeLayerUpdater()->updateCount()); |
| EXPECT_FALSE(m_queue->hasMoreUpdates()); |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| layerPushPropertiesTo(layer.get(), layerImpl.get()); |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| resourceManagerClearAllMemory(m_layerTreeHost->contentsTextureManager(), m_resourceProvider.get()); |
| m_layerTreeHost->setRootLayer(0); |
| } |
| |
| TEST_F(TiledLayerTest, tilesPaintedWithoutOcclusion) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles. |
| layer->setBounds(gfx::Size(100, 200)); |
| layer->setDrawableContentRect(gfx::Rect(0, 0, 100, 200)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 100, 200)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 100, 200)); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), 0, m_stats); |
| EXPECT_EQ(2, layer->fakeLayerUpdater()->updateCount()); |
| } |
| |
| TEST_F(TiledLayerTest, tilesPaintedWithOcclusion) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| TestOcclusionTracker occluded; |
| |
| // The tile size is 100x100. |
| |
| layer->setBounds(gfx::Size(600, 600)); |
| |
| occluded.setOcclusion(gfx::Rect(200, 200, 300, 100)); |
| layer->setDrawableContentRect(gfx::Rect(gfx::Point(), layer->contentBounds())); |
| layer->setVisibleContentRect(gfx::Rect(gfx::Point(), layer->contentBounds())); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(36-3, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000, 1); |
| EXPECT_EQ(3, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| occluded.setOcclusion(gfx::Rect(250, 200, 300, 100)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(36-2, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000 + 340000, 1); |
| EXPECT_EQ(3 + 2, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| occluded.setOcclusion(gfx::Rect(250, 250, 300, 100)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(36, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000 + 340000 + 360000, 1); |
| EXPECT_EQ(3 + 2, occluded.overdrawMetrics().tilesCulledForUpload()); |
| } |
| |
| TEST_F(TiledLayerTest, tilesPaintedWithOcclusionAndVisiblityConstraints) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| TestOcclusionTracker occluded; |
| |
| // The tile size is 100x100. |
| |
| layer->setBounds(gfx::Size(600, 600)); |
| |
| // The partially occluded tiles (by the 150 occlusion height) are visible beyond the occlusion, so not culled. |
| occluded.setOcclusion(gfx::Rect(200, 200, 300, 150)); |
| layer->setDrawableContentRect(gfx::Rect(0, 0, 600, 360)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 600, 360)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(24-3, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 210000, 1); |
| EXPECT_EQ(3, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| |
| // Now the visible region stops at the edge of the occlusion so the partly visible tiles become fully occluded. |
| occluded.setOcclusion(gfx::Rect(200, 200, 300, 150)); |
| layer->setDrawableContentRect(gfx::Rect(0, 0, 600, 350)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 600, 350)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(24-6, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 210000 + 180000, 1); |
| EXPECT_EQ(3 + 6, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| |
| // Now the visible region is even smaller than the occlusion, it should have the same result. |
| occluded.setOcclusion(gfx::Rect(200, 200, 300, 150)); |
| layer->setDrawableContentRect(gfx::Rect(0, 0, 600, 340)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 600, 340)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(24-6, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 210000 + 180000 + 180000, 1); |
| EXPECT_EQ(3 + 6 + 6, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| } |
| |
| TEST_F(TiledLayerTest, tilesNotPaintedWithoutInvalidation) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| TestOcclusionTracker occluded; |
| |
| // The tile size is 100x100. |
| |
| layer->setBounds(gfx::Size(600, 600)); |
| |
| occluded.setOcclusion(gfx::Rect(200, 200, 300, 100)); |
| layer->setDrawableContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->setVisibleContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(36-3, layer->fakeLayerUpdater()->updateCount()); |
| { |
| updateTextures(); |
| } |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000, 1); |
| EXPECT_EQ(3, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| // Repaint without marking it dirty. The 3 culled tiles will be pre-painted now. |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(3, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000, 1); |
| EXPECT_EQ(6, occluded.overdrawMetrics().tilesCulledForUpload()); |
| } |
| |
| TEST_F(TiledLayerTest, tilesPaintedWithOcclusionAndTransforms) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| TestOcclusionTracker occluded; |
| |
| // The tile size is 100x100. |
| |
| // This makes sure the painting works when the occluded region (in screen space) |
| // is transformed differently than the layer. |
| layer->setBounds(gfx::Size(600, 600)); |
| gfx::Transform screenTransform; |
| screenTransform.Scale(0.5, 0.5); |
| layer->setScreenSpaceTransform(screenTransform); |
| layer->setDrawTransform(screenTransform); |
| |
| occluded.setOcclusion(gfx::Rect(100, 100, 150, 50)); |
| layer->setDrawableContentRect(gfx::Rect(gfx::Point(), layer->contentBounds())); |
| layer->setVisibleContentRect(gfx::Rect(gfx::Point(), layer->contentBounds())); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(36-3, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000, 1); |
| EXPECT_EQ(3, occluded.overdrawMetrics().tilesCulledForUpload()); |
| } |
| |
| TEST_F(TiledLayerTest, tilesPaintedWithOcclusionAndScaling) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| TestOcclusionTracker occluded; |
| |
| // The tile size is 100x100. |
| |
| // This makes sure the painting works when the content space is scaled to |
| // a different layer space. In this case tiles are scaled to be 200x200 |
| // pixels, which means none should be occluded. |
| layer->setContentsScale(0.5); |
| EXPECT_FLOAT_EQ(layer->contentsScaleX(), layer->contentsScaleY()); |
| layer->setBounds(gfx::Size(600, 600)); |
| gfx::Transform drawTransform; |
| double invScaleFactor = 1 / layer->contentsScaleX(); |
| drawTransform.Scale(invScaleFactor, invScaleFactor); |
| layer->setDrawTransform(drawTransform); |
| layer->setScreenSpaceTransform(drawTransform); |
| |
| occluded.setOcclusion(gfx::Rect(200, 200, 300, 100)); |
| layer->setDrawableContentRect(gfx::Rect(gfx::Point(), layer->bounds())); |
| layer->setVisibleContentRect(gfx::Rect(gfx::Point(), layer->contentBounds())); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| // The content is half the size of the layer (so the number of tiles is fewer). |
| // In this case, the content is 300x300, and since the tile size is 100, the |
| // number of tiles 3x3. |
| EXPECT_EQ(9, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 90000, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| |
| // This makes sure the painting works when the content space is scaled to |
| // a different layer space. In this case the occluded region catches the |
| // blown up tiles. |
| occluded.setOcclusion(gfx::Rect(200, 200, 300, 200)); |
| layer->setDrawableContentRect(gfx::Rect(gfx::Point(), layer->bounds())); |
| layer->setVisibleContentRect(gfx::Rect(gfx::Point(), layer->contentBounds())); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(9-1, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 90000 + 80000, 1); |
| EXPECT_EQ(1, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| layer->fakeLayerUpdater()->clearUpdateCount(); |
| |
| // This makes sure content scaling and transforms work together. |
| gfx::Transform screenTransform; |
| screenTransform.Scale(0.5, 0.5); |
| layer->setScreenSpaceTransform(screenTransform); |
| layer->setDrawTransform(screenTransform); |
| |
| occluded.setOcclusion(gfx::Rect(100, 100, 150, 100)); |
| |
| gfx::Rect layerBoundsRect(gfx::Point(), layer->bounds()); |
| layer->setDrawableContentRect(gfx::ToEnclosingRect(gfx::ScaleRect(layerBoundsRect, 0.5))); |
| layer->setVisibleContentRect(gfx::Rect(gfx::Point(), layer->contentBounds())); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 600, 600)); |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| EXPECT_EQ(9-1, layer->fakeLayerUpdater()->updateCount()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 90000 + 80000 + 80000, 1); |
| EXPECT_EQ(1 + 1, occluded.overdrawMetrics().tilesCulledForUpload()); |
| } |
| |
| TEST_F(TiledLayerTest, visibleContentOpaqueRegion) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| TestOcclusionTracker occluded; |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles in various ways. |
| |
| gfx::Rect opaquePaintRect; |
| Region opaqueContents; |
| |
| gfx::Rect contentBounds = gfx::Rect(0, 0, 100, 200); |
| gfx::Rect visibleBounds = gfx::Rect(0, 0, 100, 150); |
| |
| layer->setBounds(contentBounds.size()); |
| layer->setDrawableContentRect(visibleBounds); |
| layer->setVisibleContentRect(visibleBounds); |
| layer->setDrawOpacity(1); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| // If the layer doesn't paint opaque content, then the visibleContentOpaqueRegion should be empty. |
| layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect()); |
| layer->invalidateContentRect(contentBounds); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| opaqueContents = layer->visibleContentOpaqueRegion(); |
| EXPECT_TRUE(opaqueContents.IsEmpty()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| // visibleContentOpaqueRegion should match the visible part of what is painted opaque. |
| opaquePaintRect = gfx::Rect(10, 10, 90, 190); |
| layer->fakeLayerUpdater()->setOpaquePaintRect(opaquePaintRect); |
| layer->invalidateContentRect(contentBounds); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| updateTextures(); |
| opaqueContents = layer->visibleContentOpaqueRegion(); |
| EXPECT_EQ(gfx::IntersectRects(opaquePaintRect, visibleBounds).ToString(), opaqueContents.ToString()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| // If we paint again without invalidating, the same stuff should be opaque. |
| layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect()); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| updateTextures(); |
| opaqueContents = layer->visibleContentOpaqueRegion(); |
| EXPECT_EQ(gfx::IntersectRects(opaquePaintRect, visibleBounds).ToString(), opaqueContents.ToString()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| // If we repaint a non-opaque part of the tile, then it shouldn't lose its opaque-ness. And other tiles should |
| // not be affected. |
| layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect()); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 1, 1)); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| updateTextures(); |
| opaqueContents = layer->visibleContentOpaqueRegion(); |
| EXPECT_EQ(gfx::IntersectRects(opaquePaintRect, visibleBounds).ToString(), opaqueContents.ToString()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2 + 1, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100 + 1, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| // If we repaint an opaque part of the tile, then it should lose its opaque-ness. But other tiles should still |
| // not be affected. |
| layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect()); |
| layer->invalidateContentRect(gfx::Rect(10, 10, 1, 1)); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| updateTextures(); |
| opaqueContents = layer->visibleContentOpaqueRegion(); |
| EXPECT_EQ(gfx::IntersectRects(gfx::Rect(10, 100, 90, 100), visibleBounds).ToString(), opaqueContents.ToString()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2 + 1 + 1, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100 + 1 + 1, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| } |
| |
| TEST_F(TiledLayerTest, pixelsPaintedMetrics) |
| { |
| scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get())); |
| TestOcclusionTracker occluded; |
| |
| // The tile size is 100x100, so this invalidates and then paints two tiles in various ways. |
| |
| gfx::Rect opaquePaintRect; |
| Region opaqueContents; |
| |
| gfx::Rect contentBounds = gfx::Rect(0, 0, 100, 300); |
| gfx::Rect visibleBounds = gfx::Rect(0, 0, 100, 300); |
| |
| layer->setBounds(contentBounds.size()); |
| layer->setDrawableContentRect(visibleBounds); |
| layer->setVisibleContentRect(visibleBounds); |
| layer->setDrawOpacity(1); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| // Invalidates and paints the whole layer. |
| layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect()); |
| layer->invalidateContentRect(contentBounds); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| updateTextures(); |
| opaqueContents = layer->visibleContentOpaqueRegion(); |
| EXPECT_TRUE(opaqueContents.IsEmpty()); |
| |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 30000, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 30000, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| |
| // Invalidates an area on the top and bottom tile, which will cause us to paint the tile in the middle, |
| // even though it is not dirty and will not be uploaded. |
| layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect()); |
| layer->invalidateContentRect(gfx::Rect(0, 0, 1, 1)); |
| layer->invalidateContentRect(gfx::Rect(50, 200, 10, 10)); |
| layer->update(*m_queue.get(), &occluded, m_stats); |
| updateTextures(); |
| opaqueContents = layer->visibleContentOpaqueRegion(); |
| EXPECT_TRUE(opaqueContents.IsEmpty()); |
| |
| // The middle tile was painted even though not invalidated. |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 30000 + 60 * 210, 1); |
| // The pixels uploaded will not include the non-invalidated tile in the middle. |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); |
| EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 30000 + 1 + 100, 1); |
| EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); |
| } |
| |
| TEST_F(TiledLayerTest, dontAllocateContentsWhenTargetSurfaceCantBeAllocated) |
| { |
| // Tile size is 100x100. |
| gfx::Rect rootRect(0, 0, 300, 200); |
| gfx::Rect childRect(0, 0, 300, 100); |
| gfx::Rect child2Rect(0, 100, 300, 100); |
| |
| scoped_refptr<FakeTiledLayer> root = make_scoped_refptr(new FakeTiledLayer(m_layerTreeHost->contentsTextureManager())); |
| scoped_refptr<Layer> surface = Layer::create(); |
| scoped_refptr<FakeTiledLayer> child = make_scoped_refptr(new FakeTiledLayer(m_layerTreeHost->contentsTextureManager())); |
| scoped_refptr<FakeTiledLayer> child2 = make_scoped_refptr(new FakeTiledLayer(m_layerTreeHost->contentsTextureManager())); |
| |
| root->setBounds(rootRect.size()); |
| root->setAnchorPoint(gfx::PointF()); |
| root->setDrawableContentRect(rootRect); |
| root->setVisibleContentRect(rootRect); |
| root->addChild(surface); |
| |
| surface->setForceRenderSurface(true); |
| surface->setAnchorPoint(gfx::PointF()); |
| surface->setOpacity(0.5); |
| surface->addChild(child); |
| surface->addChild(child2); |
| |
| child->setBounds(childRect.size()); |
| child->setAnchorPoint(gfx::PointF()); |
| child->setPosition(childRect.origin()); |
| child->setVisibleContentRect(childRect); |
| child->setDrawableContentRect(rootRect); |
| |
| child2->setBounds(child2Rect.size()); |
| child2->setAnchorPoint(gfx::PointF()); |
| child2->setPosition(child2Rect.origin()); |
| child2->setVisibleContentRect(child2Rect); |
| child2->setDrawableContentRect(rootRect); |
| |
| m_layerTreeHost->setRootLayer(root); |
| m_layerTreeHost->setViewportSize(rootRect.size(), rootRect.size()); |
| |
| // With a huge memory limit, all layers should update and push their textures. |
| root->invalidateContentRect(rootRect); |
| child->invalidateContentRect(childRect); |
| child2->invalidateContentRect(child2Rect); |
| m_layerTreeHost->updateLayers( |
| *m_queue.get(), std::numeric_limits<size_t>::max()); |
| { |
| updateTextures(); |
| EXPECT_EQ(6, root->fakeLayerUpdater()->updateCount()); |
| EXPECT_EQ(3, child->fakeLayerUpdater()->updateCount()); |
| EXPECT_EQ(3, child2->fakeLayerUpdater()->updateCount()); |
| EXPECT_FALSE(m_queue->hasMoreUpdates()); |
| |
| root->fakeLayerUpdater()->clearUpdateCount(); |
| child->fakeLayerUpdater()->clearUpdateCount(); |
| child2->fakeLayerUpdater()->clearUpdateCount(); |
| |
| ScopedFakeTiledLayerImpl rootImpl(root->id()); |
| ScopedFakeTiledLayerImpl childImpl(child->id()); |
| ScopedFakeTiledLayerImpl child2Impl(child2->id()); |
| layerPushPropertiesTo(root.get(), rootImpl.get()); |
| layerPushPropertiesTo(child.get(), childImpl.get()); |
| layerPushPropertiesTo(child2.get(), child2Impl.get()); |
| |
| for (unsigned i = 0; i < 3; ++i) { |
| for (unsigned j = 0; j < 2; ++j) |
| EXPECT_TRUE(rootImpl->hasResourceIdForTileAt(i, j)); |
| EXPECT_TRUE(childImpl->hasResourceIdForTileAt(i, 0)); |
| EXPECT_TRUE(child2Impl->hasResourceIdForTileAt(i, 0)); |
| } |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| // With a memory limit that includes only the root layer (3x2 tiles) and half the surface that |
| // the child layers draw into, the child layers will not be allocated. If the surface isn't |
| // accounted for, then one of the children would fit within the memory limit. |
| root->invalidateContentRect(rootRect); |
| child->invalidateContentRect(childRect); |
| child2->invalidateContentRect(child2Rect); |
| m_layerTreeHost->updateLayers( |
| *m_queue.get(), (3 * 2 + 3 * 1) * (100 * 100) * 4); |
| { |
| updateTextures(); |
| EXPECT_EQ(6, root->fakeLayerUpdater()->updateCount()); |
| EXPECT_EQ(0, child->fakeLayerUpdater()->updateCount()); |
| EXPECT_EQ(0, child2->fakeLayerUpdater()->updateCount()); |
| EXPECT_FALSE(m_queue->hasMoreUpdates()); |
| |
| root->fakeLayerUpdater()->clearUpdateCount(); |
| child->fakeLayerUpdater()->clearUpdateCount(); |
| child2->fakeLayerUpdater()->clearUpdateCount(); |
| |
| ScopedFakeTiledLayerImpl rootImpl(root->id()); |
| ScopedFakeTiledLayerImpl childImpl(child->id()); |
| ScopedFakeTiledLayerImpl child2Impl(child2->id()); |
| layerPushPropertiesTo(root.get(), rootImpl.get()); |
| layerPushPropertiesTo(child.get(), childImpl.get()); |
| layerPushPropertiesTo(child2.get(), child2Impl.get()); |
| |
| for (unsigned i = 0; i < 3; ++i) { |
| for (unsigned j = 0; j < 2; ++j) |
| EXPECT_TRUE(rootImpl->hasResourceIdForTileAt(i, j)); |
| EXPECT_FALSE(childImpl->hasResourceIdForTileAt(i, 0)); |
| EXPECT_FALSE(child2Impl->hasResourceIdForTileAt(i, 0)); |
| } |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| // With a memory limit that includes only half the root layer, no contents will be |
| // allocated. If render surface memory wasn't accounted for, there is enough space |
| // for one of the children layers, but they draw into a surface that can't be |
| // allocated. |
| root->invalidateContentRect(rootRect); |
| child->invalidateContentRect(childRect); |
| child2->invalidateContentRect(child2Rect); |
| m_layerTreeHost->updateLayers( |
| *m_queue.get(), (3 * 1) * (100 * 100) * 4); |
| { |
| updateTextures(); |
| EXPECT_EQ(0, root->fakeLayerUpdater()->updateCount()); |
| EXPECT_EQ(0, child->fakeLayerUpdater()->updateCount()); |
| EXPECT_EQ(0, child2->fakeLayerUpdater()->updateCount()); |
| EXPECT_FALSE(m_queue->hasMoreUpdates()); |
| |
| root->fakeLayerUpdater()->clearUpdateCount(); |
| child->fakeLayerUpdater()->clearUpdateCount(); |
| child2->fakeLayerUpdater()->clearUpdateCount(); |
| |
| ScopedFakeTiledLayerImpl rootImpl(root->id()); |
| ScopedFakeTiledLayerImpl childImpl(child->id()); |
| ScopedFakeTiledLayerImpl child2Impl(child2->id()); |
| layerPushPropertiesTo(root.get(), rootImpl.get()); |
| layerPushPropertiesTo(child.get(), childImpl.get()); |
| layerPushPropertiesTo(child2.get(), child2Impl.get()); |
| |
| for (unsigned i = 0; i < 3; ++i) { |
| for (unsigned j = 0; j < 2; ++j) |
| EXPECT_FALSE(rootImpl->hasResourceIdForTileAt(i, j)); |
| EXPECT_FALSE(childImpl->hasResourceIdForTileAt(i, 0)); |
| EXPECT_FALSE(child2Impl->hasResourceIdForTileAt(i, 0)); |
| } |
| } |
| m_layerTreeHost->commitComplete(); |
| |
| resourceManagerClearAllMemory(m_layerTreeHost->contentsTextureManager(), m_resourceProvider.get()); |
| m_layerTreeHost->setRootLayer(0); |
| } |
| |
| class TrackingLayerPainter : public LayerPainter { |
| public: |
| static scoped_ptr<TrackingLayerPainter> create() { return make_scoped_ptr(new TrackingLayerPainter()); } |
| |
| virtual void paint(SkCanvas*, const gfx::Rect& contentRect, gfx::RectF&) OVERRIDE |
| { |
| m_paintedRect = contentRect; |
| } |
| |
| const gfx::Rect& paintedRect() const { return m_paintedRect; } |
| void resetPaintedRect() { m_paintedRect = gfx::Rect(); } |
| |
| private: |
| TrackingLayerPainter() { } |
| |
| gfx::Rect m_paintedRect; |
| }; |
| |
| class UpdateTrackingTiledLayer : public FakeTiledLayer { |
| public: |
| explicit UpdateTrackingTiledLayer(PrioritizedResourceManager* manager) |
| : FakeTiledLayer(manager) |
| { |
| scoped_ptr<TrackingLayerPainter> trackingLayerPainter(TrackingLayerPainter::create()); |
| m_trackingLayerPainter = trackingLayerPainter.get(); |
| m_layerUpdater = BitmapContentLayerUpdater::create(trackingLayerPainter.PassAs<LayerPainter>()); |
| } |
| |
| TrackingLayerPainter* trackingLayerPainter() const { return m_trackingLayerPainter; } |
| |
| protected: |
| virtual ~UpdateTrackingTiledLayer() { } |
| |
| virtual LayerUpdater* updater() const OVERRIDE { return m_layerUpdater.get(); } |
| |
| private: |
| TrackingLayerPainter* m_trackingLayerPainter; |
| scoped_refptr<BitmapContentLayerUpdater> m_layerUpdater; |
| }; |
| |
| TEST_F(TiledLayerTest, nonIntegerContentsScaleIsNotDistortedDuringPaint) |
| { |
| scoped_refptr<UpdateTrackingTiledLayer> layer = make_scoped_refptr(new UpdateTrackingTiledLayer(m_resourceManager.get())); |
| |
| gfx::Rect layerRect(0, 0, 30, 31); |
| layer->setPosition(layerRect.origin()); |
| layer->setBounds(layerRect.size()); |
| layer->setContentsScale(1.5); |
| |
| gfx::Rect contentRect(0, 0, 45, 47); |
| EXPECT_EQ(contentRect.size(), layer->contentBounds()); |
| layer->setVisibleContentRect(contentRect); |
| layer->setDrawableContentRect(contentRect); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| // Update the whole tile. |
| layer->update(*m_queue.get(), 0, m_stats); |
| layer->trackingLayerPainter()->resetPaintedRect(); |
| |
| EXPECT_RECT_EQ(gfx::Rect(), layer->trackingLayerPainter()->paintedRect()); |
| updateTextures(); |
| |
| // Invalidate the entire layer in content space. When painting, the rect given to webkit should match the layer's bounds. |
| layer->invalidateContentRect(contentRect); |
| layer->update(*m_queue.get(), 0, m_stats); |
| |
| EXPECT_RECT_EQ(layerRect, layer->trackingLayerPainter()->paintedRect()); |
| } |
| |
| TEST_F(TiledLayerTest, nonIntegerContentsScaleIsNotDistortedDuringInvalidation) |
| { |
| scoped_refptr<UpdateTrackingTiledLayer> layer = make_scoped_refptr(new UpdateTrackingTiledLayer(m_resourceManager.get())); |
| |
| gfx::Rect layerRect(0, 0, 30, 31); |
| layer->setPosition(layerRect.origin()); |
| layer->setBounds(layerRect.size()); |
| layer->setContentsScale(1.3f); |
| |
| gfx::Rect contentRect(gfx::Point(), layer->contentBounds()); |
| layer->setVisibleContentRect(contentRect); |
| layer->setDrawableContentRect(contentRect); |
| |
| layer->setTexturePriorities(m_priorityCalculator); |
| m_resourceManager->prioritizeTextures(); |
| |
| // Update the whole tile. |
| layer->update(*m_queue.get(), 0, m_stats); |
| layer->trackingLayerPainter()->resetPaintedRect(); |
| |
| EXPECT_RECT_EQ(gfx::Rect(), layer->trackingLayerPainter()->paintedRect()); |
| updateTextures(); |
| |
| // Invalidate the entire layer in layer space. When painting, the rect given to webkit should match the layer's bounds. |
| layer->setNeedsDisplayRect(layerRect); |
| layer->update(*m_queue.get(), 0, m_stats); |
| |
| EXPECT_RECT_EQ(layerRect, layer->trackingLayerPainter()->paintedRect()); |
| } |
| |
| } // namespace |
| } // namespace cc |