summaryrefslogtreecommitdiff
path: root/chromium/cc/layers/tiled_layer_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/cc/layers/tiled_layer_unittest.cc')
-rw-r--r--chromium/cc/layers/tiled_layer_unittest.cc1942
1 files changed, 1942 insertions, 0 deletions
diff --git a/chromium/cc/layers/tiled_layer_unittest.cc b/chromium/cc/layers/tiled_layer_unittest.cc
new file mode 100644
index 00000000000..a2f0ca7a64d
--- /dev/null
+++ b/chromium/cc/layers/tiled_layer_unittest.cc
@@ -0,0 +1,1942 @@
+// 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/layers/tiled_layer.h"
+
+#include <limits>
+#include <vector>
+
+#include "cc/debug/overdraw_metrics.h"
+#include "cc/resources/bitmap_content_layer_updater.h"
+#include "cc/resources/layer_painter.h"
+#include "cc/resources/prioritized_resource_manager.h"
+#include "cc/resources/resource_update_controller.h"
+#include "cc/test/animation_test_common.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_proxy.h"
+#include "cc/test/fake_rendering_stats_instrumentation.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/tiled_layer_test_common.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+class TestOcclusionTracker : public OcclusionTracker {
+ public:
+ TestOcclusionTracker() : OcclusionTracker(gfx::Rect(0, 0, 1000, 1000), true) {
+ stack_.push_back(StackObject());
+ }
+
+ void SetRenderTarget(Layer* render_target) {
+ stack_.back().target = render_target;
+ }
+
+ void SetOcclusion(const Region& occlusion) {
+ stack_.back().occlusion_from_inside_target = occlusion;
+ }
+};
+
+class TiledLayerTest : public testing::Test {
+ public:
+ TiledLayerTest()
+ : proxy_(NULL),
+ output_surface_(CreateFakeOutputSurface()),
+ queue_(make_scoped_ptr(new ResourceUpdateQueue)),
+ fake_layer_impl_tree_host_client_(FakeLayerTreeHostClient::DIRECT_3D),
+ occlusion_(NULL) {
+ settings_.max_partial_texture_updates = std::numeric_limits<size_t>::max();
+ settings_.layer_transforms_should_scale_layer_contents = true;
+ }
+
+ virtual void SetUp() {
+ layer_tree_host_ = LayerTreeHost::Create(&fake_layer_impl_tree_host_client_,
+ settings_,
+ NULL);
+ proxy_ = layer_tree_host_->proxy();
+ resource_manager_ = PrioritizedResourceManager::Create(proxy_);
+ layer_tree_host_->InitializeOutputSurfaceIfNeeded();
+ layer_tree_host_->SetRootLayer(Layer::Create());
+
+ DebugScopedSetImplThreadAndMainThreadBlocked
+ impl_thread_and_main_thread_blocked(proxy_);
+ resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0);
+ host_impl_ = make_scoped_ptr(new FakeLayerTreeHostImpl(proxy_));
+ }
+
+ virtual ~TiledLayerTest() {
+ ResourceManagerClearAllMemory(resource_manager_.get(),
+ resource_provider_.get());
+
+ DebugScopedSetImplThreadAndMainThreadBlocked
+ impl_thread_and_main_thread_blocked(proxy_);
+ resource_provider_.reset();
+ host_impl_.reset();
+ }
+
+ void ResourceManagerClearAllMemory(
+ PrioritizedResourceManager* resource_manager,
+ ResourceProvider* resource_provider) {
+ {
+ DebugScopedSetImplThreadAndMainThreadBlocked
+ impl_thread_and_main_thread_blocked(proxy_);
+ resource_manager->ClearAllMemory(resource_provider);
+ resource_manager->ReduceMemory(resource_provider);
+ }
+ resource_manager->UnlinkAndClearEvictedBackings();
+ }
+
+ void UpdateTextures() {
+ DebugScopedSetImplThreadAndMainThreadBlocked
+ impl_thread_and_main_thread_blocked(proxy_);
+ DCHECK(queue_);
+ scoped_ptr<ResourceUpdateController> update_controller =
+ ResourceUpdateController::Create(NULL,
+ proxy_->ImplThreadTaskRunner(),
+ queue_.Pass(),
+ resource_provider_.get());
+ update_controller->Finalize();
+ queue_ = make_scoped_ptr(new ResourceUpdateQueue);
+ }
+
+ void LayerPushPropertiesTo(FakeTiledLayer* layer,
+ FakeTiledLayerImpl* layer_impl) {
+ DebugScopedSetImplThreadAndMainThreadBlocked
+ impl_thread_and_main_thread_blocked(proxy_);
+ layer->PushPropertiesTo(layer_impl);
+ layer->ResetNumDependentsNeedPushProperties();
+ }
+
+ void LayerUpdate(FakeTiledLayer* layer, TestOcclusionTracker* occluded) {
+ DebugScopedSetMainThread main_thread(proxy_);
+ layer->Update(queue_.get(), occluded);
+ }
+
+ void CalcDrawProps(RenderSurfaceLayerList* render_surface_layer_list) {
+ if (occlusion_)
+ occlusion_->SetRenderTarget(layer_tree_host_->root_layer());
+
+ LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
+ layer_tree_host_->root_layer(),
+ layer_tree_host_->device_viewport_size(),
+ render_surface_layer_list);
+ inputs.device_scale_factor = layer_tree_host_->device_scale_factor();
+ inputs.max_texture_size =
+ layer_tree_host_->GetRendererCapabilities().max_texture_size;
+ inputs.can_adjust_raster_scales = true;
+ LayerTreeHostCommon::CalculateDrawProperties(&inputs);
+ }
+
+ bool UpdateAndPush(const scoped_refptr<FakeTiledLayer>& layer1,
+ const scoped_ptr<FakeTiledLayerImpl>& layer_impl1) {
+ scoped_refptr<FakeTiledLayer> layer2;
+ scoped_ptr<FakeTiledLayerImpl> layer_impl2;
+ return UpdateAndPush(layer1, layer_impl1, layer2, layer_impl2);
+ }
+
+ bool UpdateAndPush(const scoped_refptr<FakeTiledLayer>& layer1,
+ const scoped_ptr<FakeTiledLayerImpl>& layer_impl1,
+ const scoped_refptr<FakeTiledLayer>& layer2,
+ const scoped_ptr<FakeTiledLayerImpl>& layer_impl2) {
+ // Get textures
+ resource_manager_->ClearPriorities();
+ if (layer1.get())
+ layer1->SetTexturePriorities(priority_calculator_);
+ if (layer2.get())
+ layer2->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+
+ // Save paint properties
+ if (layer1.get())
+ layer1->SavePaintProperties();
+ if (layer2.get())
+ layer2->SavePaintProperties();
+
+ // Update content
+ if (layer1.get())
+ layer1->Update(queue_.get(), occlusion_);
+ if (layer2.get())
+ layer2->Update(queue_.get(), occlusion_);
+
+ bool needs_update = false;
+ if (layer1.get())
+ needs_update |= layer1->NeedsIdlePaint();
+ if (layer2.get())
+ needs_update |= layer2->NeedsIdlePaint();
+
+ // Update textures and push.
+ UpdateTextures();
+ if (layer1.get())
+ LayerPushPropertiesTo(layer1.get(), layer_impl1.get());
+ if (layer2.get())
+ LayerPushPropertiesTo(layer2.get(), layer_impl2.get());
+
+ return needs_update;
+ }
+
+ public:
+ Proxy* proxy_;
+ LayerTreeSettings settings_;
+ scoped_ptr<OutputSurface> output_surface_;
+ scoped_ptr<ResourceProvider> resource_provider_;
+ scoped_ptr<ResourceUpdateQueue> queue_;
+ PriorityCalculator priority_calculator_;
+ FakeLayerTreeHostClient fake_layer_impl_tree_host_client_;
+ scoped_ptr<LayerTreeHost> layer_tree_host_;
+ scoped_ptr<FakeLayerTreeHostImpl> host_impl_;
+ scoped_ptr<PrioritizedResourceManager> resource_manager_;
+ TestOcclusionTracker* occlusion_;
+};
+
+TEST_F(TiledLayerTest, PushDirtyTiles) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ CalcDrawProps(&render_surface_layer_list);
+ UpdateAndPush(layer, layer_impl);
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 1));
+
+ // Invalidates both tiles, but then only update one of them.
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 100, 200));
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ UpdateAndPush(layer, layer_impl);
+
+ // We should only have the first tile since the other tile was invalidated but
+ // not painted.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, PushOccludedDirtyTiles) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+ layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ {
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ CalcDrawProps(&render_surface_layer_list);
+ UpdateAndPush(layer, layer_impl);
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 1));
+ }
+
+ {
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ // 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));
+ CalcDrawProps(&render_surface_layer_list);
+ UpdateAndPush(layer, layer_impl);
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 20000 + 2500,
+ 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // We should still have both tiles, as part of the top tile is still
+ // unoccluded.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 1));
+ }
+}
+
+TEST_F(TiledLayerTest, PushDeletedTiles) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ CalcDrawProps(&render_surface_layer_list);
+ UpdateAndPush(layer, layer_impl);
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 1));
+
+ resource_manager_->ClearPriorities();
+ ResourceManagerClearAllMemory(resource_manager_.get(),
+ resource_provider_.get());
+ resource_manager_->SetMaxMemoryLimitBytes(4 * 1024 * 1024);
+
+ // This should drop the tiles on the impl thread.
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+
+ // We should now have no textures on the impl thread.
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(0, 1));
+
+ // This should recreate and update one of the deleted textures.
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ UpdateAndPush(layer, layer_impl);
+
+ // We should have one tiles on the impl side.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, PushIdlePaintTiles) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // 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));
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = gfx::Rect(200, 200, 100, 100);
+ bool needs_update = UpdateAndPush(layer, layer_impl);
+ // We should need idle-painting for surrounding tiles.
+ EXPECT_TRUE(needs_update);
+
+ // We should have one tile on the impl side.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(2, 2));
+
+ // For the next four updates, we should detect we still need idle painting.
+ for (int i = 0; i < 4; i++) {
+ needs_update = UpdateAndPush(layer, layer_impl);
+ EXPECT_TRUE(needs_update);
+ }
+
+ // We should always finish painting eventually.
+ for (int i = 0; i < 20; i++)
+ needs_update = UpdateAndPush(layer, layer_impl);
+
+ // 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(layer_impl->HasResourceIdForTileAt(i, j));
+ }
+
+ EXPECT_FALSE(needs_update);
+}
+
+TEST_F(TiledLayerTest, PredictivePainting) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // 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 pushed_visible_tiles[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 pushed_prepaint_tiles[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 bounds = gfx::Size(500, 500);
+ gfx::Rect visible_rect = gfx::Rect(200, 200, 100, 100);
+ gfx::Rect previous_visible_rect =
+ gfx::Rect(visible_rect.origin() + directions[k], visible_rect.size());
+ gfx::Rect next_visible_rect =
+ gfx::Rect(visible_rect.origin() - directions[k], visible_rect.size());
+
+ // Setup. Use the previous_visible_rect to setup the prediction for next
+ // frame.
+ layer->SetBounds(bounds);
+
+ RenderSurfaceLayerList render_surface_layer_list;
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = previous_visible_rect;
+ bool needs_update = UpdateAndPush(layer, layer_impl);
+
+ // Invalidate and move the visible_rect in the scroll direction.
+ // Check that the correct tiles have been painted in the visible pass.
+ layer->SetNeedsDisplay();
+ layer->draw_properties().visible_content_rect = visible_rect;
+ needs_update = UpdateAndPush(layer, layer_impl);
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 5; j++)
+ EXPECT_EQ(layer_impl->HasResourceIdForTileAt(i, j),
+ pushed_visible_tiles[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->draw_properties().visible_content_rect = next_visible_rect;
+ needs_update = UpdateAndPush(layer, layer_impl);
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 5; j++)
+ EXPECT_EQ(layer_impl->HasResourceIdForTileAt(i, j),
+ pushed_prepaint_tiles[k].Contains(i, j));
+ }
+ }
+
+ // We should always finish painting eventually.
+ for (int i = 0; i < 20; i++)
+ needs_update = UpdateAndPush(layer, layer_impl);
+ EXPECT_FALSE(needs_update);
+ }
+}
+
+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.
+ resource_manager_->SetMaxMemoryLimitBytes(2 * 1024 * 1024);
+ scoped_refptr<FakeTiledLayer> layer1 =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl1 =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ scoped_refptr<FakeTiledLayer> layer2 =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl2 =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 2));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer1);
+ layer_tree_host_->root_layer()->AddChild(layer2);
+
+ // 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 layer1_rect(0, 0, 100, 2400);
+
+ // This requires 4*30000 bytes of memory.
+ gfx::Rect layer2_rect(0, 0, 100, 300);
+
+ // Paint a single tile in layer2 so that it will idle paint.
+ layer1->SetBounds(layer1_rect.size());
+ layer2->SetBounds(layer2_rect.size());
+ CalcDrawProps(&render_surface_layer_list);
+ layer1->draw_properties().visible_content_rect = layer1_rect;
+ layer2->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ bool needs_update = UpdateAndPush(layer1, layer_impl1, layer2, layer_impl2);
+ // We should need idle-painting for both remaining tiles in layer2.
+ EXPECT_TRUE(needs_update);
+
+ // Reduce our memory limits to 1mb.
+ resource_manager_->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++) {
+ needs_update = UpdateAndPush(layer1, layer_impl1, layer2, layer_impl2);
+ }
+
+ // Sanity check, we should have textures for the big layer.
+ EXPECT_TRUE(layer_impl1->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl1->HasResourceIdForTileAt(0, 23));
+
+ // We should only have the first two tiles from layer2 since
+ // it failed to idle update the last tile.
+ EXPECT_TRUE(layer_impl2->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl2->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl2->HasResourceIdForTileAt(0, 1));
+ EXPECT_TRUE(layer_impl2->HasResourceIdForTileAt(0, 1));
+
+ EXPECT_FALSE(needs_update);
+ EXPECT_FALSE(layer_impl2->HasResourceIdForTileAt(0, 2));
+}
+
+TEST_F(TiledLayerTest, PushIdlePaintedOccludedTiles) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // 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));
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ UpdateAndPush(layer, layer_impl);
+
+ // We should have the prepainted tile on the impl side, but culled it during
+ // paint.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_EQ(1, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, PushTilesMarkedDirtyDuringPaint) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // 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->fake_layer_updater()->SetRectToInvalidate(
+ gfx::Rect(0, 50, 100, 50), layer.get());
+ layer->SetBounds(gfx::Size(100, 200));
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ UpdateAndPush(layer, layer_impl);
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, PushTilesLayerMarkedDirtyDuringPaintOnNextLayer) {
+ scoped_refptr<FakeTiledLayer> layer1 =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_refptr<FakeTiledLayer> layer2 =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer1_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ scoped_ptr<FakeTiledLayerImpl> layer2_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 2));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer1);
+ layer_tree_host_->root_layer()->AddChild(layer2);
+
+ // Invalidate a tile on layer1, during update of layer 2.
+ layer2->fake_layer_updater()->SetRectToInvalidate(
+ gfx::Rect(0, 50, 100, 50), layer1.get());
+ layer1->SetBounds(gfx::Size(100, 200));
+ layer2->SetBounds(gfx::Size(100, 200));
+ CalcDrawProps(&render_surface_layer_list);
+ layer1->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ layer2->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ UpdateAndPush(layer1, layer1_impl, layer2, layer2_impl);
+
+ // We should have both tiles on the impl side for all layers.
+ EXPECT_TRUE(layer1_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer1_impl->HasResourceIdForTileAt(0, 1));
+ EXPECT_TRUE(layer2_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer2_impl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, PushTilesLayerMarkedDirtyDuringPaintOnPreviousLayer) {
+ scoped_refptr<FakeTiledLayer> layer1 =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_refptr<FakeTiledLayer> layer2 =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer1_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ scoped_ptr<FakeTiledLayerImpl> layer2_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 2));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer1);
+ layer_tree_host_->root_layer()->AddChild(layer2);
+
+ layer1->fake_layer_updater()->SetRectToInvalidate(
+ gfx::Rect(0, 50, 100, 50), layer2.get());
+ layer1->SetBounds(gfx::Size(100, 200));
+ layer2->SetBounds(gfx::Size(100, 200));
+ CalcDrawProps(&render_surface_layer_list);
+ layer1->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ layer2->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ UpdateAndPush(layer1, layer1_impl, layer2, layer2_impl);
+
+ // We should have both tiles on the impl side for all layers.
+ EXPECT_TRUE(layer1_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer1_impl->HasResourceIdForTileAt(0, 1));
+ EXPECT_TRUE(layer2_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer2_impl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, PaintSmallAnimatedLayersImmediately) {
+ // Create a LayerTreeHost that has the right viewportsize,
+ // so the layer is considered small enough.
+ bool run_out_of_memory[2] = { false, true };
+ for (int i = 0; i < 2; i++) {
+ // Create a layer with 5x5 tiles, with 4x4 size viewport.
+ int viewport_width = 4 * FakeTiledLayer::tile_size().width();
+ int viewport_height = 4 * FakeTiledLayer::tile_size().width();
+ int layer_width = 5 * FakeTiledLayer::tile_size().width();
+ int layer_height = 5 * FakeTiledLayer::tile_size().height();
+ int memory_for_layer = layer_width * layer_height * 4;
+ layer_tree_host_->SetViewportSize(
+ gfx::Size(viewport_width, viewport_height));
+
+ // Use 10x5 tiles to run out of memory.
+ if (run_out_of_memory[i])
+ layer_width *= 2;
+
+ resource_manager_->SetMaxMemoryLimitBytes(memory_for_layer);
+
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // Full size layer with half being visible.
+ layer->SetBounds(gfx::Size(layer_width, layer_height));
+ gfx::Rect visible_rect(0, 0, layer_width / 2, layer_height);
+ CalcDrawProps(&render_surface_layer_list);
+
+ // Pretend the layer is animating.
+ layer->draw_properties().target_space_transform_is_animating = true;
+ layer->draw_properties().visible_content_rect = visible_rect;
+ layer->SetLayerTreeHost(layer_tree_host_.get());
+
+ // The layer should paint its entire contents on the first paint
+ // if it is close to the viewport size and has the available memory.
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+ UpdateTextures();
+ LayerPushPropertiesTo(layer.get(), layer_impl.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 (!run_out_of_memory[i]) {
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 5; ++j)
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(i, j));
+ }
+ } else {
+ for (int i = 0; i < 10; ++i) {
+ for (int j = 0; j < 5; ++j)
+ EXPECT_EQ(layer_impl->HasResourceIdForTileAt(i, j), i < 5);
+ }
+ }
+
+ layer->RemoveFromParent();
+ }
+}
+
+TEST_F(TiledLayerTest, IdlePaintOutOfMemory) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // We have enough memory for only the visible rect, so we will run out of
+ // memory in first idle paint.
+ int memory_limit = 4 * 100 * 100; // 1 tiles, 4 bytes per pixel.
+ resource_manager_->SetMaxMemoryLimitBytes(memory_limit);
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ bool needs_update = false;
+ layer->SetBounds(gfx::Size(300, 300));
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = gfx::Rect(100, 100, 100, 100);
+ for (int i = 0; i < 2; i++)
+ needs_update = UpdateAndPush(layer, layer_impl);
+
+ // Idle-painting should see no more priority tiles for painting.
+ EXPECT_FALSE(needs_update);
+
+ // We should have one tile on the impl side.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(1, 1));
+}
+
+TEST_F(TiledLayerTest, IdlePaintZeroSizedLayer) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ bool animating[2] = { false, true };
+ for (int i = 0; i < 2; i++) {
+ // Pretend the layer is animating.
+ layer->draw_properties().target_space_transform_is_animating = animating[i];
+
+ // The layer's bounds are empty.
+ // Empty layers don't paint or idle-paint.
+ layer->SetBounds(gfx::Size());
+
+ RenderSurfaceLayerList render_surface_layer_list;
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = gfx::Rect();
+ bool needs_update = UpdateAndPush(layer, layer_impl);
+
+ // Empty layers don't have tiles.
+ EXPECT_EQ(0u, layer->NumPaintedTiles());
+
+ // Empty layers don't need prepaint.
+ EXPECT_FALSE(needs_update);
+
+ // Empty layers don't have tiles.
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(0, 0));
+ }
+}
+
+TEST_F(TiledLayerTest, IdlePaintNonVisibleLayers) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+
+ // Alternate between not visible and visible.
+ gfx::Rect v(0, 0, 100, 100);
+ gfx::Rect nv(0, 0, 0, 0);
+ gfx::Rect visible_rect[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 have_tile[10] = { false, false, true, true, false, false, true, true,
+ true, true };
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ for (int i = 0; i < 10; i++) {
+ layer->SetBounds(gfx::Size(100, 100));
+
+ RenderSurfaceLayerList render_surface_layer_list;
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = visible_rect[i];
+
+ if (invalidate[i])
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 100, 100));
+ bool needs_update = UpdateAndPush(layer, layer_impl);
+
+ // We should never signal idle paint, as we painted the entire layer
+ // or the layer was not visible.
+ EXPECT_FALSE(needs_update);
+ EXPECT_EQ(layer_impl->HasResourceIdForTileAt(0, 0), have_tile[i]);
+ }
+}
+
+TEST_F(TiledLayerTest, InvalidateFromPrepare) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ UpdateAndPush(layer, layer_impl);
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 1));
+
+ layer->fake_layer_updater()->ClearPrepareCount();
+ // Invoke update again. As the layer is valid update shouldn't be invoked on
+ // the LayerUpdater.
+ UpdateAndPush(layer, layer_impl);
+ EXPECT_EQ(0, layer->fake_layer_updater()->prepare_count());
+
+ // SetRectToInvalidate triggers InvalidateContentRect() being invoked from
+ // update.
+ layer->fake_layer_updater()->SetRectToInvalidate(
+ gfx::Rect(25, 25, 50, 50), layer.get());
+ layer->fake_layer_updater()->ClearPrepareCount();
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 50, 50));
+ UpdateAndPush(layer, layer_impl);
+ EXPECT_EQ(1, layer->fake_layer_updater()->prepare_count());
+ layer->fake_layer_updater()->ClearPrepareCount();
+
+ // The layer should still be invalid as update invoked invalidate.
+ UpdateAndPush(layer, layer_impl); // visible
+ EXPECT_EQ(1, layer->fake_layer_updater()->prepare_count());
+}
+
+TEST_F(TiledLayerTest, VerifyUpdateRectWhenContentBoundsAreScaled) {
+ // The update rect (that indicates what was actually painted) should be in
+ // layer space, not the content space.
+ scoped_refptr<FakeTiledLayerWithScaledBounds> layer = make_scoped_refptr(
+ new FakeTiledLayerWithScaledBounds(resource_manager_.get()));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ gfx::Rect layer_bounds(0, 0, 300, 200);
+ gfx::Rect content_bounds(0, 0, 200, 250);
+
+ layer->SetBounds(layer_bounds.size());
+ layer->SetContentBounds(content_bounds.size());
+ layer->draw_properties().visible_content_rect = content_bounds;
+
+ // On first update, the update_rect includes all tiles, even beyond the
+ // boundaries of the layer.
+ // However, it should still be in layer space, not content space.
+ layer->InvalidateContentRect(content_bounds);
+
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 300, 300 * 0.8), layer->update_rect());
+ UpdateTextures();
+
+ // After the tiles are updated once, another invalidate only needs to update
+ // the bounds of the layer.
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->InvalidateContentRect(content_bounds);
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(layer_bounds), layer->update_rect());
+ UpdateTextures();
+
+ // Partial re-paint should also be represented by the update rect in layer
+ // space, not content space.
+ gfx::Rect partial_damage(30, 100, 10, 10);
+ layer->InvalidateContentRect(partial_damage);
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(45, 80, 15, 8), layer->update_rect());
+}
+
+TEST_F(TiledLayerTest, VerifyInvalidationWhenContentsScaleChanges) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // Create a layer with one tile.
+ layer->SetBounds(gfx::Size(100, 100));
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ layer->Update(queue_.get(), NULL);
+ UpdateTextures();
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 100, 100),
+ layer->last_needs_display_rect());
+
+ // Push the tiles to the impl side and check that there is exactly one.
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+ UpdateTextures();
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(0, 1));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(1, 0));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(1, 1));
+
+ layer->SetNeedsDisplayRect(gfx::Rect());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(), layer->last_needs_display_rect());
+
+ // Change the contents scale.
+ layer->UpdateContentsScale(2.f);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 200, 200);
+
+ // The impl side should get 2x2 tiles now.
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+ UpdateTextures();
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 1));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(1, 0));
+ EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(1, 1));
+
+ // Verify that changing the contents scale caused invalidation, and
+ // that the layer-space rectangle requiring painting is not scaled.
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 100, 100),
+ layer->last_needs_display_rect());
+
+ // Invalidate the entire layer again, but do not paint. All tiles should be
+ // gone now from the impl side.
+ layer->SetNeedsDisplay();
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(0, 1));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(1, 0));
+ EXPECT_FALSE(layer_impl->HasResourceIdForTileAt(1, 1));
+}
+
+TEST_F(TiledLayerTest, SkipsDrawGetsReset) {
+ // Create two 300 x 300 tiled layers.
+ gfx::Size content_bounds(300, 300);
+ gfx::Rect content_rect(content_bounds);
+
+ // We have enough memory for only one of the two layers.
+ int memory_limit = 4 * 300 * 300; // 4 bytes per pixel.
+
+ scoped_refptr<FakeTiledLayer> root_layer = make_scoped_refptr(
+ new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ scoped_refptr<FakeTiledLayer> child_layer = make_scoped_refptr(
+ new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ root_layer->AddChild(child_layer);
+
+ root_layer->SetBounds(content_bounds);
+ root_layer->draw_properties().visible_content_rect = content_rect;
+ root_layer->SetPosition(gfx::PointF(0, 0));
+ child_layer->SetBounds(content_bounds);
+ child_layer->draw_properties().visible_content_rect = content_rect;
+ child_layer->SetPosition(gfx::PointF(0, 0));
+ root_layer->InvalidateContentRect(content_rect);
+ child_layer->InvalidateContentRect(content_rect);
+
+ layer_tree_host_->SetRootLayer(root_layer);
+ layer_tree_host_->SetViewportSize(gfx::Size(300, 300));
+
+ layer_tree_host_->UpdateLayers(queue_.get(), memory_limit);
+
+ // We'll skip the root layer.
+ EXPECT_TRUE(root_layer->SkipsDraw());
+ EXPECT_FALSE(child_layer->SkipsDraw());
+
+ layer_tree_host_->CommitComplete();
+
+ // Remove the child layer.
+ root_layer->RemoveAllChildren();
+
+ layer_tree_host_->UpdateLayers(queue_.get(), memory_limit);
+ EXPECT_FALSE(root_layer->SkipsDraw());
+
+ ResourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(),
+ resource_provider_.get());
+ layer_tree_host_->SetRootLayer(NULL);
+}
+
+TEST_F(TiledLayerTest, ResizeToSmaller) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ layer->SetBounds(gfx::Size(700, 700));
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 700, 700);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 700, 700));
+
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+
+ 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(resource_manager_.get()));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ int size = 1 << 30;
+ layer->SetBounds(gfx::Size(size, size));
+ layer->draw_properties().visible_content_rect = 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(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+}
+
+class TiledLayerPartialUpdateTest : public TiledLayerTest {
+ public:
+ TiledLayerPartialUpdateTest() { settings_.max_partial_texture_updates = 4; }
+};
+
+TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) {
+ // Create one 300 x 200 tiled layer with 3 x 2 tiles.
+ gfx::Size content_bounds(300, 200);
+ gfx::Rect content_rect(content_bounds);
+
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(
+ new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ layer->SetBounds(content_bounds);
+ layer->SetPosition(gfx::PointF(0, 0));
+ layer->draw_properties().visible_content_rect = content_rect;
+ layer->InvalidateContentRect(content_rect);
+
+ layer_tree_host_->SetRootLayer(layer);
+ layer_tree_host_->SetViewportSize(gfx::Size(300, 200));
+
+ // Full update of all 6 tiles.
+ layer_tree_host_->UpdateLayers(queue_.get(),
+ std::numeric_limits<size_t>::max());
+ {
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ EXPECT_EQ(6u, queue_->FullUploadSize());
+ EXPECT_EQ(0u, queue_->PartialUploadSize());
+ UpdateTextures();
+ EXPECT_EQ(6, layer->fake_layer_updater()->update_count());
+ EXPECT_FALSE(queue_->HasMoreUpdates());
+ layer->fake_layer_updater()->ClearUpdateCount();
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Full update of 3 tiles and partial update of 3 tiles.
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 300, 150));
+ layer_tree_host_->UpdateLayers(queue_.get(),
+ std::numeric_limits<size_t>::max());
+ {
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ EXPECT_EQ(3u, queue_->FullUploadSize());
+ EXPECT_EQ(3u, queue_->PartialUploadSize());
+ UpdateTextures();
+ EXPECT_EQ(6, layer->fake_layer_updater()->update_count());
+ EXPECT_FALSE(queue_->HasMoreUpdates());
+ layer->fake_layer_updater()->ClearUpdateCount();
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Partial update of 6 tiles.
+ layer->InvalidateContentRect(gfx::Rect(50, 50, 200, 100));
+ {
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ layer_tree_host_->UpdateLayers(queue_.get(),
+ std::numeric_limits<size_t>::max());
+ EXPECT_EQ(2u, queue_->FullUploadSize());
+ EXPECT_EQ(4u, queue_->PartialUploadSize());
+ UpdateTextures();
+ EXPECT_EQ(6, layer->fake_layer_updater()->update_count());
+ EXPECT_FALSE(queue_->HasMoreUpdates());
+ layer->fake_layer_updater()->ClearUpdateCount();
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Checkerboard all tiles.
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 300, 200));
+ {
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Partial update of 6 checkerboard tiles.
+ layer->InvalidateContentRect(gfx::Rect(50, 50, 200, 100));
+ {
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ layer_tree_host_->UpdateLayers(queue_.get(),
+ std::numeric_limits<size_t>::max());
+ EXPECT_EQ(6u, queue_->FullUploadSize());
+ EXPECT_EQ(0u, queue_->PartialUploadSize());
+ UpdateTextures();
+ EXPECT_EQ(6, layer->fake_layer_updater()->update_count());
+ EXPECT_FALSE(queue_->HasMoreUpdates());
+ layer->fake_layer_updater()->ClearUpdateCount();
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Partial update of 4 tiles.
+ layer->InvalidateContentRect(gfx::Rect(50, 50, 100, 100));
+ {
+ scoped_ptr<FakeTiledLayerImpl> layer_impl =
+ make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
+ layer_tree_host_->UpdateLayers(queue_.get(),
+ std::numeric_limits<size_t>::max());
+ EXPECT_EQ(0u, queue_->FullUploadSize());
+ EXPECT_EQ(4u, queue_->PartialUploadSize());
+ UpdateTextures();
+ EXPECT_EQ(4, layer->fake_layer_updater()->update_count());
+ EXPECT_FALSE(queue_->HasMoreUpdates());
+ layer->fake_layer_updater()->ClearUpdateCount();
+ LayerPushPropertiesTo(layer.get(), layer_impl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ ResourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(),
+ resource_provider_.get());
+ layer_tree_host_->SetRootLayer(NULL);
+}
+
+TEST_F(TiledLayerTest, TilesPaintedWithoutOcclusion) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ RenderSurfaceLayerList render_surface_layer_list;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ CalcDrawProps(&render_surface_layer_list);
+
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), NULL);
+ EXPECT_EQ(2, layer->fake_layer_updater()->update_count());
+}
+
+TEST_F(TiledLayerTest, TilesPaintedWithOcclusion) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ RenderSurfaceLayerList render_surface_layer_list;
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100.
+
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ CalcDrawProps(&render_surface_layer_list);
+
+ occluded.SetOcclusion(gfx::Rect(200, 200, 300, 100));
+ layer->draw_properties().drawable_content_rect =
+ gfx::Rect(layer->content_bounds());
+ layer->draw_properties().visible_content_rect =
+ gfx::Rect(layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(36 - 3, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000, 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fake_layer_updater()->ClearUpdateCount();
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+
+ occluded.SetOcclusion(gfx::Rect(250, 200, 300, 100));
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(36 - 2, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 330000 + 340000,
+ 1);
+ EXPECT_EQ(3 + 2, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fake_layer_updater()->ClearUpdateCount();
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+
+ occluded.SetOcclusion(gfx::Rect(250, 250, 300, 100));
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(36, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 330000 + 340000 + 360000,
+ 1);
+ EXPECT_EQ(3 + 2, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, TilesPaintedWithOcclusionAndVisiblityConstraints) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ RenderSurfaceLayerList render_surface_layer_list;
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100.
+
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ CalcDrawProps(&render_surface_layer_list);
+
+ // 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->draw_properties().drawable_content_rect = gfx::Rect(0, 0, 600, 360);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 600, 360);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(24 - 3, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_uploaded_translucent(), 210000, 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fake_layer_updater()->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->draw_properties().drawable_content_rect = gfx::Rect(0, 0, 600, 350);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 600, 350);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(24 - 6, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 210000 + 180000,
+ 1);
+ EXPECT_EQ(3 + 6, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fake_layer_updater()->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->draw_properties().drawable_content_rect = gfx::Rect(0, 0, 600, 340);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 600, 340);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(24 - 6, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 210000 + 180000 + 180000,
+ 1);
+ EXPECT_EQ(3 + 6 + 6, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, TilesNotPaintedWithoutInvalidation) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ RenderSurfaceLayerList render_surface_layer_list;
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100.
+
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ CalcDrawProps(&render_surface_layer_list);
+
+ occluded.SetOcclusion(gfx::Rect(200, 200, 300, 100));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(0, 0, 600, 600);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 600, 600);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(36 - 3, layer->fake_layer_updater()->update_count());
+ { UpdateTextures(); }
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000, 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fake_layer_updater()->ClearUpdateCount();
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+
+ // Repaint without marking it dirty. The 3 culled tiles will be pre-painted
+ // now.
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(3, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000, 1);
+ EXPECT_EQ(6, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, TilesPaintedWithOcclusionAndTransforms) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ RenderSurfaceLayerList render_surface_layer_list;
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // 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_tree_host_->SetViewportSize(gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ CalcDrawProps(&render_surface_layer_list);
+ gfx::Transform screen_transform;
+ screen_transform.Scale(0.5, 0.5);
+ layer->draw_properties().screen_space_transform = screen_transform;
+ layer->draw_properties().target_space_transform = screen_transform;
+
+ occluded.SetOcclusion(gfx::Rect(100, 100, 150, 50));
+ layer->draw_properties().drawable_content_rect =
+ gfx::Rect(layer->content_bounds());
+ layer->draw_properties().visible_content_rect =
+ gfx::Rect(layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ EXPECT_EQ(36 - 3, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000, 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, TilesPaintedWithOcclusionAndScaling) {
+ scoped_refptr<FakeTiledLayer> layer =
+ new FakeTiledLayer(resource_manager_.get());
+ RenderSurfaceLayerList render_surface_layer_list;
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+
+ scoped_refptr<FakeTiledLayer> scale_layer =
+ new FakeTiledLayer(resource_manager_.get());
+ gfx::Transform scale_transform;
+ scale_transform.Scale(2.0, 2.0);
+ scale_layer->SetTransform(scale_transform);
+
+ layer_tree_host_->root_layer()->AddChild(scale_layer);
+
+ // The tile size is 100x100.
+
+ // This makes sure the painting works when the content space is scaled to
+ // a different layer space.
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600));
+ layer->SetAnchorPoint(gfx::PointF());
+ layer->SetBounds(gfx::Size(300, 300));
+ scale_layer->AddChild(layer);
+ CalcDrawProps(&render_surface_layer_list);
+ EXPECT_FLOAT_EQ(2.f, layer->contents_scale_x());
+ EXPECT_FLOAT_EQ(2.f, layer->contents_scale_y());
+ EXPECT_EQ(gfx::Size(600, 600).ToString(),
+ layer->content_bounds().ToString());
+
+ // No tiles are covered by the 300x50 occlusion.
+ occluded.SetOcclusion(gfx::Rect(200, 200, 300, 50));
+ layer->draw_properties().drawable_content_rect =
+ gfx::Rect(layer->bounds());
+ layer->draw_properties().visible_content_rect =
+ gfx::Rect(layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ int visible_tiles1 = 6 * 6;
+ EXPECT_EQ(visible_tiles1, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ visible_tiles1 * 100 * 100,
+ 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fake_layer_updater()->ClearUpdateCount();
+
+ // The occlusion of 300x100 will be cover 3 tiles as tiles are 100x100 still.
+ occluded.SetOcclusion(gfx::Rect(200, 200, 300, 100));
+ layer->draw_properties().drawable_content_rect =
+ gfx::Rect(layer->bounds());
+ layer->draw_properties().visible_content_rect =
+ gfx::Rect(layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ int visible_tiles2 = 6 * 6 - 3;
+ EXPECT_EQ(visible_tiles2, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ visible_tiles2 * 100 * 100 +
+ visible_tiles1 * 100 * 100,
+ 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fake_layer_updater()->ClearUpdateCount();
+
+ // This makes sure content scaling and transforms work together.
+ // When the tiles are scaled down by half, they are 50x50 each in the
+ // screen.
+ gfx::Transform screen_transform;
+ screen_transform.Scale(0.5, 0.5);
+ layer->draw_properties().screen_space_transform = screen_transform;
+ layer->draw_properties().target_space_transform = screen_transform;
+
+ // An occlusion of 150x100 will cover 3*2 = 6 tiles.
+ occluded.SetOcclusion(gfx::Rect(100, 100, 150, 100));
+
+ gfx::Rect layer_bounds_rect(layer->bounds());
+ layer->draw_properties().drawable_content_rect =
+ gfx::ScaleToEnclosingRect(layer_bounds_rect, 0.5f);
+ layer->draw_properties().visible_content_rect =
+ gfx::Rect(layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ int visible_tiles3 = 6 * 6 - 6;
+ EXPECT_EQ(visible_tiles3, layer->fake_layer_updater()->update_count());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ visible_tiles3 * 100 * 100 +
+ visible_tiles2 * 100 * 100 +
+ visible_tiles1 * 100 * 100,
+ 1);
+ EXPECT_EQ(6 + 3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, VisibleContentOpaqueRegion) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ RenderSurfaceLayerList render_surface_layer_list;
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+ layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles in
+ // various ways.
+
+ gfx::Rect opaque_paint_rect;
+ Region opaque_contents;
+
+ gfx::Rect content_bounds = gfx::Rect(0, 0, 100, 200);
+ gfx::Rect visible_bounds = gfx::Rect(0, 0, 100, 150);
+
+ layer->SetBounds(content_bounds.size());
+ CalcDrawProps(&render_surface_layer_list);
+ layer->draw_properties().drawable_content_rect = visible_bounds;
+ layer->draw_properties().visible_content_rect = visible_bounds;
+
+ // If the layer doesn't paint opaque content, then the
+ // VisibleContentOpaqueRegion should be empty.
+ layer->fake_layer_updater()->SetOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(content_bounds);
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ opaque_contents = layer->VisibleContentOpaqueRegion();
+ EXPECT_TRUE(opaque_contents.IsEmpty());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // VisibleContentOpaqueRegion should match the visible part of what is painted
+ // opaque.
+ opaque_paint_rect = gfx::Rect(10, 10, 90, 190);
+ layer->fake_layer_updater()->SetOpaquePaintRect(opaque_paint_rect);
+ layer->InvalidateContentRect(content_bounds);
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ UpdateTextures();
+ opaque_contents = layer->VisibleContentOpaqueRegion();
+ EXPECT_EQ(gfx::IntersectRects(opaque_paint_rect, visible_bounds).ToString(),
+ opaque_contents.ToString());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000 * 2, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 17100, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 20000 + 20000 - 17100,
+ 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // If we paint again without invalidating, the same stuff should be opaque.
+ layer->fake_layer_updater()->SetOpaquePaintRect(gfx::Rect());
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ UpdateTextures();
+ opaque_contents = layer->VisibleContentOpaqueRegion();
+ EXPECT_EQ(gfx::IntersectRects(opaque_paint_rect, visible_bounds).ToString(),
+ opaque_contents.ToString());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000 * 2, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 17100, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 20000 + 20000 - 17100,
+ 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // 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->fake_layer_updater()->SetOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 1, 1));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ UpdateTextures();
+ opaque_contents = layer->VisibleContentOpaqueRegion();
+ EXPECT_EQ(gfx::IntersectRects(opaque_paint_rect, visible_bounds).ToString(),
+ opaque_contents.ToString());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000 * 2 + 1, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 17100, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 20000 + 20000 - 17100 + 1,
+ 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // 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->fake_layer_updater()->SetOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(gfx::Rect(10, 10, 1, 1));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ UpdateTextures();
+ opaque_contents = layer->VisibleContentOpaqueRegion();
+ EXPECT_EQ(gfx::IntersectRects(gfx::Rect(10, 100, 90, 100),
+ visible_bounds).ToString(),
+ opaque_contents.ToString());
+
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_painted(), 20000 * 2 + 1 + 1, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 17100, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 20000 + 20000 - 17100 + 1 + 1,
+ 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, PixelsPaintedMetrics) {
+ scoped_refptr<FakeTiledLayer> layer =
+ make_scoped_refptr(new FakeTiledLayer(resource_manager_.get()));
+ RenderSurfaceLayerList render_surface_layer_list;
+ TestOcclusionTracker occluded;
+ occlusion_ = &occluded;
+ layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles in
+ // various ways.
+
+ gfx::Rect opaque_paint_rect;
+ Region opaque_contents;
+
+ gfx::Rect content_bounds = gfx::Rect(0, 0, 100, 300);
+ layer->SetBounds(content_bounds.size());
+ CalcDrawProps(&render_surface_layer_list);
+
+ // Invalidates and paints the whole layer.
+ layer->fake_layer_updater()->SetOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(content_bounds);
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ UpdateTextures();
+ opaque_contents = layer->VisibleContentOpaqueRegion();
+ EXPECT_TRUE(opaque_contents.IsEmpty());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 30000, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_uploaded_translucent(), 30000, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // 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->fake_layer_updater()->SetOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 1, 1));
+ layer->InvalidateContentRect(gfx::Rect(50, 200, 10, 10));
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+ layer->Update(queue_.get(), &occluded);
+ UpdateTextures();
+ opaque_contents = layer->VisibleContentOpaqueRegion();
+ EXPECT_TRUE(opaque_contents.IsEmpty());
+
+ // The middle tile was painted even though not invalidated.
+ EXPECT_NEAR(
+ occluded.overdraw_metrics()->pixels_painted(), 30000 + 60 * 210, 1);
+ // The pixels uploaded will not include the non-invalidated tile in the
+ // middle.
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(),
+ 30000 + 1 + 100,
+ 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) {
+ // Tile size is 100x100.
+ gfx::Rect root_rect(0, 0, 300, 200);
+ gfx::Rect child_rect(0, 0, 300, 100);
+ gfx::Rect child2_rect(0, 100, 300, 100);
+
+ scoped_refptr<FakeTiledLayer> root = make_scoped_refptr(
+ new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ scoped_refptr<Layer> surface = Layer::Create();
+ scoped_refptr<FakeTiledLayer> child = make_scoped_refptr(
+ new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ scoped_refptr<FakeTiledLayer> child2 = make_scoped_refptr(
+ new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+
+ root->SetBounds(root_rect.size());
+ root->SetAnchorPoint(gfx::PointF());
+ root->draw_properties().drawable_content_rect = root_rect;
+ root->draw_properties().visible_content_rect = root_rect;
+ root->AddChild(surface);
+
+ surface->SetForceRenderSurface(true);
+ surface->SetAnchorPoint(gfx::PointF());
+ surface->SetOpacity(0.5);
+ surface->AddChild(child);
+ surface->AddChild(child2);
+
+ child->SetBounds(child_rect.size());
+ child->SetAnchorPoint(gfx::PointF());
+ child->SetPosition(child_rect.origin());
+ child->draw_properties().visible_content_rect = child_rect;
+ child->draw_properties().drawable_content_rect = root_rect;
+
+ child2->SetBounds(child2_rect.size());
+ child2->SetAnchorPoint(gfx::PointF());
+ child2->SetPosition(child2_rect.origin());
+ child2->draw_properties().visible_content_rect = child2_rect;
+ child2->draw_properties().drawable_content_rect = root_rect;
+
+ layer_tree_host_->SetRootLayer(root);
+ layer_tree_host_->SetViewportSize(root_rect.size());
+
+ // With a huge memory limit, all layers should update and push their textures.
+ root->InvalidateContentRect(root_rect);
+ child->InvalidateContentRect(child_rect);
+ child2->InvalidateContentRect(child2_rect);
+ layer_tree_host_->UpdateLayers(queue_.get(),
+ std::numeric_limits<size_t>::max());
+ {
+ UpdateTextures();
+ EXPECT_EQ(6, root->fake_layer_updater()->update_count());
+ EXPECT_EQ(3, child->fake_layer_updater()->update_count());
+ EXPECT_EQ(3, child2->fake_layer_updater()->update_count());
+ EXPECT_FALSE(queue_->HasMoreUpdates());
+
+ root->fake_layer_updater()->ClearUpdateCount();
+ child->fake_layer_updater()->ClearUpdateCount();
+ child2->fake_layer_updater()->ClearUpdateCount();
+
+ scoped_ptr<FakeTiledLayerImpl> root_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), root->id()));
+ scoped_ptr<FakeTiledLayerImpl> child_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), child->id()));
+ scoped_ptr<FakeTiledLayerImpl> child2_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), child2->id()));
+ LayerPushPropertiesTo(child2.get(), child2_impl.get());
+ LayerPushPropertiesTo(child.get(), child_impl.get());
+ LayerPushPropertiesTo(root.get(), root_impl.get());
+
+ for (unsigned i = 0; i < 3; ++i) {
+ for (unsigned j = 0; j < 2; ++j)
+ EXPECT_TRUE(root_impl->HasResourceIdForTileAt(i, j));
+ EXPECT_TRUE(child_impl->HasResourceIdForTileAt(i, 0));
+ EXPECT_TRUE(child2_impl->HasResourceIdForTileAt(i, 0));
+ }
+ }
+ layer_tree_host_->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(root_rect);
+ child->InvalidateContentRect(child_rect);
+ child2->InvalidateContentRect(child2_rect);
+ layer_tree_host_->UpdateLayers(queue_.get(),
+ (3 * 2 + 3 * 1) * (100 * 100) * 4);
+ {
+ UpdateTextures();
+ EXPECT_EQ(6, root->fake_layer_updater()->update_count());
+ EXPECT_EQ(0, child->fake_layer_updater()->update_count());
+ EXPECT_EQ(0, child2->fake_layer_updater()->update_count());
+ EXPECT_FALSE(queue_->HasMoreUpdates());
+
+ root->fake_layer_updater()->ClearUpdateCount();
+ child->fake_layer_updater()->ClearUpdateCount();
+ child2->fake_layer_updater()->ClearUpdateCount();
+
+ scoped_ptr<FakeTiledLayerImpl> root_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), root->id()));
+ scoped_ptr<FakeTiledLayerImpl> child_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), child->id()));
+ scoped_ptr<FakeTiledLayerImpl> child2_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), child2->id()));
+ LayerPushPropertiesTo(child2.get(), child2_impl.get());
+ LayerPushPropertiesTo(child.get(), child_impl.get());
+ LayerPushPropertiesTo(root.get(), root_impl.get());
+
+ for (unsigned i = 0; i < 3; ++i) {
+ for (unsigned j = 0; j < 2; ++j)
+ EXPECT_TRUE(root_impl->HasResourceIdForTileAt(i, j));
+ EXPECT_FALSE(child_impl->HasResourceIdForTileAt(i, 0));
+ EXPECT_FALSE(child2_impl->HasResourceIdForTileAt(i, 0));
+ }
+ }
+ layer_tree_host_->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(root_rect);
+ child->InvalidateContentRect(child_rect);
+ child2->InvalidateContentRect(child2_rect);
+ layer_tree_host_->UpdateLayers(queue_.get(), (3 * 1) * (100 * 100) * 4);
+ {
+ UpdateTextures();
+ EXPECT_EQ(0, root->fake_layer_updater()->update_count());
+ EXPECT_EQ(0, child->fake_layer_updater()->update_count());
+ EXPECT_EQ(0, child2->fake_layer_updater()->update_count());
+ EXPECT_FALSE(queue_->HasMoreUpdates());
+
+ root->fake_layer_updater()->ClearUpdateCount();
+ child->fake_layer_updater()->ClearUpdateCount();
+ child2->fake_layer_updater()->ClearUpdateCount();
+
+ scoped_ptr<FakeTiledLayerImpl> root_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), root->id()));
+ scoped_ptr<FakeTiledLayerImpl> child_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), child->id()));
+ scoped_ptr<FakeTiledLayerImpl> child2_impl = make_scoped_ptr(
+ new FakeTiledLayerImpl(host_impl_->active_tree(), child2->id()));
+ LayerPushPropertiesTo(child2.get(), child2_impl.get());
+ LayerPushPropertiesTo(child.get(), child_impl.get());
+ LayerPushPropertiesTo(root.get(), root_impl.get());
+
+ for (unsigned i = 0; i < 3; ++i) {
+ for (unsigned j = 0; j < 2; ++j)
+ EXPECT_FALSE(root_impl->HasResourceIdForTileAt(i, j));
+ EXPECT_FALSE(child_impl->HasResourceIdForTileAt(i, 0));
+ EXPECT_FALSE(child2_impl->HasResourceIdForTileAt(i, 0));
+ }
+ }
+ layer_tree_host_->CommitComplete();
+
+ ResourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(),
+ resource_provider_.get());
+ layer_tree_host_->SetRootLayer(NULL);
+}
+
+class TrackingLayerPainter : public LayerPainter {
+ public:
+ static scoped_ptr<TrackingLayerPainter> Create() {
+ return make_scoped_ptr(new TrackingLayerPainter());
+ }
+
+ virtual void Paint(SkCanvas* canvas,
+ gfx::Rect content_rect,
+ gfx::RectF* opaque) OVERRIDE {
+ painted_rect_ = content_rect;
+ }
+
+ gfx::Rect PaintedRect() const { return painted_rect_; }
+ void ResetPaintedRect() { painted_rect_ = gfx::Rect(); }
+
+ private:
+ gfx::Rect painted_rect_;
+};
+
+class UpdateTrackingTiledLayer : public FakeTiledLayer {
+ public:
+ explicit UpdateTrackingTiledLayer(PrioritizedResourceManager* manager)
+ : FakeTiledLayer(manager) {
+ scoped_ptr<TrackingLayerPainter> painter(TrackingLayerPainter::Create());
+ tracking_layer_painter_ = painter.get();
+ layer_updater_ =
+ BitmapContentLayerUpdater::Create(painter.PassAs<LayerPainter>(),
+ &stats_instrumentation_,
+ 0);
+ }
+
+ TrackingLayerPainter* tracking_layer_painter() const {
+ return tracking_layer_painter_;
+ }
+
+ private:
+ virtual LayerUpdater* Updater() const OVERRIDE {
+ return layer_updater_.get();
+ }
+ virtual ~UpdateTrackingTiledLayer() {}
+
+ TrackingLayerPainter* tracking_layer_painter_;
+ scoped_refptr<BitmapContentLayerUpdater> layer_updater_;
+ FakeRenderingStatsInstrumentation stats_instrumentation_;
+};
+
+TEST_F(TiledLayerTest, NonIntegerContentsScaleIsNotDistortedDuringPaint) {
+ scoped_refptr<UpdateTrackingTiledLayer> layer =
+ make_scoped_refptr(new UpdateTrackingTiledLayer(resource_manager_.get()));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ gfx::Rect layer_rect(0, 0, 30, 31);
+ layer->SetPosition(layer_rect.origin());
+ layer->SetBounds(layer_rect.size());
+ layer->UpdateContentsScale(1.5f);
+
+ gfx::Rect content_rect(0, 0, 45, 47);
+ EXPECT_EQ(content_rect.size(), layer->content_bounds());
+ layer->draw_properties().visible_content_rect = content_rect;
+ layer->draw_properties().drawable_content_rect = content_rect;
+
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+
+ // Update the whole tile.
+ layer->Update(queue_.get(), NULL);
+ layer->tracking_layer_painter()->ResetPaintedRect();
+
+ EXPECT_RECT_EQ(gfx::Rect(), layer->tracking_layer_painter()->PaintedRect());
+ UpdateTextures();
+
+ // Invalidate the entire layer in content space. When painting, the rect given
+ // to webkit should match the layer's bounds.
+ layer->InvalidateContentRect(content_rect);
+ layer->Update(queue_.get(), NULL);
+
+ EXPECT_RECT_EQ(layer_rect, layer->tracking_layer_painter()->PaintedRect());
+}
+
+TEST_F(TiledLayerTest,
+ NonIntegerContentsScaleIsNotDistortedDuringInvalidation) {
+ scoped_refptr<UpdateTrackingTiledLayer> layer =
+ make_scoped_refptr(new UpdateTrackingTiledLayer(resource_manager_.get()));
+
+ layer_tree_host_->root_layer()->AddChild(layer);
+
+ gfx::Rect layer_rect(0, 0, 30, 31);
+ layer->SetPosition(layer_rect.origin());
+ layer->SetBounds(layer_rect.size());
+ layer->UpdateContentsScale(1.3f);
+
+ gfx::Rect content_rect(layer->content_bounds());
+ layer->draw_properties().visible_content_rect = content_rect;
+ layer->draw_properties().drawable_content_rect = content_rect;
+
+ layer->SetTexturePriorities(priority_calculator_);
+ resource_manager_->PrioritizeTextures();
+ layer->SavePaintProperties();
+
+ // Update the whole tile.
+ layer->Update(queue_.get(), NULL);
+ layer->tracking_layer_painter()->ResetPaintedRect();
+
+ EXPECT_RECT_EQ(gfx::Rect(), layer->tracking_layer_painter()->PaintedRect());
+ UpdateTextures();
+
+ // Invalidate the entire layer in layer space. When painting, the rect given
+ // to webkit should match the layer's bounds.
+ layer->SetNeedsDisplayRect(layer_rect);
+ layer->Update(queue_.get(), NULL);
+
+ EXPECT_RECT_EQ(layer_rect, layer->tracking_layer_painter()->PaintedRect());
+}
+
+} // namespace
+} // namespace cc