|  | // 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 "base/run_loop.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_output_surface_client.h" | 
|  | #include "cc/test/fake_proxy.h" | 
|  | #include "cc/test/geometry_test_utils.h" | 
|  | #include "cc/test/test_shared_bitmap_manager.h" | 
|  | #include "cc/test/tiled_layer_test_common.h" | 
|  | #include "cc/trees/occlusion_tracker.h" | 
|  | #include "cc/trees/single_thread_proxy.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "ui/gfx/geometry/rect_conversions.h" | 
|  | #include "ui/gfx/transform.h" | 
|  |  | 
|  | namespace cc { | 
|  | namespace { | 
|  |  | 
|  | class TestOcclusionTracker : public OcclusionTracker<Layer> { | 
|  | public: | 
|  | TestOcclusionTracker() : OcclusionTracker(gfx::Rect(0, 0, 1000, 1000)) { | 
|  | stack_.push_back(StackObject()); | 
|  | } | 
|  |  | 
|  | void SetRenderTarget(Layer* render_target) { | 
|  | stack_.back().target = render_target; | 
|  | } | 
|  |  | 
|  | void SetOcclusion(const SimpleEnclosedRegion& occlusion) { | 
|  | stack_.back().occlusion_from_inside_target = occlusion; | 
|  | } | 
|  | }; | 
|  |  | 
|  | class SynchronousOutputSurfaceClient : public FakeLayerTreeHostClient { | 
|  | public: | 
|  | SynchronousOutputSurfaceClient() | 
|  | : FakeLayerTreeHostClient(FakeLayerTreeHostClient::DIRECT_3D) {} | 
|  |  | 
|  | bool EnsureOutputSurfaceCreated() { | 
|  | base::MessageLoop::current()->PostDelayedTask( | 
|  | FROM_HERE, | 
|  | run_loop_.QuitClosure(), | 
|  | base::TimeDelta::FromSeconds(5)); | 
|  | run_loop_.Run(); | 
|  | return output_surface_created_; | 
|  | } | 
|  |  | 
|  | void DidInitializeOutputSurface() override { | 
|  | FakeLayerTreeHostClient::DidInitializeOutputSurface(); | 
|  | output_surface_created_ = true; | 
|  | run_loop_.Quit(); | 
|  | } | 
|  |  | 
|  | void DidFailToInitializeOutputSurface() override { | 
|  | FakeLayerTreeHostClient::DidFailToInitializeOutputSurface(); | 
|  | output_surface_created_ = false; | 
|  | run_loop_.Quit(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool output_surface_created_; | 
|  | base::RunLoop run_loop_; | 
|  | }; | 
|  |  | 
|  | class TiledLayerTest : public testing::Test { | 
|  | public: | 
|  | TiledLayerTest() | 
|  | : proxy_(nullptr), | 
|  | output_surface_(FakeOutputSurface::Create3d()), | 
|  | queue_(make_scoped_ptr(new ResourceUpdateQueue)), | 
|  | impl_thread_("ImplThread"), | 
|  | occlusion_(nullptr) { | 
|  | settings_.max_partial_texture_updates = std::numeric_limits<size_t>::max(); | 
|  | settings_.layer_transforms_should_scale_layer_contents = true; | 
|  | settings_.verify_property_trees = true; | 
|  | } | 
|  |  | 
|  | void SetUp() override { | 
|  | impl_thread_.Start(); | 
|  | shared_bitmap_manager_.reset(new TestSharedBitmapManager()); | 
|  | layer_tree_host_ = LayerTreeHost::CreateThreaded( | 
|  | &synchonous_output_surface_client_, shared_bitmap_manager_.get(), | 
|  | nullptr, settings_, base::MessageLoopProxy::current(), | 
|  | impl_thread_.message_loop_proxy(), nullptr); | 
|  | synchonous_output_surface_client_.SetLayerTreeHost(layer_tree_host_.get()); | 
|  | proxy_ = layer_tree_host_->proxy(); | 
|  | resource_manager_ = PrioritizedResourceManager::Create(proxy_); | 
|  | layer_tree_host_->SetLayerTreeHostClientReady(); | 
|  | CHECK(synchonous_output_surface_client_.EnsureOutputSurfaceCreated()); | 
|  |  | 
|  | layer_tree_host_->SetRootLayer(Layer::Create()); | 
|  |  | 
|  | CHECK(output_surface_->BindToClient(&output_surface_client_)); | 
|  |  | 
|  | DebugScopedSetImplThreadAndMainThreadBlocked | 
|  | impl_thread_and_main_thread_blocked(proxy_); | 
|  | resource_provider_ = ResourceProvider::Create(output_surface_.get(), | 
|  | shared_bitmap_manager_.get(), | 
|  | nullptr, | 
|  | nullptr, | 
|  | 0, | 
|  | false, | 
|  | 1); | 
|  | host_impl_ = make_scoped_ptr( | 
|  | new FakeLayerTreeHostImpl(proxy_, shared_bitmap_manager_.get())); | 
|  | } | 
|  |  | 
|  | ~TiledLayerTest() override { | 
|  | ResourceManagerClearAllMemory(resource_manager_.get(), | 
|  | resource_provider_.get()); | 
|  |  | 
|  | DebugScopedSetImplThreadAndMainThreadBlocked | 
|  | impl_thread_and_main_thread_blocked(proxy_); | 
|  | resource_provider_ = nullptr; | 
|  | host_impl_ = nullptr; | 
|  | } | 
|  |  | 
|  | 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(nullptr, | 
|  | 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_; | 
|  | FakeOutputSurfaceClient output_surface_client_; | 
|  | scoped_ptr<OutputSurface> output_surface_; | 
|  | scoped_ptr<SharedBitmapManager> shared_bitmap_manager_; | 
|  | scoped_ptr<ResourceProvider> resource_provider_; | 
|  | scoped_ptr<ResourceUpdateQueue> queue_; | 
|  | PriorityCalculator priority_calculator_; | 
|  | base::Thread impl_thread_; | 
|  | SynchronousOutputSurfaceClient synchonous_output_surface_client_; | 
|  | scoped_ptr<LayerTreeHost> layer_tree_host_; | 
|  | scoped_ptr<FakeLayerTreeHostImpl> host_impl_; | 
|  | scoped_ptr<PrioritizedResourceManager> resource_manager_; | 
|  | TestOcclusionTracker* occlusion_; | 
|  | }; | 
|  |  | 
|  | TEST_F(TiledLayerTest, PushDirtyTiles) { | 
|  | layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000)); | 
|  |  | 
|  | 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, Scale) { | 
|  | layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000)); | 
|  |  | 
|  | layer_tree_host_->SetDeviceScaleFactor(1.5); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | layer->SetBounds(gfx::Size(100, 200)); | 
|  | CalcDrawProps(&render_surface_layer_list); | 
|  |  | 
|  | // Change the width so that it doesn't divide cleanly by the scale. | 
|  | layer->SetBounds(gfx::Size(101, 200)); | 
|  | UpdateAndPush(layer, layer_impl); | 
|  |  | 
|  | EXPECT_EQ(1.5, layer->fake_layer_updater()->last_contents_width_scale()); | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | // 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(SimpleEnclosedRegion(gfx::Rect(0, 0, 50, 50))); | 
|  | CalcDrawProps(&render_surface_layer_list); | 
|  | UpdateAndPush(layer, layer_impl); | 
|  |  | 
|  | // 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) { | 
|  | layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000)); | 
|  |  | 
|  | 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(SimpleEnclosedRegion(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); | 
|  |  | 
|  | EXPECT_TRUE(layer_impl->HasResourceIdForTileAt(0, 0)); | 
|  | } | 
|  |  | 
|  | 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(), nullptr); | 
|  | 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, 150, 250); | 
|  |  | 
|  | layer->SetBounds(layer_bounds.size()); | 
|  | layer->SetContentBounds(content_bounds.size()); | 
|  | layer->draw_properties().visible_content_rect = content_bounds; | 
|  | layer->draw_properties().contents_scale_x = .5f; | 
|  | layer->draw_properties().contents_scale_y = 1.25f; | 
|  |  | 
|  | // 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(), nullptr); | 
|  |  | 
|  | // Update rect is 200x300 (tile size of 100x100). Scaled this gives 400x240. | 
|  | EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 400, 240), 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(), nullptr); | 
|  | 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(), nullptr); | 
|  | EXPECT_FLOAT_RECT_EQ(gfx::RectF(60, 80, 20, 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(), nullptr); | 
|  | 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(), nullptr); | 
|  | 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(), nullptr); | 
|  | 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_->contents_texture_manager()->SetMaxMemoryLimitBytes( | 
|  | memory_limit); | 
|  |  | 
|  | layer_tree_host_->UpdateLayers(queue_.get()); | 
|  |  | 
|  | // 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()); | 
|  | EXPECT_FALSE(root_layer->SkipsDraw()); | 
|  |  | 
|  | ResourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(), | 
|  | resource_provider_.get()); | 
|  | layer_tree_host_->SetRootLayer(nullptr); | 
|  | } | 
|  |  | 
|  | 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(), nullptr); | 
|  |  | 
|  | 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(), nullptr); | 
|  | } | 
|  |  | 
|  | 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()); | 
|  | { | 
|  | 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()); | 
|  | { | 
|  | 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()); | 
|  | 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()); | 
|  | 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()); | 
|  | 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(nullptr); | 
|  | } | 
|  |  | 
|  | TEST_F(TiledLayerTest, TilesPaintedWithoutOcclusion) { | 
|  | layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000)); | 
|  |  | 
|  | 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(), nullptr); | 
|  | 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(SimpleEnclosedRegion(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()); | 
|  |  | 
|  | layer->fake_layer_updater()->ClearUpdateCount(); | 
|  | layer->SetTexturePriorities(priority_calculator_); | 
|  | resource_manager_->PrioritizeTextures(); | 
|  |  | 
|  | occluded.SetOcclusion(SimpleEnclosedRegion(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()); | 
|  |  | 
|  | layer->fake_layer_updater()->ClearUpdateCount(); | 
|  | layer->SetTexturePriorities(priority_calculator_); | 
|  | resource_manager_->PrioritizeTextures(); | 
|  |  | 
|  | occluded.SetOcclusion(SimpleEnclosedRegion(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()); | 
|  | } | 
|  |  | 
|  | 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(SimpleEnclosedRegion(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()); | 
|  |  | 
|  | 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(SimpleEnclosedRegion(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()); | 
|  |  | 
|  | layer->fake_layer_updater()->ClearUpdateCount(); | 
|  |  | 
|  | // Now the visible region is even smaller than the occlusion, it should have | 
|  | // the same result. | 
|  | occluded.SetOcclusion(SimpleEnclosedRegion(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()); | 
|  | } | 
|  |  | 
|  | 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(SimpleEnclosedRegion(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(); | 
|  |  | 
|  | 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()); | 
|  | } | 
|  |  | 
|  | 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(SimpleEnclosedRegion(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()); | 
|  | } | 
|  |  | 
|  | 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->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(SimpleEnclosedRegion(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()); | 
|  |  | 
|  | layer->fake_layer_updater()->ClearUpdateCount(); | 
|  |  | 
|  | // The occlusion of 300x100 will be cover 3 tiles as tiles are 100x100 still. | 
|  | occluded.SetOcclusion(SimpleEnclosedRegion(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()); | 
|  |  | 
|  | 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(SimpleEnclosedRegion(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()); | 
|  | } | 
|  |  | 
|  | 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->draw_properties().drawable_content_rect = root_rect; | 
|  | root->draw_properties().visible_content_rect = root_rect; | 
|  | root->AddChild(surface); | 
|  |  | 
|  | surface->SetForceRenderSurface(true); | 
|  | surface->SetOpacity(0.5); | 
|  | surface->AddChild(child); | 
|  | surface->AddChild(child2); | 
|  |  | 
|  | child->SetBounds(child_rect.size()); | 
|  | 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->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()); | 
|  | { | 
|  | 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); | 
|  |  | 
|  | size_t memory_limit = (3 * 2 + 3 * 1) * (100 * 100) * 4; | 
|  | layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( | 
|  | memory_limit); | 
|  | layer_tree_host_->UpdateLayers(queue_.get()); | 
|  | { | 
|  | 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); | 
|  |  | 
|  | memory_limit = (3 * 1) * (100 * 100) * 4; | 
|  | layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( | 
|  | memory_limit); | 
|  | layer_tree_host_->UpdateLayers(queue_.get()); | 
|  | { | 
|  | 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(nullptr); | 
|  | } | 
|  |  | 
|  | class TrackingLayerPainter : public LayerPainter { | 
|  | public: | 
|  | static scoped_ptr<TrackingLayerPainter> Create() { | 
|  | return make_scoped_ptr(new TrackingLayerPainter()); | 
|  | } | 
|  |  | 
|  | void Paint(SkCanvas* canvas, const gfx::Rect& content_rect) 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.Pass(), 0); | 
|  | } | 
|  |  | 
|  | TrackingLayerPainter* tracking_layer_painter() const { | 
|  | return tracking_layer_painter_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | LayerUpdater* Updater() const override { return layer_updater_.get(); } | 
|  | ~UpdateTrackingTiledLayer() override {} | 
|  |  | 
|  | TrackingLayerPainter* tracking_layer_painter_; | 
|  | scoped_refptr<BitmapContentLayerUpdater> layer_updater_; | 
|  | }; | 
|  |  | 
|  | 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(), nullptr); | 
|  | layer->tracking_layer_painter()->ResetPaintedRect(); | 
|  |  | 
|  | EXPECT_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(), nullptr); | 
|  |  | 
|  | // Rounding leads to an extra pixel. | 
|  | gfx::Rect expanded_layer_rect(layer_rect); | 
|  | expanded_layer_rect.set_height(32); | 
|  | EXPECT_EQ(expanded_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(), nullptr); | 
|  | layer->tracking_layer_painter()->ResetPaintedRect(); | 
|  |  | 
|  | EXPECT_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(), nullptr); | 
|  |  | 
|  | // Rounding leads to an extra pixel. | 
|  | gfx::Rect expanded_layer_rect(layer_rect); | 
|  | expanded_layer_rect.set_height(32); | 
|  | EXPECT_EQ(expanded_layer_rect, | 
|  | layer->tracking_layer_painter()->PaintedRect()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace cc |