diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-17 13:57:45 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-19 13:44:40 +0000 |
commit | 6ec7b8da05d21a3878bd21c691b41e675d74bb1c (patch) | |
tree | b87f250bc19413750b9bb9cdbf2da20ef5014820 /chromium/cc | |
parent | ec02ee4181c49b61fce1c8fb99292dbb8139cc90 (diff) | |
download | qtwebengine-chromium-6ec7b8da05d21a3878bd21c691b41e675d74bb1c.tar.gz |
BASELINE: Update Chromium to 60.0.3112.70
Change-Id: I9911c2280a014d4632f254857876a395d4baed2d
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/cc')
365 files changed, 17936 insertions, 11488 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index f791854447d..18e185613e9 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -48,6 +48,7 @@ cc_component("cc") { "input/selection.h", "input/single_scrollbar_animation_controller_thinning.cc", "input/single_scrollbar_animation_controller_thinning.h", + "input/touch_action.h", "layers/append_quads_data.cc", "layers/append_quads_data.h", "layers/content_layer_client.h", @@ -79,7 +80,6 @@ cc_component("cc") { "layers/nine_patch_layer.h", "layers/nine_patch_layer_impl.cc", "layers/nine_patch_layer_impl.h", - "layers/paint_properties.h", "layers/painted_overlay_scrollbar_layer.cc", "layers/painted_overlay_scrollbar_layer.h", "layers/painted_overlay_scrollbar_layer_impl.cc", @@ -519,6 +519,8 @@ cc_static_library("test_support") { "test/begin_frame_args_test.h", "test/begin_frame_source_test.cc", "test/begin_frame_source_test.h", + "test/compositor_frame_helpers.cc", + "test/compositor_frame_helpers.h", "test/fake_compositor_frame_sink.cc", "test/fake_compositor_frame_sink.h", "test/fake_compositor_frame_sink_client.cc", @@ -566,6 +568,8 @@ cc_static_library("test_support") { "test/fake_scoped_ui_resource.h", "test/fake_scrollbar.cc", "test/fake_scrollbar.h", + "test/fake_surface_resource_holder_client.cc", + "test/fake_surface_resource_holder_client.h", "test/fake_tile_manager.cc", "test/fake_tile_manager.h", "test/fake_tile_manager_client.cc", @@ -588,6 +592,8 @@ cc_static_library("test_support") { "test/layer_tree_pixel_test.h", "test/layer_tree_test.cc", "test/layer_tree_test.h", + "test/mock_compositor_frame_sink_support_client.cc", + "test/mock_compositor_frame_sink_support_client.h", "test/mock_helper.h", "test/mock_occlusion_tracker.h", "test/ordered_simple_task_runner.cc", @@ -620,6 +626,7 @@ cc_static_library("test_support") { "test/stub_layer_tree_host_client.h", "test/stub_layer_tree_host_single_thread_client.cc", "test/stub_layer_tree_host_single_thread_client.h", + "test/stub_surface_factory_client.h", "test/surface_aggregator_test_helpers.cc", "test/surface_aggregator_test_helpers.h", "test/surface_hittest_test_helpers.cc", @@ -647,6 +654,8 @@ cc_static_library("test_support") { "test/test_occlusion_tracker.h", "test/test_shared_bitmap_manager.cc", "test/test_shared_bitmap_manager.h", + "test/test_skcanvas.cc", + "test/test_skcanvas.h", "test/test_task_graph_runner.cc", "test/test_task_graph_runner.h", "test/test_texture.cc", @@ -704,7 +713,6 @@ cc_test("cc_unittests") { "base/index_rect_unittest.cc", "base/list_container_unittest.cc", "base/math_util_unittest.cc", - "base/random_access_list_container_unittest.cc", "base/region_unittest.cc", "base/rolling_time_delta_history_unittest.cc", "base/rtree_unittest.cc", @@ -764,6 +772,7 @@ cc_test("cc_unittests") { "output/texture_mailbox_deleter_unittest.cc", "paint/discardable_image_map_unittest.cc", "paint/display_item_list_unittest.cc", + "paint/paint_op_buffer_unittest.cc", "quads/draw_polygon_unittest.cc", "quads/draw_quad_unittest.cc", "quads/nine_patch_generator_unittest.cc", @@ -849,11 +858,11 @@ cc_test("cc_unittests") { "surfaces/display_unittest.cc", "surfaces/referenced_surface_tracker_unittest.cc", "surfaces/surface_aggregator_unittest.cc", - "surfaces/surface_factory_unittest.cc", "surfaces/surface_hittest_unittest.cc", "surfaces/surface_manager_ref_unittest.cc", "surfaces/surface_manager_unittest.cc", "surfaces/surface_sequence_generator_unittest.cc", + "surfaces/surface_synchronization_unittest.cc", "surfaces/surface_unittest.cc", "surfaces/surfaces_pixeltest.cc", diff --git a/chromium/cc/OWNERS b/chromium/cc/OWNERS index 5a30b1399e8..23652bf0d5d 100644 --- a/chromium/cc/OWNERS +++ b/chromium/cc/OWNERS @@ -52,6 +52,9 @@ weiliangc@chromium.org vollick@chromium.org ajuma@chromium.org +# property trees +chrishtr@chromium.org + # we miss you # jamesr@chromium.org # nduca@chromium.org diff --git a/chromium/cc/README.md b/chromium/cc/README.md new file mode 100644 index 00000000000..8180794bfcb --- /dev/null +++ b/chromium/cc/README.md @@ -0,0 +1,199 @@ +# cc/ + +This directory contains a compositor, used in both the renderer and the +browser. In the renderer, Blink is the client. In the browser, both +ui and Android browser compositor are the clients. + +The public API of the compositor is LayerTreeHost and Layer and its +derived types. Embedders create a LayerTreeHost (single, multithreaded, +or synchronous) and then attach a tree of Layers to it. + +When Layers are updated they request a commit, which takes the data +of and structure of the tree of Layers and the data of its host and +atomically pushes it to a tree of LayerImpls and a LayerTreeHostImpl +and LayerTreeImpl. The main thread (which owns the tree of Layers +and the embedder) is blocked during this commit operation. + +The commit is from the main thread Layer tree to the pending tree in +multithreaded mode. The pending tree is a staging tree for +rasterization. When enough rasterization has completed for +invalidations and the pending tree is ready to activate. Activate is an +analogous operation to commit, and pushes data from the pending tree to +the active tree. The pending tree exists so that all the of the updates +from the main thread can be displayed to the user atomically while +the previous frame can be scrolled or animated. + +The single threaded compositor commits directly to the active +tree and then stops drawing until the content is ready to be drawn. + +The active tree is responsible for drawing. The Scheduler and its +SchedulerStateMachine decide when to draw (along with when to commit, +etc etc). "Drawing" in a compositor consists of LayerImpl::AppendQuads +which batches up a set of DrawQuads and RenderPasses into a +CompositorFrame which is sent via a CompositorFrameSink. + +CompositorFrames from individual compositors are sent to the +SurfaceManager (currently in the browser process). The +SurfaceAggregator combines all CompositorFrames together and asks +the Display to finally draw the frame via Renderer, which is either +a GLRenderer or a SoftwareRenderer, which finally draws the entire +composited browser contents into a backbuffer or a bitmap, respectively. + +Design documents for the graphics stack can be found at +[chromium-graphics](https://www.chromium.org/developers/design-documents/chromium-graphics). + +## Glossaries + +### Active CompositorFrame + +### Active Tree +The set of layers and property trees that was/will be used to submit a +CompositorFrame from the layer compositor. Composited effects such as scrolling, +pinch, and animations are done by modifying the active tree, which allows for +producing and submitting a new CompositorFrame. + +### CompositorFrame +A set of RenderPasses (which are a list of DrawQuads) along with metadata. +Conceptually this is the instructions (transforms, texture ids, etc) for how to +draw an entire scene which will be presented in a surface. + +### CopyOutputRequest (or Copy Request) +A request for a texture (or bitmap) copy of some part of the compositor's +output. Such requests force the compositor to use a separate RenderPass for the +content to be copied, which allows it to do the copy operation once the +RenderPass has been drawn to. + +### ElementID +Chosen by cc's clients and can be used as a stable identifier across updates. +For example, blink uses ElementIDs as a stable id for the object (opaque to cc) +that is responsible for a composited animation. Some additional information in +[element_id.h](https://codesearch.chromium.org/chromium/src/cc/trees/element_id.h) + +### DirectRenderer +An abstraction that provides an API for the Display to draw a fully-aggregated +CompositorFrame to a physical output. Subclasses of it provide implementations +for various backends, currently GL or Software. + +### Layer +A conceptual piece of content that can appear on screen and has some known +position with respect to the viewport. The Layer class only is used on the +main thread. This, along with LayerTreeHost, is the main API for the +compositor. + +### LayerImpl +The same as Layer, but on the compositor thread. + +### LayerTree + +### Occlusion Culling +Avoiding work by skipping over things which are not visible due to being +occluded (hidden from sight by other opaque things in front of them). Most +commonly refers to skipping drawing (ie culling) of DrawQuads when other +DrawQuads will be in front and occluding them. + +### Property Trees + +See also presentations on [Compositor Property Trees](https://docs.google.com/presentation/d/1V7gCqKR-edNdRDv0bDnJa_uEs6iARAU2h5WhgxHyejQ/preview) +and [Blink Property Trees](https://docs.google.com/presentation/u/1/d/1ak7YVrJITGXxqQ7tyRbwOuXB1dsLJlfpgC4wP7lykeo/preview). + +### Display +A controller class that takes CompositorFrames for each surface and draws them +to a physical output. + +### Draw +Filling pixels in a physical output (technically could be to an offscreen +texture), but this is the final output of the display compositor. + +### DrawQuad +A unit of work for drawing. Each DrawQuad has its own texture id, transform, +offset, etc. + +### Shared Quad State +A shared set of states used by multiple draw quads. DrawQuads that are linked to +the same shared quad state will all use the same properties from it, with the +addition of things found on their individual DrawQuad structures. + +### Render Pass +A list of DrawQuads which will all be drawn together into the same render target +(either a texture or physical output). Most times all DrawQuads are part of a +single RenderPass. Additional RenderPasses are used for effects that require a +set of DrawQuads to be drawn together into a buffer first, with the effect +applied then to the buffer instead of each individual DrawQuad. + +### Render Surface +Synonym for RenderPass now. Historically part of the Layer tree data structures, +with a 1:1 mapping to RenderPasses. RenderSurfaceImpl is a legacy piece that +remains. + +### Surface + +### Record + +### Raster + +### Paint + +### Pending CompositorFrame + +### Pending Tree +The set of layers and property trees that is generated from a main frame (or +BeginMainFrame, or commit). The pending tree exists to do raster work in the +layer compositor without clobbering the active tree until it is done. This +allows the active tree to be used in the meantime. + +### Composite +To produce a single graphical output from multiple inputs. In practice, the +layer compositor does raster from recordings and manages memory, performs +composited effects such as scrolling, pinch, animations, producing a +CompositorFrame. The display compositor does an actual "composite" to draw the +final output into a single physical output. + +### Invalidation +Invalidation is a unit of content update. Any content updates from +Blink or ui must be accompanied by an invalidation to tell the compositor +that a piece of content must be rerasterized. For example, if a 10x10 +div with a background color has its width increased by 5 pixels, then +there will be a 5x10 invalidation (at least) for the new space covered +by the larger div. + +Ideally, invalidations represent the minimum amount of content that must +be rerastered from the previous frame. They are passed to the compositor +via Layer::SetNeedsDisplay(Rect). Invalidation is tracked both to +minimize the amount of raster work needed, but also to allow for +partial raster of Tiles. Invalidations also eventually become damage. + +### Damage +Damage is the equivalent of invalidation, but for the final display. +As invalidation is the difference between two frames worth of content, +damage is the difference between two CompositorFrames. Damage is +tracked via the DamageTracker. This allows for partial swap, where +only the parts of the final CompositorFrame that touch the screen +are drawn, and only that drawn portion is swapped, which saves quite +a bit of power for small bits of damage. + +Invalidation creates damage, in that if a piece of content updates, then +that content invalidation creates damage on screen. Other things that +cause damage are analogous operations to invalidations, but on Layers. +For example, moving a Layer around, changing properties of Layers (e.g. +opacity), and adding/removing/reordering Layers will all create damage +(aka screen updates) but do not create invalidations (aka raster work). + +### Tiles +An abstraction of a piece of content of a Layer. A tile may be +rasterized or not. It may be known to be a solid color or not. +A PictureLayerImpl indirectly owns a sparse set of Tiles to +represent its rasterizable content. When tiles are invalidated, +they are replaced with new tiles. + +### Prepare Tiles +Prioritize and schedule needed tiles for raster. This is the entry point to a +system that converts painting (raster sources / recording sources) into +rasterized resources that live on tiles. This also kicks off any dependent image +decodes for images that need to be decode for the raster to take place. + +### Device Scale Factor +The scale at which we want to display content on the output device. For very +high resolution monitors, everything would become too small if just presented +1:1 with the pixels. So we use a larger number of physical pixels per logical +pixels. This ratio is the device scale factor. 1 or 2 is the most common on +ChromeOS. Values between 1 and 2 are common on Windows. diff --git a/chromium/cc/animation/animation_host.cc b/chromium/cc/animation/animation_host.cc index 80dfae1670c..d56e7585711 100644 --- a/chromium/cc/animation/animation_host.cc +++ b/chromium/cc/animation/animation_host.cc @@ -528,10 +528,12 @@ void AnimationHost::ImplOnlyScrollAnimationCreate( ElementId element_id, const gfx::ScrollOffset& target_offset, const gfx::ScrollOffset& current_offset, - base::TimeDelta delayed_by) { + base::TimeDelta delayed_by, + base::TimeDelta animation_start_offset) { DCHECK(scroll_offset_animations_impl_); scroll_offset_animations_impl_->ScrollAnimationCreate( - element_id, target_offset, current_offset, delayed_by); + element_id, target_offset, current_offset, delayed_by, + animation_start_offset); } bool AnimationHost::ImplOnlyScrollAnimationUpdateTarget( diff --git a/chromium/cc/animation/animation_host.h b/chromium/cc/animation/animation_host.h index 5e4f8e077cb..31e45e69332 100644 --- a/chromium/cc/animation/animation_host.h +++ b/chromium/cc/animation/animation_host.h @@ -155,10 +155,12 @@ class CC_ANIMATION_EXPORT AnimationHost bool HasAnyAnimation(ElementId element_id) const override; bool HasTickingAnimationForTesting(ElementId element_id) const override; - void ImplOnlyScrollAnimationCreate(ElementId element_id, - const gfx::ScrollOffset& target_offset, - const gfx::ScrollOffset& current_offset, - base::TimeDelta delayed_by) override; + void ImplOnlyScrollAnimationCreate( + ElementId element_id, + const gfx::ScrollOffset& target_offset, + const gfx::ScrollOffset& current_offset, + base::TimeDelta delayed_by, + base::TimeDelta animation_start_offset) override; bool ImplOnlyScrollAnimationUpdateTarget( ElementId element_id, const gfx::Vector2dF& scroll_delta, diff --git a/chromium/cc/animation/animation_host_unittest.cc b/chromium/cc/animation/animation_host_unittest.cc index 4ae691a64e2..fdaefc7ba09 100644 --- a/chromium/cc/animation/animation_host_unittest.cc +++ b/chromium/cc/animation/animation_host_unittest.cc @@ -87,7 +87,8 @@ TEST_F(AnimationHostTest, ImplOnlyScrollAnimationUpdateTargetIfDetached) { gfx::ScrollOffset target_offset(0., 2.); gfx::ScrollOffset current_offset(0., 1.); host_impl_->ImplOnlyScrollAnimationCreate(element_id_, target_offset, - current_offset, base::TimeDelta()); + current_offset, base::TimeDelta(), + base::TimeDelta()); gfx::Vector2dF scroll_delta(0, 0.5); gfx::ScrollOffset max_scroll_offset(0., 3.); diff --git a/chromium/cc/animation/animation_player.cc b/chromium/cc/animation/animation_player.cc index 634d64948ae..3174dcbdab0 100644 --- a/chromium/cc/animation/animation_player.cc +++ b/chromium/cc/animation/animation_player.cc @@ -374,6 +374,10 @@ bool AnimationPlayer::NotifyAnimationFinished(const AnimationEvent& event) { } } + // This is for the case when an animation is already removed on main thread, + // but the impl version of it sent a finished event and is now waiting for + // deletion. We would need to delete that animation during push properties. + SetNeedsPushProperties(); return false; } @@ -795,6 +799,21 @@ void AnimationPlayer::MarkFinishedAnimations(base::TimeTicks monotonic_time) { animations_[i]->IsFinishedAt(monotonic_time)) { animations_[i]->SetRunState(Animation::FINISHED, monotonic_time); animation_finished = true; + SetNeedsPushProperties(); + } + if (!animations_[i]->affects_active_elements() && + !animations_[i]->affects_pending_elements()) { + switch (animations_[i]->run_state()) { + case Animation::WAITING_FOR_TARGET_AVAILABILITY: + case Animation::STARTING: + case Animation::RUNNING: + case Animation::PAUSED: + animations_[i]->SetRunState(Animation::FINISHED, monotonic_time); + animation_finished = true; + break; + default: + break; + } } } @@ -814,11 +833,6 @@ void AnimationPlayer::ActivateAnimations() { animations_[i]->set_affects_active_elements( animations_[i]->affects_pending_elements()); } - auto affects_no_elements = [](const std::unique_ptr<Animation>& animation) { - return !animation->affects_active_elements() && - !animation->affects_pending_elements(); - }; - base::EraseIf(animations_, affects_no_elements); if (animation_activated) element_animations_->UpdateClientAnimationState(); @@ -1151,7 +1165,9 @@ static bool IsCompleted(Animation* animation, if (animation->is_impl_only()) { return (animation->run_state() == Animation::WAITING_FOR_DELETION); } else { - return !main_thread_player->GetAnimationById(animation->id()); + Animation* main_thread_animation = + main_thread_player->GetAnimationById(animation->id()); + return !main_thread_animation || main_thread_animation->is_finished(); } } diff --git a/chromium/cc/animation/animation_player_unittest.cc b/chromium/cc/animation/animation_player_unittest.cc index 4f234a0f157..7b4b82d850f 100644 --- a/chromium/cc/animation/animation_player_unittest.cc +++ b/chromium/cc/animation/animation_player_unittest.cc @@ -172,7 +172,7 @@ TEST_F(AnimationPlayerTest, PropertiesMutate) { time += base::TimeDelta::FromSecondsD(duration); TickAnimationsTransferEvents(time, 3u); - EXPECT_TRUE(CheckPlayerTimelineNeedsPushProperties(false)); + EXPECT_TRUE(CheckPlayerTimelineNeedsPushProperties(true)); client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE, end_opacity); @@ -265,8 +265,8 @@ TEST_F(AnimationPlayerTest, AttachTwoPlayersToOneLayer) { EXPECT_TRUE(delegate1.finished()); EXPECT_TRUE(delegate2.finished()); - EXPECT_FALSE(player1->needs_push_properties()); - EXPECT_FALSE(player2->needs_push_properties()); + EXPECT_TRUE(player1->needs_push_properties()); + EXPECT_TRUE(player2->needs_push_properties()); client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE, end_opacity); @@ -394,7 +394,7 @@ TEST_F(AnimationPlayerTest, SwitchToLayer) { EXPECT_EQ(player_impl_->element_id(), element_id_); EXPECT_TRUE(CheckPlayerTimelineNeedsPushProperties(false)); - const ElementId new_element_id(NextTestLayerId(), 0); + const ElementId new_element_id(NextTestLayerId()); player_->DetachElement(); player_->AttachElement(new_element_id); diff --git a/chromium/cc/animation/element_animations_unittest.cc b/chromium/cc/animation/element_animations_unittest.cc index 75e69c9b25a..8974943990b 100644 --- a/chromium/cc/animation/element_animations_unittest.cc +++ b/chromium/cc/animation/element_animations_unittest.cc @@ -2696,10 +2696,11 @@ TEST_F(ElementAnimationsTest, ObserverNotifiedWhenTransformAnimationChanges) { PushProperties(); - // animations_impl hasn't yet ticked at/past the end of the animation. - EXPECT_TRUE(client_impl_.GetHasPotentialTransformAnimation( + // Finished animations are pushed, but animations_impl hasn't yet ticked + // at/past the end of the animation. + EXPECT_FALSE(client_impl_.GetHasPotentialTransformAnimation( element_id_, ElementListType::PENDING)); - EXPECT_TRUE(client_impl_.GetTransformIsCurrentlyAnimating( + EXPECT_FALSE(client_impl_.GetTransformIsCurrentlyAnimating( element_id_, ElementListType::PENDING)); EXPECT_TRUE(client_impl_.GetHasPotentialTransformAnimation( element_id_, ElementListType::ACTIVE)); @@ -2913,10 +2914,11 @@ TEST_F(ElementAnimationsTest, ObserverNotifiedWhenOpacityAnimationChanges) { PushProperties(); - // animations_impl hasn't yet ticked at/past the end of the animation. - EXPECT_TRUE(client_impl_.GetHasPotentialOpacityAnimation( + // Finished animations are pushed, but animations_impl hasn't yet ticked + // at/past the end of the animation. + EXPECT_FALSE(client_impl_.GetHasPotentialOpacityAnimation( element_id_, ElementListType::PENDING)); - EXPECT_TRUE(client_impl_.GetOpacityIsCurrentlyAnimating( + EXPECT_FALSE(client_impl_.GetOpacityIsCurrentlyAnimating( element_id_, ElementListType::PENDING)); EXPECT_TRUE(client_impl_.GetHasPotentialOpacityAnimation( element_id_, ElementListType::ACTIVE)); @@ -3123,10 +3125,11 @@ TEST_F(ElementAnimationsTest, ObserverNotifiedWhenFilterAnimationChanges) { PushProperties(); - // animations_impl hasn't yet ticked at/past the end of the animation. - EXPECT_TRUE(client_impl_.GetHasPotentialFilterAnimation( + // Finished animations are pushed, but animations_impl hasn't yet ticked + // at/past the end of the animation. + EXPECT_FALSE(client_impl_.GetHasPotentialFilterAnimation( element_id_, ElementListType::PENDING)); - EXPECT_TRUE(client_impl_.GetFilterIsCurrentlyAnimating( + EXPECT_FALSE(client_impl_.GetFilterIsCurrentlyAnimating( element_id_, ElementListType::PENDING)); EXPECT_TRUE(client_impl_.GetHasPotentialFilterAnimation( element_id_, ElementListType::ACTIVE)); @@ -3304,7 +3307,6 @@ TEST_F(ElementAnimationsTest, PushedDeletedAnimationWaitsForActivation) { const int animation_id = AddOpacityTransitionToPlayer(player_.get(), 1, 0.5f, 1.f, true); - PushProperties(); player_impl_->ActivateAnimations(); player_impl_->Tick(kInitialTickTime); @@ -3342,8 +3344,20 @@ TEST_F(ElementAnimationsTest, PushedDeletedAnimationWaitsForActivation) { client_impl_.GetOpacity(element_id_, ElementListType::ACTIVE)); player_impl_->ActivateAnimations(); + events = CreateEventsForTesting(); + player_impl_->UpdateState(true, events.get()); - // Activation should cause the animation to be deleted. + // After Activation the animation doesn't affect neither active nor pending + // thread. UpdateState for this animation would put the animation to wait for + // deletion state. + EXPECT_EQ(Animation::WAITING_FOR_DELETION, + player_impl_->GetAnimationById(animation_id)->run_state()); + EXPECT_EQ(1u, events->events_.size()); + + // The animation is finished on impl thread, and main thread will delete it + // during commit. + player_->animation_host()->SetAnimationEvents(std::move(events)); + PushProperties(); EXPECT_FALSE(player_impl_->has_any_animation()); } @@ -3402,9 +3416,12 @@ TEST_F(ElementAnimationsTest, StartAnimationsAffectingDifferentObservers) { player_impl_->ActivateAnimations(); - // The original animation should have been deleted, and the new animation - // should now affect both elements. - EXPECT_FALSE(player_impl_->GetAnimationById(first_animation_id)); + // The original animation no longer affect either elements, and the new + // animation should now affect both elements. + EXPECT_FALSE(player_impl_->GetAnimationById(first_animation_id) + ->affects_pending_elements()); + EXPECT_FALSE(player_impl_->GetAnimationById(first_animation_id) + ->affects_active_elements()); EXPECT_TRUE(player_impl_->GetAnimationById(second_animation_id) ->affects_pending_elements()); EXPECT_TRUE(player_impl_->GetAnimationById(second_animation_id) @@ -3413,6 +3430,10 @@ TEST_F(ElementAnimationsTest, StartAnimationsAffectingDifferentObservers) { player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); player_impl_->UpdateState(true, events.get()); + // The original animation should be marked for waiting for deletion. + EXPECT_EQ(Animation::WAITING_FOR_DELETION, + player_impl_->GetAnimationById(first_animation_id)->run_state()); + // The new animation should be running, and the active observer should have // been ticked at the new animation's starting point. EXPECT_EQ(Animation::RUNNING, diff --git a/chromium/cc/animation/keyframed_animation_curve_unittest.cc b/chromium/cc/animation/keyframed_animation_curve_unittest.cc index a7b0e345b99..c050404d714 100644 --- a/chromium/cc/animation/keyframed_animation_curve_unittest.cc +++ b/chromium/cc/animation/keyframed_animation_curve_unittest.cc @@ -535,6 +535,30 @@ TEST(KeyframedAnimationCurveTest, StepsTimingFunctionStepAtEnd) { } } +// Tests a frames timing function. +TEST(KeyframedAnimationCurveTest, FramesTimingFunction) { + std::unique_ptr<KeyframedFloatAnimationCurve> curve( + KeyframedFloatAnimationCurve::Create()); + curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, + FramesTimingFunction::Create(5))); + curve->AddKeyframe( + FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 1.f, nullptr)); + + struct Expected { + double time; + float value; + } expectations[] = { + {0.0, 0.f}, {0.1999, 0.f}, {0.2001, 0.25f}, {0.3999, 0.25f}, + {0.4001, 0.5f}, {0.5999, 0.5f}, {0.6001, 0.75f}, {0.7999, 0.75f}, + {0.8001, 1.f}, {1.0, 1.f}, + }; + for (const auto& expectation : expectations) { + EXPECT_FLOAT_EQ( + expectation.value, + curve->GetValue(base::TimeDelta::FromSecondsD(expectation.time))); + } +} + // Tests that animated bounds are computed as expected. TEST(KeyframedAnimationCurveTest, AnimatedBounds) { std::unique_ptr<KeyframedTransformAnimationCurve> curve( @@ -818,13 +842,13 @@ TEST(KeyframedAnimationCurveTest, CurveTimingInputsOutsideZeroOneRange) { // Tests that a step timing function works as expected for inputs outside of // range [0,1] -TEST(KeyframedAnimationCurveTest, StepsTimingInputsOutsideZeroOneRange) { +TEST(KeyframedAnimationCurveTest, StepsTimingStartInputsOutsideZeroOneRange) { std::unique_ptr<KeyframedFloatAnimationCurve> curve( KeyframedFloatAnimationCurve::Create()); curve->AddKeyframe( FloatKeyframe::Create(base::TimeDelta(), 0.f, StepsTimingFunction::Create( - 4, StepsTimingFunction::StepPosition::MIDDLE))); + 4, StepsTimingFunction::StepPosition::START))); curve->AddKeyframe( FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 2.f, nullptr)); // Curve timing function producing timing outputs outside of range [0,1]. @@ -832,9 +856,42 @@ TEST(KeyframedAnimationCurveTest, StepsTimingInputsOutsideZeroOneRange) { CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f))); + EXPECT_FLOAT_EQ(2.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); +} + +TEST(KeyframedAnimationCurveTest, StepsTimingEndInputsOutsideZeroOneRange) { + std::unique_ptr<KeyframedFloatAnimationCurve> curve( + KeyframedFloatAnimationCurve::Create()); + curve->AddKeyframe(FloatKeyframe::Create( + base::TimeDelta(), 0.f, + StepsTimingFunction::Create(4, StepsTimingFunction::StepPosition::END))); + curve->AddKeyframe( + FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 2.f, nullptr)); + // Curve timing function producing timing outputs outside of range [0,1]. + curve->SetTimingFunction( + CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); + + EXPECT_FLOAT_EQ(-0.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f))); EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); } +// Tests that a frames timing function works as expected for inputs outside of +// range [0,1] +TEST(KeyframedAnimationCurveTest, FramesTimingInputsOutsideZeroOneRange) { + std::unique_ptr<KeyframedFloatAnimationCurve> curve( + KeyframedFloatAnimationCurve::Create()); + curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, + FramesTimingFunction::Create(5))); + curve->AddKeyframe( + FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 2.f, nullptr)); + // Curve timing function producing timing outputs outside of range [0,1]. + curve->SetTimingFunction( + CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f)); + + EXPECT_FLOAT_EQ(-0.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f))); + EXPECT_FLOAT_EQ(2.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); +} + // Tests that an animation with a curve timing function and multiple keyframes // works as expected. TEST(KeyframedAnimationCurveTest, CurveTimingMultipleKeyframes) { diff --git a/chromium/cc/animation/scroll_offset_animations_impl.cc b/chromium/cc/animation/scroll_offset_animations_impl.cc index 71a96b429f5..512133cde53 100644 --- a/chromium/cc/animation/scroll_offset_animations_impl.cc +++ b/chromium/cc/animation/scroll_offset_animations_impl.cc @@ -36,7 +36,8 @@ void ScrollOffsetAnimationsImpl::ScrollAnimationCreate( ElementId element_id, const gfx::ScrollOffset& target_offset, const gfx::ScrollOffset& current_offset, - base::TimeDelta delayed_by) { + base::TimeDelta delayed_by, + base::TimeDelta animation_start_offset) { std::unique_ptr<ScrollOffsetAnimationCurve> curve = ScrollOffsetAnimationCurve::Create( target_offset, CubicBezierTimingFunction::CreatePreset( @@ -47,6 +48,7 @@ void ScrollOffsetAnimationsImpl::ScrollAnimationCreate( std::unique_ptr<Animation> animation = Animation::Create( std::move(curve), AnimationIdProvider::NextAnimationId(), AnimationIdProvider::NextGroupId(), TargetProperty::SCROLL_OFFSET); + animation->set_time_offset(animation_start_offset); animation->set_is_impl_only(true); DCHECK(scroll_offset_animation_player_); diff --git a/chromium/cc/animation/scroll_offset_animations_impl.h b/chromium/cc/animation/scroll_offset_animations_impl.h index e6d402f925c..876e3b07bfe 100644 --- a/chromium/cc/animation/scroll_offset_animations_impl.h +++ b/chromium/cc/animation/scroll_offset_animations_impl.h @@ -31,10 +31,14 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationsImpl ~ScrollOffsetAnimationsImpl() override; + // |delayed_by| shrinks the duration of the + // animation. |animation_start_offset| causes us to start the animation + // partway through. void ScrollAnimationCreate(ElementId element_id, const gfx::ScrollOffset& target_offset, const gfx::ScrollOffset& current_offset, - base::TimeDelta delayed_by); + base::TimeDelta delayed_by, + base::TimeDelta animation_start_offset); bool ScrollAnimationUpdateTarget(ElementId element_id, const gfx::Vector2dF& scroll_delta, diff --git a/chromium/cc/animation/timing_function.cc b/chromium/cc/animation/timing_function.cc index b7308bb31c9..0271c57bb44 100644 --- a/chromium/cc/animation/timing_function.cc +++ b/chromium/cc/animation/timing_function.cc @@ -109,8 +109,12 @@ float StepsTimingFunction::Velocity(double x) const { double StepsTimingFunction::GetPreciseValue(double t) const { const double steps = static_cast<double>(steps_); - return MathUtil::ClampToRange( - std::floor((steps * t) + GetStepsStartOffset()) / steps, 0.0, 1.0); + double current_step = std::floor((steps * t) + GetStepsStartOffset()); + if (t >= 0 && current_step < 0) + current_step = 0; + if (t <= 1 && current_step > steps) + current_step = steps; + return current_step / steps; } float StepsTimingFunction::GetStepsStartOffset() const { @@ -127,4 +131,41 @@ float StepsTimingFunction::GetStepsStartOffset() const { } } +std::unique_ptr<FramesTimingFunction> FramesTimingFunction::Create(int frames) { + return base::WrapUnique(new FramesTimingFunction(frames)); +} + +FramesTimingFunction::FramesTimingFunction(int frames) : frames_(frames) {} + +FramesTimingFunction::~FramesTimingFunction() {} + +TimingFunction::Type FramesTimingFunction::GetType() const { + return Type::FRAMES; +} + +float FramesTimingFunction::GetValue(double t) const { + return static_cast<float>(GetPreciseValue(t)); +} + +std::unique_ptr<TimingFunction> FramesTimingFunction::Clone() const { + return base::WrapUnique(new FramesTimingFunction(*this)); +} + +void FramesTimingFunction::Range(float* min, float* max) const { + *min = 0.0f; + *max = 1.0f; +} + +float FramesTimingFunction::Velocity(double x) const { + return 0.0f; +} + +double FramesTimingFunction::GetPreciseValue(double t) const { + const double frames = static_cast<double>(frames_); + double output_progress = std::floor(frames * t) / (frames - 1); + if (t <= 1 && output_progress > 1) + output_progress = 1; + return output_progress; +} + } // namespace cc diff --git a/chromium/cc/animation/timing_function.h b/chromium/cc/animation/timing_function.h index 0f475e7624a..5ff8d3561bd 100644 --- a/chromium/cc/animation/timing_function.h +++ b/chromium/cc/animation/timing_function.h @@ -19,7 +19,7 @@ class CC_ANIMATION_EXPORT TimingFunction { virtual ~TimingFunction(); // Note that LINEAR is a nullptr TimingFunction (for now). - enum class Type { LINEAR, CUBIC_BEZIER, STEPS }; + enum class Type { LINEAR, CUBIC_BEZIER, STEPS, FRAMES }; virtual Type GetType() const = 0; virtual float GetValue(double t) const = 0; @@ -101,6 +101,29 @@ class CC_ANIMATION_EXPORT StepsTimingFunction : public TimingFunction { DISALLOW_ASSIGN(StepsTimingFunction); }; +class CC_ANIMATION_EXPORT FramesTimingFunction : public TimingFunction { + public: + static std::unique_ptr<FramesTimingFunction> Create(int frames); + ~FramesTimingFunction() override; + + // TimingFunction implementation. + Type GetType() const override; + float GetValue(double t) const override; + std::unique_ptr<TimingFunction> Clone() const override; + void Range(float* min, float* max) const override; + float Velocity(double time) const override; + + int frames() const { return frames_; } + double GetPreciseValue(double t) const; + + private: + explicit FramesTimingFunction(int frames); + + int frames_; + + DISALLOW_ASSIGN(FramesTimingFunction); +}; + } // namespace cc #endif // CC_ANIMATION_TIMING_FUNCTION_H_ diff --git a/chromium/cc/base/BUILD.gn b/chromium/cc/base/BUILD.gn index db70d19adb3..8b4eef21627 100644 --- a/chromium/cc/base/BUILD.gn +++ b/chromium/cc/base/BUILD.gn @@ -33,7 +33,6 @@ component("base") { "list_container_helper.h", "math_util.cc", "math_util.h", - "random_access_list_container.h", "region.cc", "region.h", "render_surface_filters.cc", diff --git a/chromium/cc/base/list_container.h b/chromium/cc/base/list_container.h index cb386f9b343..93a6a67fc6d 100644 --- a/chromium/cc/base/list_container.h +++ b/chromium/cc/base/list_container.h @@ -26,28 +26,15 @@ namespace cc { template <class BaseElementType> class ListContainer { public: - ListContainer(ListContainer&& other) : helper_(sizeof(BaseElementType)) { - helper_.data_.swap(other.helper_.data_); - } - - // BaseElementType is the type of raw pointers this class hands out; however, - // its derived classes might require different memory sizes. - // max_size_for_derived_class the largest memory size required for all the - // derived classes to use for allocation. - explicit ListContainer(size_t max_size_for_derived_class) - : helper_(max_size_for_derived_class) {} - - // This constructor omits input variable for max_size_for_derived_class. This - // is used when there is no derived classes from BaseElementType we need to - // worry about, and allocation size is just sizeof(BaseElementType). - ListContainer() : helper_(sizeof(BaseElementType)) {} - // This constructor reserves the requested memory up front so only single // allocation is needed. When num_of_elements_to_reserve_for is zero, use the // default size. - ListContainer(size_t max_size_for_derived_class, + ListContainer(size_t max_alignment, + size_t max_size_for_derived_class, size_t num_of_elements_to_reserve_for) - : helper_(max_size_for_derived_class, num_of_elements_to_reserve_for) {} + : helper_(max_alignment, + max_size_for_derived_class, + num_of_elements_to_reserve_for) {} ~ListContainer() { for (Iterator i = begin(); i != end(); ++i) { @@ -114,7 +101,8 @@ class ListContainer { // Allocate(). template <typename DerivedElementType> DerivedElementType* AllocateAndConstruct() { - return new (helper_.Allocate(sizeof(DerivedElementType))) + return new (helper_.Allocate(ALIGNOF(DerivedElementType), + sizeof(DerivedElementType))) DerivedElementType; } @@ -122,7 +110,8 @@ class ListContainer { // Allocate(). template <typename DerivedElementType> DerivedElementType* AllocateAndCopyFrom(const DerivedElementType* source) { - return new (helper_.Allocate(sizeof(DerivedElementType))) + return new (helper_.Allocate(ALIGNOF(DerivedElementType), + sizeof(DerivedElementType))) DerivedElementType(*source); } @@ -165,7 +154,8 @@ class ListContainer { template <typename DerivedElementType> DerivedElementType* AppendByMoving(DerivedElementType* item) { size_t max_size_for_derived_class = helper_.MaxSizeForDerivedClass(); - void* new_item = helper_.Allocate(max_size_for_derived_class); + void* new_item = helper_.Allocate(ALIGNOF(DerivedElementType), + max_size_for_derived_class); memcpy(new_item, static_cast<void*>(item), max_size_for_derived_class); // Construct a new element in-place so it can be destructed safely. new (item) DerivedElementType; diff --git a/chromium/cc/base/list_container_helper.cc b/chromium/cc/base/list_container_helper.cc index 9d8a0293a03..84ecd9bac40 100644 --- a/chromium/cc/base/list_container_helper.cc +++ b/chromium/cc/base/list_container_helper.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/memory/aligned_memory.h" namespace { const size_t kDefaultNumElementTypesToReserve = 32; @@ -29,7 +30,7 @@ class ListContainerHelper::CharAllocator { // This class holds the raw memory chunk, as well as information about its // size and availability. struct InnerList { - std::unique_ptr<char[]> data; + std::unique_ptr<char[], base::AlignedFreeDeleter> data; // The number of elements in total the memory can hold. The difference // between capacity and size is the how many more elements this list can // hold. @@ -56,7 +57,7 @@ class ListContainerHelper::CharAllocator { --capacity; } - void InsertBefore(char** position, size_t count) { + void InsertBefore(size_t alignment, char** position, size_t count) { DCHECK_LE(*position, LastElement() + step); DCHECK_GE(*position, Begin()); @@ -66,7 +67,8 @@ class ListContainerHelper::CharAllocator { capacity = size; // Allocate the new data and update the iterator's pointer. - std::unique_ptr<char[]> new_data(new char[size * step]); + std::unique_ptr<char[], base::AlignedFreeDeleter> new_data( + static_cast<char*>(base::AlignedAlloc(size * step, alignment))); size_t position_offset = *position - Begin(); *position = new_data.get() + position_offset; @@ -75,7 +77,7 @@ class ListContainerHelper::CharAllocator { // Copy the data after the inserted segment. memcpy(new_data.get() + position_offset + count * step, data.get() + position_offset, old_size * step - position_offset); - new_data.swap(data); + data = std::move(new_data); } bool IsEmpty() const { return !size; } @@ -102,20 +104,16 @@ class ListContainerHelper::CharAllocator { DISALLOW_COPY_AND_ASSIGN(InnerList); }; - explicit CharAllocator(size_t element_size) - : element_size_(element_size), - size_(0), - last_list_index_(0), - last_list_(nullptr) { - AllocateNewList(kDefaultNumElementTypesToReserve); - last_list_ = storage_[last_list_index_].get(); - } - - CharAllocator(size_t element_size, size_t element_count) - : element_size_(element_size), + CharAllocator(size_t alignment, size_t element_size, size_t element_count) + // base::AlignedAlloc does not accept alignment less than sizeof(void*). + : alignment_(std::max(sizeof(void*), alignment)), + element_size_(element_size), size_(0), last_list_index_(0), last_list_(nullptr) { + // If this fails, then alignment of elements after the first could be wrong, + // and we need to pad sizes to fix that. + DCHECK_EQ(element_size % alignment, 0u); AllocateNewList(element_count > 0 ? element_count : kDefaultNumElementTypesToReserve); last_list_ = storage_[last_list_index_].get(); @@ -138,6 +136,7 @@ class ListContainerHelper::CharAllocator { return last_list_->AddElement(); } + size_t alignment() const { return alignment_; } size_t element_size() const { return element_size_; } size_t list_count() const { return storage_.size(); } size_t size() const { return size_; } @@ -203,8 +202,8 @@ class ListContainerHelper::CharAllocator { for (size_t i = 1; i < count; ++i) Allocate(); } else { - storage_[position->vector_index]->InsertBefore(&position->item_iterator, - count); + storage_[position->vector_index]->InsertBefore( + alignment_, &position->item_iterator, count); size_ += count; } } @@ -244,11 +243,13 @@ class ListContainerHelper::CharAllocator { new_list->capacity = list_size; new_list->size = 0; new_list->step = element_size_; - new_list->data.reset(new char[list_size * element_size_]); + new_list->data.reset(static_cast<char*>( + base::AlignedAlloc(list_size * element_size_, alignment_))); storage_.push_back(std::move(new_list)); } std::vector<std::unique_ptr<InnerList>> storage_; + const size_t alignment_; const size_t element_size_; // The number of elements in the list. @@ -341,12 +342,11 @@ ListContainerHelper::PositionInCharAllocator::ReverseIncrement() { // ListContainerHelper //////////////////////////////////////////// -ListContainerHelper::ListContainerHelper(size_t max_size_for_derived_class) - : data_(new CharAllocator(max_size_for_derived_class)) {} - -ListContainerHelper::ListContainerHelper(size_t max_size_for_derived_class, +ListContainerHelper::ListContainerHelper(size_t alignment, + size_t max_size_for_derived_class, size_t num_of_elements_to_reserve_for) - : data_(new CharAllocator(max_size_for_derived_class, + : data_(new CharAllocator(alignment, + max_size_for_derived_class, num_of_elements_to_reserve_for)) {} ListContainerHelper::~ListContainerHelper() {} @@ -456,7 +456,9 @@ ListContainerHelper::Iterator ListContainerHelper::IteratorAt(size_t index) { original_index); } -void* ListContainerHelper::Allocate(size_t size_of_actual_element_in_bytes) { +void* ListContainerHelper::Allocate(size_t alignment, + size_t size_of_actual_element_in_bytes) { + DCHECK_LE(alignment, data_->alignment()); DCHECK_LE(size_of_actual_element_in_bytes, data_->element_size()); return data_->Allocate(); } diff --git a/chromium/cc/base/list_container_helper.h b/chromium/cc/base/list_container_helper.h index ce98c1ec4ef..c79cf1f1884 100644 --- a/chromium/cc/base/list_container_helper.h +++ b/chromium/cc/base/list_container_helper.h @@ -22,12 +22,9 @@ class CC_BASE_EXPORT ListContainerHelper final { template <typename T> friend class ListContainer; - template <typename T> - friend class RandomAccessListContainer; - - explicit ListContainerHelper(size_t max_size_for_derived_class); - ListContainerHelper(size_t max_size_for_derived_class, - size_t num_of_elements_to_reserve_for); + explicit ListContainerHelper(size_t alignment, + size_t max_size_for_derived_class, + size_t num_of_elements_to_reserve_for); ~ListContainerHelper(); // This class deals only with char* and void*. It does allocation and passing @@ -170,7 +167,7 @@ class CC_BASE_EXPORT ListContainerHelper final { size_t AvailableSizeWithoutAnotherAllocationForTesting() const; // Hands out memory location for an element at the end of data structure. - void* Allocate(size_t size_of_actual_element_in_bytes); + void* Allocate(size_t alignment, size_t size_of_actual_element_in_bytes); std::unique_ptr<CharAllocator> data_; diff --git a/chromium/cc/base/list_container_unittest.cc b/chromium/cc/base/list_container_unittest.cc index 13630252959..31ef300b923 100644 --- a/chromium/cc/base/list_container_unittest.cc +++ b/chromium/cc/base/list_container_unittest.cc @@ -51,17 +51,20 @@ class DerivedElement3 : public DerivedElement { }; const size_t kLargestDerivedElementSize = sizeof(DerivedElement3); - -size_t LargestDerivedElementSize() { - static_assert(sizeof(DerivedElement1) <= kLargestDerivedElementSize, - "Largest Derived Element size needs update. DerivedElement1 is " - "currently largest."); - static_assert(sizeof(DerivedElement2) <= kLargestDerivedElementSize, - "Largest Derived Element size needs update. DerivedElement2 is " - "currently largest."); - - return kLargestDerivedElementSize; -} +const size_t kLargestDerivedElementAlign = ALIGNOF(DerivedElement3); + +static_assert(sizeof(DerivedElement1) <= kLargestDerivedElementSize, + "Largest Derived Element size needs update. DerivedElement1 is " + "currently largest."); +static_assert(sizeof(DerivedElement2) <= kLargestDerivedElementSize, + "Largest Derived Element size needs update. DerivedElement2 is " + "currently largest."); +static_assert(ALIGNOF(DerivedElement1) <= kLargestDerivedElementSize, + "Largest Derived Element align needs update. DerivedElement1 is " + "currently largest."); +static_assert(ALIGNOF(DerivedElement2) <= kLargestDerivedElementSize, + "Largest Derived Element align needs update. DerivedElement2 is " + "currently largest."); // Element class having no derived classes. class NonDerivedElement { @@ -132,10 +135,13 @@ class MockDerivedElementSubclass : public MockDerivedElement { }; const size_t kCurrentLargestDerivedElementSize = - std::max(LargestDerivedElementSize(), sizeof(MockDerivedElementSubclass)); + std::max(kLargestDerivedElementSize, sizeof(MockDerivedElementSubclass)); +const size_t kCurrentLargestDerivedElementAlign = + std::max(kLargestDerivedElementAlign, ALIGNOF(MockDerivedElementSubclass)); TEST(ListContainerTest, ConstructorCalledInAllocateAndConstruct) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); size_t size = 2; SimpleDerivedElementConstructMagicNumberOne* de_1 = @@ -152,7 +158,8 @@ TEST(ListContainerTest, ConstructorCalledInAllocateAndConstruct) { } TEST(ListContainerTest, DestructorCalled) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); size_t size = 1; MockDerivedElement* de_1 = list.AllocateAndConstruct<MockDerivedElement>(); @@ -163,7 +170,8 @@ TEST(ListContainerTest, DestructorCalled) { } TEST(ListContainerTest, DestructorCalledOnceWhenClear) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); size_t size = 1; MockDerivedElement* de_1 = list.AllocateAndConstruct<MockDerivedElement>(); @@ -186,7 +194,8 @@ TEST(ListContainerTest, DestructorCalledOnceWhenClear) { TEST(ListContainerTest, ClearDoesNotMalloc) { const size_t reserve = 10; - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize, + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, reserve); // Memory from the initial inner list that should be re-used after clear(). @@ -218,7 +227,8 @@ TEST(ListContainerTest, ClearDoesNotMalloc) { } TEST(ListContainerTest, ReplaceExistingElement) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); size_t size = 1; MockDerivedElement* de_1 = list.AllocateAndConstruct<MockDerivedElement>(); @@ -244,7 +254,8 @@ TEST(ListContainerTest, ReplaceExistingElement) { } TEST(ListContainerTest, DestructorCalledOnceWhenErase) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); size_t size = 1; MockDerivedElement* de_1 = list.AllocateAndConstruct<MockDerivedElement>(); @@ -266,7 +277,8 @@ TEST(ListContainerTest, DestructorCalledOnceWhenErase) { } TEST(ListContainerTest, SimpleIndexAccessNonDerivedElement) { - ListContainer<NonDerivedElement> list; + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 0); size_t size = 3; NonDerivedElement* nde_1 = list.AllocateAndConstruct<NonDerivedElement>(); @@ -282,7 +294,8 @@ TEST(ListContainerTest, SimpleIndexAccessNonDerivedElement) { } TEST(ListContainerTest, SimpleInsertionNonDerivedElement) { - ListContainer<NonDerivedElement> list; + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 0); size_t size = 3; NonDerivedElement* nde_1 = list.AllocateAndConstruct<NonDerivedElement>(); @@ -295,7 +308,8 @@ TEST(ListContainerTest, SimpleInsertionNonDerivedElement) { } TEST(ListContainerTest, SimpleInsertionAndClearNonDerivedElement) { - ListContainer<NonDerivedElement> list; + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 0); EXPECT_TRUE(list.empty()); EXPECT_EQ(0u, list.size()); @@ -315,7 +329,8 @@ TEST(ListContainerTest, SimpleInsertionAndClearNonDerivedElement) { } TEST(ListContainerTest, SimpleInsertionClearAndInsertAgainNonDerivedElement) { - ListContainer<NonDerivedElement> list; + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 0); EXPECT_TRUE(list.empty()); EXPECT_EQ(0u, list.size()); @@ -347,7 +362,8 @@ TEST(ListContainerTest, SimpleInsertionClearAndInsertAgainNonDerivedElement) { // for, ListContainer can still perform like normal vector. TEST(ListContainerTest, SimpleInsertionTriggerMoreThanOneAllocationNonDerivedElement) { - ListContainer<NonDerivedElement> list(sizeof(NonDerivedElement), 2); + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 2); std::vector<NonDerivedElement*> nde_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { @@ -369,7 +385,8 @@ TEST(ListContainerTest, // Constructor sets the allocation size to 2. Every time ListContainer needs // to allocate again, it doubles allocation size. In this test, 10 elements is // needed, thus ListContainerShould allocate spaces 2, 4 and 8 elements. - ListContainer<NonDerivedElement> list(sizeof(NonDerivedElement), 2); + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 2); std::vector<NonDerivedElement*> nde_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { @@ -449,7 +466,8 @@ TEST(ListContainerTest, } TEST(ListContainerTest, SimpleIterationNonDerivedElement) { - ListContainer<NonDerivedElement> list; + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 0); std::vector<NonDerivedElement*> nde_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { @@ -484,7 +502,8 @@ TEST(ListContainerTest, SimpleIterationNonDerivedElement) { } TEST(ListContainerTest, SimpleConstIteratorIterationNonDerivedElement) { - ListContainer<NonDerivedElement> list; + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 0); std::vector<const NonDerivedElement*> nde_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { @@ -526,7 +545,8 @@ TEST(ListContainerTest, SimpleConstIteratorIterationNonDerivedElement) { } TEST(ListContainerTest, SimpleReverseInsertionNonDerivedElement) { - ListContainer<NonDerivedElement> list; + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 0); std::vector<NonDerivedElement*> nde_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { @@ -556,7 +576,8 @@ TEST(ListContainerTest, SimpleReverseInsertionNonDerivedElement) { } TEST(ListContainerTest, SimpleDeletion) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); std::vector<SimpleDerivedElement*> sde_list; int size = 10; for (int i = 0; i < size; ++i) { @@ -578,7 +599,8 @@ TEST(ListContainerTest, SimpleDeletion) { TEST(ListContainerTest, DeletionAllInAllocation) { const size_t kReserve = 10; - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize, + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, kReserve); std::vector<SimpleDerivedElement*> sde_list; // Add enough elements to cause another allocation. @@ -604,7 +626,8 @@ TEST(ListContainerTest, DeletionAllInAllocation) { TEST(ListContainerTest, DeletionAllInAllocationReversed) { const size_t kReserve = 10; - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize, + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, kReserve); std::vector<SimpleDerivedElement*> sde_list; // Add enough elements to cause another allocation. @@ -660,7 +683,8 @@ TEST(ListContainerTest, DeletionAllInAllocationReversed) { } TEST(ListContainerTest, DeletionWhileIterating) { - ListContainer<SimpleDerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<SimpleDerivedElement> list( + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); for (int i = 0; i < 4; ++i) list.AllocateAndConstruct<SimpleDerivedElement>()->set_value(i); @@ -684,7 +708,8 @@ TEST(ListContainerTest, DeletionWhileIterating) { } TEST(ListContainerTest, InsertBeforeBegin) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); std::vector<SimpleDerivedElement*> sde_list; const int size = 4; for (int i = 0; i < size; ++i) { @@ -713,7 +738,8 @@ TEST(ListContainerTest, InsertBeforeBegin) { } TEST(ListContainerTest, InsertBeforeEnd) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); std::vector<SimpleDerivedElement*> sde_list; const int size = 4; for (int i = 0; i < size; ++i) { @@ -742,7 +768,8 @@ TEST(ListContainerTest, InsertBeforeEnd) { } TEST(ListContainerTest, InsertBeforeEmpty) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); const int count = 3; ListContainer<DerivedElement>::Iterator iter = @@ -764,7 +791,8 @@ TEST(ListContainerTest, InsertBeforeEmpty) { } TEST(ListContainerTest, InsertBeforeMany) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); std::vector<SimpleDerivedElement*> sde_list; // Create a partial list of 1,...,99. int initial_list[] = { @@ -810,7 +838,8 @@ TEST(ListContainerTest, InsertBeforeMany) { } TEST(ListContainerTest, SimpleManipulationWithIndexSimpleDerivedElement) { - ListContainer<DerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); std::vector<SimpleDerivedElement*> de_list; int size = 10; for (int i = 0; i < size; ++i) { @@ -832,7 +861,8 @@ TEST(ListContainerTest, SimpleManipulationWithIndexSimpleDerivedElement) { TEST(ListContainerTest, SimpleManipulationWithIndexMoreThanOneAllocationSimpleDerivedElement) { - ListContainer<DerivedElement> list(LargestDerivedElementSize(), 2); + ListContainer<DerivedElement> list(kLargestDerivedElementAlign, + kLargestDerivedElementSize, 2); std::vector<SimpleDerivedElement*> de_list; int size = 10; for (int i = 0; i < size; ++i) { @@ -854,7 +884,8 @@ TEST(ListContainerTest, TEST(ListContainerTest, SimpleIterationAndReverseIterationWithIndexNonDerivedElement) { - ListContainer<NonDerivedElement> list; + ListContainer<NonDerivedElement> list(ALIGNOF(NonDerivedElement), + sizeof(NonDerivedElement), 0); std::vector<NonDerivedElement*> nde_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { @@ -900,7 +931,8 @@ TEST(ListContainerTest, RemoveLastDestruction) { // We keep an explicit instance count to make sure that the destructors are // indeed getting called. int counter = 0; - ListContainer<InstanceCounter> list(sizeof(InstanceCounter), 1); + ListContainer<InstanceCounter> list(ALIGNOF(InstanceCounter), + sizeof(InstanceCounter), 1); EXPECT_EQ(0, counter); EXPECT_EQ(0u, list.size()); @@ -944,7 +976,7 @@ TEST(ListContainerTest, RemoveLastIteration) { struct SmallStruct { char dummy[16]; }; - ListContainer<SmallStruct> list(sizeof(SmallStruct), 1); + ListContainer<SmallStruct> list(ALIGNOF(SmallStruct), sizeof(SmallStruct), 1); std::vector<SmallStruct*> pointers; // Utilities which keep these two lists in sync and check that their iteration @@ -990,7 +1022,8 @@ TEST(ListContainerTest, RemoveLastIteration) { } TEST(ListContainerTest, AppendByMovingSameList) { - ListContainer<SimpleDerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<SimpleDerivedElement> list( + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); list.AllocateAndConstruct<SimpleDerivedElementConstructMagicNumberOne>(); list.AppendByMoving(list.front()); @@ -1008,8 +1041,10 @@ TEST(ListContainerTest, AppendByMovingSameList) { } TEST(ListContainerTest, AppendByMovingDoesNotDestruct) { - ListContainer<DerivedElement> list_1(kCurrentLargestDerivedElementSize); - ListContainer<DerivedElement> list_2(kCurrentLargestDerivedElementSize); + ListContainer<DerivedElement> list_1(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); + ListContainer<DerivedElement> list_2(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); MockDerivedElement* mde_1 = list_1.AllocateAndConstruct<MockDerivedElement>(); // Make sure destructor isn't called during AppendByMoving. @@ -1021,8 +1056,10 @@ TEST(ListContainerTest, AppendByMovingDoesNotDestruct) { } TEST(ListContainerTest, AppendByMovingReturnsMovedPointer) { - ListContainer<SimpleDerivedElement> list_1(kCurrentLargestDerivedElementSize); - ListContainer<SimpleDerivedElement> list_2(kCurrentLargestDerivedElementSize); + ListContainer<SimpleDerivedElement> list_1( + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); + ListContainer<SimpleDerivedElement> list_2( + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); SimpleDerivedElement* simple_element = list_1.AllocateAndConstruct<SimpleDerivedElement>(); @@ -1036,9 +1073,9 @@ TEST(ListContainerTest, AppendByMovingReturnsMovedPointer) { TEST(ListContainerTest, AppendByMovingReplacesSourceWithNewDerivedElement) { ListContainer<SimpleDerivedElementConstructMagicNumberOne> list_1( - kCurrentLargestDerivedElementSize); + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); ListContainer<SimpleDerivedElementConstructMagicNumberTwo> list_2( - kCurrentLargestDerivedElementSize); + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); list_1.AllocateAndConstruct<SimpleDerivedElementConstructMagicNumberOne>(); EXPECT_EQ(kMagicNumberToUseForSimpleDerivedElementOne, @@ -1106,7 +1143,8 @@ TEST(ListContainerTest, AppendByMovingLongAndSimpleDerivedElements) { "LongSimpleDerivedElement should be smaller than the maximum " "DerivedElement size."); - ListContainer<SimpleDerivedElement> list(kCurrentLargestDerivedElementSize); + ListContainer<SimpleDerivedElement> list( + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); list.AllocateAndConstruct<LongSimpleDerivedElementConstructMagicNumber>(); list.AllocateAndConstruct<SimpleDerivedElementConstructMagicNumberOne>(); @@ -1136,9 +1174,11 @@ TEST(ListContainerTest, AppendByMovingLongAndSimpleDerivedElements) { } TEST(ListContainerTest, Swap) { - ListContainer<SimpleDerivedElement> list_1(kCurrentLargestDerivedElementSize); + ListContainer<SimpleDerivedElement> list_1( + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); list_1.AllocateAndConstruct<SimpleDerivedElementConstructMagicNumberOne>(); - ListContainer<SimpleDerivedElement> list_2(kCurrentLargestDerivedElementSize); + ListContainer<SimpleDerivedElement> list_2( + kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); list_2.AllocateAndConstruct<SimpleDerivedElementConstructMagicNumberTwo>(); list_2.AllocateAndConstruct<SimpleDerivedElementConstructMagicNumberThree>(); @@ -1181,24 +1221,25 @@ TEST(ListContainerTest, GetCapacityInBytes) { // memory, due to the exponential growth strategy). const size_t max_waste_factor = 8; - ListContainer<DerivedElement> list(LargestDerivedElementSize(), + ListContainer<DerivedElement> list(kLargestDerivedElementAlign, + kLargestDerivedElementSize, initial_capacity); // The capacity should grow with the list. for (int i = 0; i < iterations; i++) { size_t capacity = list.GetCapacityInBytes(); - ASSERT_GE(capacity, list.size() * LargestDerivedElementSize()); + ASSERT_GE(capacity, list.size() * kLargestDerivedElementSize); ASSERT_LE(capacity, std::max(list.size(), upper_bound_on_min_capacity) * - max_waste_factor * LargestDerivedElementSize()); + max_waste_factor * kLargestDerivedElementSize); list.AllocateAndConstruct<DerivedElement1>(); } // The capacity should shrink with the list. for (int i = 0; i < iterations; i++) { size_t capacity = list.GetCapacityInBytes(); - ASSERT_GE(capacity, list.size() * LargestDerivedElementSize()); + ASSERT_GE(capacity, list.size() * kLargestDerivedElementSize); ASSERT_LE(capacity, std::max(list.size(), upper_bound_on_min_capacity) * - max_waste_factor * LargestDerivedElementSize()); + max_waste_factor * kLargestDerivedElementSize); list.RemoveLast(); } } diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc index 262f6586dba..53dde152d57 100644 --- a/chromium/cc/base/math_util.cc +++ b/chromium/cc/base/math_util.cc @@ -588,6 +588,12 @@ gfx::Vector2dF MathUtil::ComputeTransform2dScaleComponents( return gfx::Vector2dF(x_scale, y_scale); } +float MathUtil::ComputeApproximateMaxScale(const gfx::Transform& transform) { + gfx::Vector3dF unit(1, 1, 0); + transform.TransformVector(&unit); + return std::max(std::abs(unit.x()), std::abs(unit.y())); +} + float MathUtil::SmallestAngleBetweenVectors(const gfx::Vector2dF& v1, const gfx::Vector2dF& v2) { double dot_product = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length(); @@ -817,7 +823,7 @@ ScopedSubnormalFloatDisabler::~ScopedSubnormalFloatDisabler() { #endif } -bool MathUtil::IsNearlyTheSameForTesting(float left, float right) { +bool MathUtil::IsFloatNearlyTheSame(float left, float right) { return IsNearlyTheSame(left, right); } diff --git a/chromium/cc/base/math_util.h b/chromium/cc/base/math_util.h index 7bccfea3718..f2c3784b97b 100644 --- a/chromium/cc/base/math_util.h +++ b/chromium/cc/base/math_util.h @@ -219,6 +219,10 @@ class CC_BASE_EXPORT MathUtil { static gfx::Vector2dF ComputeTransform2dScaleComponents(const gfx::Transform&, float fallbackValue); + // Returns an approximate max scale value of the transform even if it has + // perspective. Prefer to use ComputeTransform2dScaleComponents if there is no + // perspective, since it can produce more accurate results. + static float ComputeApproximateMaxScale(const gfx::Transform& transform); // Makes a rect that has the same relationship to input_outer_rect as // scale_inner_rect has to scale_outer_rect. scale_inner_rect should be @@ -296,7 +300,7 @@ class CC_BASE_EXPORT MathUtil { // Returns vector that y axis (0,1,0) transforms to under given transform. static gfx::Vector3dF GetYAxis(const gfx::Transform& transform); - static bool IsNearlyTheSameForTesting(float left, float right); + static bool IsFloatNearlyTheSame(float left, float right); static bool IsNearlyTheSameForTesting(const gfx::PointF& l, const gfx::PointF& r); static bool IsNearlyTheSameForTesting(const gfx::Point3F& l, diff --git a/chromium/cc/base/math_util_unittest.cc b/chromium/cc/base/math_util_unittest.cc index 86afb34b68b..ce765c741ff 100644 --- a/chromium/cc/base/math_util_unittest.cc +++ b/chromium/cc/base/math_util_unittest.cc @@ -457,9 +457,9 @@ TEST(MathUtilTest, RoundDownUnderflow) { } #define EXPECT_SIMILAR_VALUE(x, y) \ - EXPECT_TRUE(MathUtil::IsNearlyTheSameForTesting(x, y)) + EXPECT_TRUE(MathUtil::IsFloatNearlyTheSame(x, y)) #define EXPECT_DISSIMILAR_VALUE(x, y) \ - EXPECT_FALSE(MathUtil::IsNearlyTheSameForTesting(x, y)) + EXPECT_FALSE(MathUtil::IsFloatNearlyTheSame(x, y)) // Arbitrary point that shouldn't be different from zero. static const float zeroish = 1.0e-11f; diff --git a/chromium/cc/base/random_access_list_container.h b/chromium/cc/base/random_access_list_container.h deleted file mode 100644 index 34eb6990824..00000000000 --- a/chromium/cc/base/random_access_list_container.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2015 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. - -#ifndef CC_BASE_RANDOM_ACCESS_LIST_CONTAINER_H_ -#define CC_BASE_RANDOM_ACCESS_LIST_CONTAINER_H_ - -#include <stddef.h> - -#include <vector> - -#include "base/logging.h" -#include "cc/base/list_container_helper.h" - -namespace cc { - -// RandomAccessListContainer is a container similar to ListContainer (see -// list_container.h), but it allows random access into its elements via -// operator[]. In order to have efficient support for random access, some -// functionality is not available for RandomAccessListContainers, such as -// insert/deletes in the middle of the list. -template <class BaseElementType> -class RandomAccessListContainer { - public: - // BaseElementType is the type of raw pointers this class hands out; however, - // its derived classes might require different memory sizes. - // max_size_for_derived_class the largest memory size required for all the - // derived classes to use for allocation. - explicit RandomAccessListContainer(size_t max_size_for_derived_class) - : helper_(max_size_for_derived_class) {} - - // This constructor reserves the requested memory up front so only a single - // allocation is needed. When num_of_elements_to_reserve_for is zero, use the - // default size. - RandomAccessListContainer(size_t max_size_for_derived_class, - size_t num_of_elements_to_reserve_for) - : helper_(max_size_for_derived_class, num_of_elements_to_reserve_for) { - items_.reserve(num_of_elements_to_reserve_for); - } - - ~RandomAccessListContainer() { - for (BaseElementType* item : items_) - item->~BaseElementType(); - } - - void clear() { - for (BaseElementType* item : items_) - item->~BaseElementType(); - helper_.clear(); - items_.clear(); - } - - bool empty() const { return helper_.empty(); } - size_t size() const { return helper_.size(); } - size_t GetCapacityInBytes() const { return helper_.GetCapacityInBytes(); } - - template <typename DerivedElementType> - DerivedElementType* AllocateAndConstruct() { - auto* value = - new (helper_.Allocate(sizeof(DerivedElementType))) DerivedElementType; - items_.push_back(value); - return value; - } - - void RemoveLast() { - items_.back()->~BaseElementType(); - items_.pop_back(); - helper_.RemoveLast(); - } - - const BaseElementType* operator[](size_t index) const { - DCHECK_GE(index, 0u); - DCHECK_LT(index, items_.size()); - return items_[index]; - } - - BaseElementType* operator[](size_t index) { - DCHECK_GE(index, 0u); - DCHECK_LT(index, items_.size()); - return items_[index]; - } - - // Note that although BaseElementType objects can change, the pointer itself - // (in the vector) cannot. So this class only supports a const iterator. - using ConstIterator = typename std::vector<BaseElementType*>::const_iterator; - ConstIterator begin() const { return items_.begin(); } - ConstIterator end() const { return items_.end(); } - - private: - ListContainerHelper helper_; - std::vector<BaseElementType*> items_; -}; - -} // namespace cc - -#endif // CC_BASE_RANDOM_ACCESS_LIST_CONTAINER_H_ diff --git a/chromium/cc/base/random_access_list_container_unittest.cc b/chromium/cc/base/random_access_list_container_unittest.cc deleted file mode 100644 index c02ca784aef..00000000000 --- a/chromium/cc/base/random_access_list_container_unittest.cc +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2015 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/base/random_access_list_container.h" - -#include <stddef.h> - -#include <algorithm> -#include <vector> - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cc { -namespace { - -class Base { - public: - virtual ~Base() {} - int get_value() const { return value_; } - - protected: - explicit Base(int value) : value_(value) {} - - int value_; -}; - -const int kMagicNumberOne = 1; -const int kMagicNumberTwo = 2; -const int kMagicNumberThree = 3; - -class Derived1 : public Base { - public: - Derived1() : Base(kMagicNumberOne) {} -}; - -class Derived2 : public Base { - public: - Derived2() : Base(kMagicNumberTwo) {} -}; - -class Derived3 : public Base { - public: - Derived3() : Base(kMagicNumberThree) {} -}; - -size_t LargestDerivedElementSize() { - static_assert(sizeof(Derived1) >= sizeof(Derived2), - "Derived2 is larger than Derived1"); - static_assert(sizeof(Derived1) >= sizeof(Derived3), - "Derived3 is larger than Derived1"); - return sizeof(Derived1); -} - -TEST(RandomAccessListContainerTest, RandomAccess) { - RandomAccessListContainer<Base> list(LargestDerivedElementSize(), 1); - - list.AllocateAndConstruct<Derived1>(); - list.AllocateAndConstruct<Derived2>(); - list.AllocateAndConstruct<Derived3>(); - list.AllocateAndConstruct<Derived1>(); - list.AllocateAndConstruct<Derived2>(); - list.AllocateAndConstruct<Derived3>(); - - EXPECT_EQ(kMagicNumberOne, list[0]->get_value()); - EXPECT_EQ(kMagicNumberTwo, list[1]->get_value()); - EXPECT_EQ(kMagicNumberThree, list[2]->get_value()); - EXPECT_EQ(kMagicNumberOne, list[3]->get_value()); - EXPECT_EQ(kMagicNumberTwo, list[4]->get_value()); - EXPECT_EQ(kMagicNumberThree, list[5]->get_value()); - - list.RemoveLast(); - list.RemoveLast(); - list.RemoveLast(); - - EXPECT_EQ(kMagicNumberOne, list[0]->get_value()); - EXPECT_EQ(kMagicNumberTwo, list[1]->get_value()); - EXPECT_EQ(kMagicNumberThree, list[2]->get_value()); - - list.AllocateAndConstruct<Derived3>(); - list.AllocateAndConstruct<Derived2>(); - list.AllocateAndConstruct<Derived1>(); - - EXPECT_EQ(kMagicNumberOne, list[0]->get_value()); - EXPECT_EQ(kMagicNumberTwo, list[1]->get_value()); - EXPECT_EQ(kMagicNumberThree, list[2]->get_value()); - EXPECT_EQ(kMagicNumberThree, list[3]->get_value()); - EXPECT_EQ(kMagicNumberTwo, list[4]->get_value()); - EXPECT_EQ(kMagicNumberOne, list[5]->get_value()); -} - -TEST(RandomAccessListContainerTest, Clear) { - RandomAccessListContainer<Base> list(LargestDerivedElementSize(), 1); - - list.AllocateAndConstruct<Derived1>(); - list.AllocateAndConstruct<Derived2>(); - - EXPECT_EQ(kMagicNumberOne, list[0]->get_value()); - EXPECT_EQ(kMagicNumberTwo, list[1]->get_value()); - - list.clear(); - list.AllocateAndConstruct<Derived3>(); - - EXPECT_EQ(kMagicNumberThree, list[0]->get_value()); -} - -} // namespace -} // namespace cc diff --git a/chromium/cc/base/resource_id.h b/chromium/cc/base/resource_id.h index 2b25ea55fee..5ae6189cfe7 100644 --- a/chromium/cc/base/resource_id.h +++ b/chromium/cc/base/resource_id.h @@ -6,12 +6,13 @@ #define CC_BASE_RESOURCE_ID_H_ #include <stdint.h> -#include <unordered_set> + +#include "base/containers/flat_set.h" namespace cc { using ResourceId = uint32_t; -using ResourceIdSet = std::unordered_set<ResourceId>; +using ResourceIdSet = base::flat_set<ResourceId>; } // namespace cc diff --git a/chromium/cc/base/rtree.cc b/chromium/cc/base/rtree.cc index 1cb0f997cd0..0859fcc3d13 100644 --- a/chromium/cc/base/rtree.cc +++ b/chromium/cc/base/rtree.cc @@ -23,11 +23,8 @@ RTree::Node* RTree::AllocateNodeAtLevel(int level) { // We don't allow reallocations, since that would invalidate references to // existing nodes, so verify that capacity > size. DCHECK_GT(nodes_.capacity(), nodes_.size()); - nodes_.emplace_back(); - Node& node = nodes_.back(); - node.num_children = 0; - node.level = level; - return &node; + nodes_.emplace_back(level); + return &nodes_.back(); } RTree::Branch RTree::BuildRecursive(std::vector<Branch>* branches, int level) { @@ -112,9 +109,11 @@ RTree::Branch RTree::BuildRecursive(std::vector<Branch>* branches, int level) { return BuildRecursive(branches, level + 1); } -void RTree::Search(const gfx::Rect& query, std::vector<size_t>* results) const { +std::vector<size_t> RTree::Search(const gfx::Rect& query) const { + std::vector<size_t> results; if (num_data_elements_ > 0 && query.Intersects(root_.bounds)) - SearchRecursive(root_.subtree, query, results); + SearchRecursive(root_.subtree, query, &results); + return results; } void RTree::SearchRecursive(Node* node, diff --git a/chromium/cc/base/rtree.h b/chromium/cc/base/rtree.h index 3a19c29ac3d..3d4d742c93e 100644 --- a/chromium/cc/base/rtree.h +++ b/chromium/cc/base/rtree.h @@ -40,6 +40,16 @@ class CC_BASE_EXPORT RTree { RTree(); ~RTree(); + // Constructs the rtree from a given container of gfx::Rects. Queries using + // Search will then return indices into this container. + template <typename Container> + void Build(const Container& items) { + Build(items, [](const gfx::Rect& bounds) { return bounds; }); + } + + // Build helper that takes a container and a function used to get gfx::Rect + // from each item. That is, "bounds_getter(items[i]);" should return a + // gfx::Rect representing the bounds of items[i] for each i. template <typename Container, typename Functor> void Build(const Container& items, const Functor& bounds_getter) { DCHECK_EQ(0u, num_data_elements_); @@ -52,10 +62,7 @@ class CC_BASE_EXPORT RTree { if (bounds.IsEmpty()) continue; - branches.push_back(Branch()); - Branch& branch = branches.back(); - branch.bounds = bounds; - branch.index = i; + branches.emplace_back(i, bounds); } num_data_elements_ = branches.size(); @@ -89,13 +96,11 @@ class CC_BASE_EXPORT RTree { static_cast<size_t>(kMinChildren)); } - template <typename Container> - void Build(const Container& items) { - Build(items, [](const gfx::Rect& bounds) { return bounds; }); - } - - void Search(const gfx::Rect& query, std::vector<size_t>* results) const; + // Given a query rect, returns sorted indices of elements that were used to + // construct this rtree. + std::vector<size_t> Search(const gfx::Rect& query) const; + // Returns the total bounds of all items in this rtree. gfx::Rect GetBounds() const; private: @@ -115,12 +120,18 @@ class CC_BASE_EXPORT RTree { size_t index; }; gfx::Rect bounds; + + Branch() {} + Branch(size_t index, const gfx::Rect& bounds) + : index(index), bounds(bounds) {} }; struct Node { uint16_t num_children; uint16_t level; Branch children[kMaxChildren]; + + explicit Node(uint16_t level) : num_children(0), level(level) {} }; void SearchRecursive(Node* root, diff --git a/chromium/cc/base/rtree_perftest.cc b/chromium/cc/base/rtree_perftest.cc index d18d0f66fdf..22445338d8e 100644 --- a/chromium/cc/base/rtree_perftest.cc +++ b/chromium/cc/base/rtree_perftest.cc @@ -15,6 +15,14 @@ static const int kTimeLimitMillis = 2000; static const int kWarmupRuns = 5; static const int kTimeCheckInterval = 10; +template <typename Container> +size_t Accumulate(const Container& container) { + size_t result = 0; + for (size_t index : container) + result += index; + return result; +} + class RTreePerfTest : public testing::Test { public: RTreePerfTest() @@ -50,8 +58,7 @@ class RTreePerfTest : public testing::Test { timer_.Reset(); do { - std::vector<size_t> results; - rtree.Search(queries[query_index], &results); + Accumulate(rtree.Search(queries[query_index])); query_index = (query_index + 1) % queries.size(); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); diff --git a/chromium/cc/base/rtree_unittest.cc b/chromium/cc/base/rtree_unittest.cc index 7b13f352c03..487d0c4f9c2 100644 --- a/chromium/cc/base/rtree_unittest.cc +++ b/chromium/cc/base/rtree_unittest.cc @@ -35,22 +35,20 @@ TEST(RTreeTest, NoOverlap) { RTree rtree; rtree.Build(rects); - std::vector<size_t> results; - rtree.Search(gfx::Rect(0, 0, 50, 50), &results); + std::vector<size_t> results = rtree.Search(gfx::Rect(0, 0, 50, 50)); ASSERT_EQ(2500u, results.size()); + // Note that the results have to be sorted. for (size_t i = 0; i < 2500; ++i) { ASSERT_EQ(results[i], i); } - results.clear(); - rtree.Search(gfx::Rect(0, 0, 50, 49), &results); + results = rtree.Search(gfx::Rect(0, 0, 50, 49)); ASSERT_EQ(2450u, results.size()); for (size_t i = 0; i < 2450; ++i) { ASSERT_EQ(results[i], i); } - results.clear(); - rtree.Search(gfx::Rect(5, 6, 1, 1), &results); + results = rtree.Search(gfx::Rect(5, 6, 1, 1)); ASSERT_EQ(1u, results.size()); EXPECT_EQ(6u * 50 + 5u, results[0]); } @@ -66,21 +64,49 @@ TEST(RTreeTest, Overlap) { RTree rtree; rtree.Build(rects); - std::vector<size_t> results; - rtree.Search(gfx::Rect(0, 0, 1, 1), &results); + std::vector<size_t> results = rtree.Search(gfx::Rect(0, 0, 1, 1)); ASSERT_EQ(2500u, results.size()); + // Both the checks for the elements assume elements are sorted. for (size_t i = 0; i < 2500; ++i) { ASSERT_EQ(results[i], i); } - results.clear(); - rtree.Search(gfx::Rect(0, 49, 1, 1), &results); + results = rtree.Search(gfx::Rect(0, 49, 1, 1)); ASSERT_EQ(50u, results.size()); for (size_t i = 0; i < 50; ++i) { EXPECT_EQ(results[i], 2450u + i); } } +static void VerifySorted(const std::vector<size_t>& results) { + for (size_t i = 1; i < results.size(); ++i) { + ASSERT_LT(results[i - 1], results[i]); + } +} + +TEST(RTreeTest, SortedResults) { + // This test verifies that all queries return sorted elements. + std::vector<gfx::Rect> rects; + for (int y = 0; y < 50; ++y) { + for (int x = 0; x < 50; ++x) { + rects.push_back(gfx::Rect(x, y, 1, 1)); + rects.push_back(gfx::Rect(x, y, 2, 2)); + rects.push_back(gfx::Rect(x, y, 3, 3)); + } + } + + RTree rtree; + rtree.Build(rects); + + for (int y = 0; y < 50; ++y) { + for (int x = 0; x < 50; ++x) { + VerifySorted(rtree.Search(gfx::Rect(x, y, 1, 1))); + VerifySorted(rtree.Search(gfx::Rect(x, y, 50, 1))); + VerifySorted(rtree.Search(gfx::Rect(x, y, 1, 50))); + } + } +} + TEST(RTreeTest, GetBoundsEmpty) { RTree rtree; ASSERT_EQ(gfx::Rect(), rtree.GetBounds()); diff --git a/chromium/cc/base/simple_enclosed_region.cc b/chromium/cc/base/simple_enclosed_region.cc index 50e905bb68c..d3f3385cb9c 100644 --- a/chromium/cc/base/simple_enclosed_region.cc +++ b/chromium/cc/base/simple_enclosed_region.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "cc/base/region.h" +#include "ui/gfx/geometry/rect.h" namespace cc { @@ -124,10 +125,22 @@ void SimpleEnclosedRegion::Union(const gfx::Rect& new_rect) { } rect_.SetRect(left, top, right - left, bottom - top); - + int64_t rect_area = static_cast<int64_t>(rect_.width()) * rect_.height(); gfx::Rect adjusted_new_rect( new_left, new_top, new_right - new_left, new_bottom - new_top); - if (RectIsLargerArea(adjusted_new_rect, rect_)) + int64_t adjust_new_rect_area = + static_cast<int64_t>(adjusted_new_rect.width()) * + adjusted_new_rect.height(); + gfx::Rect overlap = gfx::IntersectRects(rect_, adjusted_new_rect); + int64_t overlap_area = + static_cast<int64_t>(overlap.width()) * overlap.height(); + + // Based on the assumption that as we compute occlusion, each step is + // more likely to be occluded by things added to this region more recently due + // to the way we build scenes with overlapping elements adjacent to each other + // in the Z order. So, the area of the new rect has a weight of 2 in the + // weighted area calculation. + if (adjust_new_rect_area * 2 > rect_area + overlap_area) rect_ = adjusted_new_rect; } diff --git a/chromium/cc/base/simple_enclosed_region_unittest.cc b/chromium/cc/base/simple_enclosed_region_unittest.cc index ca3ad619874..66f3f877362 100644 --- a/chromium/cc/base/simple_enclosed_region_unittest.cc +++ b/chromium/cc/base/simple_enclosed_region_unittest.cc @@ -337,80 +337,134 @@ TEST(SimpleEnclosedRegionTest, Union) { r.Union(gfx::Rect(2, 3, 9, 10)); EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r)); - // Union with a smaller disjoint rect is ignored. - r.Union(gfx::Rect(20, 21, 9, 9)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r)); + // Union with a second disjoint rect with area larger than half of the first + // one. + // +---+ +--+ + // | | | | + // +---+ +--+ + r.Union(gfx::Rect(20, 20, 6, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(20, 20, 6, 10), r)); - // Union with a smaller overlapping rect is ignored. - r.Union(gfx::Rect(3, 4, 9, 9)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r)); + // Union with a second disjoint rect with area smaller than half of the first + // one. + // +----+ +--+ + // | | +--+ + // +----+ + r.Union(gfx::Rect(2, 3, 3, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(20, 20, 6, 10), r)); - // Union with an equal sized rect can be either one. - r.Union(gfx::Rect(4, 4, 9, 10)); - EXPECT_EQ(1u, r.GetRegionComplexity()); - EXPECT_TRUE(r.bounds() == gfx::Rect(2, 3, 9, 10) || - r.bounds() == gfx::Rect(4, 4, 9, 10)); + // Union with a second disjoint rect with area larger than the first one. + // +---+ +-------+ + // | | | | + // +---+ +-------+ + r.Union(gfx::Rect(2, 3, 15, 15)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 15, 15), r)); - // Union with a larger disjoint rect is taken. - r.Union(gfx::Rect(20, 21, 12, 13)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(20, 21, 12, 13), r)); + // Union with rect which extends from the first one: + // +----------+ + // | 1 | 2 | + // +-----+----+ + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(20, 10, 5, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 15, 10), r)); - // Union with a larger overlapping rect is taken. - r.Union(gfx::Rect(19, 19, 12, 14)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(19, 19, 12, 14), r)); + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(10, 5, 10, 5)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 5, 10, 15), r)); - // True also when the rect covers one edge of the existing region. r = gfx::Rect(10, 10, 10, 10); - r.Union(gfx::Rect(12, 7, 9, 16)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 7, 9, 16), r)); + r.Union(gfx::Rect(5, 10, 5, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(5, 10, 15, 10), r)); r = gfx::Rect(10, 10, 10, 10); - r.Union(gfx::Rect(9, 7, 9, 16)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 7, 9, 16), r)); + r.Union(gfx::Rect(10, 20, 10, 5)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 15), r)); + // Union with rect which overlaps and extends from the first one: + // +----+--+---+ + // | 1 | |2 | + // | | | | + // +----+--+---+ r = gfx::Rect(10, 10, 10, 10); - r.Union(gfx::Rect(7, 12, 16, 9)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(7, 12, 16, 9), r)); + r.Union(gfx::Rect(10, 2, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 2, 10, 18), r)); r = gfx::Rect(10, 10, 10, 10); - r.Union(gfx::Rect(7, 9, 16, 9)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(7, 9, 16, 9), r)); + r.Union(gfx::Rect(2, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 10, 18, 10), r)); - // But if the existing region can be expanded to make a larger rect, then it - // will. Union area is 9*12 = 108. By merging, we make a rect with an area of - // 10*11 = 110. The resulting rect is expanded as far as possible while - // remaining enclosed in the Union. r = gfx::Rect(10, 10, 10, 10); - r.Union(gfx::Rect(12, 9, 9, 12)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 11, 10), r)); + r.Union(gfx::Rect(10, 18, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 18), r)); r = gfx::Rect(10, 10, 10, 10); - r.Union(gfx::Rect(9, 9, 9, 12)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 10, 11, 10), r)); + r.Union(gfx::Rect(18, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 18, 10), r)); + // Union with a second rect which overlaps with the first one and + // area(rect 1) + area(overlap) > area(rect 2)*2 and + // area(rect 1) < area(rect 2)*2. + // +---+ + // +---|+ 2| + // | +---+ + // | 1 | + // +----+ (same figure for next test case.) r = gfx::Rect(10, 10, 10, 10); - r.Union(gfx::Rect(9, 12, 12, 9)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 11), r)); + r.Union(gfx::Rect(14, 12, 8, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 10), r)); r = gfx::Rect(10, 10, 10, 10); - r.Union(gfx::Rect(9, 9, 12, 9)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 9, 10, 11), r)); + r.Union(gfx::Rect(11, 9, 8, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 10), r)); - r = gfx::Rect(12, 9, 9, 12); - r.Union(gfx::Rect(10, 10, 10, 10)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 11, 10), r)); + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(9, 12, 8, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(13, 11, 8, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 10), r)); - r = gfx::Rect(9, 9, 9, 12); - r.Union(gfx::Rect(10, 10, 10, 10)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 10, 11, 10), r)); + // Union with a second rect which overlaps with the first one and + // area(rect 1) + area(overlap) < area(rect 2)*2 and + // area(rect 1) > area(rect 2). + r = gfx::Rect(10, 10, 5, 5); + r.Union(gfx::Rect(7, 7, 4, 4)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(7, 7, 4, 4), r)); + + r = gfx::Rect(10, 10, 5, 5); + r.Union(gfx::Rect(14, 7, 4, 4)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(14, 7, 4, 4), r)); + + r = gfx::Rect(10, 10, 5, 5); + r.Union(gfx::Rect(7, 14, 4, 4)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(7, 14, 4, 4), r)); + + r = gfx::Rect(10, 10, 5, 5); + r.Union(gfx::Rect(14, 14, 4, 4)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(14, 14, 4, 4), r)); + + // Union with a second rect which overlaps with the first one and the new + // unioned rect should combine both rect. + // +---+-+-----------+ + // | 1| | 2 | + // | +-|-----------+ + // +-----+ + r = gfx::Rect(10, 10, 5, 5); + r.Union(gfx::Rect(5, 11, 7, 4)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(5, 11, 10, 4), r)); + + r = gfx::Rect(10, 10, 5, 5); + r.Union(gfx::Rect(13, 10, 7, 4)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r)); - r = gfx::Rect(9, 12, 12, 9); - r.Union(gfx::Rect(10, 10, 10, 10)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 11), r)); + r = gfx::Rect(10, 10, 5, 5); + r.Union(gfx::Rect(10, 12, 4, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 9), r)); - r = gfx::Rect(9, 9, 12, 9); - r.Union(gfx::Rect(10, 10, 10, 10)); - EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 9, 10, 11), r)); + r = gfx::Rect(10, 10, 5, 5); + r.Union(gfx::Rect(11, 11, 4, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(11, 10, 4, 8), r)); } TEST(SimpleEnclosedRegionTest, Subtract) { diff --git a/chromium/cc/base/switches.cc b/chromium/cc/base/switches.cc index 1c562c9b4d7..f4aff0cf2cf 100644 --- a/chromium/cc/base/switches.cc +++ b/chromium/cc/base/switches.cc @@ -43,10 +43,6 @@ const char kSlowDownRasterScaleFactor[] = "slow-down-raster-scale-factor"; // Compress tile textures for GPUs supporting it. const char kEnableTileCompression[] = "enable-tile-compression"; -// Convert rasterization and compositing inputs to the output color space -// before operating on them. -const char kEnableColorCorrectRendering[] = "enable-color-correct-rendering"; - // Enables the GPU benchmarking extension const char kEnableGpuBenchmarking[] = "enable-gpu-benchmarking"; @@ -58,7 +54,7 @@ const char kEnableSurfaceSynchronization[] = "enable-surface-synchronization"; // Renders a border around compositor layers to help debug and study // layer compositing. const char kShowCompositedLayerBorders[] = "show-composited-layer-borders"; -const char kUIShowCompositedLayerBorders[] = "ui-show-layer-borders"; +const char kUIShowCompositedLayerBorders[] = "ui-show-composited-layer-borders"; const char kCompositedRenderPassBorders[] = "renderpass"; const char kCompositedSurfaceBorders[] = "surface"; const char kCompositedLayerBorders[] = "layer"; @@ -117,5 +113,10 @@ const char kCCLayerTreeTestLongTimeout[] = "cc-layer-tree-test-long-timeout"; // Makes pixel tests write their output instead of read it. const char kCCRebaselinePixeltests[] = "cc-rebaseline-pixeltests"; +// Disable re-use of non-exact resources to fulfill ResourcePool requests. +// Intended only for use in layout or pixel tests to reduce noise. +const char kDisallowNonExactResourceReuse[] = + "disallow-non-exact-resource-reuse"; + } // namespace switches } // namespace cc diff --git a/chromium/cc/base/switches.h b/chromium/cc/base/switches.h index 81177019bdb..3f58249334a 100644 --- a/chromium/cc/base/switches.h +++ b/chromium/cc/base/switches.h @@ -28,7 +28,6 @@ CC_BASE_EXPORT extern const char kStrictLayerPropertyChangeChecking[]; CC_BASE_EXPORT extern const char kEnableTileCompression[]; // Switches for both the renderer and ui compositors. -CC_BASE_EXPORT extern const char kEnableColorCorrectRendering[]; CC_BASE_EXPORT extern const char kEnableGpuBenchmarking[]; CC_BASE_EXPORT extern const char kEnableSurfaceSynchronization[]; @@ -53,10 +52,11 @@ CC_BASE_EXPORT extern const char kCompositedRenderPassBorders[]; CC_BASE_EXPORT extern const char kCompositedSurfaceBorders[]; CC_BASE_EXPORT extern const char kCompositedLayerBorders[]; -// Unit test related. +// Test related. CC_BASE_EXPORT extern const char kCCLayerTreeTestNoTimeout[]; CC_BASE_EXPORT extern const char kCCLayerTreeTestLongTimeout[]; CC_BASE_EXPORT extern const char kCCRebaselinePixeltests[]; +CC_BASE_EXPORT extern const char kDisallowNonExactResourceReuse[]; } // namespace switches } // namespace cc diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc index 6314fdef87e..c19dac1e6d3 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc @@ -139,6 +139,7 @@ void RasterizeAndRecordBenchmark::RunOnLayer(PictureLayer* layer) { return; ContentLayerClient* painter = layer->client(); + RecordingSource recording_source; for (int mode_index = 0; mode_index < RecordingSource::RECORDING_MODE_COUNT; mode_index++) { @@ -158,11 +159,8 @@ void RasterizeAndRecordBenchmark::RunOnLayer(PictureLayer* layer) { do { display_list = painter->PaintContentsToDisplayList(painting_control); - if (display_list->ShouldBeAnalyzedForSolidColor()) { - gfx::Size layer_size = layer->paint_properties().bounds; - skia::AnalysisCanvas canvas(layer_size.width(), layer_size.height()); - display_list->Raster(&canvas, nullptr, gfx::Rect(layer_size), 1.f); - } + recording_source.UpdateDisplayItemList( + display_list, painter->GetApproximateUnsharedMemoryUsage()); if (memory_used) { // Verify we are recording the same thing each time. diff --git a/chromium/cc/benchmarks/unittest_only_benchmark.h b/chromium/cc/benchmarks/unittest_only_benchmark.h index 277c4274dba..da95605dcc3 100644 --- a/chromium/cc/benchmarks/unittest_only_benchmark.h +++ b/chromium/cc/benchmarks/unittest_only_benchmark.h @@ -6,6 +6,7 @@ #define CC_BENCHMARKS_UNITTEST_ONLY_BENCHMARK_H_ #include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" #include "cc/benchmarks/micro_benchmark.h" namespace cc { diff --git a/chromium/cc/blink/web_display_item_list_impl.cc b/chromium/cc/blink/web_display_item_list_impl.cc index 889566b24ab..c1c47de0c9b 100644 --- a/chromium/cc/blink/web_display_item_list_impl.cc +++ b/chromium/cc/blink/web_display_item_list_impl.cc @@ -23,6 +23,7 @@ #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/safe_integer_conversions.h" +#include "ui/gfx/skia_util.h" #include "ui/gfx/transform.h" namespace cc_blink { @@ -41,9 +42,10 @@ WebDisplayItemListImpl::WebDisplayItemListImpl( void WebDisplayItemListImpl::AppendDrawingItem( const blink::WebRect& visual_rect, - sk_sp<const cc::PaintRecord> record) { + sk_sp<const cc::PaintRecord> record, + const blink::WebRect& record_bounds) { display_item_list_->CreateAndAppendDrawingItem<cc::DrawingDisplayItem>( - visual_rect, std::move(record)); + visual_rect, std::move(record), gfx::RectToSkRect(record_bounds)); } void WebDisplayItemListImpl::AppendClipItem( diff --git a/chromium/cc/blink/web_display_item_list_impl.h b/chromium/cc/blink/web_display_item_list_impl.h index b540f1cd563..e3fa9b87920 100644 --- a/chromium/cc/blink/web_display_item_list_impl.h +++ b/chromium/cc/blink/web_display_item_list_impl.h @@ -41,7 +41,8 @@ class WebDisplayItemListImpl : public blink::WebDisplayItemList { // blink::WebDisplayItemList implementation. void AppendDrawingItem(const blink::WebRect& visual_rect, - sk_sp<const cc::PaintRecord> record) override; + sk_sp<const cc::PaintRecord> record, + const blink::WebRect& record_bounds) override; void AppendClipItem( const blink::WebRect& clip_rect, const blink::WebVector<SkRRect>& rounded_clip_rects) override; diff --git a/chromium/cc/blink/web_image_layer_impl.cc b/chromium/cc/blink/web_image_layer_impl.cc index f9ab77718d8..7c965e79e71 100644 --- a/chromium/cc/blink/web_image_layer_impl.cc +++ b/chromium/cc/blink/web_image_layer_impl.cc @@ -22,11 +22,12 @@ blink::WebLayer* WebImageLayerImpl::Layer() { return layer_.get(); } -void WebImageLayerImpl::SetImage(const SkImage* image) { - static_cast<cc::PictureImageLayer*>(layer_->layer()) - ->SetImage(sk_ref_sp(image)); +void WebImageLayerImpl::SetImage(cc::PaintImage image) { static_cast<WebLayerImplFixedBounds*>(layer_.get()) - ->SetFixedBounds(gfx::Size(image->width(), image->height())); + ->SetFixedBounds( + gfx::Size(image.sk_image()->width(), image.sk_image()->height())); + static_cast<cc::PictureImageLayer*>(layer_->layer()) + ->SetImage(std::move(image)); } void WebImageLayerImpl::SetNearestNeighbor(bool nearest_neighbor) { diff --git a/chromium/cc/blink/web_image_layer_impl.h b/chromium/cc/blink/web_image_layer_impl.h index a6c3452e410..934bdfa1d97 100644 --- a/chromium/cc/blink/web_image_layer_impl.h +++ b/chromium/cc/blink/web_image_layer_impl.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "cc/blink/cc_blink_export.h" +#include "cc/paint/paint_image.h" #include "third_party/WebKit/public/platform/WebImageLayer.h" namespace cc_blink { @@ -22,7 +23,7 @@ class WebImageLayerImpl : public blink::WebImageLayer { // blink::WebImageLayer implementation. blink::WebLayer* Layer() override; - void SetImage(const SkImage* image) override; + void SetImage(cc::PaintImage image) override; void SetNearestNeighbor(bool nearest_neighbor) override; private: diff --git a/chromium/cc/blink/web_layer_impl.cc b/chromium/cc/blink/web_layer_impl.cc index e44d007e6ea..5526a70f430 100644 --- a/chromium/cc/blink/web_layer_impl.cc +++ b/chromium/cc/blink/web_layer_impl.cc @@ -299,10 +299,12 @@ WebVector<WebRect> WebLayerImpl::NonFastScrollableRegion() const { return result; } -void WebLayerImpl::SetTouchEventHandlerRegion(const WebVector<WebRect>& rects) { +void WebLayerImpl::SetTouchEventHandlerRegion( + const WebVector<blink::WebTouchInfo>& touch_info) { cc::Region region; - for (size_t i = 0; i < rects.size(); ++i) - region.Union(rects[i]); + for (size_t i = 0; i < touch_info.size(); ++i) + region.Union(touch_info[i].rect); + // TODO(xidachen): set the touch action bit for the region. layer_->SetTouchEventHandlerRegion(region); } diff --git a/chromium/cc/blink/web_layer_impl.h b/chromium/cc/blink/web_layer_impl.h index 573bd2ff393..2cb948e4291 100644 --- a/chromium/cc/blink/web_layer_impl.h +++ b/chromium/cc/blink/web_layer_impl.h @@ -106,7 +106,7 @@ class CC_BLINK_EXPORT WebLayerImpl : public NON_EXPORTED_BASE(blink::WebLayer) { const blink::WebVector<blink::WebRect>& region) override; blink::WebVector<blink::WebRect> NonFastScrollableRegion() const override; void SetTouchEventHandlerRegion( - const blink::WebVector<blink::WebRect>& region) override; + const blink::WebVector<blink::WebTouchInfo>& touch_info) override; blink::WebVector<blink::WebRect> TouchEventHandlerRegion() const override; void SetIsContainerForFixedPositionLayers(bool is_container) override; bool IsContainerForFixedPositionLayers() const override; diff --git a/chromium/cc/blink/web_scrollbar_layer_impl.cc b/chromium/cc/blink/web_scrollbar_layer_impl.cc index 2a3f2a12f17..0db9d0f5c5d 100644 --- a/chromium/cc/blink/web_scrollbar_layer_impl.cc +++ b/chromium/cc/blink/web_scrollbar_layer_impl.cc @@ -14,6 +14,7 @@ #include "cc/layers/painted_scrollbar_layer.h" #include "cc/layers/scrollbar_layer_interface.h" #include "cc/layers/solid_color_scrollbar_layer.h" +#include "cc/trees/element_id.h" using cc::PaintedOverlayScrollbarLayer; using cc::PaintedScrollbarLayer; @@ -41,12 +42,12 @@ WebScrollbarLayerImpl::WebScrollbarLayerImpl( base::MakeUnique<ScrollbarImpl>(std::move(scrollbar), painter, std::move(geometry)), - cc::Layer::INVALID_ID)) + cc::ElementId())) : new WebLayerImpl(PaintedScrollbarLayer::Create( base::MakeUnique<ScrollbarImpl>(std::move(scrollbar), painter, std::move(geometry)), - cc::Layer::INVALID_ID))) {} + cc::ElementId()))) {} WebScrollbarLayerImpl::WebScrollbarLayerImpl( blink::WebScrollbar::Orientation orientation, @@ -58,7 +59,7 @@ WebScrollbarLayerImpl::WebScrollbarLayerImpl( thumb_thickness, track_start, is_left_side_vertical_scrollbar, - cc::Layer::INVALID_ID))) {} + cc::ElementId()))) {} WebScrollbarLayerImpl::~WebScrollbarLayerImpl() { } @@ -69,9 +70,13 @@ blink::WebLayer* WebScrollbarLayerImpl::Layer() { void WebScrollbarLayerImpl::SetScrollLayer(blink::WebLayer* layer) { cc::Layer* scroll_layer = - layer ? static_cast<WebLayerImpl*>(layer)->layer() : 0; - layer_->layer()->ToScrollbarLayer()->SetScrollLayer( - scroll_layer ? scroll_layer->id() : cc::Layer::INVALID_ID); + layer ? static_cast<WebLayerImpl*>(layer)->layer() : nullptr; + layer_->layer()->ToScrollbarLayer()->SetScrollElementId( + scroll_layer ? scroll_layer->element_id() : cc::ElementId()); +} + +void WebScrollbarLayerImpl::SetElementId(const cc::ElementId& element_id) { + layer_->SetElementId(element_id); } } // namespace cc_blink diff --git a/chromium/cc/blink/web_scrollbar_layer_impl.h b/chromium/cc/blink/web_scrollbar_layer_impl.h index 952277635c0..e8e0fe438aa 100644 --- a/chromium/cc/blink/web_scrollbar_layer_impl.h +++ b/chromium/cc/blink/web_scrollbar_layer_impl.h @@ -39,6 +39,8 @@ class WebScrollbarLayerImpl : public blink::WebScrollbarLayer { blink::WebLayer* Layer() override; void SetScrollLayer(blink::WebLayer* layer) override; + void SetElementId(const cc::ElementId&) override; + private: std::unique_ptr<WebLayerImpl> layer_; diff --git a/chromium/cc/debug/debug_colors.cc b/chromium/cc/debug/debug_colors.cc index db0fbd70287..89e3d0bc3bb 100644 --- a/chromium/cc/debug/debug_colors.cc +++ b/chromium/cc/debug/debug_colors.cc @@ -78,7 +78,7 @@ SkColor DebugColors::HighResTileBorderColor() { return SkColorSetARGB(100, 80, 200, 200); } int DebugColors::HighResTileBorderWidth(float device_scale_factor) { - return Scale(1, device_scale_factor); + return Scale(3, device_scale_factor); } // Low-res tile borders are purple. diff --git a/chromium/cc/input/browser_controls_offset_manager.h b/chromium/cc/input/browser_controls_offset_manager.h index 88e76fa113d..ae4d9e4268f 100644 --- a/chromium/cc/input/browser_controls_offset_manager.h +++ b/chromium/cc/input/browser_controls_offset_manager.h @@ -8,7 +8,6 @@ #include <memory> #include "base/macros.h" -#include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "cc/input/browser_controls_state.h" #include "cc/layers/layer_impl.h" @@ -20,8 +19,7 @@ namespace cc { class BrowserControlsOffsetManagerClient; // Manages the position of the browser controls. -class CC_EXPORT BrowserControlsOffsetManager - : public base::SupportsWeakPtr<BrowserControlsOffsetManager> { +class CC_EXPORT BrowserControlsOffsetManager { public: enum AnimationDirection { NO_ANIMATION, SHOWING_CONTROLS, HIDING_CONTROLS }; diff --git a/chromium/cc/input/main_thread_scrolling_reason.h b/chromium/cc/input/main_thread_scrolling_reason.h index a8c586eee95..e7d88471aff 100644 --- a/chromium/cc/input/main_thread_scrolling_reason.h +++ b/chromium/cc/input/main_thread_scrolling_reason.h @@ -44,7 +44,8 @@ struct MainThreadScrollingReason { kHasBorderRadius = 1 << 19, kHasClipRelatedProperty = 1 << 20, kHasBoxShadowFromNonRootLayer = 1 << 21, - kNonCompositedReasonsLast = 21, + kIsNotStackingContextAndLCDText = 1 << 22, + kNonCompositedReasonsLast = 22, // Transient scrolling reasons. These are computed for each scroll begin. kNonFastScrollableRegion = 1 << 5, @@ -59,13 +60,14 @@ struct MainThreadScrollingReason { // New flags should increment this number but it should never be decremented // because the values are used in UMA histograms. It should also be noted // that it excludes the kNotScrollingOnMain value. - kMainThreadScrollingReasonCount = 22, + kMainThreadScrollingReasonCount = 23, }; static const uint32_t kNonCompositedReasons = kHasOpacityAndLCDText | kHasTransformAndLCDText | kBackgroundNotOpaqueInRectAndLCDText | kHasBorderRadius | - kHasClipRelatedProperty | kHasBoxShadowFromNonRootLayer; + kHasClipRelatedProperty | kHasBoxShadowFromNonRootLayer | + kIsNotStackingContextAndLCDText; // Returns true if the given MainThreadScrollingReason can be set by the main // thread. @@ -140,6 +142,8 @@ struct MainThreadScrollingReason { tracedValue->AppendString("Has clip related property"); if (reasons & MainThreadScrollingReason::kHasBoxShadowFromNonRootLayer) tracedValue->AppendString("Has box shadow from non-root layer"); + if (reasons & MainThreadScrollingReason::kIsNotStackingContextAndLCDText) + tracedValue->AppendString("Is not stacking context and LCD text"); // Transient scrolling reasons. if (reasons & MainThreadScrollingReason::kNonFastScrollableRegion) diff --git a/chromium/cc/input/scrollbar_animation_controller.cc b/chromium/cc/input/scrollbar_animation_controller.cc index 8fb3c25a48b..6234d3ce53f 100644 --- a/chromium/cc/input/scrollbar_animation_controller.cc +++ b/chromium/cc/input/scrollbar_animation_controller.cc @@ -13,86 +13,82 @@ namespace cc { std::unique_ptr<ScrollbarAnimationController> ScrollbarAnimationController::CreateScrollbarAnimationControllerAndroid( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarAnimationControllerClient* client, base::TimeDelta fade_delay, - base::TimeDelta fade_out_resize_delay, - base::TimeDelta fade_duration) { - return base::WrapUnique( - new ScrollbarAnimationController(scroll_layer_id, client, fade_delay, - fade_out_resize_delay, fade_duration)); + base::TimeDelta fade_duration, + float initial_opacity) { + return base::WrapUnique(new ScrollbarAnimationController( + scroll_element_id, client, fade_delay, fade_duration, initial_opacity)); } std::unique_ptr<ScrollbarAnimationController> ScrollbarAnimationController::CreateScrollbarAnimationControllerAuraOverlay( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarAnimationControllerClient* client, base::TimeDelta fade_delay, - base::TimeDelta fade_out_resize_delay, base::TimeDelta fade_duration, - base::TimeDelta thinning_duration) { + base::TimeDelta thinning_duration, + float initial_opacity) { return base::WrapUnique(new ScrollbarAnimationController( - scroll_layer_id, client, fade_delay, fade_out_resize_delay, - fade_duration, thinning_duration)); + scroll_element_id, client, fade_delay, fade_duration, thinning_duration, + initial_opacity)); } ScrollbarAnimationController::ScrollbarAnimationController( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarAnimationControllerClient* client, base::TimeDelta fade_delay, - base::TimeDelta fade_out_resize_delay, - base::TimeDelta fade_duration) + base::TimeDelta fade_duration, + float initial_opacity) : client_(client), fade_delay_(fade_delay), - fade_out_resize_delay_(fade_out_resize_delay), fade_duration_(fade_duration), - need_trigger_scrollbar_show_(false), + need_trigger_scrollbar_fade_in_(false), is_animating_(false), animation_change_(NONE), - scroll_layer_id_(scroll_layer_id), + scroll_element_id_(scroll_element_id), currently_scrolling_(false), show_in_fast_scroll_(false), - opacity_(0.0f), + opacity_(initial_opacity), show_scrollbars_on_scroll_gesture_(false), need_thinning_animation_(false), - weak_factory_(this) { - ApplyOpacityToScrollbars(0.0f); -} + is_mouse_down_(false), + weak_factory_(this) {} ScrollbarAnimationController::ScrollbarAnimationController( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarAnimationControllerClient* client, base::TimeDelta fade_delay, - base::TimeDelta fade_out_resize_delay, base::TimeDelta fade_duration, - base::TimeDelta thinning_duration) + base::TimeDelta thinning_duration, + float initial_opacity) : client_(client), fade_delay_(fade_delay), - fade_out_resize_delay_(fade_out_resize_delay), fade_duration_(fade_duration), - need_trigger_scrollbar_show_(false), + need_trigger_scrollbar_fade_in_(false), is_animating_(false), animation_change_(NONE), - scroll_layer_id_(scroll_layer_id), + scroll_element_id_(scroll_element_id), currently_scrolling_(false), show_in_fast_scroll_(false), - opacity_(0.0f), + opacity_(initial_opacity), show_scrollbars_on_scroll_gesture_(true), need_thinning_animation_(true), + is_mouse_down_(false), weak_factory_(this) { vertical_controller_ = SingleScrollbarAnimationControllerThinning::Create( - scroll_layer_id, ScrollbarOrientation::VERTICAL, client, + scroll_element_id, ScrollbarOrientation::VERTICAL, client, thinning_duration); horizontal_controller_ = SingleScrollbarAnimationControllerThinning::Create( - scroll_layer_id, ScrollbarOrientation::HORIZONTAL, client, + scroll_element_id, ScrollbarOrientation::HORIZONTAL, client, thinning_duration); - ApplyOpacityToScrollbars(0.0f); } ScrollbarAnimationController::~ScrollbarAnimationController() {} ScrollbarSet ScrollbarAnimationController::Scrollbars() const { - return client_->ScrollbarsFor(scroll_layer_id_); + return client_->ScrollbarsFor(scroll_element_id_); } SingleScrollbarAnimationControllerThinning& @@ -108,6 +104,7 @@ ScrollbarAnimationController::GetScrollbarAnimationController( void ScrollbarAnimationController::StartAnimation() { DCHECK(animation_change_ != NONE); delayed_scrollbar_animation_.Cancel(); + need_trigger_scrollbar_fade_in_ = false; is_animating_ = true; last_awaken_time_ = base::TimeTicks(); client_->SetNeedsAnimateForScrollbarAnimation(); @@ -115,23 +112,20 @@ void ScrollbarAnimationController::StartAnimation() { void ScrollbarAnimationController::StopAnimation() { delayed_scrollbar_animation_.Cancel(); + need_trigger_scrollbar_fade_in_ = false; is_animating_ = false; animation_change_ = NONE; } void ScrollbarAnimationController::PostDelayedAnimation( - AnimationChange animation_change, - bool on_resize) { + AnimationChange animation_change) { animation_change_ = animation_change; - - base::TimeDelta delay = on_resize ? fade_out_resize_delay_ : fade_delay_; - delayed_scrollbar_animation_.Cancel(); delayed_scrollbar_animation_.Reset( base::Bind(&ScrollbarAnimationController::StartAnimation, weak_factory_.GetWeakPtr())); client_->PostDelayedScrollbarAnimationTask( - delayed_scrollbar_animation_.callback(), delay); + delayed_scrollbar_animation_.callback(), fade_delay_); } bool ScrollbarAnimationController::Animate(base::TimeTicks now) { @@ -196,7 +190,7 @@ void ScrollbarAnimationController::DidScrollEnd() { return; if (has_scrolled) - PostDelayedAnimation(FADE_OUT, false); + PostDelayedAnimation(FADE_OUT); } void ScrollbarAnimationController::DidScrollUpdate() { @@ -213,7 +207,7 @@ void ScrollbarAnimationController::DidScrollUpdate() { // We don't fade out scrollbar if they need thinning animation and mouse is // near. if (!need_thinning_animation_ || !MouseIsNearAnyScrollbar()) - PostDelayedAnimation(FADE_OUT, false); + PostDelayedAnimation(FADE_OUT); } else { show_in_fast_scroll_ = true; } @@ -230,39 +224,46 @@ void ScrollbarAnimationController::WillUpdateScroll() { } void ScrollbarAnimationController::DidRequestShowFromMainThread() { - // TODO(skobes): Call DidScrollUpdate here (suppressed for crbug.com/706927). + DidScrollUpdate(); } -void ScrollbarAnimationController::DidResize() { - StopAnimation(); - Show(); +void ScrollbarAnimationController::DidMouseDown() { + if (!need_thinning_animation_) + return; - // As an optimization, we avoid spamming fade delay tasks during active fast - // scrolls. - if (!currently_scrolling_) { - PostDelayedAnimation(FADE_OUT, true); - } else { - show_in_fast_scroll_ = true; - } -} + is_mouse_down_ = true; -void ScrollbarAnimationController::DidMouseDown() { - if (!need_thinning_animation_ || ScrollbarsHidden()) + if (ScrollbarsHidden()) { + if (need_trigger_scrollbar_fade_in_) { + delayed_scrollbar_animation_.Cancel(); + need_trigger_scrollbar_fade_in_ = false; + } return; + } vertical_controller_->DidMouseDown(); horizontal_controller_->DidMouseDown(); } void ScrollbarAnimationController::DidMouseUp() { - if (!need_thinning_animation_ || !Captured()) + if (!need_thinning_animation_) return; + is_mouse_down_ = false; + + if (!Captured()) { + if (MouseIsNearAnyScrollbar() && ScrollbarsHidden()) { + PostDelayedAnimation(FADE_IN); + need_trigger_scrollbar_fade_in_ = true; + } + return; + } + vertical_controller_->DidMouseUp(); horizontal_controller_->DidMouseUp(); - if (!MouseIsNearAnyScrollbar()) - PostDelayedAnimation(FADE_OUT, false); + if (!MouseIsNearAnyScrollbar() && !ScrollbarsHidden()) + PostDelayedAnimation(FADE_OUT); } void ScrollbarAnimationController::DidMouseLeave() { @@ -273,34 +274,39 @@ void ScrollbarAnimationController::DidMouseLeave() { horizontal_controller_->DidMouseLeave(); delayed_scrollbar_animation_.Cancel(); - need_trigger_scrollbar_show_ = false; + need_trigger_scrollbar_fade_in_ = false; if (ScrollbarsHidden() || Captured()) return; - PostDelayedAnimation(FADE_OUT, false); + PostDelayedAnimation(FADE_OUT); } -void ScrollbarAnimationController::DidMouseMoveNear( - ScrollbarOrientation orientation, - float distance) { +void ScrollbarAnimationController::DidMouseMove( + const gfx::PointF& device_viewport_point) { if (!need_thinning_animation_) return; - bool need_trigger_scrollbar_show_before = need_trigger_scrollbar_show_; + bool need_trigger_scrollbar_fade_in_before = need_trigger_scrollbar_fade_in_; - GetScrollbarAnimationController(orientation).DidMouseMoveNear(distance); + vertical_controller_->DidMouseMove(device_viewport_point); + horizontal_controller_->DidMouseMove(device_viewport_point); - need_trigger_scrollbar_show_ = - CalcNeedTriggerScrollbarShow(orientation, distance); - - if (Captured()) + if (Captured()) { + DCHECK(!ScrollbarsHidden()); return; + } if (ScrollbarsHidden()) { - if (need_trigger_scrollbar_show_before != need_trigger_scrollbar_show_) { - if (need_trigger_scrollbar_show_) { - PostDelayedAnimation(FADE_IN, false); + // Do not fade in scrollbar when user interacting with the content below + // scrollbar. + if (is_mouse_down_) + return; + need_trigger_scrollbar_fade_in_ = MouseIsNearAnyScrollbar(); + if (need_trigger_scrollbar_fade_in_before != + need_trigger_scrollbar_fade_in_) { + if (need_trigger_scrollbar_fade_in_) { + PostDelayedAnimation(FADE_IN); } else { delayed_scrollbar_animation_.Cancel(); } @@ -310,47 +316,36 @@ void ScrollbarAnimationController::DidMouseMoveNear( Show(); StopAnimation(); } else if (!is_animating_) { - PostDelayedAnimation(FADE_OUT, false); + PostDelayedAnimation(FADE_OUT); } } } -bool ScrollbarAnimationController::CalcNeedTriggerScrollbarShow( - ScrollbarOrientation orientation, - float distance) const { +bool ScrollbarAnimationController::MouseIsOverScrollbarThumb( + ScrollbarOrientation orientation) const { DCHECK(need_thinning_animation_); - - if (vertical_controller_->mouse_is_over_scrollbar() || - horizontal_controller_->mouse_is_over_scrollbar()) - return true; - - for (ScrollbarLayerImplBase* scrollbar : Scrollbars()) { - if (scrollbar->orientation() != orientation) - continue; - - if (distance < kMouseMoveDistanceToTriggerFadeIn) - return true; - } - - return false; + return GetScrollbarAnimationController(orientation) + .mouse_is_over_scrollbar_thumb(); } -bool ScrollbarAnimationController::MouseIsOverScrollbar( +bool ScrollbarAnimationController::MouseIsNearScrollbarThumb( ScrollbarOrientation orientation) const { DCHECK(need_thinning_animation_); - return GetScrollbarAnimationController(orientation).mouse_is_over_scrollbar(); + return GetScrollbarAnimationController(orientation) + .mouse_is_near_scrollbar_thumb(); } bool ScrollbarAnimationController::MouseIsNearScrollbar( ScrollbarOrientation orientation) const { DCHECK(need_thinning_animation_); - return GetScrollbarAnimationController(orientation).mouse_is_near_scrollbar(); + return GetScrollbarAnimationController(orientation) + .mouse_is_near_scrollbar_track(); } bool ScrollbarAnimationController::MouseIsNearAnyScrollbar() const { DCHECK(need_thinning_animation_); - return vertical_controller_->mouse_is_near_scrollbar() || - horizontal_controller_->mouse_is_near_scrollbar(); + return vertical_controller_->mouse_is_near_scrollbar_track() || + horizontal_controller_->mouse_is_near_scrollbar_track(); } bool ScrollbarAnimationController::ScrollbarsHidden() const { @@ -359,7 +354,8 @@ bool ScrollbarAnimationController::ScrollbarsHidden() const { bool ScrollbarAnimationController::Captured() const { DCHECK(need_thinning_animation_); - return vertical_controller_->captured() || horizontal_controller_->captured(); + return GetScrollbarAnimationController(VERTICAL).captured() || + GetScrollbarAnimationController(HORIZONTAL).captured(); } void ScrollbarAnimationController::Show() { @@ -369,8 +365,7 @@ void ScrollbarAnimationController::Show() { void ScrollbarAnimationController::ApplyOpacityToScrollbars(float opacity) { for (ScrollbarLayerImplBase* scrollbar : Scrollbars()) { - if (!scrollbar->is_overlay_scrollbar()) - continue; + DCHECK(scrollbar->is_overlay_scrollbar()); float effective_opacity = scrollbar->CanScrollOrientation() ? opacity : 0; scrollbar->SetOverlayScrollbarLayerOpacityAnimated(effective_opacity); } diff --git a/chromium/cc/input/scrollbar_animation_controller.h b/chromium/cc/input/scrollbar_animation_controller.h index 60676d95044..64bf2b6ac06 100644 --- a/chromium/cc/input/scrollbar_animation_controller.h +++ b/chromium/cc/input/scrollbar_animation_controller.h @@ -23,7 +23,7 @@ class CC_EXPORT ScrollbarAnimationControllerClient { virtual void SetNeedsRedrawForScrollbarAnimation() = 0; virtual void SetNeedsAnimateForScrollbarAnimation() = 0; virtual void DidChangeScrollbarVisibility() = 0; - virtual ScrollbarSet ScrollbarsFor(int scroll_layer_id) const = 0; + virtual ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const = 0; protected: virtual ~ScrollbarAnimationControllerClient() {} @@ -41,22 +41,22 @@ class CC_EXPORT ScrollbarAnimationController { // animation. static std::unique_ptr<ScrollbarAnimationController> CreateScrollbarAnimationControllerAndroid( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarAnimationControllerClient* client, base::TimeDelta fade_delay, - base::TimeDelta fade_out_resize_delay, - base::TimeDelta fade_duration); + base::TimeDelta fade_duration, + float initial_opacity); // ScrollbarAnimationController for Desktop Overlay Scrollbar. It has show & // fade out animation and thinning animation. static std::unique_ptr<ScrollbarAnimationController> CreateScrollbarAnimationControllerAuraOverlay( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarAnimationControllerClient* client, base::TimeDelta fade_delay, - base::TimeDelta fade_out_resize_delay, base::TimeDelta fade_duration, - base::TimeDelta thinning_duration); + base::TimeDelta thinning_duration, + float initial_opacity); ~ScrollbarAnimationController(); @@ -72,46 +72,45 @@ class CC_EXPORT ScrollbarAnimationController { // Effect both Android and Aura Overlay Scrollbar. void DidScrollUpdate(); - // DidResize expects to be called when clip layer size changed or scroll layer - // size changed. - void DidResize(); - void DidScrollBegin(); void DidScrollEnd(); void DidMouseDown(); void DidMouseUp(); void DidMouseLeave(); - void DidMouseMoveNear(ScrollbarOrientation, float); + void DidMouseMove(const gfx::PointF& device_viewport_point); // Called when Blink wants to show the scrollbars (via // ScrollableArea::showOverlayScrollbars). void DidRequestShowFromMainThread(); - bool MouseIsOverScrollbar(ScrollbarOrientation orientation) const; + // These methods are public for testing. + bool MouseIsOverScrollbarThumb(ScrollbarOrientation orientation) const; + bool MouseIsNearScrollbarThumb(ScrollbarOrientation orientation) const; bool MouseIsNearScrollbar(ScrollbarOrientation orientation) const; bool MouseIsNearAnyScrollbar() const; + ScrollbarSet Scrollbars() const; + static constexpr float kMouseMoveDistanceToTriggerFadeIn = 30.0f; private: // Describes whether the current animation should FadeIn or FadeOut. enum AnimationChange { NONE, FADE_IN, FADE_OUT }; - ScrollbarAnimationController(int scroll_layer_id, + ScrollbarAnimationController(ElementId scroll_element_id, ScrollbarAnimationControllerClient* client, base::TimeDelta fade_delay, - base::TimeDelta fade_out_resize_delay, - base::TimeDelta fade_duration); + base::TimeDelta fade_duration, + float initial_opacity); - ScrollbarAnimationController(int scroll_layer_id, + ScrollbarAnimationController(ElementId scroll_element_id, ScrollbarAnimationControllerClient* client, base::TimeDelta fade_delay, - base::TimeDelta fade_out_resize_delay, base::TimeDelta fade_duration, - base::TimeDelta thinning_duration); + base::TimeDelta thinning_duration, + float initial_opacity); - ScrollbarSet Scrollbars() const; SingleScrollbarAnimationControllerThinning& GetScrollbarAnimationController( ScrollbarOrientation) const; @@ -125,13 +124,10 @@ class CC_EXPORT ScrollbarAnimationController { void Show(); - void PostDelayedAnimation(AnimationChange animation_change, bool on_resize); + void PostDelayedAnimation(AnimationChange animation_change); bool Captured() const; - bool CalcNeedTriggerScrollbarShow(ScrollbarOrientation orientation, - float distance) const; - void ApplyOpacityToScrollbars(float opacity); ScrollbarAnimationControllerClient* client_; @@ -139,16 +135,15 @@ class CC_EXPORT ScrollbarAnimationController { base::TimeTicks last_awaken_time_; base::TimeDelta fade_delay_; - base::TimeDelta fade_out_resize_delay_; base::TimeDelta fade_duration_; - bool need_trigger_scrollbar_show_; + bool need_trigger_scrollbar_fade_in_; bool is_animating_; AnimationChange animation_change_; - const int scroll_layer_id_; + const ElementId scroll_element_id_; bool currently_scrolling_; bool show_in_fast_scroll_; @@ -158,6 +153,9 @@ class CC_EXPORT ScrollbarAnimationController { const bool show_scrollbars_on_scroll_gesture_; const bool need_thinning_animation_; + + bool is_mouse_down_; + std::unique_ptr<SingleScrollbarAnimationControllerThinning> vertical_controller_; std::unique_ptr<SingleScrollbarAnimationControllerThinning> diff --git a/chromium/cc/input/scrollbar_animation_controller_unittest.cc b/chromium/cc/input/scrollbar_animation_controller_unittest.cc index ebb22757703..e7e4926c3e5 100644 --- a/chromium/cc/input/scrollbar_animation_controller_unittest.cc +++ b/chromium/cc/input/scrollbar_animation_controller_unittest.cc @@ -23,11 +23,11 @@ namespace { const float kIdleThicknessScale = SingleScrollbarAnimationControllerThinning::kIdleThicknessScale; -const float kDefaultMouseMoveDistanceToTriggerAnimation = - SingleScrollbarAnimationControllerThinning:: - kDefaultMouseMoveDistanceToTriggerAnimation; const float kMouseMoveDistanceToTriggerFadeIn = ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; +const float kMouseMoveDistanceToTriggerExpand = + SingleScrollbarAnimationControllerThinning:: + kMouseMoveDistanceToTriggerExpand; const int kThumbThickness = 10; class MockScrollbarAnimationControllerClient @@ -44,8 +44,8 @@ class MockScrollbarAnimationControllerClient } void SetNeedsRedrawForScrollbarAnimation() override {} void SetNeedsAnimateForScrollbarAnimation() override {} - ScrollbarSet ScrollbarsFor(int scroll_layer_id) const override { - return host_impl_->ScrollbarsFor(scroll_layer_id); + ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const override { + return host_impl_->ScrollbarsFor(scroll_element_id); } MOCK_METHOD0(DidChangeScrollbarVisibility, void()); @@ -71,7 +71,6 @@ class ScrollbarAnimationControllerAuraOverlayTest : public testing::Test { protected: const base::TimeDelta kFadeDelay = base::TimeDelta::FromSeconds(4); - const base::TimeDelta kResizeFadeOutDelay = base::TimeDelta::FromSeconds(5); const base::TimeDelta kFadeDuration = base::TimeDelta::FromSeconds(3); const base::TimeDelta kThinningDuration = base::TimeDelta::FromSeconds(2); @@ -81,10 +80,13 @@ class ScrollbarAnimationControllerAuraOverlayTest : public testing::Test { std::unique_ptr<LayerImpl> clip = LayerImpl::Create(host_impl_.active_tree(), 2); clip_layer_ = clip.get(); + scroll_layer->SetElementId( + LayerIdToElementIdForTesting(scroll_layer->id())); scroll_layer->SetScrollClipLayer(clip_layer_->id()); LayerImpl* scroll_layer_ptr = scroll_layer.get(); const int kTrackStart = 0; + const int kTrackLength = 100; const bool kIsLeftSideVerticalScrollbar = false; const bool kIsOverlayScrollbar = true; @@ -92,10 +94,12 @@ class ScrollbarAnimationControllerAuraOverlayTest : public testing::Test { SolidColorScrollbarLayerImpl::Create( host_impl_.active_tree(), 3, HORIZONTAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar); + h_scrollbar->test_properties()->opacity = 0.0f; std::unique_ptr<SolidColorScrollbarLayerImpl> v_scrollbar = SolidColorScrollbarLayerImpl::Create( host_impl_.active_tree(), 4, VERTICAL, kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar); + v_scrollbar->test_properties()->opacity = 0.0f; v_scrollbar_layer_ = v_scrollbar.get(); h_scrollbar_layer_ = h_scrollbar.get(); @@ -104,18 +108,48 @@ class ScrollbarAnimationControllerAuraOverlayTest : public testing::Test { clip_layer_->test_properties()->AddChild(std::move(scroll_layer)); host_impl_.active_tree()->SetRootLayerForTesting(std::move(clip)); - v_scrollbar_layer_->SetScrollLayerId(scroll_layer_ptr->id()); - h_scrollbar_layer_->SetScrollLayerId(scroll_layer_ptr->id()); + v_scrollbar_layer_->SetBounds(gfx::Size(kThumbThickness, kTrackLength)); + v_scrollbar_layer_->SetPosition(gfx::PointF(90, 0)); + v_scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); v_scrollbar_layer_->test_properties()->opacity_can_animate = true; + + h_scrollbar_layer_->SetBounds(gfx::Size(kTrackLength, kThumbThickness)); + h_scrollbar_layer_->SetPosition(gfx::PointF(0, 90)); + h_scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); h_scrollbar_layer_->test_properties()->opacity_can_animate = true; + clip_layer_->SetBounds(gfx::Size(100, 100)); scroll_layer_ptr->SetBounds(gfx::Size(200, 200)); host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); scrollbar_controller_ = ScrollbarAnimationController:: CreateScrollbarAnimationControllerAuraOverlay( - scroll_layer_ptr->id(), &client_, kFadeDelay, - kResizeFadeOutDelay, kFadeDuration, kThinningDuration); + scroll_layer_ptr->element_id(), &client_, kFadeDelay, kFadeDuration, + kThinningDuration, 0.0f); + v_scrollbar_layer_->SetCurrentPos(0); + h_scrollbar_layer_->SetCurrentPos(0); + } + + // Return a point with given offset from the top-left of vertical scrollbar. + gfx::PointF NearVerticalScrollbarBegin(float offset_x, float offset_y) { + gfx::PointF p(90, 0); + p.Offset(offset_x, offset_y); + return p; + } + + // Return a point with given offset from the bottom-left of vertical + // scrollbar. + gfx::PointF NearVerticalScrollbarEnd(float offset_x, float offset_y) { + gfx::PointF p(90, 90); + p.Offset(offset_x, offset_y); + return p; + } + + // Return a point with given offset from the top-left of horizontal scrollbar. + gfx::PointF NearHorizontalScrollbarBegin(float offset_x, float offset_y) { + gfx::PointF p(0, 90); + p.Offset(offset_x, offset_y); + return p; } FakeImplTaskRunnerProvider task_runner_provider_; @@ -257,8 +291,70 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden()); } -// Scroll content. Move the mouse near the scrollbar and confirm it becomes -// thick. Ensure it remains visible as long as the mouse is near the scrollbar. +// Scroll content. Move the mouse near the scrollbar track but not near thumb +// and confirm it stay thin. Move the mouse near the scrollbar thumb and +// confirm it becomes thick. +TEST_F(ScrollbarAnimationControllerAuraOverlayTest, + MoveNearTrackThenNearThumb) { + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(1); + + scrollbar_controller_->DidScrollBegin(); + scrollbar_controller_->DidScrollUpdate(); + scrollbar_controller_->DidScrollEnd(); + + // An fade out animation should have been enqueued. + EXPECT_EQ(kFadeDelay, client_.delay()); + EXPECT_FALSE(client_.start_fade().is_null()); + EXPECT_FALSE(client_.start_fade().IsCancelled()); + + // Now move the mouse near the vertical scrollbar track. This should cancel + // the currently queued fading animation and stay scrollbar thin. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarEnd(-1, 0)); + ExpectScrollbarsOpacity(1); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + v_scrollbar_layer_->thumb_thickness_scale_factor()); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + h_scrollbar_layer_->thumb_thickness_scale_factor()); + EXPECT_TRUE(client_.start_fade().IsCancelled()); + + scrollbar_controller_->Animate(time); + time += kThinningDuration; + scrollbar_controller_->Animate(time); + + EXPECT_FLOAT_EQ(kIdleThicknessScale, + v_scrollbar_layer_->thumb_thickness_scale_factor()); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + h_scrollbar_layer_->thumb_thickness_scale_factor()); + + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0)); + scrollbar_controller_->Animate(time); + time += kThinningDuration; + scrollbar_controller_->Animate(time); + + EXPECT_FLOAT_EQ(1, v_scrollbar_layer_->thumb_thickness_scale_factor()); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + h_scrollbar_layer_->thumb_thickness_scale_factor()); + + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarEnd(-1, 0)); + EXPECT_FLOAT_EQ(1, v_scrollbar_layer_->thumb_thickness_scale_factor()); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + h_scrollbar_layer_->thumb_thickness_scale_factor()); + EXPECT_TRUE(client_.start_fade().IsCancelled()); + + scrollbar_controller_->Animate(time); + time += kThinningDuration; + scrollbar_controller_->Animate(time); + + EXPECT_FLOAT_EQ(kIdleThicknessScale, + v_scrollbar_layer_->thumb_thickness_scale_factor()); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + h_scrollbar_layer_->thumb_thickness_scale_factor()); +} + +// Scroll content. Move the mouse near the scrollbar thumb and confirm it +// becomes thick. Ensure it remains visible as long as the mouse is near the +// scrollbar. TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MoveNearAndDontFadeOut) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); @@ -272,9 +368,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MoveNearAndDontFadeOut) { EXPECT_FALSE(client_.start_fade().is_null()); EXPECT_FALSE(client_.start_fade().IsCancelled()); - // Now move the mouse near the scrollbar. This should cancel the currently - // queued fading animation and start animating thickness. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 1); + // Now move the mouse near the vertical scrollbar thumb. This should cancel + // the currently queued fading animation and start animating thickness. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0)); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(kIdleThicknessScale, v_scrollbar_layer_->thumb_thickness_scale_factor()); @@ -313,9 +409,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MoveOverAndDontFadeOut) { EXPECT_FALSE(client_.start_fade().is_null()); EXPECT_FALSE(client_.start_fade().IsCancelled()); - // Now move the mouse over the scrollbar. This should cancel the currently - // queued fading animation and start animating thickness. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 0); + // Now move the mouse over the vertical scrollbar thumb. This should cancel + // the currently queued fading animation and start animating thickness. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(kIdleThicknessScale, v_scrollbar_layer_->thumb_thickness_scale_factor()); @@ -354,9 +450,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_EQ(kFadeDelay, client_.delay()); EXPECT_FALSE(client_.start_fade().is_null()); - // Now move the mouse over the scrollbar and capture it. It should become - // thick without need for an animation. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 0); + // Now move the mouse over the vertical scrollbar thumb and capture it. It + // should become thick without need for an animation. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); scrollbar_controller_->DidMouseDown(); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(1, v_scrollbar_layer_->thumb_thickness_scale_factor()); @@ -382,9 +478,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_EQ(kFadeDelay, client_.delay()); EXPECT_FALSE(client_.start_fade().is_null()); - // Now move the mouse over the scrollbar and capture it. It should become - // thick without need for an animation. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 0); + // Now move the mouse over the vertical scrollbar and capture it. It should + // become thick without need for an animation. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); scrollbar_controller_->DidMouseDown(); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(1, v_scrollbar_layer_->thumb_thickness_scale_factor()); @@ -397,8 +493,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, // Then move mouse away, The fade out animation should have been cleared or // cancelled. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kDefaultMouseMoveDistanceToTriggerAnimation); + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerExpand, 0)); EXPECT_TRUE(client_.start_fade().is_null() || client_.start_fade().IsCancelled()); @@ -419,8 +515,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, DontFadeWhileCaptured) { EXPECT_FALSE(client_.start_fade().is_null()); EXPECT_FALSE(client_.start_fade().IsCancelled()); - // Now move the mouse over the scrollbar and animate it until it's thick. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 0); + // Now move the mouse over the vertical scrollbar thumb and animate it until + // it's thick. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -456,8 +553,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, FadeAfterReleasedFar) { EXPECT_FALSE(client_.start_fade().is_null()); EXPECT_FALSE(client_.start_fade().IsCancelled()); - // Now move the mouse over the scrollbar and capture it. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 0); + // Now move the mouse over the vertical scrollbar thumb and capture it. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); scrollbar_controller_->DidMouseDown(); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(1, v_scrollbar_layer_->thumb_thickness_scale_factor()); @@ -470,8 +567,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, FadeAfterReleasedFar) { client_.start_fade().IsCancelled()); // Now move the mouse away from the scrollbar and release it. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kDefaultMouseMoveDistanceToTriggerAnimation); + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn, 0)); scrollbar_controller_->DidMouseUp(); scrollbar_controller_->Animate(time); @@ -507,8 +604,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, DontFadeAfterReleasedNear) { EXPECT_FALSE(client_.start_fade().is_null()); EXPECT_FALSE(client_.start_fade().IsCancelled()); - // Now move the mouse over the scrollbar and capture it. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 0); + // Now move the mouse over the vertical scrollbar thumb and capture it. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); scrollbar_controller_->DidMouseDown(); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(1, v_scrollbar_layer_->thumb_thickness_scale_factor()); @@ -554,9 +651,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, scrollbar_controller_->Animate(time); ExpectScrollbarsOpacity(.5f); - // Now move the mouse near the scrollbar. It should reset opacity to 1 - // instantly and start animating to thick. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 1); + // Now move the mouse near the vertical scrollbar thumb. It should reset + // opacity to 1 instantly and start animating to thick. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(kIdleThicknessScale, v_scrollbar_layer_->thumb_thickness_scale_factor()); @@ -593,9 +690,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, TestCantCaptureWhenFaded) { scrollbar_controller_->Animate(time); ExpectScrollbarsOpacity(0); - // Move mouse over the scrollbar. It shouldn't thicken the scrollbar since - // it's completely faded out. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 0); + // Move mouse over the vertical scrollbar thumb. It shouldn't thicken the + // scrollbar since it's completely faded out. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -615,13 +712,28 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, TestCantCaptureWhenFaded) { h_scrollbar_layer_->thumb_thickness_scale_factor()); EXPECT_TRUE(client_.start_fade().is_null()); - // Similarly, releasing the scrollbar should have no effect. + // Similarly, releasing the scrollbar should have no effect but trigger a fade + // in. scrollbar_controller_->DidMouseUp(); ExpectScrollbarsOpacity(0); EXPECT_FLOAT_EQ(1, v_scrollbar_layer_->thumb_thickness_scale_factor()); EXPECT_FLOAT_EQ(kIdleThicknessScale, h_scrollbar_layer_->thumb_thickness_scale_factor()); - EXPECT_TRUE(client_.start_fade().is_null()); + + // An fade in animation should have been enqueued. + EXPECT_FALSE(client_.start_fade().is_null()); + EXPECT_FALSE(client_.start_fade().IsCancelled()); + EXPECT_EQ(kFadeDelay, client_.delay()); + + // Play the delay animation. + client_.start_fade().Run(); + EXPECT_TRUE(client_.start_fade().IsCancelled()); + + scrollbar_controller_->Animate(time); + time += kFadeDuration; + scrollbar_controller_->Animate(time); + + EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden()); } // Initiate a scroll when the pointer is already near the scrollbar. It should @@ -630,7 +742,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, ScrollWithMouseNear) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 1); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; @@ -693,27 +805,6 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_EQ(kFadeDelay, client_.delay()); } -// Make sure that if the scroll update is as a result of a resize, we use the -// resize delay time instead of the default one. -TEST_F(ScrollbarAnimationControllerAuraOverlayTest, ResizeFadeDuration) { - ASSERT_TRUE(client_.delay().is_zero()); - - scrollbar_controller_->DidResize(); - EXPECT_FALSE(client_.start_fade().is_null()); - EXPECT_EQ(kResizeFadeOutDelay, client_.delay()); - - client_.delay() = base::TimeDelta(); - - // We should use the gesture delay rather than the resize delay if we're in a - // gesture scroll, even if it is resizing. - scrollbar_controller_->DidScrollBegin(); - scrollbar_controller_->DidResize(); - scrollbar_controller_->DidScrollEnd(); - - EXPECT_FALSE(client_.start_fade().is_null()); - EXPECT_EQ(kFadeDelay, client_.delay()); -} - // Tests that the fade effect is animated. TEST_F(ScrollbarAnimationControllerAuraOverlayTest, FadeAnimated) { base::TimeTicks time; @@ -804,8 +895,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) { scrollbar_controller_->DidScrollUpdate(); scrollbar_controller_->DidScrollEnd(); - // Near vertical scrollbar - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 1); + // Near vertical scrollbar. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0)); scrollbar_controller_->Animate(time); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(kIdleThicknessScale, @@ -822,7 +913,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) { h_scrollbar_layer_->thumb_thickness_scale_factor()); // Subsequent moves within the nearness threshold should not change anything. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 2); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-2, 0)); scrollbar_controller_->Animate(time); time += base::TimeDelta::FromSeconds(10); scrollbar_controller_->Animate(time); @@ -832,8 +923,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) { h_scrollbar_layer_->thumb_thickness_scale_factor()); // Now move away from bar. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kDefaultMouseMoveDistanceToTriggerAnimation); + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerExpand, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -844,7 +935,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) { h_scrollbar_layer_->thumb_thickness_scale_factor()); // Near horizontal scrollbar - scrollbar_controller_->DidMouseMoveNear(HORIZONTAL, 2); + scrollbar_controller_->DidMouseMove(NearHorizontalScrollbarBegin(0, -1)); scrollbar_controller_->Animate(time); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(kIdleThicknessScale, @@ -861,7 +952,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) { EXPECT_FLOAT_EQ(1, h_scrollbar_layer_->thumb_thickness_scale_factor()); // Subsequent moves within the nearness threshold should not change anything. - scrollbar_controller_->DidMouseMoveNear(HORIZONTAL, 1); + scrollbar_controller_->DidMouseMove(NearHorizontalScrollbarBegin(0, -2)); scrollbar_controller_->Animate(time); time += base::TimeDelta::FromSeconds(10); scrollbar_controller_->Animate(time); @@ -871,8 +962,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) { EXPECT_FLOAT_EQ(1, h_scrollbar_layer_->thumb_thickness_scale_factor()); // Now move away from bar. - scrollbar_controller_->DidMouseMoveNear( - HORIZONTAL, kDefaultMouseMoveDistanceToTriggerAnimation); + scrollbar_controller_->DidMouseMove( + NearHorizontalScrollbarBegin(0, -kMouseMoveDistanceToTriggerExpand)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -897,9 +988,12 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearBoth) { scrollbar_controller_->DidScrollUpdate(); scrollbar_controller_->DidScrollEnd(); + // Move scrollbar thumb to the end of track. + v_scrollbar_layer_->SetCurrentPos(100); + h_scrollbar_layer_->SetCurrentPos(100); + // Near both Scrollbar - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 1); - scrollbar_controller_->DidMouseMoveNear(HORIZONTAL, 1); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarEnd(-1, -1)); scrollbar_controller_->Animate(time); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(kIdleThicknessScale, @@ -928,7 +1022,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, scrollbar_controller_->DidScrollEnd(); // Near vertical scrollbar. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 1); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0)); scrollbar_controller_->Animate(time); ExpectScrollbarsOpacity(1); EXPECT_FLOAT_EQ(kIdleThicknessScale, @@ -946,9 +1040,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, h_scrollbar_layer_->thumb_thickness_scale_factor()); // Away vertical scrollbar and near horizontal scrollbar. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kDefaultMouseMoveDistanceToTriggerAnimation); - scrollbar_controller_->DidMouseMoveNear(HORIZONTAL, 1); + scrollbar_controller_->DidMouseMove(gfx::PointF(0, 0)); + scrollbar_controller_->DidMouseMove(NearHorizontalScrollbarBegin(0, -1)); scrollbar_controller_->Animate(time); // Vertical scrollbar animate to thin. horizontal scrollbar animate to @@ -961,8 +1054,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_FLOAT_EQ(1, h_scrollbar_layer_->thumb_thickness_scale_factor()); // Away horizontal scrollbar. - scrollbar_controller_->DidMouseMoveNear( - HORIZONTAL, kDefaultMouseMoveDistanceToTriggerAnimation); + scrollbar_controller_->DidMouseMove(gfx::PointF(0, 0)); scrollbar_controller_->Animate(time); // Horizontal scrollbar animate to thin. @@ -986,7 +1078,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseLeaveFadeOut) { time += base::TimeDelta::FromSeconds(1); // Move mouse near scrollbar. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, 1); + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0)); // Scroll to make the scrollbars visible. scrollbar_controller_->DidScrollBegin(); @@ -1011,9 +1103,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, BasicMouseHoverFadeIn) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - // Move mouse hover the fade in scrollbar region of scrollbar. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kMouseMoveDistanceToTriggerFadeIn - 1); + // Move mouse over the fade in region of scrollbar. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1045,9 +1137,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - // Move mouse hover the fade in scrollbar region of scrollbar. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kMouseMoveDistanceToTriggerFadeIn - 1); + // Move mouse over the fade in region of scrollbar. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1055,10 +1147,10 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_EQ(kFadeDelay, client_.delay()); base::Closure& fade = client_.start_fade(); - // Move mouse still hover the fade in scrollbar region of scrollbar should not + // Move mouse still hover the fade in region of scrollbar should not // post a new fade in. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kMouseMoveDistanceToTriggerFadeIn - 2); + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 2, 0)); EXPECT_TRUE(fade.Equals(client_.start_fade())); } @@ -1070,9 +1162,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - // Move mouse hover the fade in scrollbar region of scrollbar. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kMouseMoveDistanceToTriggerFadeIn - 1); + // Move mouse over the fade in region of scrollbar. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1080,8 +1172,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_EQ(kFadeDelay, client_.delay()); // Move mouse far away,delay fade in should be canceled. - scrollbar_controller_->DidMouseMoveNear(VERTICAL, - kMouseMoveDistanceToTriggerFadeIn); + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn, 0)); + EXPECT_TRUE(client_.start_fade().is_null() || client_.start_fade().IsCancelled()); } @@ -1093,9 +1186,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - // Move mouse hover the fade in scrollbar region of scrollbar. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kMouseMoveDistanceToTriggerFadeIn - 1); + // Move mouse over the fade in region of scrollbar. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1107,9 +1200,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_TRUE(client_.start_fade().is_null() || client_.start_fade().IsCancelled()); - // Move mouse hover the fade in scrollbar region of scrollbar. - scrollbar_controller_->DidMouseMoveNear( - VERTICAL, kMouseMoveDistanceToTriggerFadeIn - 1); + // Move mouse over the fade in region of scrollbar. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); // An fade in animation should have been enqueued. EXPECT_FALSE(client_.start_fade().is_null()); @@ -1127,6 +1220,94 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden()); } +// Make sure mouse down will cancel hover fade in timer, then mouse move with +// press will not trigger hover fade in, mouse release near will trigger new +// hover fade in. +TEST_F(ScrollbarAnimationControllerAuraOverlayTest, + MouseHoverThenMouseDownShouldCancelFadeInThenReleaseNearShouldFadeIn) { + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(1); + + // Move mouse over the fade in region of scrollbar. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + + // An fade in animation should have been enqueued. + EXPECT_FALSE(client_.start_fade().is_null()); + EXPECT_FALSE(client_.start_fade().IsCancelled()); + EXPECT_EQ(kFadeDelay, client_.delay()); + + // Mouse down,delay fade in should be canceled. + scrollbar_controller_->DidMouseDown(); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Move mouse hover the fade in region of scrollbar with press. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + + // Should not have delay fade animation. + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Mouse up. + scrollbar_controller_->DidMouseUp(); + + // An fade in animation should have been enqueued. + EXPECT_FALSE(client_.start_fade().is_null()); + EXPECT_FALSE(client_.start_fade().IsCancelled()); + EXPECT_EQ(kFadeDelay, client_.delay()); + + // Play the delay animation. + client_.start_fade().Run(); + EXPECT_TRUE(client_.start_fade().IsCancelled()); + + scrollbar_controller_->Animate(time); + time += kFadeDuration; + scrollbar_controller_->Animate(time); + + EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden()); +} + +// Make sure mouse down will cancel hover fade in timer, then mouse move with +// press will not trigger hover fade in, mouse release far will not trigger new +// hover fade in. +TEST_F(ScrollbarAnimationControllerAuraOverlayTest, + MouseReleaseFarShouldNotFadeIn) { + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(1); + + // Move mouse over the fade in region of scrollbar. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn + 1, 0)); + + // An fade in animation should have been enqueued. + EXPECT_FALSE(client_.start_fade().is_null()); + EXPECT_FALSE(client_.start_fade().IsCancelled()); + EXPECT_EQ(kFadeDelay, client_.delay()); + + // Mouse down,delay fade in should be canceled. + scrollbar_controller_->DidMouseDown(); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Move mouse far from hover the fade in region of scrollbar with + // press. + scrollbar_controller_->DidMouseMove( + NearVerticalScrollbarBegin(-kMouseMoveDistanceToTriggerFadeIn, 0)); + + // Should not have delay fade animation. + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Mouse up. + scrollbar_controller_->DidMouseUp(); + + // Should not have delay fade animation. + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); +} + class ScrollbarAnimationControllerAndroidTest : public testing::Test, public ScrollbarAnimationControllerClient { @@ -1147,8 +1328,8 @@ class ScrollbarAnimationControllerAndroidTest void SetNeedsAnimateForScrollbarAnimation() override { did_request_animate_ = true; } - ScrollbarSet ScrollbarsFor(int scroll_layer_id) const override { - return host_impl_.ScrollbarsFor(scroll_layer_id); + ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const override { + return host_impl_.ScrollbarsFor(scroll_element_id); } void DidChangeScrollbarVisibility() override {} @@ -1164,26 +1345,31 @@ class ScrollbarAnimationControllerAndroidTest SolidColorScrollbarLayerImpl::Create( host_impl_.active_tree(), 2, orientation(), kThumbThickness, kTrackStart, kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar); + scrollbar->test_properties()->opacity = 0.0f; scrollbar_layer_ = scrollbar.get(); scrollbar_layer_->test_properties()->opacity_can_animate = true; std::unique_ptr<LayerImpl> clip = LayerImpl::Create(host_impl_.active_tree(), 3); clip_layer_ = clip.get(); + scroll_layer->SetElementId( + LayerIdToElementIdForTesting(scroll_layer->id())); + scroll_layer->SetScrollClipLayer(clip_layer_->id()); LayerImpl* scroll_layer_ptr = scroll_layer.get(); scroll_layer->test_properties()->AddChild(std::move(scrollbar)); clip->test_properties()->AddChild(std::move(scroll_layer)); host_impl_.active_tree()->SetRootLayerForTesting(std::move(clip)); - scrollbar_layer_->SetScrollLayerId(scroll_layer_ptr->id()); + scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); clip_layer_->SetBounds(gfx::Size(100, 100)); scroll_layer_ptr->SetBounds(gfx::Size(200, 200)); host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); scrollbar_controller_ = ScrollbarAnimationController::CreateScrollbarAnimationControllerAndroid( - scroll_layer_ptr->id(), this, base::TimeDelta::FromSeconds(2), - base::TimeDelta::FromSeconds(5), base::TimeDelta::FromSeconds(3)); + scroll_layer_ptr->element_id(), this, + base::TimeDelta::FromSeconds(2), base::TimeDelta::FromSeconds(3), + 0.0f); } virtual ScrollbarOrientation orientation() const { return HORIZONTAL; } @@ -1207,24 +1393,6 @@ class VerticalScrollbarAnimationControllerAndroidTest ScrollbarOrientation orientation() const override { return VERTICAL; } }; -TEST_F(ScrollbarAnimationControllerAndroidTest, DelayAnimationOnResize) { - scrollbar_layer_->SetOverlayScrollbarLayerOpacityAnimated(0.f); - // We should use the gesture delay rather than the resize delay if we're in a - // gesture scroll, even if it is resizing. - scrollbar_controller_->DidScrollBegin(); - scrollbar_controller_->DidResize(); - scrollbar_controller_->DidScrollEnd(); - // Normal Animation delay of 2 seconds. - EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity()); - EXPECT_EQ(delay_, base::TimeDelta::FromSeconds(2)); - - scrollbar_layer_->SetOverlayScrollbarLayerOpacityAnimated(0.f); - scrollbar_controller_->DidResize(); - // Delay animation on resize to 5 seconds. - EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity()); - EXPECT_EQ(delay_, base::TimeDelta::FromSeconds(5)); -} - TEST_F(ScrollbarAnimationControllerAndroidTest, HiddenInBegin) { scrollbar_layer_->SetOverlayScrollbarLayerOpacityAnimated(0.f); scrollbar_controller_->Animate(base::TimeTicks()); diff --git a/chromium/cc/input/scroller_size_metrics.h b/chromium/cc/input/scroller_size_metrics.h new file mode 100644 index 00000000000..c805934522e --- /dev/null +++ b/chromium/cc/input/scroller_size_metrics.h @@ -0,0 +1,20 @@ +// Copyright 2017 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. + +#ifndef CC_INPUT_SCROLLER_SIZE_METRICS_H_ +#define CC_INPUT_SCROLLER_SIZE_METRICS_H_ + +namespace cc { + +// Use the two constants to add scroller size related UMA metrics. +// The goal of the metrics is to find the frequency of scrollers of +// different sizes that get scrolled. This experiment is aiming at small +// scrollers therefore the big ones, e.g. larger than 400px * 500px, will get +// capped into one bucket. +static constexpr int kScrollerSizeLargestBucket = 200000; +static constexpr int kScrollerSizeBucketCount = 50; + +} // namespace cc + +#endif // CC_INPUT_SCROLLER_SIZE_METRICS_H_ diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc index d09dd054310..0515aaff879 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc @@ -15,34 +15,69 @@ namespace cc { +namespace { + +float DistanceToScrollbarPart(const gfx::PointF& device_viewport_point, + const ScrollbarLayerImplBase& scrollbar, + const ScrollbarPart part) { + gfx::RectF rect; + if (part == ScrollbarPart::THUMB) { + rect = gfx::RectF(gfx::Rect(scrollbar.ComputeExpandedThumbQuadRect())); + } else { + rect = gfx::RectF(gfx::Rect(scrollbar.bounds())); + } + + gfx::RectF device_viewport_rect = + MathUtil::MapClippedRect(scrollbar.ScreenSpaceTransform(), rect); + + return device_viewport_rect.ManhattanDistanceToPoint(device_viewport_point) / + scrollbar.layer_tree_impl()->device_scale_factor(); +} + +} // namespace + std::unique_ptr<SingleScrollbarAnimationControllerThinning> SingleScrollbarAnimationControllerThinning::Create( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarOrientation orientation, ScrollbarAnimationControllerClient* client, base::TimeDelta thinning_duration) { return base::WrapUnique(new SingleScrollbarAnimationControllerThinning( - scroll_layer_id, orientation, client, thinning_duration)); + scroll_element_id, orientation, client, thinning_duration)); } SingleScrollbarAnimationControllerThinning:: SingleScrollbarAnimationControllerThinning( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarOrientation orientation, ScrollbarAnimationControllerClient* client, base::TimeDelta thinning_duration) : client_(client), is_animating_(false), - scroll_layer_id_(scroll_layer_id), + scroll_element_id_(scroll_element_id), orientation_(orientation), captured_(false), - mouse_is_over_scrollbar_(false), - mouse_is_near_scrollbar_(false), + mouse_is_over_scrollbar_thumb_(false), + mouse_is_near_scrollbar_thumb_(false), + mouse_is_near_scrollbar_track_(false), thickness_change_(NONE), thinning_duration_(thinning_duration) { ApplyThumbThicknessScale(kIdleThicknessScale); } +ScrollbarLayerImplBase* +SingleScrollbarAnimationControllerThinning::GetScrollbar() const { + for (ScrollbarLayerImplBase* scrollbar : + client_->ScrollbarsFor(scroll_element_id_)) { + DCHECK(scrollbar->is_overlay_scrollbar()); + + if (scrollbar->orientation() == orientation_) + return scrollbar; + } + + return nullptr; +} + bool SingleScrollbarAnimationControllerThinning::Animate(base::TimeTicks now) { if (!is_animating_) return false; @@ -92,7 +127,7 @@ void SingleScrollbarAnimationControllerThinning::StopAnimation() { } void SingleScrollbarAnimationControllerThinning::DidMouseDown() { - if (!mouse_is_over_scrollbar_) + if (!mouse_is_over_scrollbar_thumb_) return; StopAnimation(); @@ -107,7 +142,7 @@ void SingleScrollbarAnimationControllerThinning::DidMouseUp() { captured_ = false; StopAnimation(); - if (!mouse_is_near_scrollbar_) { + if (!mouse_is_near_scrollbar_thumb_) { thickness_change_ = DECREASE; StartAnimation(); } else { @@ -116,11 +151,12 @@ void SingleScrollbarAnimationControllerThinning::DidMouseUp() { } void SingleScrollbarAnimationControllerThinning::DidMouseLeave() { - if (!mouse_is_over_scrollbar_ && !mouse_is_near_scrollbar_) + if (!mouse_is_over_scrollbar_thumb_ && !mouse_is_near_scrollbar_thumb_) return; - mouse_is_over_scrollbar_ = false; - mouse_is_near_scrollbar_ = false; + mouse_is_over_scrollbar_thumb_ = false; + mouse_is_near_scrollbar_thumb_ = false; + mouse_is_near_scrollbar_track_ = false; if (captured_) return; @@ -129,24 +165,39 @@ void SingleScrollbarAnimationControllerThinning::DidMouseLeave() { StartAnimation(); } -void SingleScrollbarAnimationControllerThinning::DidMouseMoveNear( - float distance) { - bool mouse_is_over_scrollbar = distance == 0.0f; - bool mouse_is_near_scrollbar = - distance < kDefaultMouseMoveDistanceToTriggerAnimation; +void SingleScrollbarAnimationControllerThinning::DidMouseMove( + const gfx::PointF& device_viewport_point) { + ScrollbarLayerImplBase* scrollbar = GetScrollbar(); + + if (!scrollbar) + return; + + float distance_to_scrollbar_track = DistanceToScrollbarPart( + device_viewport_point, *scrollbar, ScrollbarPart::TRACK); + float distance_to_scrollbar_thumb = DistanceToScrollbarPart( + device_viewport_point, *scrollbar, ScrollbarPart::THUMB); + + mouse_is_near_scrollbar_track_ = + distance_to_scrollbar_track < + ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; + + bool mouse_is_over_scrollbar_thumb = distance_to_scrollbar_thumb == 0.0f; + bool mouse_is_near_scrollbar_thumb = + distance_to_scrollbar_thumb < kMouseMoveDistanceToTriggerExpand; - if (!captured_ && mouse_is_near_scrollbar != mouse_is_near_scrollbar_) { - thickness_change_ = mouse_is_near_scrollbar ? INCREASE : DECREASE; + if (!captured_ && + mouse_is_near_scrollbar_thumb != mouse_is_near_scrollbar_thumb_) { + thickness_change_ = mouse_is_near_scrollbar_thumb ? INCREASE : DECREASE; StartAnimation(); } - mouse_is_near_scrollbar_ = mouse_is_near_scrollbar; - mouse_is_over_scrollbar_ = mouse_is_over_scrollbar; + mouse_is_near_scrollbar_thumb_ = mouse_is_near_scrollbar_thumb; + mouse_is_over_scrollbar_thumb_ = mouse_is_over_scrollbar_thumb; } float SingleScrollbarAnimationControllerThinning::ThumbThicknessScaleAt( float progress) { if (thickness_change_ == NONE) - return mouse_is_near_scrollbar_ ? 1.f : kIdleThicknessScale; + return mouse_is_near_scrollbar_thumb_ ? 1.f : kIdleThicknessScale; float factor = thickness_change_ == INCREASE ? progress : (1.f - progress); return ((1.f - kIdleThicknessScale) * factor) + kIdleThicknessScale; } @@ -173,18 +224,16 @@ float SingleScrollbarAnimationControllerThinning::AdjustScale( void SingleScrollbarAnimationControllerThinning::UpdateThumbThicknessScale() { StopAnimation(); - ApplyThumbThicknessScale(mouse_is_near_scrollbar_ ? 1.f - : kIdleThicknessScale); + ApplyThumbThicknessScale( + mouse_is_near_scrollbar_thumb_ ? 1.f : kIdleThicknessScale); } void SingleScrollbarAnimationControllerThinning::ApplyThumbThicknessScale( float thumb_thickness_scale) { - for (ScrollbarLayerImplBase* scrollbar : - client_->ScrollbarsFor(scroll_layer_id_)) { + for (auto* scrollbar : client_->ScrollbarsFor(scroll_element_id_)) { if (scrollbar->orientation() != orientation_) continue; - if (!scrollbar->is_overlay_scrollbar()) - continue; + DCHECK(scrollbar->is_overlay_scrollbar()); float scale = AdjustScale(thumb_thickness_scale, scrollbar->thumb_thickness_scale_factor(), diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning.h b/chromium/cc/input/single_scrollbar_animation_controller_thinning.h index 32ba069a12b..20c06835b3e 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning.h +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning.h @@ -23,18 +23,26 @@ class ScrollbarAnimationControllerClient; class CC_EXPORT SingleScrollbarAnimationControllerThinning { public: static constexpr float kIdleThicknessScale = 0.4f; - static constexpr float kDefaultMouseMoveDistanceToTriggerAnimation = 25.f; + static constexpr float kMouseMoveDistanceToTriggerExpand = 25.f; static std::unique_ptr<SingleScrollbarAnimationControllerThinning> Create( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarOrientation orientation, ScrollbarAnimationControllerClient* client, base::TimeDelta thinning_duration); ~SingleScrollbarAnimationControllerThinning() {} - bool mouse_is_over_scrollbar() const { return mouse_is_over_scrollbar_; } - bool mouse_is_near_scrollbar() const { return mouse_is_near_scrollbar_; } + bool mouse_is_over_scrollbar_thumb() const { + return mouse_is_over_scrollbar_thumb_; + } + bool mouse_is_near_scrollbar_thumb() const { + return mouse_is_near_scrollbar_thumb_; + } + bool mouse_is_near_scrollbar_track() const { + return mouse_is_near_scrollbar_track_; + } + bool captured() const { return captured_; } bool Animate(base::TimeTicks now); @@ -46,15 +54,16 @@ class CC_EXPORT SingleScrollbarAnimationControllerThinning { void DidMouseDown(); void DidMouseUp(); void DidMouseLeave(); - void DidMouseMoveNear(float distance); + void DidMouseMove(const gfx::PointF& device_viewport_point); private: SingleScrollbarAnimationControllerThinning( - int scroll_layer_id, + ElementId scroll_element_id, ScrollbarOrientation orientation, ScrollbarAnimationControllerClient* client, base::TimeDelta thinning_duration); + ScrollbarLayerImplBase* GetScrollbar() const; float AnimationProgressAtTime(base::TimeTicks now); void RunAnimationFrame(float progress); const base::TimeDelta& Duration(); @@ -76,12 +85,13 @@ class CC_EXPORT SingleScrollbarAnimationControllerThinning { base::TimeTicks last_awaken_time_; bool is_animating_; - int scroll_layer_id_; + ElementId scroll_element_id_; ScrollbarOrientation orientation_; bool captured_; - bool mouse_is_over_scrollbar_; - bool mouse_is_near_scrollbar_; + bool mouse_is_over_scrollbar_thumb_; + bool mouse_is_near_scrollbar_thumb_; + bool mouse_is_near_scrollbar_track_; // Are we narrowing or thickening the bars. AnimationChange thickness_change_; diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc b/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc index 50659ff44d8..0faa8d3265b 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning_unittest.cc @@ -23,9 +23,11 @@ namespace { const float kIdleThicknessScale = SingleScrollbarAnimationControllerThinning::kIdleThicknessScale; -const float kDefaultMouseMoveDistanceToTriggerAnimation = +const float kMouseMoveDistanceToTriggerExpand = SingleScrollbarAnimationControllerThinning:: - kDefaultMouseMoveDistanceToTriggerAnimation; + kMouseMoveDistanceToTriggerExpand; +const float kMouseMoveDistanceToTriggerFadeIn = + ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; class MockSingleScrollbarAnimationControllerClient : public ScrollbarAnimationControllerClient { @@ -35,8 +37,8 @@ class MockSingleScrollbarAnimationControllerClient : host_impl_(host_impl) {} virtual ~MockSingleScrollbarAnimationControllerClient() {} - ScrollbarSet ScrollbarsFor(int scroll_layer_id) const override { - return host_impl_->ScrollbarsFor(scroll_layer_id); + ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const override { + return host_impl_->ScrollbarsFor(scroll_element_id); } MOCK_METHOD2(PostDelayedScrollbarAnimationTask, @@ -65,6 +67,8 @@ class SingleScrollbarAnimationControllerThinningTest : public testing::Test { LayerImpl::Create(host_impl_.active_tree(), 1); std::unique_ptr<LayerImpl> clip = LayerImpl::Create(host_impl_.active_tree(), 3); + scroll_layer->SetElementId( + LayerIdToElementIdForTesting(scroll_layer->id())); clip_layer_ = clip.get(); scroll_layer->SetScrollClipLayer(clip_layer_->id()); LayerImpl* scroll_layer_ptr = scroll_layer.get(); @@ -72,6 +76,7 @@ class SingleScrollbarAnimationControllerThinningTest : public testing::Test { const int kId = 2; const int kThumbThickness = 10; const int kTrackStart = 0; + const int kTrackLength = 100; const bool kIsLeftSideVerticalScrollbar = false; const bool kIsOverlayScrollbar = true; @@ -85,14 +90,17 @@ class SingleScrollbarAnimationControllerThinningTest : public testing::Test { clip_layer_->test_properties()->AddChild(std::move(scroll_layer)); host_impl_.active_tree()->SetRootLayerForTesting(std::move(clip)); - scrollbar_layer_->SetScrollLayerId(scroll_layer_ptr->id()); + scrollbar_layer_->SetBounds(gfx::Size(kThumbThickness, kTrackLength)); + scrollbar_layer_->SetPosition(gfx::PointF(90, 0)); + scrollbar_layer_->SetScrollElementId(scroll_layer_ptr->element_id()); scrollbar_layer_->test_properties()->opacity_can_animate = true; clip_layer_->SetBounds(gfx::Size(100, 100)); scroll_layer_ptr->SetBounds(gfx::Size(200, 200)); host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting(); scrollbar_controller_ = SingleScrollbarAnimationControllerThinning::Create( - scroll_layer_ptr->id(), HORIZONTAL, &client_, kThinningDuration); + scroll_layer_ptr->element_id(), HORIZONTAL, &client_, + kThinningDuration); } FakeImplTaskRunnerProvider task_runner_provider_; @@ -105,6 +113,13 @@ class SingleScrollbarAnimationControllerThinningTest : public testing::Test { NiceMock<MockSingleScrollbarAnimationControllerClient> client_; }; +// Return a point with given offset from the top-left of scrollbar. +gfx::PointF NearScrollbar(float offset_x, float offset_y) { + gfx::PointF p(90, 0); + p.Offset(offset_x, offset_y); + return p; +} + // Check initialization of scrollbar. Should start thin. TEST_F(SingleScrollbarAnimationControllerThinningTest, Idle) { EXPECT_FLOAT_EQ(kIdleThicknessScale, @@ -117,7 +132,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseNear) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - scrollbar_controller_->DidMouseMoveNear(1); + scrollbar_controller_->DidMouseMove(NearScrollbar(-1, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(kIdleThicknessScale, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -128,15 +143,24 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseNear) { EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); // Subsequent moves within the nearness threshold should not change anything. - scrollbar_controller_->DidMouseMoveNear(2); + scrollbar_controller_->DidMouseMove(NearScrollbar(-2, 0)); scrollbar_controller_->Animate(time); time += base::TimeDelta::FromSeconds(10); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); - // Now move away from bar. - scrollbar_controller_->DidMouseMoveNear( - kDefaultMouseMoveDistanceToTriggerAnimation); + // Now move away from thumb. + scrollbar_controller_->DidMouseMove( + NearScrollbar(-kMouseMoveDistanceToTriggerExpand, 0)); + scrollbar_controller_->Animate(time); + time += kThinningDuration; + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(kIdleThicknessScale, + scrollbar_layer_->thumb_thickness_scale_factor()); + + // Move away from track. + scrollbar_controller_->DidMouseMove( + NearScrollbar(-kMouseMoveDistanceToTriggerFadeIn, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -150,7 +174,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseOver) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - scrollbar_controller_->DidMouseMoveNear(0); + scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(kIdleThicknessScale, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -161,7 +185,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseOver) { EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); // Subsequent moves should not change anything. - scrollbar_controller_->DidMouseMoveNear(0); + scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0)); scrollbar_controller_->Animate(time); time += base::TimeDelta::FromSeconds(10); scrollbar_controller_->Animate(time); @@ -169,16 +193,16 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseOver) { // Moving off the scrollbar but still withing the "near" threshold should do // nothing. - scrollbar_controller_->DidMouseMoveNear( - kDefaultMouseMoveDistanceToTriggerAnimation - 1.f); + scrollbar_controller_->DidMouseMove( + NearScrollbar(-kMouseMoveDistanceToTriggerExpand + 1, 0)); scrollbar_controller_->Animate(time); time += base::TimeDelta::FromSeconds(10); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); - // Now move away from bar. - scrollbar_controller_->DidMouseMoveNear( - kDefaultMouseMoveDistanceToTriggerAnimation); + // Now move away from thumb. + scrollbar_controller_->DidMouseMove( + NearScrollbar(-kMouseMoveDistanceToTriggerExpand, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -188,13 +212,13 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseOver) { // First move the pointer over the scrollbar off of it. Make sure the thinning // animation kicked off in DidMouseMoveOffScrollbar gets overridden by the -// thickening animation in the DidMouseMoveNear call. +// thickening animation in the DidMouseMove call. TEST_F(SingleScrollbarAnimationControllerThinningTest, MouseNearThenAwayWhileAnimating) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - scrollbar_controller_->DidMouseMoveNear(0); + scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(kIdleThicknessScale, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -205,7 +229,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); // This is tricky. The DidMouseLeave() is sent before the - // subsequent DidMouseMoveNear(), if the mouse moves in that direction. + // subsequent DidMouseMove(), if the mouse moves in that direction. // This results in the thumb thinning. We want to make sure that when the // thumb starts expanding it doesn't first narrow to the idle thinness. time += base::TimeDelta::FromSeconds(1); @@ -222,7 +246,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // Now we get a notification for the mouse moving over the scroller. The // animation is reset to the thickening direction but we won't start // thickening until the new animation catches up to the current thickness. - scrollbar_controller_->DidMouseMoveNear(1); + scrollbar_controller_->DidMouseMove(NearScrollbar(-1, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f - (1.0f - kIdleThicknessScale) / 2.0f, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -260,7 +284,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, time += base::TimeDelta::FromSeconds(1); // Move over the scrollbar. - scrollbar_controller_->DidMouseMoveNear(0); + scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -278,8 +302,8 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // Move outside the "near" threshold. Because the scrollbar is captured it // should remain thick. - scrollbar_controller_->DidMouseMoveNear( - kDefaultMouseMoveDistanceToTriggerAnimation); + scrollbar_controller_->DidMouseMove( + NearScrollbar(-kMouseMoveDistanceToTriggerExpand, 0)); time += kThinningDuration; scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -305,7 +329,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, time += base::TimeDelta::FromSeconds(1); // Move over scrollbar. - scrollbar_controller_->DidMouseMoveNear(0); + scrollbar_controller_->DidMouseMove(NearScrollbar(0, 0)); scrollbar_controller_->Animate(time); time += kThinningDuration; scrollbar_controller_->Animate(time); @@ -320,8 +344,8 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); // Move away from scrollbar. Nothing should change. - scrollbar_controller_->DidMouseMoveNear( - kDefaultMouseMoveDistanceToTriggerAnimation); + scrollbar_controller_->DidMouseMove( + NearScrollbar(kMouseMoveDistanceToTriggerExpand, 0)); time += base::TimeDelta::FromSeconds(1); scrollbar_controller_->Animate(time); time += base::TimeDelta::FromSeconds(10); @@ -330,7 +354,8 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, // Move over scrollbar and release. Since we're near the scrollbar, it should // remain thick. - scrollbar_controller_->DidMouseMoveNear(0); + scrollbar_controller_->DidMouseMove( + NearScrollbar(-kMouseMoveDistanceToTriggerExpand + 1, 0)); scrollbar_controller_->DidMouseUp(); time += base::TimeDelta::FromSeconds(1); scrollbar_controller_->Animate(time); @@ -346,7 +371,7 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, ThicknessAnimated) { // Move mouse near scrollbar. Test that at half the duration time, the // thickness is half way through its animation. - scrollbar_controller_->DidMouseMoveNear(1); + scrollbar_controller_->DidMouseMove(NearScrollbar(-1, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(kIdleThicknessScale, scrollbar_layer_->thumb_thickness_scale_factor()); @@ -362,8 +387,8 @@ TEST_F(SingleScrollbarAnimationControllerThinningTest, ThicknessAnimated) { // Move mouse away from scrollbar. Same check. time += base::TimeDelta::FromSeconds(1); - scrollbar_controller_->DidMouseMoveNear( - kDefaultMouseMoveDistanceToTriggerAnimation); + scrollbar_controller_->DidMouseMove( + NearScrollbar(-kMouseMoveDistanceToTriggerExpand, 0)); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); diff --git a/chromium/cc/input/touch_action.h b/chromium/cc/input/touch_action.h new file mode 100644 index 00000000000..8b11c4c9d7e --- /dev/null +++ b/chromium/cc/input/touch_action.h @@ -0,0 +1,56 @@ +// Copyright 2017 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. + +#ifndef CC_INPUT_TOUCH_ACTION_H_ +#define CC_INPUT_TOUCH_ACTION_H_ + +#include <cstdlib> + +namespace cc { + +// The current touch action specifies what accelerated browser operations +// (panning and zooming) are currently permitted via touch input. +// See http://www.w3.org/TR/pointerevents/#the-touch-action-css-property. +// This is intended to be the single canonical definition of the enum, it's used +// elsewhere in both Blink and content since touch action logic spans those +// subsystems. +// TODO(crbug.com/720553): rework this enum to enum class. +const size_t kTouchActionBits = 6; + +enum TouchAction { + // No scrolling or zooming allowed. + kTouchActionNone = 0x0, + kTouchActionPanLeft = 0x1, + kTouchActionPanRight = 0x2, + kTouchActionPanX = kTouchActionPanLeft | kTouchActionPanRight, + kTouchActionPanUp = 0x4, + kTouchActionPanDown = 0x8, + kTouchActionPanY = kTouchActionPanUp | kTouchActionPanDown, + kTouchActionPan = kTouchActionPanX | kTouchActionPanY, + kTouchActionPinchZoom = 0x10, + kTouchActionManipulation = kTouchActionPan | kTouchActionPinchZoom, + kTouchActionDoubleTapZoom = 0x20, + kTouchActionAuto = kTouchActionManipulation | kTouchActionDoubleTapZoom, + kTouchActionMax = (1 << 6) - 1 +}; + +inline TouchAction operator|(TouchAction a, TouchAction b) { + return static_cast<TouchAction>(int(a) | int(b)); +} + +inline TouchAction& operator|=(TouchAction& a, TouchAction b) { + return a = a | b; +} + +inline TouchAction operator&(TouchAction a, TouchAction b) { + return static_cast<TouchAction>(int(a) & int(b)); +} + +inline TouchAction& operator&=(TouchAction& a, TouchAction b) { + return a = a & b; +} + +} // namespace cc + +#endif // CC_INPUT_TOUCH_ACTION_H_ diff --git a/chromium/cc/ipc/begin_frame_args_struct_traits.cc b/chromium/cc/ipc/begin_frame_args_struct_traits.cc index 8ba827dbe1b..fedd0ddc4ec 100644 --- a/chromium/cc/ipc/begin_frame_args_struct_traits.cc +++ b/chromium/cc/ipc/begin_frame_args_struct_traits.cc @@ -29,6 +29,8 @@ bool StructTraits<cc::mojom::BeginFrameArgsDataView, cc::BeginFrameArgs>::Read( bool StructTraits<cc::mojom::BeginFrameAckDataView, cc::BeginFrameAck>::Read( cc::mojom::BeginFrameAckDataView data, cc::BeginFrameAck* out) { + if (data.sequence_number() < cc::BeginFrameArgs::kStartingFrameNumber) + return false; out->source_id = data.source_id(); out->sequence_number = data.sequence_number(); out->latest_confirmed_sequence_number = diff --git a/chromium/cc/ipc/cc_param_traits.cc b/chromium/cc/ipc/cc_param_traits.cc index 3b04eb68511..02483340a76 100644 --- a/chromium/cc/ipc/cc_param_traits.cc +++ b/chromium/cc/ipc/cc_param_traits.cc @@ -795,7 +795,7 @@ bool ParamTraits<cc::CompositorFrame>::Read(const base::Pickle* m, uint32_t num_render_passes; if (!ReadParam(m, iter, &p->resource_list) || - !ReadParam(m, iter, &num_render_passes) || + !ReadParam(m, iter, &num_render_passes) || num_render_passes == 0 || num_render_passes > kMaxRenderPasses) return false; for (uint32_t i = 0; i < num_render_passes; ++i) { @@ -960,6 +960,40 @@ void ParamTraits<cc::YUVVideoDrawQuad>::Log(const param_type& p, l->append("])"); } +void ParamTraits<cc::BeginFrameAck>::GetSize(base::PickleSizer* s, + const param_type& p) { + GetParamSize(s, p.sequence_number); + GetParamSize(s, p.latest_confirmed_sequence_number); + GetParamSize(s, p.source_id); +} + +void ParamTraits<cc::BeginFrameAck>::Write(base::Pickle* m, + const param_type& p) { + m->WriteUInt64(p.sequence_number); + m->WriteUInt64(p.latest_confirmed_sequence_number); + m->WriteUInt32(p.source_id); + // |has_damage| is implicit through IPC message name, so not transmitted. +} + +bool ParamTraits<cc::BeginFrameAck>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + return iter->ReadUInt64(&p->sequence_number) && + p->sequence_number >= cc::BeginFrameArgs::kStartingFrameNumber && + iter->ReadUInt64(&p->latest_confirmed_sequence_number) && + iter->ReadUInt32(&p->source_id); +} + +void ParamTraits<cc::BeginFrameAck>::Log(const param_type& p, std::string* l) { + l->append("("); + LogParam(p.sequence_number, l); + l->append(", "); + LogParam(p.latest_confirmed_sequence_number, l); + l->append(", "); + LogParam(p.source_id, l); + l->append(")"); +} + } // namespace IPC // Generate param traits size methods. diff --git a/chromium/cc/ipc/cc_param_traits.h b/chromium/cc/ipc/cc_param_traits.h index 14e3dbacd99..d2e8b59978c 100644 --- a/chromium/cc/ipc/cc_param_traits.h +++ b/chromium/cc/ipc/cc_param_traits.h @@ -142,6 +142,17 @@ struct CC_IPC_EXPORT ParamTraits<cc::YUVVideoDrawQuad> { static void Log(const param_type& p, std::string* l); }; +template <> +struct CC_IPC_EXPORT ParamTraits<cc::BeginFrameAck> { + typedef cc::BeginFrameAck param_type; + static void GetSize(base::PickleSizer* s, const param_type& p); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p); + static void Log(const param_type& p, std::string* l); +}; + } // namespace IPC #endif // CC_IPC_CC_PARAM_TRAITS_H_ diff --git a/chromium/cc/ipc/cc_param_traits_macros.h b/chromium/cc/ipc/cc_param_traits_macros.h index 39a4ecf2fe2..019a16e127d 100644 --- a/chromium/cc/ipc/cc_param_traits_macros.h +++ b/chromium/cc/ipc/cc_param_traits_macros.h @@ -126,7 +126,7 @@ IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(cc::SharedQuadState) IPC_STRUCT_TRAITS_MEMBER(quad_to_target_transform) - IPC_STRUCT_TRAITS_MEMBER(quad_layer_bounds) + IPC_STRUCT_TRAITS_MEMBER(quad_layer_rect) IPC_STRUCT_TRAITS_MEMBER(visible_quad_layer_rect) IPC_STRUCT_TRAITS_MEMBER(clip_rect) IPC_STRUCT_TRAITS_MEMBER(is_clipped) @@ -176,13 +176,6 @@ IPC_STRUCT_TRAITS_BEGIN(cc::BeginFrameArgs) IPC_STRUCT_TRAITS_MEMBER(type) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(cc::BeginFrameAck) - IPC_STRUCT_TRAITS_MEMBER(sequence_number) - IPC_STRUCT_TRAITS_MEMBER(latest_confirmed_sequence_number) - IPC_STRUCT_TRAITS_MEMBER(source_id) -// |has_damage| is implicit through IPC message name, so not transmitted. -IPC_STRUCT_TRAITS_END() - IPC_STRUCT_TRAITS_BEGIN(cc::CompositorFrameMetadata) IPC_STRUCT_TRAITS_MEMBER(device_scale_factor) IPC_STRUCT_TRAITS_MEMBER(root_scroll_offset) @@ -205,7 +198,7 @@ IPC_STRUCT_TRAITS_BEGIN(cc::CompositorFrameMetadata) IPC_STRUCT_TRAITS_MEMBER(selection) IPC_STRUCT_TRAITS_MEMBER(latency_info) IPC_STRUCT_TRAITS_MEMBER(referenced_surfaces) - IPC_STRUCT_TRAITS_MEMBER(embedded_surfaces) + IPC_STRUCT_TRAITS_MEMBER(activation_dependencies) IPC_STRUCT_TRAITS_MEMBER(content_source_id) IPC_STRUCT_TRAITS_MEMBER(begin_frame_ack) IPC_STRUCT_TRAITS_MEMBER(frame_token) diff --git a/chromium/cc/ipc/cc_param_traits_unittest.cc b/chromium/cc/ipc/cc_param_traits_unittest.cc index edd451d7fe3..776e5c2ac30 100644 --- a/chromium/cc/ipc/cc_param_traits_unittest.cc +++ b/chromium/cc/ipc/cc_param_traits_unittest.cc @@ -81,7 +81,7 @@ class CCParamTraitsTest : public testing::Test { void Compare(const SharedQuadState* a, const SharedQuadState* b) { EXPECT_EQ(a->quad_to_target_transform, b->quad_to_target_transform); - EXPECT_EQ(a->quad_layer_bounds, b->quad_layer_bounds); + EXPECT_EQ(a->quad_layer_rect, b->quad_layer_rect); EXPECT_EQ(a->visible_quad_layer_rect, b->visible_quad_layer_rect); EXPECT_EQ(a->clip_rect, b->clip_rect); EXPECT_EQ(a->is_clipped, b->is_clipped); @@ -315,7 +315,7 @@ TEST_F(CCParamTraitsTest, AllQuads) { arbitrary_bool1); SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state1_in->SetAll(arbitrary_matrix1, arbitrary_size1, arbitrary_rect1, + shared_state1_in->SetAll(arbitrary_matrix1, arbitrary_rect1, arbitrary_rect1, arbitrary_rect2, arbitrary_bool1, arbitrary_float1, arbitrary_blend_mode1, arbitrary_context_id1); @@ -338,7 +338,7 @@ TEST_F(CCParamTraitsTest, AllQuads) { debugborder_in->shared_quad_state); SharedQuadState* shared_state2_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state2_in->SetAll(arbitrary_matrix2, arbitrary_size2, arbitrary_rect2, + shared_state2_in->SetAll(arbitrary_matrix2, arbitrary_rect2, arbitrary_rect2, arbitrary_rect3, arbitrary_bool1, arbitrary_float2, arbitrary_blend_mode2, arbitrary_context_id2); SharedQuadState* shared_state2_cmp = @@ -357,7 +357,7 @@ TEST_F(CCParamTraitsTest, AllQuads) { renderpass_in->render_pass_id); SharedQuadState* shared_state3_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state3_in->SetAll(arbitrary_matrix1, arbitrary_size3, arbitrary_rect3, + shared_state3_in->SetAll(arbitrary_matrix1, arbitrary_rect3, arbitrary_rect3, arbitrary_rect1, arbitrary_bool1, arbitrary_float3, arbitrary_blend_mode3, arbitrary_context_id3); SharedQuadState* shared_state3_cmp = @@ -456,6 +456,8 @@ TEST_F(CCParamTraitsTest, AllQuads) { CompositorFrame frame_in; frame_in.render_pass_list.push_back(std::move(child_pass_in)); frame_in.render_pass_list.push_back(std::move(pass_in)); + frame_in.metadata.begin_frame_ack.sequence_number = + cc::BeginFrameArgs::kStartingFrameNumber; IPC::ParamTraits<CompositorFrame>::Write(&msg, frame_in); @@ -505,7 +507,7 @@ TEST_F(CCParamTraitsTest, UnusedSharedQuadStates) { // The first SharedQuadState is used. SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state1_in->SetAll(gfx::Transform(), gfx::Size(1, 1), gfx::Rect(), + shared_state1_in->SetAll(gfx::Transform(), gfx::Rect(1, 1), gfx::Rect(), gfx::Rect(), false, 1.f, SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* quad1 = @@ -515,16 +517,16 @@ TEST_F(CCParamTraitsTest, UnusedSharedQuadStates) { // The second and third SharedQuadStates are not used. SharedQuadState* shared_state2_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state2_in->SetAll(gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), + shared_state2_in->SetAll(gfx::Transform(), gfx::Rect(2, 2), gfx::Rect(), gfx::Rect(), false, 1.f, SkBlendMode::kSrcOver, 0); SharedQuadState* shared_state3_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state3_in->SetAll(gfx::Transform(), gfx::Size(3, 3), gfx::Rect(), + shared_state3_in->SetAll(gfx::Transform(), gfx::Rect(3, 3), gfx::Rect(), gfx::Rect(), false, 1.f, SkBlendMode::kSrcOver, 0); // The fourth SharedQuadState is used. SharedQuadState* shared_state4_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state4_in->SetAll(gfx::Transform(), gfx::Size(4, 4), gfx::Rect(), + shared_state4_in->SetAll(gfx::Transform(), gfx::Rect(4, 4), gfx::Rect(), gfx::Rect(), false, 1.f, SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* quad2 = @@ -534,7 +536,7 @@ TEST_F(CCParamTraitsTest, UnusedSharedQuadStates) { // The fifth is not used again. SharedQuadState* shared_state5_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state5_in->SetAll(gfx::Transform(), gfx::Size(5, 5), gfx::Rect(), + shared_state5_in->SetAll(gfx::Transform(), gfx::Rect(5, 5), gfx::Rect(), gfx::Rect(), false, 1.f, SkBlendMode::kSrcOver, 0); // 5 SharedQuadStates go in. @@ -543,6 +545,8 @@ TEST_F(CCParamTraitsTest, UnusedSharedQuadStates) { CompositorFrame frame_in; frame_in.render_pass_list.push_back(std::move(pass_in)); + frame_in.metadata.begin_frame_ack.sequence_number = + cc::BeginFrameArgs::kStartingFrameNumber; IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); IPC::ParamTraits<CompositorFrame>::Write(&msg, frame_in); @@ -559,12 +563,12 @@ TEST_F(CCParamTraitsTest, UnusedSharedQuadStates) { ASSERT_EQ(2u, pass_out->shared_quad_state_list.size()); ASSERT_EQ(2u, pass_out->quad_list.size()); - EXPECT_EQ(gfx::Size(1, 1).ToString(), + EXPECT_EQ(gfx::Rect(1, 1).ToString(), pass_out->shared_quad_state_list.ElementAt(0) - ->quad_layer_bounds.ToString()); - EXPECT_EQ(gfx::Size(4, 4).ToString(), + ->quad_layer_rect.ToString()); + EXPECT_EQ(gfx::Rect(4, 4).ToString(), pass_out->shared_quad_state_list.ElementAt(1) - ->quad_layer_bounds.ToString()); + ->quad_layer_rect.ToString()); } TEST_F(CCParamTraitsTest, Resources) { @@ -620,6 +624,8 @@ TEST_F(CCParamTraitsTest, Resources) { frame_in.resource_list.push_back(arbitrary_resource1); frame_in.resource_list.push_back(arbitrary_resource2); frame_in.render_pass_list.push_back(std::move(renderpass_in)); + frame_in.metadata.begin_frame_ack.sequence_number = + cc::BeginFrameArgs::kStartingFrameNumber; IPC::ParamTraits<CompositorFrame>::Write(&msg, frame_in); diff --git a/chromium/cc/ipc/cc_serialization_perftest.cc b/chromium/cc/ipc/cc_serialization_perftest.cc index ee90e503630..85a03a0e680 100644 --- a/chromium/cc/ipc/cc_serialization_perftest.cc +++ b/chromium/cc/ipc/cc_serialization_perftest.cc @@ -309,7 +309,7 @@ class CCSerializationPerfTest : public testing::Test { for (uint32_t i = 0; i < 10; ++i) { SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state1_in->SetAll(arbitrary_matrix1, arbitrary_size1, + shared_state1_in->SetAll(arbitrary_matrix1, arbitrary_rect1, arbitrary_rect1, arbitrary_rect2, arbitrary_bool1, arbitrary_float1, arbitrary_blend_mode1, arbitrary_context_id1); @@ -355,7 +355,7 @@ class CCSerializationPerfTest : public testing::Test { for (uint32_t i = 0; i < 10; ++i) { SharedQuadState* shared_state2_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state2_in->SetAll(arbitrary_matrix2, arbitrary_size2, + shared_state2_in->SetAll(arbitrary_matrix2, arbitrary_rect2, arbitrary_rect2, arbitrary_rect3, arbitrary_bool1, arbitrary_float2, arbitrary_blend_mode2, arbitrary_context_id2); @@ -374,7 +374,7 @@ class CCSerializationPerfTest : public testing::Test { for (uint32_t i = 0; i < 5; ++i) { SharedQuadState* shared_state3_in = pass_in->CreateAndAppendSharedQuadState(); - shared_state3_in->SetAll(arbitrary_matrix1, arbitrary_size3, + shared_state3_in->SetAll(arbitrary_matrix1, arbitrary_rect3, arbitrary_rect3, arbitrary_rect1, arbitrary_bool1, arbitrary_float3, arbitrary_blend_mode3, arbitrary_context_id3); diff --git a/chromium/cc/ipc/compositor_frame_metadata.mojom b/chromium/cc/ipc/compositor_frame_metadata.mojom index 0b1a3d7b388..abbe76d395f 100644 --- a/chromium/cc/ipc/compositor_frame_metadata.mojom +++ b/chromium/cc/ipc/compositor_frame_metadata.mojom @@ -31,7 +31,7 @@ struct CompositorFrameMetadata { Selection selection; array<ui.mojom.LatencyInfo> latency_info; array<SurfaceId> referenced_surfaces; - array<SurfaceId> embedded_surfaces; + array<SurfaceId> activation_dependencies; bool can_activate_before_dependencies; uint32 content_source_id; BeginFrameAck begin_frame_ack; diff --git a/chromium/cc/ipc/compositor_frame_metadata_struct_traits.cc b/chromium/cc/ipc/compositor_frame_metadata_struct_traits.cc index ab79ed7eb63..fc3d85bfc14 100644 --- a/chromium/cc/ipc/compositor_frame_metadata_struct_traits.cc +++ b/chromium/cc/ipc/compositor_frame_metadata_struct_traits.cc @@ -45,7 +45,7 @@ bool StructTraits<cc::mojom::CompositorFrameMetadataDataView, return data.ReadSelection(&out->selection) && data.ReadLatencyInfo(&out->latency_info) && data.ReadReferencedSurfaces(&out->referenced_surfaces) && - data.ReadEmbeddedSurfaces(&out->embedded_surfaces) && + data.ReadActivationDependencies(&out->activation_dependencies) && data.ReadBeginFrameAck(&out->begin_frame_ack); } diff --git a/chromium/cc/ipc/compositor_frame_metadata_struct_traits.h b/chromium/cc/ipc/compositor_frame_metadata_struct_traits.h index f3077ce9495..2959ababfe8 100644 --- a/chromium/cc/ipc/compositor_frame_metadata_struct_traits.h +++ b/chromium/cc/ipc/compositor_frame_metadata_struct_traits.h @@ -106,9 +106,9 @@ struct StructTraits<cc::mojom::CompositorFrameMetadataDataView, return metadata.referenced_surfaces; } - static const std::vector<cc::SurfaceId>& embedded_surfaces( + static const std::vector<cc::SurfaceId>& activation_dependencies( const cc::CompositorFrameMetadata& metadata) { - return metadata.embedded_surfaces; + return metadata.activation_dependencies; } static bool can_activate_before_dependencies( diff --git a/chromium/cc/ipc/compositor_frame_struct_traits.cc b/chromium/cc/ipc/compositor_frame_struct_traits.cc index e3d5442855a..f06f3704e64 100644 --- a/chromium/cc/ipc/compositor_frame_struct_traits.cc +++ b/chromium/cc/ipc/compositor_frame_struct_traits.cc @@ -14,11 +14,9 @@ bool StructTraits<cc::mojom::CompositorFrameDataView, cc::CompositorFrame>::Read(cc::mojom::CompositorFrameDataView data, cc::CompositorFrame* out) { - if (!data.ReadMetadata(&out->metadata)) - return false; - - return data.ReadResources(&out->resource_list) && - data.ReadPasses(&out->render_pass_list); + return data.ReadPasses(&out->render_pass_list) && + !out->render_pass_list.empty() && data.ReadMetadata(&out->metadata) && + data.ReadResources(&out->resource_list); } } // namespace mojo diff --git a/chromium/cc/ipc/mojo_compositor_frame_sink.mojom b/chromium/cc/ipc/mojo_compositor_frame_sink.mojom index 1ad4ae14d5e..e4c0ae3ef54 100644 --- a/chromium/cc/ipc/mojo_compositor_frame_sink.mojom +++ b/chromium/cc/ipc/mojo_compositor_frame_sink.mojom @@ -37,11 +37,11 @@ interface MojoCompositorFrameSink { // Notifies the frame sink that a BeginFrame was completed, but that no // CompositorFrame was produced as a result of it. - BeginFrameDidNotSwap(cc.mojom.BeginFrameAck ack); + DidNotProduceFrame(cc.mojom.BeginFrameAck ack); // Notify that the surface is no longer in use (and is okay to be evicted) so // that its resources gets returned in time. - EvictFrame(); + EvictCurrentSurface(); }; interface MojoCompositorFrameSinkClient { diff --git a/chromium/cc/ipc/quads.mojom b/chromium/cc/ipc/quads.mojom index 5ec7e45fca1..436c4433b0c 100644 --- a/chromium/cc/ipc/quads.mojom +++ b/chromium/cc/ipc/quads.mojom @@ -8,6 +8,7 @@ import "cc/ipc/filter_operations.mojom"; import "cc/ipc/shared_quad_state.mojom"; import "cc/ipc/surface_id.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; +import "ui/gfx/mojo/color_space.mojom"; import "ui/gfx/mojo/transform.mojom"; struct DebugBorderQuadState { @@ -102,6 +103,7 @@ struct YUVVideoQuadState { float resource_offset; float resource_multiplier; uint32 bits_per_channel; + gfx.mojom.ColorSpace video_color_space; }; union DrawQuadState { diff --git a/chromium/cc/ipc/quads_struct_traits.cc b/chromium/cc/ipc/quads_struct_traits.cc index 011f62771f9..2de061d5507 100644 --- a/chromium/cc/ipc/quads_struct_traits.cc +++ b/chromium/cc/ipc/quads_struct_traits.cc @@ -229,7 +229,8 @@ bool StructTraits<cc::mojom::YUVVideoQuadStateDataView, cc::DrawQuad>::Read( if (!data.ReadYaTexCoordRect(&quad->ya_tex_coord_rect) || !data.ReadUvTexCoordRect(&quad->uv_tex_coord_rect) || !data.ReadYaTexSize(&quad->ya_tex_size) || - !data.ReadUvTexSize(&quad->uv_tex_size)) { + !data.ReadUvTexSize(&quad->uv_tex_size) || + !data.ReadVideoColorSpace(&quad->video_color_space)) { return false; } quad->resources.ids[cc::YUVVideoDrawQuad::kYPlaneResourceIdIndex] = diff --git a/chromium/cc/ipc/quads_struct_traits.h b/chromium/cc/ipc/quads_struct_traits.h index ed27b9921ce..ebeb86c7de8 100644 --- a/chromium/cc/ipc/quads_struct_traits.h +++ b/chromium/cc/ipc/quads_struct_traits.h @@ -21,6 +21,7 @@ #include "cc/quads/tile_draw_quad.h" #include "cc/quads/yuv_video_draw_quad.h" #include "ui/gfx/geometry/mojo/geometry_struct_traits.h" +#include "ui/gfx/ipc/color/gfx_param_traits.h" namespace mojo { @@ -414,6 +415,11 @@ struct StructTraits<cc::mojom::YUVVideoQuadStateDataView, cc::DrawQuad> { cc::YUVVideoDrawQuad::MaterialCast(&input); return quad->bits_per_channel; } + static gfx::ColorSpace video_color_space(const cc::DrawQuad& input) { + const cc::YUVVideoDrawQuad* quad = + cc::YUVVideoDrawQuad::MaterialCast(&input); + return quad->video_color_space; + } static bool Read(cc::mojom::YUVVideoQuadStateDataView data, cc::DrawQuad* out); diff --git a/chromium/cc/ipc/shared_quad_state.mojom b/chromium/cc/ipc/shared_quad_state.mojom index d819acb0e66..cb1fdc0f9bd 100644 --- a/chromium/cc/ipc/shared_quad_state.mojom +++ b/chromium/cc/ipc/shared_quad_state.mojom @@ -11,8 +11,8 @@ struct SharedQuadState { // gfx.mojom.Transforms quad rects into the target content space. gfx.mojom.Transform quad_to_target_transform; - // The size of the quads' originating layer in the space of the quad rects. - gfx.mojom.Size quad_layer_bounds; + // The rect of the quads' originating layer in the space of the quad rects. + gfx.mojom.Rect quad_layer_rect; // The size of the visible area in the quads' originating layer, in the space // of the quad rects. diff --git a/chromium/cc/ipc/shared_quad_state_struct_traits.h b/chromium/cc/ipc/shared_quad_state_struct_traits.h index 8b62a34eacc..5b6dd2877de 100644 --- a/chromium/cc/ipc/shared_quad_state_struct_traits.h +++ b/chromium/cc/ipc/shared_quad_state_struct_traits.h @@ -25,8 +25,8 @@ struct StructTraits<cc::mojom::SharedQuadStateDataView, OptSharedQuadState> { return input.sqs->quad_to_target_transform; } - static const gfx::Size& quad_layer_bounds(const OptSharedQuadState& input) { - return input.sqs->quad_layer_bounds; + static const gfx::Rect& quad_layer_rect(const OptSharedQuadState& input) { + return input.sqs->quad_layer_rect; } static const gfx::Rect& visible_quad_layer_rect( @@ -62,8 +62,8 @@ struct StructTraits<cc::mojom::SharedQuadStateDataView, cc::SharedQuadState> { return sqs.quad_to_target_transform; } - static const gfx::Size& quad_layer_bounds(const cc::SharedQuadState& sqs) { - return sqs.quad_layer_bounds; + static const gfx::Rect& quad_layer_rect(const cc::SharedQuadState& sqs) { + return sqs.quad_layer_rect; } static const gfx::Rect& visible_quad_layer_rect( @@ -92,7 +92,7 @@ struct StructTraits<cc::mojom::SharedQuadStateDataView, cc::SharedQuadState> { static bool Read(cc::mojom::SharedQuadStateDataView data, cc::SharedQuadState* out) { if (!data.ReadQuadToTargetTransform(&out->quad_to_target_transform) || - !data.ReadQuadLayerBounds(&out->quad_layer_bounds) || + !data.ReadQuadLayerRect(&out->quad_layer_rect) || !data.ReadVisibleQuadLayerRect(&out->visible_quad_layer_rect) || !data.ReadClipRect(&out->clip_rect)) { return false; diff --git a/chromium/cc/ipc/struct_traits_unittest.cc b/chromium/cc/ipc/struct_traits_unittest.cc index d132d29b7bb..40c402b3dc0 100644 --- a/chromium/cc/ipc/struct_traits_unittest.cc +++ b/chromium/cc/ipc/struct_traits_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <utility> + #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "cc/input/selection.h" @@ -38,99 +40,90 @@ class StructTraitsTest : public testing::Test, public mojom::TraitsTestService { private: // TraitsTestService: void EchoBeginFrameArgs(const BeginFrameArgs& b, - const EchoBeginFrameArgsCallback& callback) override { - callback.Run(b); + EchoBeginFrameArgsCallback callback) override { + std::move(callback).Run(b); } void EchoBeginFrameAck(const BeginFrameAck& b, - const EchoBeginFrameAckCallback& callback) override { - callback.Run(b); + EchoBeginFrameAckCallback callback) override { + std::move(callback).Run(b); } - void EchoCompositorFrame( - CompositorFrame c, - const EchoCompositorFrameCallback& callback) override { - callback.Run(std::move(c)); + void EchoCompositorFrame(CompositorFrame c, + EchoCompositorFrameCallback callback) override { + std::move(callback).Run(std::move(c)); } void EchoCompositorFrameMetadata( CompositorFrameMetadata c, - const EchoCompositorFrameMetadataCallback& callback) override { - callback.Run(std::move(c)); + EchoCompositorFrameMetadataCallback callback) override { + std::move(callback).Run(std::move(c)); } - void EchoCopyOutputRequest( - std::unique_ptr<CopyOutputRequest> c, - const EchoCopyOutputRequestCallback& callback) override { - callback.Run(std::move(c)); + void EchoCopyOutputRequest(std::unique_ptr<CopyOutputRequest> c, + EchoCopyOutputRequestCallback callback) override { + std::move(callback).Run(std::move(c)); } - void EchoCopyOutputResult( - std::unique_ptr<CopyOutputResult> c, - const EchoCopyOutputResultCallback& callback) override { - callback.Run(std::move(c)); + void EchoCopyOutputResult(std::unique_ptr<CopyOutputResult> c, + EchoCopyOutputResultCallback callback) override { + std::move(callback).Run(std::move(c)); } - void EchoFilterOperation( - const FilterOperation& f, - const EchoFilterOperationCallback& callback) override { - callback.Run(f); + void EchoFilterOperation(const FilterOperation& f, + EchoFilterOperationCallback callback) override { + std::move(callback).Run(f); } - void EchoFilterOperations( - const FilterOperations& f, - const EchoFilterOperationsCallback& callback) override { - callback.Run(f); + void EchoFilterOperations(const FilterOperations& f, + EchoFilterOperationsCallback callback) override { + std::move(callback).Run(f); } void EchoRenderPass(std::unique_ptr<RenderPass> r, - const EchoRenderPassCallback& callback) override { - callback.Run(std::move(r)); + EchoRenderPassCallback callback) override { + std::move(callback).Run(std::move(r)); } - void EchoReturnedResource( - const ReturnedResource& r, - const EchoReturnedResourceCallback& callback) override { - callback.Run(r); + void EchoReturnedResource(const ReturnedResource& r, + EchoReturnedResourceCallback callback) override { + std::move(callback).Run(r); } void EchoSelection(const Selection<gfx::SelectionBound>& s, - const EchoSelectionCallback& callback) override { - callback.Run(s); + EchoSelectionCallback callback) override { + std::move(callback).Run(s); } - void EchoSharedQuadState( - const SharedQuadState& s, - const EchoSharedQuadStateCallback& callback) override { - callback.Run(s); + void EchoSharedQuadState(const SharedQuadState& s, + EchoSharedQuadStateCallback callback) override { + std::move(callback).Run(s); } void EchoSurfaceId(const SurfaceId& s, - const EchoSurfaceIdCallback& callback) override { - callback.Run(s); + EchoSurfaceIdCallback callback) override { + std::move(callback).Run(s); } - void EchoSurfaceReference( - const SurfaceReference& s, - const EchoSurfaceReferenceCallback& callback) override { - callback.Run(s); + void EchoSurfaceReference(const SurfaceReference& s, + EchoSurfaceReferenceCallback callback) override { + std::move(callback).Run(s); } - void EchoSurfaceSequence( - const SurfaceSequence& s, - const EchoSurfaceSequenceCallback& callback) override { - callback.Run(s); + void EchoSurfaceSequence(const SurfaceSequence& s, + EchoSurfaceSequenceCallback callback) override { + std::move(callback).Run(s); } void EchoTextureMailbox(const TextureMailbox& t, - const EchoTextureMailboxCallback& callback) override { - callback.Run(t); + EchoTextureMailboxCallback callback) override { + std::move(callback).Run(t); } void EchoTransferableResource( const TransferableResource& t, - const EchoTransferableResourceCallback& callback) override { - callback.Run(t); + EchoTransferableResourceCallback callback) override { + std::move(callback).Run(t); } mojo::BindingSet<TraitsTestService> traits_test_bindings_; @@ -229,7 +222,7 @@ TEST_F(StructTraitsTest, CompositorFrame) { const gfx::Transform sqs_quad_to_target_transform( 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f); - const gfx::Size sqs_layer_bounds(1234, 5678); + const gfx::Rect sqs_layer_rect(1234, 5678); const gfx::Rect sqs_visible_layer_rect(12, 34, 56, 78); const gfx::Rect sqs_clip_rect(123, 456, 789, 101112); const bool sqs_is_clipped = true; @@ -237,7 +230,7 @@ TEST_F(StructTraitsTest, CompositorFrame) { const SkBlendMode sqs_blend_mode = SkBlendMode::kSrcOver; const int sqs_sorting_context_id = 1337; SharedQuadState* sqs = render_pass->CreateAndAppendSharedQuadState(); - sqs->SetAll(sqs_quad_to_target_transform, sqs_layer_bounds, + sqs->SetAll(sqs_quad_to_target_transform, sqs_layer_rect, sqs_visible_layer_rect, sqs_clip_rect, sqs_is_clipped, sqs_opacity, sqs_blend_mode, sqs_sorting_context_id); @@ -315,7 +308,7 @@ TEST_F(StructTraitsTest, CompositorFrame) { const SharedQuadState* out_sqs = out_render_pass->shared_quad_state_list.ElementAt(0); EXPECT_EQ(sqs_quad_to_target_transform, out_sqs->quad_to_target_transform); - EXPECT_EQ(sqs_layer_bounds, out_sqs->quad_layer_bounds); + EXPECT_EQ(sqs_layer_rect, out_sqs->quad_layer_rect); EXPECT_EQ(sqs_visible_layer_rect, out_sqs->visible_quad_layer_rect); EXPECT_EQ(sqs_clip_rect, out_sqs->clip_rect); EXPECT_EQ(sqs_is_clipped, out_sqs->is_clipped); @@ -374,11 +367,12 @@ TEST_F(StructTraitsTest, CompositorFrameMetadata) { SurfaceId id(FrameSinkId(1234, 4321), LocalSurfaceId(5678, base::UnguessableToken::Create())); referenced_surfaces.push_back(id); - std::vector<SurfaceId> embedded_surfaces; + std::vector<SurfaceId> activation_dependencies; SurfaceId id2(FrameSinkId(4321, 1234), LocalSurfaceId(8765, base::UnguessableToken::Create())); - embedded_surfaces.push_back(id2); + activation_dependencies.push_back(id2); uint32_t frame_token = 0xdeadbeef; + uint64_t begin_frame_ack_sequence_number = 0xdeadbeef; CompositorFrameMetadata input; input.device_scale_factor = device_scale_factor; @@ -401,8 +395,9 @@ TEST_F(StructTraitsTest, CompositorFrameMetadata) { input.selection = selection; input.latency_info = latency_infos; input.referenced_surfaces = referenced_surfaces; - input.embedded_surfaces = embedded_surfaces; + input.activation_dependencies = activation_dependencies; input.frame_token = frame_token; + input.begin_frame_ack.sequence_number = begin_frame_ack_sequence_number; mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); CompositorFrameMetadata output; @@ -434,10 +429,13 @@ TEST_F(StructTraitsTest, CompositorFrameMetadata) { EXPECT_EQ(referenced_surfaces.size(), output.referenced_surfaces.size()); for (uint32_t i = 0; i < referenced_surfaces.size(); ++i) EXPECT_EQ(referenced_surfaces[i], output.referenced_surfaces[i]); - EXPECT_EQ(embedded_surfaces.size(), output.embedded_surfaces.size()); - for (uint32_t i = 0; i < embedded_surfaces.size(); ++i) - EXPECT_EQ(embedded_surfaces[i], output.embedded_surfaces[i]); + EXPECT_EQ(activation_dependencies.size(), + output.activation_dependencies.size()); + for (uint32_t i = 0; i < activation_dependencies.size(); ++i) + EXPECT_EQ(activation_dependencies[i], output.activation_dependencies[i]); EXPECT_EQ(frame_token, output.frame_token); + EXPECT_EQ(begin_frame_ack_sequence_number, + output.begin_frame_ack.sequence_number); } TEST_F(StructTraitsTest, CopyOutputRequest_BitmapRequest) { @@ -531,7 +529,8 @@ TEST_F(StructTraitsTest, CopyOutputResult_Bitmap) { bitmap->allocN32Pixels(7, 8); bitmap->eraseARGB(123, 213, 77, 33); auto in_bitmap = base::MakeUnique<SkBitmap>(); - bitmap->deepCopyTo(in_bitmap.get()); + in_bitmap->allocN32Pixels(7, 8); + in_bitmap->eraseARGB(123, 213, 77, 33); auto input = CopyOutputResult::CreateBitmapResult(std::move(bitmap)); auto size = input->size(); @@ -844,14 +843,14 @@ TEST_F(StructTraitsTest, RenderPass) { shared_state_1->SetAll( gfx::Transform(16.1f, 15.3f, 14.3f, 13.7f, 12.2f, 11.4f, 10.4f, 9.8f, 8.1f, 7.3f, 6.3f, 5.7f, 4.8f, 3.4f, 2.4f, 1.2f), - gfx::Size(1, 2), gfx::Rect(1337, 5679, 9101112, 131415), + gfx::Rect(1, 2), gfx::Rect(1337, 5679, 9101112, 131415), gfx::Rect(1357, 2468, 121314, 1337), true, 2, SkBlendMode::kSrcOver, 1); SharedQuadState* shared_state_2 = input->CreateAndAppendSharedQuadState(); shared_state_2->SetAll( gfx::Transform(1.1f, 2.3f, 3.3f, 4.7f, 5.2f, 6.4f, 7.4f, 8.8f, 9.1f, 10.3f, 11.3f, 12.7f, 13.8f, 14.4f, 15.4f, 16.2f), - gfx::Size(1337, 1234), gfx::Rect(1234, 5678, 9101112, 13141516), + gfx::Rect(1337, 1234), gfx::Rect(1234, 5678, 9101112, 13141516), gfx::Rect(1357, 2468, 121314, 1337), true, 2, SkBlendMode::kSrcOver, 1); // This quad uses the first shared quad state. The next two quads use the @@ -896,7 +895,7 @@ TEST_F(StructTraitsTest, RenderPass) { SharedQuadState* out_sqs1 = output->shared_quad_state_list.ElementAt(0); EXPECT_EQ(shared_state_1->quad_to_target_transform, out_sqs1->quad_to_target_transform); - EXPECT_EQ(shared_state_1->quad_layer_bounds, out_sqs1->quad_layer_bounds); + EXPECT_EQ(shared_state_1->quad_layer_rect, out_sqs1->quad_layer_rect); EXPECT_EQ(shared_state_1->visible_quad_layer_rect, out_sqs1->visible_quad_layer_rect); EXPECT_EQ(shared_state_1->clip_rect, out_sqs1->clip_rect); @@ -908,7 +907,7 @@ TEST_F(StructTraitsTest, RenderPass) { SharedQuadState* out_sqs2 = output->shared_quad_state_list.ElementAt(1); EXPECT_EQ(shared_state_2->quad_to_target_transform, out_sqs2->quad_to_target_transform); - EXPECT_EQ(shared_state_2->quad_layer_bounds, out_sqs2->quad_layer_bounds); + EXPECT_EQ(shared_state_2->quad_layer_rect, out_sqs2->quad_layer_rect); EXPECT_EQ(shared_state_2->visible_quad_layer_rect, out_sqs2->visible_quad_layer_rect); EXPECT_EQ(shared_state_2->clip_rect, out_sqs2->clip_rect); @@ -948,7 +947,9 @@ TEST_F(StructTraitsTest, RenderPassWithEmptySharedQuadStateList) { const gfx::Transform transform_to_root = gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0); const gfx::Rect damage_rect(56, 123, 19, 43); - gfx::ColorSpace color_space = gfx::ColorSpace::CreateSCRGBLinear(); + SkMatrix44 to_XYZD50; + SkColorSpaceTransferFn fn = {1, 0, 1, 0, 0, 0, 1}; + gfx::ColorSpace color_space = gfx::ColorSpace::CreateCustom(to_XYZD50, fn); const bool has_transparent_background = true; std::unique_ptr<RenderPass> input = RenderPass::Create(); input->SetAll(id, output_rect, damage_rect, transform_to_root, @@ -1062,7 +1063,7 @@ TEST_F(StructTraitsTest, SharedQuadState) { const gfx::Transform quad_to_target_transform(1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f); - const gfx::Size layer_bounds(1234, 5678); + const gfx::Rect layer_rect(1234, 5678); const gfx::Rect visible_layer_rect(12, 34, 56, 78); const gfx::Rect clip_rect(123, 456, 789, 101112); const bool is_clipped = true; @@ -1070,14 +1071,14 @@ TEST_F(StructTraitsTest, SharedQuadState) { const SkBlendMode blend_mode = SkBlendMode::kSrcOver; const int sorting_context_id = 1337; SharedQuadState input_sqs; - input_sqs.SetAll(quad_to_target_transform, layer_bounds, visible_layer_rect, + input_sqs.SetAll(quad_to_target_transform, layer_rect, visible_layer_rect, clip_rect, is_clipped, opacity, blend_mode, sorting_context_id); mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); SharedQuadState output_sqs; proxy->EchoSharedQuadState(input_sqs, &output_sqs); EXPECT_EQ(quad_to_target_transform, output_sqs.quad_to_target_transform); - EXPECT_EQ(layer_bounds, output_sqs.quad_layer_bounds); + EXPECT_EQ(layer_rect, output_sqs.quad_layer_rect); EXPECT_EQ(visible_layer_rect, output_sqs.visible_quad_layer_rect); EXPECT_EQ(clip_rect, output_sqs.clip_rect); EXPECT_EQ(is_clipped, output_sqs.is_clipped); @@ -1101,8 +1102,9 @@ TEST_F(StructTraitsTest, TextureMailbox) { const bool is_overlay_candidate = true; const bool secure_output_only = true; const bool nearest_neighbor = true; - const gfx::ColorSpace color_space = - gfx::ColorSpace::CreateVideo(4, 5, 9, gfx::ColorSpace::RangeID::LIMITED); + const gfx::ColorSpace color_space = gfx::ColorSpace( + gfx::ColorSpace::PrimaryID::BT470M, gfx::ColorSpace::TransferID::GAMMA28, + gfx::ColorSpace::MatrixID::BT2020_NCL, gfx::ColorSpace::RangeID::LIMITED); #if defined(OS_ANDROID) const bool is_backed_by_surface_texture = true; const bool wants_promotion_hint = true; diff --git a/chromium/cc/ipc/transferable_resource.mojom b/chromium/cc/ipc/transferable_resource.mojom index 971536c3b94..ea20a63f826 100644 --- a/chromium/cc/ipc/transferable_resource.mojom +++ b/chromium/cc/ipc/transferable_resource.mojom @@ -6,6 +6,7 @@ module cc.mojom; import "gpu/ipc/common/mailbox_holder.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; +import "ui/gfx/mojo/color_space.mojom"; import "ui/gfx/mojo/buffer_types.mojom"; enum ResourceFormat { @@ -31,4 +32,5 @@ struct TransferableResource { bool is_overlay_candidate; bool is_backed_by_surface_texture; bool wants_promotion_hint; + gfx.mojom.ColorSpace color_space; }; diff --git a/chromium/cc/ipc/transferable_resource_struct_traits.cc b/chromium/cc/ipc/transferable_resource_struct_traits.cc index 140276a32b0..ea6c56d3398 100644 --- a/chromium/cc/ipc/transferable_resource_struct_traits.cc +++ b/chromium/cc/ipc/transferable_resource_struct_traits.cc @@ -16,7 +16,8 @@ bool StructTraits<cc::mojom::TransferableResourceDataView, Read(cc::mojom::TransferableResourceDataView data, cc::TransferableResource* out) { if (!data.ReadSize(&out->size) || - !data.ReadMailboxHolder(&out->mailbox_holder)) + !data.ReadMailboxHolder(&out->mailbox_holder) || + !data.ReadColorSpace(&out->color_space)) return false; out->id = data.id(); out->format = static_cast<cc::ResourceFormat>(data.format()); diff --git a/chromium/cc/ipc/transferable_resource_struct_traits.h b/chromium/cc/ipc/transferable_resource_struct_traits.h index 1e3f24b7567..e9964c6fce3 100644 --- a/chromium/cc/ipc/transferable_resource_struct_traits.h +++ b/chromium/cc/ipc/transferable_resource_struct_traits.h @@ -7,6 +7,7 @@ #include "cc/ipc/transferable_resource.mojom-shared.h" #include "cc/resources/transferable_resource.h" +#include "ui/gfx/ipc/color/gfx_param_traits.h" namespace mojo { @@ -74,6 +75,11 @@ struct StructTraits<cc::mojom::TransferableResourceDataView, #endif } + static const gfx::ColorSpace& color_space( + const cc::TransferableResource& resource) { + return resource.color_space; + } + static bool Read(cc::mojom::TransferableResourceDataView data, cc::TransferableResource* out); }; diff --git a/chromium/cc/layers/append_quads_data.h b/chromium/cc/layers/append_quads_data.h index 45afa469072..5fec36e9506 100644 --- a/chromium/cc/layers/append_quads_data.h +++ b/chromium/cc/layers/append_quads_data.h @@ -30,8 +30,10 @@ class CC_EXPORT AppendQuadsData { int64_t checkerboarded_no_recording_content_area = 0; // This is the area within interest rect. int64_t checkerboarded_needs_raster_content_area = 0; - // This is the set of surface IDs embedded in SurfaceDrawQuads. - std::vector<SurfaceId> embedded_surfaces; + // This is the set of surface IDs that must have corresponding + // active CompositorFrames so that this CompositorFrame can + // activate. + std::vector<SurfaceId> activation_dependencies; }; } // namespace cc diff --git a/chromium/cc/layers/effect_tree_layer_list_iterator.cc b/chromium/cc/layers/effect_tree_layer_list_iterator.cc index a5228dcb851..340fb94afdc 100644 --- a/chromium/cc/layers/effect_tree_layer_list_iterator.cc +++ b/chromium/cc/layers/effect_tree_layer_list_iterator.cc @@ -17,9 +17,8 @@ EffectTreeLayerListIterator::EffectTreeLayerListIterator( layer_list_iterator_ = layer_tree_impl->rbegin(); // Find the front-most drawn layer. - while ( - layer_list_iterator_ != layer_tree_impl->rend() && - !(*layer_list_iterator_)->is_drawn_render_surface_layer_list_member()) { + while (layer_list_iterator_ != layer_tree_impl->rend() && + !(*layer_list_iterator_)->contributes_to_drawn_render_surface()) { layer_list_iterator_++; } @@ -43,28 +42,13 @@ EffectTreeLayerListIterator::EffectTreeLayerListIterator( EffectTreeLayerListIterator::~EffectTreeLayerListIterator() {} -// Finds the lowest common ancestor that has a render surface. -static int LowestCommonAncestor(int effect_id_1, - int effect_id_2, - const EffectTree* effect_tree) { - while (effect_id_1 != effect_id_2) { - if (effect_id_1 < effect_id_2) - effect_id_2 = effect_tree->Node(effect_id_2)->target_id; - else - effect_id_1 = effect_tree->Node(effect_id_1)->target_id; - } - - return effect_id_1; -} - void EffectTreeLayerListIterator::operator++() { switch (state_) { case State::LAYER: // Find the next drawn layer. layer_list_iterator_++; while (layer_list_iterator_ != layer_tree_impl_->rend() && - !(*layer_list_iterator_) - ->is_drawn_render_surface_layer_list_member()) { + !(*layer_list_iterator_)->contributes_to_drawn_render_surface()) { layer_list_iterator_++; } if (layer_list_iterator_ == layer_tree_impl_->rend()) { @@ -80,8 +64,9 @@ void EffectTreeLayerListIterator::operator++() { // If the next drawn layer has a different target effect tree index, check // for surfaces whose contributors have all been visited. if (next_effect_tree_index_ != current_effect_tree_index_) { - lowest_common_effect_tree_ancestor_index_ = LowestCommonAncestor( - current_effect_tree_index_, next_effect_tree_index_, effect_tree_); + lowest_common_effect_tree_ancestor_index_ = + effect_tree_->LowestCommonAncestorWithRenderSurface( + current_effect_tree_index_, next_effect_tree_index_); // If the current layer's target effect node is an ancestor of the next // layer's target effect node, then the current effect node still has // more contributors that need to be visited. Otherwise, all diff --git a/chromium/cc/layers/effect_tree_layer_list_iterator_unittest.cc b/chromium/cc/layers/effect_tree_layer_list_iterator_unittest.cc index 6d6fb8d0a9b..71fc0fc207a 100644 --- a/chromium/cc/layers/effect_tree_layer_list_iterator_unittest.cc +++ b/chromium/cc/layers/effect_tree_layer_list_iterator_unittest.cc @@ -9,6 +9,7 @@ #include "base/memory/ptr_util.h" #include "cc/layers/layer.h" #include "cc/test/fake_layer_tree_host.h" +#include "cc/test/layer_test_common.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host_common.h" #include "testing/gmock/include/gmock/gmock.h" @@ -37,7 +38,7 @@ class TestLayerImpl : public LayerImpl { }; #define EXPECT_COUNT(layer, target, contrib, itself) \ - if (layer->GetRenderSurface()) { \ + if (GetRenderSurface(layer)) { \ EXPECT_EQ(target, target_surface_count_[layer->effect_tree_index()]); \ EXPECT_EQ(contrib, \ contributing_surface_count_[layer->effect_tree_index()]); \ @@ -108,9 +109,9 @@ TEST_F(EffectTreeLayerListIteratorTest, TreeWithNoDrawnLayers) { host_impl_.active_tree()->SetRootLayerForTesting(std::move(root_layer)); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_ptr, root_ptr->bounds(), &render_surface_layer_list); + root_ptr, root_ptr->bounds(), &render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); IterateFrontToBack(); @@ -137,17 +138,17 @@ TEST_F(EffectTreeLayerListIteratorTest, SimpleTree) { host_impl_.active_tree()->SetRootLayerForTesting(std::move(root_layer)); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_ptr, root_ptr->bounds(), &render_surface_layer_list); + root_ptr, root_ptr->bounds(), &render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); IterateFrontToBack(); EXPECT_COUNT(root_ptr, 5, -1, 4); - EXPECT_COUNT(first_ptr, -1, -1, 3); - EXPECT_COUNT(second_ptr, -1, -1, 2); - EXPECT_COUNT(third_ptr, -1, -1, 1); - EXPECT_COUNT(fourth_ptr, -1, -1, 0); + EXPECT_COUNT(first_ptr, 5, -1, 3); + EXPECT_COUNT(second_ptr, 5, -1, 2); + EXPECT_COUNT(third_ptr, 5, -1, 1); + EXPECT_COUNT(fourth_ptr, 5, -1, 0); } TEST_F(EffectTreeLayerListIteratorTest, ComplexTree) { @@ -182,21 +183,21 @@ TEST_F(EffectTreeLayerListIteratorTest, ComplexTree) { host_impl_.active_tree()->SetRootLayerForTesting(std::move(root_layer)); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_ptr, root_ptr->bounds(), &render_surface_layer_list); + root_ptr, root_ptr->bounds(), &render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); IterateFrontToBack(); EXPECT_COUNT(root_ptr, 9, -1, 8); - EXPECT_COUNT(root1_ptr, -1, -1, 7); - EXPECT_COUNT(root2_ptr, -1, -1, 6); - EXPECT_COUNT(root21_ptr, -1, -1, 5); - EXPECT_COUNT(root22_ptr, -1, -1, 4); - EXPECT_COUNT(root221_ptr, -1, -1, 3); - EXPECT_COUNT(root23_ptr, -1, -1, 2); - EXPECT_COUNT(root231_ptr, -1, -1, 1); - EXPECT_COUNT(root3_ptr, -1, -1, 0); + EXPECT_COUNT(root1_ptr, 9, -1, 7); + EXPECT_COUNT(root2_ptr, 9, -1, 6); + EXPECT_COUNT(root21_ptr, 9, -1, 5); + EXPECT_COUNT(root22_ptr, 9, -1, 4); + EXPECT_COUNT(root221_ptr, 9, -1, 3); + EXPECT_COUNT(root23_ptr, 9, -1, 2); + EXPECT_COUNT(root231_ptr, 9, -1, 1); + EXPECT_COUNT(root3_ptr, 9, -1, 0); } TEST_F(EffectTreeLayerListIteratorTest, ComplexTreeMultiSurface) { @@ -235,21 +236,21 @@ TEST_F(EffectTreeLayerListIteratorTest, ComplexTreeMultiSurface) { host_impl_.active_tree()->SetRootLayerForTesting(std::move(root_layer)); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_ptr, root_ptr->bounds(), &render_surface_layer_list); + root_ptr, root_ptr->bounds(), &render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); IterateFrontToBack(); EXPECT_COUNT(root_ptr, 14, -1, 13); - EXPECT_COUNT(root1_ptr, -1, -1, 12); + EXPECT_COUNT(root1_ptr, 14, -1, 12); EXPECT_COUNT(root2_ptr, 10, 11, -1); - EXPECT_COUNT(root21_ptr, -1, -1, 9); + EXPECT_COUNT(root21_ptr, 10, 11, 9); EXPECT_COUNT(root22_ptr, 7, 8, 6); - EXPECT_COUNT(root221_ptr, -1, -1, 5); + EXPECT_COUNT(root221_ptr, 7, 8, 5); EXPECT_COUNT(root23_ptr, 3, 4, 2); - EXPECT_COUNT(root231_ptr, -1, -1, 1); - EXPECT_COUNT(root3_ptr, -1, -1, 0); + EXPECT_COUNT(root231_ptr, 3, 4, 1); + EXPECT_COUNT(root3_ptr, 14, -1, 0); } } // namespace diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index 7340f184c1f..bde1f0c1f76 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -607,10 +607,6 @@ SkRect HeadsUpDisplayLayerImpl::DrawGpuRasterizationStatus(SkCanvas* canvas, status = "MSAA (content)"; color = SK_ColorCYAN; break; - case GpuRasterizationStatus::OFF_CONTENT: - status = "off (content)"; - color = SK_ColorYELLOW; - break; } if (status.empty()) diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index 72a530a3bf3..d3100f3daa8 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -93,6 +93,7 @@ Layer::Layer() may_contain_video_(false), is_scroll_clip_layer_(false), needs_show_scrollbars_(false), + subtree_has_copy_request_(false), safe_opaque_background_color_(0), num_unclipped_descendants_(0) {} @@ -123,7 +124,8 @@ void Layer::SetLayerTreeHost(LayerTreeHost* host) { layer_tree_host_->property_trees()->RemoveIdFromIdToIndexMaps(id()); layer_tree_host_->property_trees()->needs_rebuild = true; layer_tree_host_->UnregisterLayer(this); - if (inputs_.element_id) { + if (!layer_tree_host_->GetSettings().use_layer_lists && + inputs_.element_id) { layer_tree_host_->UnregisterElement(inputs_.element_id, ElementListType::ACTIVE, this); } @@ -131,7 +133,7 @@ void Layer::SetLayerTreeHost(LayerTreeHost* host) { if (host) { host->property_trees()->needs_rebuild = true; host->RegisterLayer(this); - if (inputs_.element_id) { + if (!host->GetSettings().use_layer_lists && inputs_.element_id) { host->RegisterElement(inputs_.element_id, ElementListType::ACTIVE, this); } } @@ -149,12 +151,10 @@ void Layer::SetLayerTreeHost(LayerTreeHost* host) { if (inputs_.mask_layer.get()) inputs_.mask_layer->SetLayerTreeHost(host); - const bool has_any_animation = - layer_tree_host_ ? GetMutatorHost()->HasAnyAnimation(element_id()) - : false; - - if (host && has_any_animation) + if (host && !host->GetSettings().use_layer_lists && + GetMutatorHost()->HasAnyAnimation(element_id())) { host->SetNeedsCommit(); + } } void Layer::SetNeedsCommit() { @@ -362,6 +362,20 @@ void Layer::RequestCopyOfOutput(std::unique_ptr<CopyOutputRequest> request) { SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); SetNeedsCommit(); + if (layer_tree_host_) + layer_tree_host_->SetHasCopyRequest(true); +} + +void Layer::SetSubtreeHasCopyRequest(bool subtree_has_copy_request) { + subtree_has_copy_request_ = subtree_has_copy_request; +} + +bool Layer::SubtreeHasCopyRequest() const { + DCHECK(layer_tree_host_); + // When the copy request is pushed to effect tree, we reset layer tree host's + // has_copy_request but do not clear subtree_has_copy_request on individual + // layers. + return layer_tree_host_->has_copy_request() && subtree_has_copy_request_; } void Layer::SetBackgroundColor(SkColor background_color) { @@ -497,10 +511,6 @@ bool Layer::OpacityCanAnimateOnImplThread() const { return false; } -bool Layer::AlwaysUseActiveTreeOpacity() const { - return false; -} - void Layer::SetBlendMode(SkBlendMode blend_mode) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.blend_mode == blend_mode) @@ -684,11 +694,6 @@ bool Layer::ScrollOffsetAnimationWasInterrupted() const { return GetMutatorHost()->ScrollOffsetAnimationWasInterrupted(element_id()); } -bool Layer::HasOnlyTranslationTransforms() const { - return GetMutatorHost()->HasOnlyTranslationTransforms( - element_id(), GetElementTypeForAnimation()); -} - void Layer::SetScrollParent(Layer* parent) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.scroll_parent == parent) @@ -761,17 +766,7 @@ void Layer::SetScrollOffset(const gfx::ScrollOffset& scroll_offset) { if (!layer_tree_host_) return; - PropertyTrees* property_trees = layer_tree_host_->property_trees(); - if (scroll_tree_index() != ScrollTree::kInvalidNodeId && scrollable()) - property_trees->scroll_tree.SetScrollOffset(id(), scroll_offset); - - if (TransformNode* transform_node = - property_trees->transform_tree.UpdateNodeFromOwningLayerId(id())) { - DCHECK_EQ(transform_tree_index(), transform_node->id); - transform_node->scroll_offset = CurrentScrollOffset(); - transform_node->needs_local_transform_update = true; - property_trees->transform_tree.set_needs_update(true); - } + UpdateScrollOffset(scroll_offset); SetNeedsCommit(); } @@ -787,17 +782,7 @@ void Layer::SetScrollOffsetFromImplSide( inputs_.scroll_offset = scroll_offset; SetNeedsPushProperties(); - PropertyTrees* property_trees = layer_tree_host_->property_trees(); - if (scroll_tree_index() != ScrollTree::kInvalidNodeId && scrollable()) - property_trees->scroll_tree.SetScrollOffset(id(), scroll_offset); - - if (TransformNode* transform_node = - property_trees->transform_tree.UpdateNodeFromOwningLayerId(id())) { - DCHECK_EQ(transform_tree_index(), transform_node->id); - transform_node->scroll_offset = CurrentScrollOffset(); - transform_node->needs_local_transform_update = true; - property_trees->transform_tree.set_needs_update(true); - } + UpdateScrollOffset(scroll_offset); if (!inputs_.did_scroll_callback.is_null()) inputs_.did_scroll_callback.Run(scroll_offset); @@ -806,6 +791,28 @@ void Layer::SetScrollOffsetFromImplSide( // "this" may have been destroyed during the process. } +void Layer::UpdateScrollOffset(const gfx::ScrollOffset& scroll_offset) { + DCHECK(scrollable()); + if (scroll_tree_index() == ScrollTree::kInvalidNodeId) { + // Ensure the property trees just have not been built yet but are marked for + // being built which will set the correct scroll offset values. + DCHECK(layer_tree_host_->property_trees()->needs_rebuild); + return; + } + + // If a scroll node exists, it should have an associated transform node. + DCHECK(transform_tree_index() != TransformTree::kInvalidNodeId); + + auto& property_trees = *layer_tree_host_->property_trees(); + property_trees.scroll_tree.SetScrollOffset(id(), scroll_offset); + auto* transform_node = + property_trees.transform_tree.Node(transform_tree_index()); + DCHECK_EQ(transform_tree_index(), transform_node->id); + transform_node->scroll_offset = CurrentScrollOffset(); + transform_node->needs_local_transform_update = true; + property_trees.transform_tree.set_needs_update(true); +} + void Layer::SetScrollClipLayerId(int clip_layer_id) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.scroll_clip_layer_id == clip_layer_id) @@ -1124,15 +1131,12 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { TRACE_EVENT0("cc", "Layer::PushPropertiesTo"); DCHECK(layer_tree_host_); - // If we did not SavePaintProperties() for the layer this frame, then push the - // real property values, not the paint property values. - bool use_paint_properties = paint_properties_.source_frame_number == - layer_tree_host_->SourceFrameNumber(); - + // The ElementId should be set first because other setters depend on it such + // as LayerImpl::SetScrollClipLayer. + layer->SetElementId(inputs_.element_id); layer->SetBackgroundColor(inputs_.background_color); layer->SetSafeOpaqueBackgroundColor(safe_opaque_background_color_); - layer->SetBounds(use_paint_properties ? paint_properties_.bounds - : inputs_.bounds); + layer->SetBounds(inputs_.bounds); #if defined(NDEBUG) if (frame_viewer_instrumentation::IsTracingLayerTreeSnapshots()) @@ -1169,9 +1173,12 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { layer->SetScrollClipLayer(inputs_.scroll_clip_layer_id); layer->set_user_scrollable_horizontal(inputs_.user_scrollable_horizontal); layer->set_user_scrollable_vertical(inputs_.user_scrollable_vertical); - layer->SetElementId(inputs_.element_id); layer->SetMutableProperties(inputs_.mutable_properties); + // The property trees must be safe to access because they will be used below + // to call |SetScrollOffsetClobberActiveValue|. + DCHECK(layer->layer_tree_impl()->lifecycle().AllowsPropertyTreeAccess()); + // When a scroll offset animation is interrupted the new scroll position on // the pending tree will clobber any impl-side scrolling occuring on the // active tree. To do so, avoid scrolling the pending tree along with it @@ -1254,20 +1261,8 @@ int Layer::NumDescendantsThatDrawContent() const { return num_descendants_that_draw_content_; } -void Layer::SavePaintProperties() { - DCHECK(layer_tree_host_); - - // TODO(reveman): Save all layer properties that we depend on not - // changing until PushProperties() has been called. crbug.com/231016 - paint_properties_.bounds = inputs_.bounds; - paint_properties_.source_frame_number = layer_tree_host_->SourceFrameNumber(); -} - bool Layer::Update() { DCHECK(layer_tree_host_); - DCHECK_EQ(layer_tree_host_->SourceFrameNumber(), - paint_properties_.source_frame_number) - << "SavePaintProperties must be called for any layer that is painted."; return false; } @@ -1307,16 +1302,6 @@ void Layer::SetScrollbarsHiddenFromImplSide(bool hidden) { inputs_.client->didChangeScrollbarsHidden(hidden); } -bool Layer::FilterIsAnimating() const { - return GetMutatorHost()->IsAnimatingFilterProperty( - element_id(), GetElementTypeForAnimation()); -} - -bool Layer::TransformIsAnimating() const { - return GetMutatorHost()->IsAnimatingTransformProperty( - element_id(), GetElementTypeForAnimation()); -} - gfx::ScrollOffset Layer::ScrollOffsetForAnimation() const { return CurrentScrollOffset(); } @@ -1436,10 +1421,10 @@ void Layer::SetMutableProperties(uint32_t properties) { SetNeedsCommit(); } -int Layer::num_copy_requests_in_target_subtree() { +bool Layer::has_copy_requests_in_target_subtree() { return layer_tree_host_->property_trees() ->effect_tree.Node(effect_tree_index()) - ->num_copy_requests_in_subtree; + ->subtree_has_copy_request; } gfx::Transform Layer::ScreenSpaceTransform() const { diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 58dde9c4577..c88721b0fa6 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -24,7 +24,6 @@ #include "cc/input/input_handler.h" #include "cc/layers/layer_collections.h" #include "cc/layers/layer_position_constraint.h" -#include "cc/layers/paint_properties.h" #include "cc/paint/paint_record.h" #include "cc/trees/element_id.h" #include "cc/trees/mutator_host_client.h" @@ -97,6 +96,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void RequestCopyOfOutput(std::unique_ptr<CopyOutputRequest> request); bool HasCopyRequest() const { return !inputs_.copy_requests.empty(); } + void SetSubtreeHasCopyRequest(bool subtree_has_copy_request); + bool SubtreeHasCopyRequest() const; + void TakeCopyRequests( std::vector<std::unique_ptr<CopyOutputRequest>>* requests); @@ -127,8 +129,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { float EffectiveOpacity() const; virtual bool OpacityCanAnimateOnImplThread() const; - virtual bool AlwaysUseActiveTreeOpacity() const; - void SetBlendMode(SkBlendMode blend_mode); SkBlendMode blend_mode() const { return inputs_.blend_mode; } @@ -163,10 +163,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void SetIsContainerForFixedPositionLayers(bool container); bool IsContainerForFixedPositionLayers() const; - gfx::Vector2dF FixedContainerSizeDelta() const { - return gfx::Vector2dF(); - } - void SetPositionConstraint(const LayerPositionConstraint& constraint); const LayerPositionConstraint& position_constraint() const { return inputs_.position_constraint; @@ -304,9 +300,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { virtual bool DrawsContent() const; // This methods typically need to be overwritten by derived classes. - // TODO(chrishtr): Blink no longer resizes anything during paint. We can - // remove this. - virtual void SavePaintProperties(); // Returns true iff anything was updated that needs to be committed. virtual bool Update(); virtual void SetLayerMaskType(Layer::LayerMaskType type) {} @@ -334,10 +327,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { bool NeedsDisplayForTesting() const { return !inputs_.update_rect.IsEmpty(); } void ResetNeedsDisplayForTesting() { inputs_.update_rect = gfx::Rect(); } - const PaintProperties& paint_properties() const { - return paint_properties_; - } - // Mark the layer as needing to push its properties to the LayerImpl during // commit. void SetNeedsPushProperties(); @@ -401,8 +390,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void SetMayContainVideo(bool yes); - int num_copy_requests_in_target_subtree(); + bool has_copy_requests_in_target_subtree(); + // Stable identifier for clients. See comment in cc/trees/element_id.h. void SetElementId(ElementId id); ElementId element_id() const { return inputs_.element_id; } @@ -480,10 +470,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void OnTransformAnimated(const gfx::Transform& transform); void OnScrollOffsetAnimated(const gfx::ScrollOffset& scroll_offset); - bool FilterIsAnimating() const; - bool TransformIsAnimating() const; bool ScrollOffsetAnimationWasInterrupted() const; - bool HasOnlyTranslationTransforms() const; void AddScrollChild(Layer* child); void RemoveScrollChild(Layer* child); @@ -513,6 +500,12 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // layer should own a property tree node or not. void SetPropertyTreesNeedRebuild(); + // Fast-path for |SetScrollOffset| and |SetScrollOffsetFromImplSide| to + // directly update scroll offset values in the property tree without needing a + // full property tree update. If property trees do not exist yet, ensures + // they are marked as needing to be rebuilt. + void UpdateScrollOffset(const gfx::ScrollOffset&); + // Encapsulates all data, callbacks or interfaces received from the embedder. // TODO(khushalsagar): This is only valid when PropertyTrees are built // internally in cc. Update this for the SPv2 path where blink generates @@ -627,13 +620,13 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { bool may_contain_video_ : 1; bool is_scroll_clip_layer_ : 1; bool needs_show_scrollbars_ : 1; + // This value is valid only when LayerTreeHost::has_copy_request() is true + bool subtree_has_copy_request_ : 1; SkColor safe_opaque_background_color_; std::unique_ptr<std::set<Layer*>> scroll_children_; std::unique_ptr<std::set<Layer*>> clip_children_; - PaintProperties paint_properties_; - // These all act like draw properties, so don't need push properties. gfx::Rect visible_layer_rect_; size_t num_unclipped_descendants_; diff --git a/chromium/cc/layers/layer_collections.h b/chromium/cc/layers/layer_collections.h index c7a2b746205..14da9699d14 100644 --- a/chromium/cc/layers/layer_collections.h +++ b/chromium/cc/layers/layer_collections.h @@ -15,10 +15,12 @@ namespace cc { class Layer; class LayerImpl; +class RenderSurfaceImpl; using LayerList = std::vector<scoped_refptr<Layer>>; using OwnedLayerImplList = std::vector<std::unique_ptr<LayerImpl>>; using LayerImplList = std::vector<LayerImpl*>; +using RenderSurfaceList = std::vector<RenderSurfaceImpl*>; using OwnedLayerImplMap = std::unordered_map<int, std::unique_ptr<LayerImpl>>; using LayerImplMap = std::unordered_map<int, LayerImpl*>; diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index b9f9b993e91..e40b5701e13 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -66,8 +66,8 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) use_local_transform_for_backface_visibility_(false), should_check_backface_visibility_(false), draws_content_(false), - is_drawn_render_surface_layer_list_member_(false), - was_ever_ready_since_last_transform_animation_(true), + contributes_to_drawn_render_surface_(false), + viewport_layer_type_(NOT_VIEWPORT_LAYER), background_color_(0), safe_opaque_background_color_(0), transform_tree_index_(TransformTree::kInvalidNodeId), @@ -80,7 +80,8 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) has_will_change_transform_hint_(false), needs_push_properties_(false), scrollbars_hidden_(false), - needs_show_scrollbars_(false) { + needs_show_scrollbars_(false), + raster_even_if_not_in_rsll_(false) { DCHECK_GT(layer_id_, 0); DCHECK(layer_tree_impl_); @@ -147,7 +148,7 @@ void LayerImpl::SetScrollTreeIndex(int index) { } void LayerImpl::PopulateSharedQuadState(SharedQuadState* state) const { - state->SetAll(draw_properties_.target_space_transform, bounds(), + state->SetAll(draw_properties_.target_space_transform, gfx::Rect(bounds()), draw_properties_.visible_layer_rect, draw_properties_.clip_rect, draw_properties_.is_clipped, draw_properties_.opacity, SkBlendMode::kSrcOver, GetSortingContextId()); @@ -167,10 +168,10 @@ void LayerImpl::PopulateScaledSharedQuadState( visible_layer_rect(), layer_to_content_scale_x, layer_to_content_scale_y); scaled_visible_layer_rect.Intersect(gfx::Rect(scaled_bounds)); - state->SetAll(scaled_draw_transform, scaled_bounds, scaled_visible_layer_rect, - draw_properties().clip_rect, draw_properties().is_clipped, - draw_properties().opacity, SkBlendMode::kSrcOver, - GetSortingContextId()); + state->SetAll(scaled_draw_transform, gfx::Rect(scaled_bounds), + scaled_visible_layer_rect, draw_properties().clip_rect, + draw_properties().is_clipped, draw_properties().opacity, + SkBlendMode::kSrcOver, GetSortingContextId()); } bool LayerImpl::WillDraw(DrawMode draw_mode, @@ -259,7 +260,8 @@ void LayerImpl::AppendDebugBorderQuad(RenderPass* render_pass, } void LayerImpl::GetContentsResourceId(ResourceId* resource_id, - gfx::Size* resource_size) const { + gfx::Size* resource_size, + gfx::SizeF* resource_uv_size) const { NOTREACHED(); *resource_id = 0; } @@ -312,6 +314,10 @@ bool LayerImpl::IsSnapped() { void LayerImpl::PushPropertiesTo(LayerImpl* layer) { DCHECK(layer->IsActive()); + // The ElementId should be set first because other setters depend on it such + // as LayerImpl::SetScrollClipLayer. + layer->SetElementId(element_id_); + layer->offset_to_transform_parent_ = offset_to_transform_parent_; layer->main_thread_scrolling_reasons_ = main_thread_scrolling_reasons_; layer->user_scrollable_horizontal_ = user_scrollable_horizontal_; @@ -346,7 +352,6 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->SetBounds(bounds_); layer->SetScrollClipLayer(scroll_clip_layer_id_); - layer->SetElementId(element_id_); layer->SetMutableProperties(mutable_properties_); // If the main thread commits multiple times before the impl thread actually @@ -373,15 +378,6 @@ bool LayerImpl::IsAffectedByPageScale() const { ->in_subtree_of_page_scale_layer; } -gfx::Vector2dF LayerImpl::FixedContainerSizeDelta() const { - LayerImpl* scroll_clip_layer = - layer_tree_impl()->LayerById(scroll_clip_layer_id_); - if (!scroll_clip_layer) - return gfx::Vector2dF(); - - return scroll_clip_layer->bounds_delta(); -} - std::unique_ptr<base::DictionaryValue> LayerImpl::LayerTreeAsJson() { std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); result->SetInteger("LayerId", id()); @@ -470,10 +466,8 @@ void LayerImpl::ResetChangeTracking() { damage_rect_.SetRect(0, 0, 0, 0); } -int LayerImpl::num_copy_requests_in_target_subtree() { - return GetEffectTree() - .Node(effect_tree_index()) - ->num_copy_requests_in_subtree; +bool LayerImpl::has_copy_requests_in_target_subtree() { + return GetEffectTree().Node(effect_tree_index())->subtree_has_copy_request; } void LayerImpl::UpdatePropertyTreeForScrollingAndAnimationIfNeeded() { @@ -486,7 +480,9 @@ void LayerImpl::UpdatePropertyTreeForScrollingAndAnimationIfNeeded() { bool has_potential_animation = HasPotentiallyRunningTransformAnimation(); if (node->has_potential_animation != has_potential_animation) { node->has_potential_animation = has_potential_animation; - node->has_only_translation_animations = HasOnlyTranslationTransforms(); + node->has_only_translation_animations = + GetMutatorHost()->HasOnlyTranslationTransforms( + element_id(), GetElementTypeForAnimation()); GetTransformTree().set_needs_update(true); layer_tree_impl()->set_needs_update_draw_properties(); } @@ -503,14 +499,15 @@ bool LayerImpl::IsActive() const { } gfx::Size LayerImpl::bounds() const { - gfx::Vector2d delta = gfx::ToCeiledVector2d(bounds_delta_); - return gfx::Size(bounds_.width() + delta.x(), - bounds_.height() + delta.y()); + auto viewport_bounds_delta = gfx::ToCeiledVector2d(ViewportBoundsDelta()); + return gfx::Size(bounds_.width() + viewport_bounds_delta.x(), + bounds_.height() + viewport_bounds_delta.y()); } gfx::SizeF LayerImpl::BoundsForScrolling() const { - return gfx::SizeF(bounds_.width() + bounds_delta_.x(), - bounds_.height() + bounds_delta_.y()); + auto viewport_bounds_delta = ViewportBoundsDelta(); + return gfx::SizeF(bounds_.width() + viewport_bounds_delta.x(), + bounds_.height() + viewport_bounds_delta.y()); } void LayerImpl::SetBounds(const gfx::Size& bounds) { @@ -524,20 +521,27 @@ void LayerImpl::SetBounds(const gfx::Size& bounds) { NoteLayerPropertyChanged(); } -void LayerImpl::SetBoundsDelta(const gfx::Vector2dF& bounds_delta) { +void LayerImpl::SetViewportBoundsDelta(const gfx::Vector2dF& bounds_delta) { DCHECK(IsActive()); - if (bounds_delta_ == bounds_delta) - return; - bounds_delta_ = bounds_delta; + if (bounds_delta == ViewportBoundsDelta()) + return; PropertyTrees* property_trees = GetPropertyTrees(); - if (this == layer_tree_impl()->InnerViewportContainerLayer()) - property_trees->SetInnerViewportContainerBoundsDelta(bounds_delta); - else if (this == layer_tree_impl()->OuterViewportContainerLayer()) - property_trees->SetOuterViewportContainerBoundsDelta(bounds_delta); - else if (this == layer_tree_impl()->InnerViewportScrollLayer()) - property_trees->SetInnerViewportScrollBoundsDelta(bounds_delta); + switch (viewport_layer_type_) { + case (INNER_VIEWPORT_CONTAINER): + property_trees->SetInnerViewportContainerBoundsDelta(bounds_delta); + break; + case (OUTER_VIEWPORT_CONTAINER): + property_trees->SetOuterViewportContainerBoundsDelta(bounds_delta); + break; + case (INNER_VIEWPORT_SCROLL): + property_trees->SetInnerViewportScrollBoundsDelta(bounds_delta); + break; + case (OUTER_VIEWPORT_SCROLL): + // OUTER_VIEWPORT_SCROLL should not have viewport bounds deltas. + NOTREACHED(); + } layer_tree_impl()->DidUpdateScrollState(id()); @@ -557,6 +561,19 @@ void LayerImpl::SetBoundsDelta(const gfx::Vector2dF& bounds_delta) { } } +gfx::Vector2dF LayerImpl::ViewportBoundsDelta() const { + switch (viewport_layer_type_) { + case (INNER_VIEWPORT_CONTAINER): + return GetPropertyTrees()->inner_viewport_container_bounds_delta(); + case (OUTER_VIEWPORT_CONTAINER): + return GetPropertyTrees()->outer_viewport_container_bounds_delta(); + case (INNER_VIEWPORT_SCROLL): + return GetPropertyTrees()->inner_viewport_scroll_bounds_delta(); + default: + return gfx::Vector2dF(); + } +} + ScrollbarLayerImplBase* LayerImpl::ToScrollbarLayer() { return nullptr; } @@ -590,11 +607,6 @@ SkColor LayerImpl::SafeOpaqueBackgroundColor() const { return color; } -bool LayerImpl::FilterIsAnimating() const { - return GetMutatorHost()->IsAnimatingFilterProperty( - element_id(), GetElementTypeForAnimation()); -} - bool LayerImpl::HasPotentiallyRunningFilterAnimation() const { return GetMutatorHost()->HasPotentiallyRunningFilterAnimation( element_id(), GetElementTypeForAnimation()); @@ -651,21 +663,11 @@ void LayerImpl::SetPosition(const gfx::PointF& position) { position_ = position; } -bool LayerImpl::TransformIsAnimating() const { - return GetMutatorHost()->IsAnimatingTransformProperty( - element_id(), GetElementTypeForAnimation()); -} - bool LayerImpl::HasPotentiallyRunningTransformAnimation() const { return GetMutatorHost()->HasPotentiallyRunningTransformAnimation( element_id(), GetElementTypeForAnimation()); } -bool LayerImpl::HasOnlyTranslationTransforms() const { - return GetMutatorHost()->HasOnlyTranslationTransforms( - element_id(), GetElementTypeForAnimation()); -} - bool LayerImpl::HasAnyAnimationTargetingProperty( TargetProperty::Type property) const { return GetMutatorHost()->HasAnyAnimationTargetingProperty(element_id(), @@ -866,14 +868,9 @@ void LayerImpl::RunMicroBenchmark(MicroBenchmarkImpl* benchmark) { gfx::Transform LayerImpl::DrawTransform() const { // Only drawn layers have up-to-date draw properties. - if (!is_drawn_render_surface_layer_list_member()) { - if (GetPropertyTrees()->non_root_surfaces_enabled) { + if (!contributes_to_drawn_render_surface()) { return draw_property_utils::DrawTransform(this, GetTransformTree(), GetEffectTree()); - } else { - return draw_property_utils::ScreenSpaceTransform(this, - GetTransformTree()); - } } return draw_properties().target_space_transform; @@ -881,7 +878,7 @@ gfx::Transform LayerImpl::DrawTransform() const { gfx::Transform LayerImpl::ScreenSpaceTransform() const { // Only drawn layers have up-to-date draw properties. - if (!is_drawn_render_surface_layer_list_member()) { + if (!contributes_to_drawn_render_surface()) { return draw_property_utils::ScreenSpaceTransform(this, GetTransformTree()); } @@ -932,13 +929,6 @@ gfx::Rect LayerImpl::GetScaledEnclosingRectInTargetSpace(float scale) const { gfx::Rect(scaled_bounds)); } -RenderSurfaceImpl* LayerImpl::GetRenderSurface() const { - EffectNode* effect_node = GetEffectTree().Node(effect_tree_index_); - if (effect_node->owning_layer_id == id()) - return GetEffectTree().GetRenderSurface(effect_tree_index_); - return nullptr; -} - RenderSurfaceImpl* LayerImpl::render_target() { return GetEffectTree().GetRenderSurface(render_target_effect_tree_index()); } @@ -960,8 +950,18 @@ float LayerImpl::GetIdealContentsScale() const { return default_scale; } - gfx::Vector2dF transform_scales = MathUtil::ComputeTransform2dScaleComponents( - ScreenSpaceTransform(), default_scale); + const auto& transform = ScreenSpaceTransform(); + if (transform.HasPerspective()) { + float scale = MathUtil::ComputeApproximateMaxScale(transform); + // Since we're approximating the scale anyway, round it to the nearest + // integer to prevent jitter when animating the transform. + scale = std::round(scale); + // Don't let the scale fall below the default scale. + return std::max(scale, default_scale); + } + + gfx::Vector2dF transform_scales = + MathUtil::ComputeTransform2dScaleComponents(transform, default_scale); return std::max(transform_scales.x(), transform_scales.y()); } @@ -985,26 +985,4 @@ TransformTree& LayerImpl::GetTransformTree() const { return GetPropertyTrees()->transform_tree; } -bool LayerImpl::HasValidPropertyTreeIndices() const { - // TODO(crbug.com/726423): LayerImpls should never have invalid PropertyTree - // indices. - const bool has_valid_transform_node = - !!GetTransformTree().Node(transform_tree_index()); - DCHECK(has_valid_transform_node); - - const bool has_valid_effect_node = - !!GetEffectTree().Node(effect_tree_index()); - DCHECK(has_valid_effect_node); - - const bool has_valid_clip_node = !!GetClipTree().Node(clip_tree_index()); - DCHECK(has_valid_clip_node); - - const bool has_valid_scroll_node = - !!GetScrollTree().Node(scroll_tree_index()); - DCHECK(has_valid_scroll_node); - - return has_valid_transform_node && has_valid_effect_node && - has_valid_clip_node && has_valid_scroll_node; -} - } // namespace cc diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h index d976ae50045..c32c208ff13 100644 --- a/chromium/cc/layers/layer_impl.h +++ b/chromium/cc/layers/layer_impl.h @@ -70,12 +70,17 @@ enum DrawMode { DRAW_MODE_RESOURCELESS_SOFTWARE }; +enum ViewportLayerType { + NOT_VIEWPORT_LAYER, + INNER_VIEWPORT_CONTAINER, + OUTER_VIEWPORT_CONTAINER, + INNER_VIEWPORT_SCROLL, + OUTER_VIEWPORT_SCROLL, + LAST_VIEWPORT_LAYER_TYPE = OUTER_VIEWPORT_SCROLL, +}; + class CC_EXPORT LayerImpl { public: - typedef LayerImplList RenderSurfaceListType; - typedef LayerImplList LayerListType; - typedef RenderSurfaceImpl RenderSurfaceType; - static std::unique_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl, int id) { return base::WrapUnique(new LayerImpl(tree_impl, id)); } @@ -147,7 +152,8 @@ class CC_EXPORT LayerImpl { } virtual void GetContentsResourceId(ResourceId* resource_id, - gfx::Size* resource_size) const; + gfx::Size* resource_size, + gfx::SizeF* resource_uv_size) const; virtual void NotifyTileStateChanged(const Tile* tile) {} @@ -170,7 +176,6 @@ class CC_EXPORT LayerImpl { // non-opaque color. Tries to return background_color(), if possible. SkColor SafeOpaqueBackgroundColor() const; - bool FilterIsAnimating() const; bool HasPotentiallyRunningFilterAnimation() const; void SetMasksToBounds(bool masks_to_bounds); @@ -182,6 +187,7 @@ class CC_EXPORT LayerImpl { float Opacity() const; const gfx::Transform& Transform() const; + // Stable identifier for clients. See comment in cc/trees/element_id.h. void SetElementId(ElementId element_id); ElementId element_id() const { return element_id_; } @@ -193,8 +199,6 @@ class CC_EXPORT LayerImpl { bool IsAffectedByPageScale() const; - gfx::Vector2dF FixedContainerSizeDelta() const; - bool Is3dSorted() const { return GetSortingContextId() != 0; } void SetUseParentBackfaceVisibility(bool use) { @@ -220,11 +224,6 @@ class CC_EXPORT LayerImpl { bool ShowDebugBorders(DebugBorderType type) const; - // TODO(http://crbug.com/557160): Currently SPv2 creates dummy layers for the - // sole purpose of representing a render surface. Once that dependency is - // removed, also remove dummy layers from PaintArtifactCompositor. - RenderSurfaceImpl* GetRenderSurface() const; - // The render surface which this layer draws into. This can be either owned by // the same layer or an ancestor of this layer. RenderSurfaceImpl* render_target(); @@ -272,8 +271,22 @@ class CC_EXPORT LayerImpl { // Like bounds() but doesn't snap to int. Lossy on giant pages (e.g. millions // of pixels) due to use of single precision float. gfx::SizeF BoundsForScrolling() const; - void SetBoundsDelta(const gfx::Vector2dF& bounds_delta); - gfx::Vector2dF bounds_delta() const { return bounds_delta_; } + + // Viewport bounds delta are only used for viewport layers and account for + // changes in the viewport layers from browser controls and page scale + // factors. These deltas are only set on the active tree. + void SetViewportBoundsDelta(const gfx::Vector2dF& bounds_delta); + gfx::Vector2dF ViewportBoundsDelta() const; + + void SetViewportLayerType(ViewportLayerType type) { + // Once set as a viewport layer type, the viewport type should not change. + DCHECK(viewport_layer_type() == NOT_VIEWPORT_LAYER || + viewport_layer_type() == type); + viewport_layer_type_ = type; + } + ViewportLayerType viewport_layer_type() const { + return static_cast<ViewportLayerType>(viewport_layer_type_); + } void SetCurrentScrollOffset(const gfx::ScrollOffset& scroll_offset); gfx::ScrollOffset CurrentScrollOffset() const; @@ -322,7 +335,6 @@ class CC_EXPORT LayerImpl { return touch_event_handler_region_; } - bool TransformIsAnimating() const; bool HasPotentiallyRunningTransformAnimation() const; bool HasFilterAnimationThatInflatesBounds() const; @@ -382,16 +394,16 @@ class CC_EXPORT LayerImpl { void SetDebugInfo( std::unique_ptr<base::trace_event::ConvertableToTraceFormat> debug_info); - void set_is_drawn_render_surface_layer_list_member(bool is_member) { - is_drawn_render_surface_layer_list_member_ = is_member; + void set_contributes_to_drawn_render_surface(bool is_member) { + contributes_to_drawn_render_surface_ = is_member; } - bool is_drawn_render_surface_layer_list_member() const { - return is_drawn_render_surface_layer_list_member_; + bool contributes_to_drawn_render_surface() const { + return contributes_to_drawn_render_surface_; } bool IsDrawnScrollbar() { - return ToScrollbarLayer() && is_drawn_render_surface_layer_list_member_; + return ToScrollbarLayer() && contributes_to_drawn_render_surface_; } void set_may_contain_video(bool yes) { may_contain_video_ = yes; } @@ -408,20 +420,12 @@ class CC_EXPORT LayerImpl { virtual gfx::Rect GetEnclosingRectInTargetSpace() const; - int num_copy_requests_in_target_subtree(); + bool has_copy_requests_in_target_subtree(); void UpdatePropertyTreeForScrollingAndAnimationIfNeeded(); float GetIdealContentsScale() const; - bool was_ever_ready_since_last_transform_animation() const { - return was_ever_ready_since_last_transform_animation_; - } - - void set_was_ever_ready_since_last_transform_animation(bool was_ready) { - was_ever_ready_since_last_transform_animation_ = was_ready; - } - void NoteLayerPropertyChanged(); void SetHasWillChangeTransformHint(bool has_will_change); @@ -436,7 +440,12 @@ class CC_EXPORT LayerImpl { void set_needs_show_scrollbars(bool yes) { needs_show_scrollbars_ = yes; } bool needs_show_scrollbars() { return needs_show_scrollbars_; } - bool HasValidPropertyTreeIndices() const; + void set_raster_even_if_not_in_rsll(bool yes) { + raster_even_if_not_in_rsll_ = yes; + } + bool raster_even_if_not_in_rsll() const { + return raster_even_if_not_in_rsll_; + } protected: LayerImpl(LayerTreeImpl* layer_impl, @@ -461,8 +470,6 @@ class CC_EXPORT LayerImpl { gfx::Rect GetScaledEnclosingRectInTargetSpace(float scale) const; private: - bool HasOnlyTranslationTransforms() const; - // This includes all animations, even those that are finished but haven't yet // been deleted. bool HasAnyAnimationTargetingProperty(TargetProperty::Type property) const; @@ -476,8 +483,6 @@ class CC_EXPORT LayerImpl { std::unique_ptr<LayerImplTestProperties> test_properties_; - gfx::Vector2dF bounds_delta_; - // Properties synchronized from the associated Layer. gfx::Size bounds_; int scroll_clip_layer_id_; @@ -499,11 +504,11 @@ class CC_EXPORT LayerImpl { bool use_local_transform_for_backface_visibility_ : 1; bool should_check_backface_visibility_ : 1; bool draws_content_ : 1; - bool is_drawn_render_surface_layer_list_member_ : 1; + bool contributes_to_drawn_render_surface_ : 1; - // This is true if and only if the layer was ever ready since it last animated - // (all content was complete). - bool was_ever_ready_since_last_transform_animation_ : 1; + static_assert(LAST_VIEWPORT_LAYER_TYPE < (1u << 3), + "enough bits for ViewportLayerType (viewport_layer_type_)"); + uint8_t viewport_layer_type_ : 3; // ViewportLayerType Region non_fast_scrollable_region_; Region touch_event_handler_region_; @@ -559,6 +564,8 @@ class CC_EXPORT LayerImpl { // layers) and consumed by LayerTreeImpl::PushPropertiesTo during activation. bool needs_show_scrollbars_ : 1; + bool raster_even_if_not_in_rsll_ : 1; + DISALLOW_COPY_AND_ASSIGN(LayerImpl); }; diff --git a/chromium/cc/layers/layer_impl_test_properties.cc b/chromium/cc/layers/layer_impl_test_properties.cc index 2ae194db2aa..9de5d11d439 100644 --- a/chromium/cc/layers/layer_impl_test_properties.cc +++ b/chromium/cc/layers/layer_impl_test_properties.cc @@ -18,6 +18,7 @@ LayerImplTestProperties::LayerImplTestProperties(LayerImpl* owning_layer) should_flatten_transform(true), hide_layer_and_subtree(false), opacity_can_animate(false), + subtree_has_copy_request(false), sorting_context_id(0), opacity(1.f), blend_mode(SkBlendMode::kSrcOver), diff --git a/chromium/cc/layers/layer_impl_test_properties.h b/chromium/cc/layers/layer_impl_test_properties.h index 5f4fd66f2db..ee2917e2a4a 100644 --- a/chromium/cc/layers/layer_impl_test_properties.h +++ b/chromium/cc/layers/layer_impl_test_properties.h @@ -37,6 +37,7 @@ struct CC_EXPORT LayerImplTestProperties { bool should_flatten_transform; bool hide_layer_and_subtree; bool opacity_can_animate; + bool subtree_has_copy_request; int sorting_context_id; float opacity; FilterOperations filters; diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc index 2180d540d58..07259ad1ab7 100644 --- a/chromium/cc/layers/layer_impl_unittest.cc +++ b/chromium/cc/layers/layer_impl_unittest.cc @@ -138,6 +138,12 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { root_clip_ptr->test_properties()->AddChild(std::move(root_ptr)); host_impl.active_tree()->SetRootLayerForTesting(std::move(root_clip_ptr)); + // Make root the inner viewport scroll layer. This ensures the later call to + // |SetViewportBoundsDelta| will be on a viewport layer. + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.inner_viewport_scroll = root->id(); + host_impl.active_tree()->SetViewportLayersFromIds(viewport_ids); + root->test_properties()->force_render_surface = true; root->SetMasksToBounds(true); root->layer_tree_impl()->ResetAllChangeTracking(); @@ -192,11 +198,13 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { arbitrary_transform)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->ScrollBy(arbitrary_vector2d); root->SetNeedsPushProperties()); - // SetBoundsDelta changes subtree only when masks_to_bounds is true and it - // doesn't set needs_push_properties as it is always called on active tree. + // SetViewportBoundsDelta changes subtree only when masks_to_bounds is true + // and it doesn't set needs_push_properties as it is always called on active + // tree. root->SetMasksToBounds(true); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetBoundsDelta(arbitrary_vector2d); - root->SetNeedsPushProperties()); + EXECUTE_AND_VERIFY_SUBTREE_CHANGED( + root->SetViewportBoundsDelta(arbitrary_vector2d); + root->SetNeedsPushProperties()); // Changing these properties only affects the layer itself. EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetDrawsContent(true)); @@ -218,7 +226,7 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { // Changing these properties does not cause the layer to be marked as changed // but does cause the layer to need to push properties. EXECUTE_AND_VERIFY_NEEDS_PUSH_PROPERTIES_AND_SUBTREE_DID_NOT_CHANGE( - root->SetElementId(ElementId(2, 0))); + root->SetElementId(ElementId(2))); EXECUTE_AND_VERIFY_NEEDS_PUSH_PROPERTIES_AND_SUBTREE_DID_NOT_CHANGE( root->SetMutableProperties(MutableProperty::kOpacity); root->SetNeedsPushProperties()); @@ -342,7 +350,7 @@ TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties) { VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES( layer->SetBackgroundColor(arbitrary_color)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(arbitrary_size)); - VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetElementId(ElementId(2, 0))); + VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetElementId(ElementId(2))); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES( layer->SetMutableProperties(MutableProperty::kTransform)); } @@ -385,6 +393,43 @@ TEST(LayerImplTest, SafeOpaqueBackgroundColor) { } } +TEST(LayerImplTest, PerspectiveTransformHasReasonableScale) { + FakeImplTaskRunnerProvider task_runner_provider; + TestTaskGraphRunner task_graph_runner; + std::unique_ptr<CompositorFrameSink> compositor_frame_sink = + FakeCompositorFrameSink::Create3d(); + LayerTreeSettings settings; + settings.layer_transforms_should_scale_layer_contents = true; + FakeLayerTreeHostImpl host_impl(settings, &task_runner_provider, + &task_graph_runner); + auto owned_layer = LayerImpl::Create(host_impl.active_tree(), 1); + LayerImpl* layer = owned_layer.get(); + layer->set_contributes_to_drawn_render_surface(true); + host_impl.active_tree()->SetRootLayerForTesting(std::move(owned_layer)); + host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); + + // Ensure that we are close to the maximum scale for the matrix. + { + gfx::Transform transform; + transform.Scale(10.2f, 15.1f); + transform.ApplyPerspectiveDepth(10); + layer->draw_properties().screen_space_transform = transform; + + ASSERT_TRUE(layer->ScreenSpaceTransform().HasPerspective()); + EXPECT_FLOAT_EQ(15.f, layer->GetIdealContentsScale()); + } + // Ensure that we don't fall below the device scale factor. + { + gfx::Transform transform; + transform.Scale(0.1f, 0.2f); + transform.ApplyPerspectiveDepth(10); + layer->draw_properties().screen_space_transform = transform; + + ASSERT_TRUE(layer->ScreenSpaceTransform().HasPerspective()); + EXPECT_FLOAT_EQ(1.f, layer->GetIdealContentsScale()); + } +} + class LayerImplScrollTest : public testing::Test { public: LayerImplScrollTest() diff --git a/chromium/cc/layers/layer_position_constraint_unittest.cc b/chromium/cc/layers/layer_position_constraint_unittest.cc index ce8e2149816..ff886dbe792 100644 --- a/chromium/cc/layers/layer_position_constraint_unittest.cc +++ b/chromium/cc/layers/layer_position_constraint_unittest.cc @@ -12,6 +12,7 @@ #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_proxy.h" #include "cc/test/geometry_test_utils.h" +#include "cc/test/layer_test_common.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host_common.h" #include "testing/gtest/include/gtest/gtest.h" @@ -47,9 +48,9 @@ void SetLayerPropertiesForTesting(Layer* layer, } void ExecuteCalculateDrawProperties(LayerImpl* root_layer) { - std::vector<LayerImpl*> dummy_render_surface_layer_list; + RenderSurfaceList dummy_render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &dummy_render_surface_layer_list); + root_layer, root_layer->bounds(), &dummy_render_surface_list); inputs.inner_viewport_scroll_layer = root_layer->layer_tree_impl()->InnerViewportScrollLayer(); inputs.outer_viewport_scroll_layer = @@ -134,8 +135,13 @@ class LayerPositionConstraintTest : public testing::Test { root_->AddChild(inner_viewport_container_layer_); layer_tree_host_->SetRootLayer(root_); - layer_tree_host_->RegisterViewportLayers(nullptr, root_, scroll_layer_, - child_); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = root_; + viewport_layers.inner_viewport_container = inner_viewport_container_layer_; + viewport_layers.outer_viewport_container = outer_viewport_container_layer_; + viewport_layers.inner_viewport_scroll = scroll_layer_; + viewport_layers.outer_viewport_scroll = child_; + layer_tree_host_->RegisterViewportLayers(viewport_layers); } void CommitAndUpdateImplPointers() { @@ -207,18 +213,6 @@ class LayerPositionConstraintTest : public testing::Test { } }; -namespace { - -void SetFixedContainerSizeDelta(LayerImpl* scroll_layer, - const gfx::Vector2d& delta) { - DCHECK(scroll_layer); - DCHECK(scroll_layer->scrollable()); - - LayerImpl* container_layer = scroll_layer->scroll_clip_layer(); - container_layer->SetBoundsDelta(delta); -} -} // namespace - TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerWithDirectContainer) { // This test checks for correct scroll compensation when the fixed-position @@ -256,7 +250,8 @@ TEST_F(LayerPositionConstraintTest, grand_child_impl_->DrawTransform()); // Case 3: fixed-container size delta of 20, 20 - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Top-left fixed-position layer should not be affected by container size. @@ -270,7 +265,8 @@ TEST_F(LayerPositionConstraintTest, CommitAndUpdateImplPointers(); SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10)); - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Bottom-right fixed-position layer moves as container resizes. @@ -331,7 +327,8 @@ TEST_F(LayerPositionConstraintTest, great_grand_child_impl_->DrawTransform()); // Case 3: fixed-container size delta of 20, 20 - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Top-left fixed-position layer should not be affected by container size. @@ -346,7 +343,8 @@ TEST_F(LayerPositionConstraintTest, great_grand_child_->SetPositionConstraint(fixed_to_bottom_right_); CommitAndUpdateImplPointers(); SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10)); - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Bottom-right fixed-position layer moves as container resizes. @@ -459,12 +457,12 @@ TEST_F(LayerPositionConstraintTest, gfx::Transform expected_grand_child_transform; gfx::Transform expected_great_grand_child_transform; expected_great_grand_child_transform.PreconcatTransform(rotation_about_z); - EXPECT_TRUE(grand_child_impl_->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(grand_child_impl_)); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_surface_draw_transform, - grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, @@ -496,19 +494,20 @@ TEST_F(LayerPositionConstraintTest, expected_great_grand_child_transform.Translate(10.0, 30.0); expected_great_grand_child_transform.PreconcatTransform(rotation_about_z); - EXPECT_TRUE(grand_child_impl_->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(grand_child_impl_)); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_surface_draw_transform, - grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child_impl_->DrawTransform()); // Case 3: fixed-container size delta of 20, 20 - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Top-left fixed-position layer should not be affected by container size. @@ -524,7 +523,8 @@ TEST_F(LayerPositionConstraintTest, CommitAndUpdateImplPointers(); SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 30)); - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); @@ -603,18 +603,18 @@ TEST_F(LayerPositionConstraintTest, gfx::Transform expected_fixed_position_child_transform; expected_fixed_position_child_transform.PreconcatTransform(rotation_about_z); - EXPECT_TRUE(grand_child_impl_->GetRenderSurface()); - EXPECT_TRUE(great_grand_child_impl_->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(grand_child_impl_)); + EXPECT_TRUE(GetRenderSurface(great_grand_child_impl_)); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_grand_child_surface_draw_transform, - grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_great_grand_child_surface_draw_transform, - great_grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(great_grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform, @@ -643,25 +643,26 @@ TEST_F(LayerPositionConstraintTest, expected_fixed_position_child_transform.Translate(10.0, 30.0); expected_fixed_position_child_transform.PreconcatTransform(rotation_about_z); - EXPECT_TRUE(grand_child_impl_->GetRenderSurface()); - EXPECT_TRUE(great_grand_child_impl_->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(grand_child_impl_)); + EXPECT_TRUE(GetRenderSurface(great_grand_child_impl_)); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_grand_child_surface_draw_transform, - grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_great_grand_child_surface_draw_transform, - great_grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(great_grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform, fixed_position_child_impl->DrawTransform()); // Case 3: fixed-container size delta of 20, 20 - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Top-left fixed-position layer should not be affected by container size. @@ -680,7 +681,8 @@ TEST_F(LayerPositionConstraintTest, fixed_position_child_impl = layer_tree_impl_->LayerById(fixed_position_child->id()); SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 30)); - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Bottom-right fixed-position layer moves as container resizes. @@ -765,18 +767,18 @@ TEST_F( gfx::Transform expected_fixed_position_child_transform; expected_fixed_position_child_transform.PreconcatTransform(rotation_about_z); - EXPECT_TRUE(grand_child_impl_->GetRenderSurface()); - EXPECT_TRUE(great_grand_child_impl_->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(grand_child_impl_)); + EXPECT_TRUE(GetRenderSurface(great_grand_child_impl_)); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_grand_child_surface_draw_transform, - grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_great_grand_child_surface_draw_transform, - great_grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(great_grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform, @@ -810,18 +812,18 @@ TEST_F( expected_fixed_position_child_transform.Translate(10.0, 30.0); expected_fixed_position_child_transform.PreconcatTransform(rotation_about_z); - EXPECT_TRUE(grand_child_impl_->GetRenderSurface()); - EXPECT_TRUE(great_grand_child_impl_->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(grand_child_impl_)); + EXPECT_TRUE(GetRenderSurface(great_grand_child_impl_)); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_grand_child_surface_draw_transform, - grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_great_grand_child_surface_draw_transform, - great_grand_child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(great_grand_child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform, @@ -848,10 +850,10 @@ TEST_F(LayerPositionConstraintTest, gfx::Transform expected_surface_draw_transform; gfx::Transform expected_child_transform; gfx::Transform expected_grand_child_transform; - EXPECT_TRUE(child_impl_->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(child_impl_)); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_surface_draw_transform, - child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, @@ -869,17 +871,18 @@ TEST_F(LayerPositionConstraintTest, expected_grand_child_transform.MakeIdentity(); expected_grand_child_transform.Translate(10.0, 10.0); - EXPECT_TRUE(child_impl_->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(child_impl_)); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_surface_draw_transform, - child_impl_->GetRenderSurface()->draw_transform()); + GetRenderSurface(child_impl_)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child_impl_->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child_impl_->DrawTransform()); // Case 3: fixed-container size delta of 20, 20 - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Top-left fixed-position layer should not be affected by container size. @@ -892,7 +895,8 @@ TEST_F(LayerPositionConstraintTest, grand_child_->SetPositionConstraint(fixed_to_bottom_right_); CommitAndUpdateImplPointers(); SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10)); - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Bottom-right fixed-position layer moves as container resizes. @@ -950,7 +954,8 @@ TEST_F(LayerPositionConstraintTest, grand_child_impl_->DrawTransform()); // Case 3: fixed-container size delta of 20, 20 - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Top-left fixed-position layer should not be affected by container size. @@ -963,7 +968,8 @@ TEST_F(LayerPositionConstraintTest, grand_child_->SetPositionConstraint(fixed_to_bottom_right_); CommitAndUpdateImplPointers(); SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10)); - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); @@ -1017,7 +1023,8 @@ TEST_F(LayerPositionConstraintTest, // Case 2: sizeDelta SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0)); - SetFixedContainerSizeDelta(child_impl_, gfx::Vector2d(20, 20)); + outer_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); expected_child_transform.MakeIdentity(); @@ -1112,7 +1119,8 @@ TEST_F(LayerPositionConstraintTest, // Case 1: fixed-container size delta of 20, 20 SetScrollOffsetDelta(scroll_layer_impl_, gfx::Vector2d(10, 10)); scroll_layer_impl_->SetDrawsContent(true); - SetFixedContainerSizeDelta(scroll_layer_impl_, gfx::Vector2d(20, 20)); + inner_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); gfx::Transform expected_scroll_layer_transform; expected_scroll_layer_transform.Translate(-10.0, -10.0); gfx::Transform expected_fixed_child_transform; @@ -1132,7 +1140,8 @@ TEST_F(LayerPositionConstraintTest, root_impl_->layer_tree_impl()->FindActiveTreeLayerById(fixed_child->id()); SetScrollOffsetDelta(scroll_layer_impl_, gfx::Vector2d(10, 10)); - SetFixedContainerSizeDelta(scroll_layer_impl_, gfx::Vector2d(20, 20)); + inner_viewport_container_layer_impl_->SetViewportBoundsDelta( + gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_impl_); // Bottom-right fixed-position layer moves as container resizes. diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index 1a227ca01ae..652d84e6dee 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -929,7 +929,7 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) { gfx::Rect(10, 10))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetForceRenderSurfaceForTesting(true)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetHideLayerAndSubtree(true)); - EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetElementId(ElementId(2, 0))); + EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetElementId(ElementId(2))); EXPECT_SET_NEEDS_COMMIT( 1, test_layer->SetMutableProperties(MutableProperty::kTransform)); @@ -1385,7 +1385,7 @@ TEST_F(LayerTest, AnimationSchedulesLayerUpdate) { // though currently there is no good place for this unittest to go. Move to // LayerTreeHost unittest when there is a good setup. scoped_refptr<Layer> layer = Layer::Create(); - layer->SetElementId(ElementId(2, 0)); + layer->SetElementId(ElementId(2)); EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, layer_tree_host_->SetRootLayer(layer)); auto element_id = layer->element_id(); @@ -1419,7 +1419,7 @@ TEST_F(LayerTest, ElementIdAndMutablePropertiesArePushed) { EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2); - test_layer->SetElementId(ElementId(2, 0)); + test_layer->SetElementId(ElementId(2)); test_layer->SetMutableProperties(MutableProperty::kTransform); EXPECT_FALSE(impl_layer->element_id()); @@ -1427,9 +1427,66 @@ TEST_F(LayerTest, ElementIdAndMutablePropertiesArePushed) { test_layer->PushPropertiesTo(impl_layer.get()); - EXPECT_EQ(ElementId(2, 0), impl_layer->element_id()); + EXPECT_EQ(ElementId(2), impl_layer->element_id()); EXPECT_EQ(MutableProperty::kTransform, impl_layer->mutable_properties()); } +TEST_F(LayerTest, NotUsingLayerListsManagesElementId) { + scoped_refptr<Layer> test_layer = Layer::Create(); + ElementId element_id = ElementId(2); + test_layer->SetElementId(element_id); + + // Expect additional call due to has-animation check. + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2); + scoped_refptr<AnimationTimeline> timeline = + AnimationTimeline::Create(AnimationIdProvider::NextTimelineId()); + animation_host_->AddAnimationTimeline(timeline); + + AddOpacityTransitionToElementWithPlayer(element_id, timeline, 10.0, 1.f, 0.f, + false); + EXPECT_TRUE(animation_host_->HasAnyAnimation(element_id)); + + EXPECT_EQ(nullptr, layer_tree_host_->LayerByElementId(element_id)); + test_layer->SetLayerTreeHost(layer_tree_host_.get()); + // Layer should now be registered by element id. + EXPECT_EQ(test_layer, layer_tree_host_->LayerByElementId(element_id)); + + test_layer->SetLayerTreeHost(nullptr); + // Layer should have been un-registered. + EXPECT_EQ(nullptr, layer_tree_host_->LayerByElementId(element_id)); +} + +class LayerTestWithLayerLists : public LayerTest { + protected: + void SetUp() override { + settings_.use_layer_lists = true; + LayerTest::SetUp(); + } +}; + +TEST_F(LayerTestWithLayerLists, UsingLayerListsDoesNotManageElementId) { + scoped_refptr<Layer> test_layer = Layer::Create(); + ElementId element_id = ElementId(2); + test_layer->SetElementId(element_id); + + // Only one call expected since we should skip the has-animation check. + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); + scoped_refptr<AnimationTimeline> timeline = + AnimationTimeline::Create(AnimationIdProvider::NextTimelineId()); + animation_host_->AddAnimationTimeline(timeline); + + AddOpacityTransitionToElementWithPlayer(element_id, timeline, 10.0, 1.f, 0.f, + false); + EXPECT_TRUE(animation_host_->HasAnyAnimation(element_id)); + + EXPECT_EQ(nullptr, layer_tree_host_->LayerByElementId(element_id)); + test_layer->SetLayerTreeHost(layer_tree_host_.get()); + // Layer shouldn't have been registered by element id. + EXPECT_EQ(nullptr, layer_tree_host_->LayerByElementId(element_id)); + + test_layer->SetLayerTreeHost(nullptr); + EXPECT_EQ(nullptr, layer_tree_host_->LayerByElementId(element_id)); +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/nine_patch_layer_unittest.cc b/chromium/cc/layers/nine_patch_layer_unittest.cc index 440b608f9df..340c6b8d730 100644 --- a/chromium/cc/layers/nine_patch_layer_unittest.cc +++ b/chromium/cc/layers/nine_patch_layer_unittest.cc @@ -56,7 +56,6 @@ TEST_F(NinePatchLayerTest, SetLayerProperties) { EXPECT_EQ(test_layer->GetLayerTreeHostForTesting(), layer_tree_host_.get()); gfx::Rect screen_space_clip_rect; - test_layer->SavePaintProperties(); test_layer->Update(); EXPECT_FALSE(test_layer->DrawsContent()); diff --git a/chromium/cc/layers/paint_properties.h b/chromium/cc/layers/paint_properties.h deleted file mode 100644 index b57f90f3127..00000000000 --- a/chromium/cc/layers/paint_properties.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2012 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. - -#ifndef CC_LAYERS_PAINT_PROPERTIES_H_ -#define CC_LAYERS_PAINT_PROPERTIES_H_ - -#include "ui/gfx/geometry/size.h" - -namespace cc { - -// Container for properties that layers need to save before they can be paint. -struct CC_EXPORT PaintProperties { - PaintProperties() : source_frame_number(-1) {} - - gfx::Size bounds; - - int source_frame_number; -}; - -} // namespace cc - -#endif // CC_LAYERS_PAINT_PROPERTIES_H_ diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc index 81a515edf1c..52ead4b4cb8 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc @@ -32,16 +32,16 @@ std::unique_ptr<LayerImpl> PaintedOverlayScrollbarLayer::CreateLayerImpl( scoped_refptr<PaintedOverlayScrollbarLayer> PaintedOverlayScrollbarLayer::Create(std::unique_ptr<Scrollbar> scrollbar, - int scroll_layer_id) { - return make_scoped_refptr( - new PaintedOverlayScrollbarLayer(std::move(scrollbar), scroll_layer_id)); + ElementId scroll_element_id) { + return make_scoped_refptr(new PaintedOverlayScrollbarLayer( + std::move(scrollbar), scroll_element_id)); } PaintedOverlayScrollbarLayer::PaintedOverlayScrollbarLayer( std::unique_ptr<Scrollbar> scrollbar, - int scroll_layer_id) + ElementId scroll_element_id) : scrollbar_(std::move(scrollbar)), - scroll_layer_id_(scroll_layer_id), + scroll_element_id_(scroll_element_id), thumb_thickness_(scrollbar_->ThumbThickness()), thumb_length_(scrollbar_->ThumbLength()) { DCHECK(scrollbar_->UsesNinePatchThumbResource()); @@ -49,15 +49,15 @@ PaintedOverlayScrollbarLayer::PaintedOverlayScrollbarLayer( PaintedOverlayScrollbarLayer::~PaintedOverlayScrollbarLayer() {} -int PaintedOverlayScrollbarLayer::ScrollLayerId() const { - return scroll_layer_id_; +ElementId PaintedOverlayScrollbarLayer::scroll_element_id() const { + return scroll_element_id_; } -void PaintedOverlayScrollbarLayer::SetScrollLayer(int layer_id) { - if (layer_id == scroll_layer_id_) +void PaintedOverlayScrollbarLayer::SetScrollElementId(ElementId element_id) { + if (element_id == scroll_element_id_) return; - scroll_layer_id_ = layer_id; + scroll_element_id_ = element_id; SetNeedsFullTreeSync(); } @@ -65,10 +65,6 @@ bool PaintedOverlayScrollbarLayer::OpacityCanAnimateOnImplThread() const { return scrollbar_->IsOverlay(); } -bool PaintedOverlayScrollbarLayer::AlwaysUseActiveTreeOpacity() const { - return true; -} - ScrollbarOrientation PaintedOverlayScrollbarLayer::orientation() const { return scrollbar_->Orientation(); } @@ -79,7 +75,7 @@ void PaintedOverlayScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { PaintedOverlayScrollbarLayerImpl* scrollbar_layer = static_cast<PaintedOverlayScrollbarLayerImpl*>(layer); - scrollbar_layer->SetScrollLayerId(scroll_layer_id_); + scrollbar_layer->SetScrollElementId(scroll_element_id_); scrollbar_layer->SetThumbThickness(thumb_thickness_); scrollbar_layer->SetThumbLength(thumb_length_); diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.h b/chromium/cc/layers/painted_overlay_scrollbar_layer.h index f7cd66e5fd5..c0537d59e3c 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.h +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.h @@ -22,15 +22,14 @@ class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerInterface, static scoped_refptr<PaintedOverlayScrollbarLayer> Create( std::unique_ptr<Scrollbar> scrollbar, - int scroll_layer_id); + ElementId scroll_element_id = ElementId()); bool OpacityCanAnimateOnImplThread() const override; - bool AlwaysUseActiveTreeOpacity() const override; ScrollbarLayerInterface* ToScrollbarLayer() override; // ScrollbarLayerInterface - int ScrollLayerId() const override; - void SetScrollLayer(int layer_id) override; + ElementId scroll_element_id() const override; + void SetScrollElementId(ElementId element_id) override; ScrollbarOrientation orientation() const override; // Layer interface @@ -40,7 +39,7 @@ class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerInterface, protected: PaintedOverlayScrollbarLayer(std::unique_ptr<Scrollbar> scrollbar, - int scroll_layer_id); + ElementId scroll_element_id); ~PaintedOverlayScrollbarLayer() override; private: @@ -58,7 +57,7 @@ class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerInterface, bool PaintThumbIfNeeded(); std::unique_ptr<Scrollbar> scrollbar_; - int scroll_layer_id_; + ElementId scroll_element_id_; int thumb_thickness_; int thumb_length_; diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc index 98aca6dbe19..66dde4c2989 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_scrollbar_layer.cc @@ -34,16 +34,16 @@ std::unique_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl( scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create( std::unique_ptr<Scrollbar> scrollbar, - int scroll_layer_id) { + ElementId scroll_element_id) { return make_scoped_refptr( - new PaintedScrollbarLayer(std::move(scrollbar), scroll_layer_id)); + new PaintedScrollbarLayer(std::move(scrollbar), scroll_element_id)); } PaintedScrollbarLayer::PaintedScrollbarLayer( std::unique_ptr<Scrollbar> scrollbar, - int scroll_layer_id) + ElementId scroll_element_id) : scrollbar_(std::move(scrollbar)), - scroll_layer_id_(scroll_layer_id), + scroll_element_id_(scroll_element_id), internal_contents_scale_(1.f), thumb_thickness_(scrollbar_->ThumbThickness()), thumb_length_(scrollbar_->ThumbLength()), @@ -57,15 +57,15 @@ PaintedScrollbarLayer::PaintedScrollbarLayer( PaintedScrollbarLayer::~PaintedScrollbarLayer() {} -int PaintedScrollbarLayer::ScrollLayerId() const { - return scroll_layer_id_; +ElementId PaintedScrollbarLayer::scroll_element_id() const { + return scroll_element_id_; } -void PaintedScrollbarLayer::SetScrollLayer(int layer_id) { - if (layer_id == scroll_layer_id_) +void PaintedScrollbarLayer::SetScrollElementId(ElementId element_id) { + if (element_id == scroll_element_id_) return; - scroll_layer_id_ = layer_id; + scroll_element_id_ = element_id; SetNeedsFullTreeSync(); } @@ -73,10 +73,6 @@ bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const { return scrollbar_->IsOverlay(); } -bool PaintedScrollbarLayer::AlwaysUseActiveTreeOpacity() const { - return true; -} - ScrollbarOrientation PaintedScrollbarLayer::orientation() const { return scrollbar_->Orientation(); } @@ -87,7 +83,7 @@ void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { PaintedScrollbarLayerImpl* scrollbar_layer = static_cast<PaintedScrollbarLayerImpl*>(layer); - scrollbar_layer->SetScrollLayerId(scroll_layer_id_); + scrollbar_layer->SetScrollElementId(scroll_element_id_); scrollbar_layer->set_internal_contents_scale_and_bounds( internal_contents_scale_, internal_content_bounds_); diff --git a/chromium/cc/layers/painted_scrollbar_layer.h b/chromium/cc/layers/painted_scrollbar_layer.h index 8e1884f44f5..6320051c1aa 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.h +++ b/chromium/cc/layers/painted_scrollbar_layer.h @@ -22,15 +22,14 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface, static scoped_refptr<PaintedScrollbarLayer> Create( std::unique_ptr<Scrollbar> scrollbar, - int scroll_layer_id); + ElementId element_id = ElementId()); bool OpacityCanAnimateOnImplThread() const override; - bool AlwaysUseActiveTreeOpacity() const override; ScrollbarLayerInterface* ToScrollbarLayer() override; // ScrollbarLayerInterface - int ScrollLayerId() const override; - void SetScrollLayer(int layer_id) override; + ElementId scroll_element_id() const override; + void SetScrollElementId(ElementId element_id) override; ScrollbarOrientation orientation() const override; @@ -45,7 +44,7 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface, protected: PaintedScrollbarLayer(std::unique_ptr<Scrollbar> scrollbar, - int scroll_layer_id); + ElementId scroll_element_id); ~PaintedScrollbarLayer() override; // For unit tests @@ -76,7 +75,7 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface, ScrollbarPart part); std::unique_ptr<Scrollbar> scrollbar_; - int scroll_layer_id_; + ElementId scroll_element_id_; float internal_contents_scale_; gfx::Size internal_content_bounds_; diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc index 3abad0dd275..6e6db9f91f7 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc @@ -209,4 +209,9 @@ const char* PaintedScrollbarLayerImpl::LayerTypeAsString() const { return "cc::PaintedScrollbarLayerImpl"; } +LayerTreeSettings::ScrollbarAnimator +PaintedScrollbarLayerImpl::GetScrollbarAnimator() const { + return LayerTreeSettings::NO_ANIMATOR; +} + } // namespace cc diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.h b/chromium/cc/layers/painted_scrollbar_layer_impl.h index 9ff7e89691f..81b5aa20538 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl.h +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.h @@ -57,6 +57,8 @@ class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase { int ThumbThickness() const override; + LayerTreeSettings::ScrollbarAnimator GetScrollbarAnimator() const override; + protected: PaintedScrollbarLayerImpl(LayerTreeImpl* tree_impl, int id, diff --git a/chromium/cc/layers/painted_scrollbar_layer_unittest.cc b/chromium/cc/layers/painted_scrollbar_layer_unittest.cc index ed58fa0136c..f4767910181 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_unittest.cc @@ -37,7 +37,7 @@ TEST(PaintedScrollbarLayerTest, NeedsPaint) { MockScrollbar* scrollbar = new MockScrollbar(); scoped_refptr<PaintedScrollbarLayer> scrollbar_layer = - PaintedScrollbarLayer::Create(std::unique_ptr<Scrollbar>(scrollbar), 1); + PaintedScrollbarLayer::Create(std::unique_ptr<Scrollbar>(scrollbar)); scrollbar_layer->SetIsDrawable(true); scrollbar_layer->SetBounds(gfx::Size(100, 100)); @@ -45,7 +45,6 @@ TEST(PaintedScrollbarLayerTest, NeedsPaint) { layer_tree_host->SetRootLayer(scrollbar_layer); EXPECT_EQ(scrollbar_layer->GetLayerTreeHostForTesting(), layer_tree_host.get()); - scrollbar_layer->SavePaintProperties(); // Request no paint, but expect them to be painted because they have not // yet been initialized. diff --git a/chromium/cc/layers/picture_image_layer.cc b/chromium/cc/layers/picture_image_layer.cc index c219d8d906d..327582e8748 100644 --- a/chromium/cc/layers/picture_image_layer.cc +++ b/chromium/cc/layers/picture_image_layer.cc @@ -33,15 +33,15 @@ std::unique_ptr<LayerImpl> PictureImageLayer::CreateLayerImpl( } bool PictureImageLayer::HasDrawableContent() const { - return image_ && PictureLayer::HasDrawableContent(); + return image_.sk_image() && PictureLayer::HasDrawableContent(); } -void PictureImageLayer::SetImage(sk_sp<const SkImage> image) { +void PictureImageLayer::SetImage(PaintImage image) { // SetImage() currently gets called whenever there is any // style change that affects the layer even if that change doesn't // affect the actual contents of the image (e.g. a CSS animation). // With this check in place we avoid unecessary texture uploads. - if (image_.get() == image.get()) + if (image_ == image) return; image_ = std::move(image); @@ -55,9 +55,9 @@ gfx::Rect PictureImageLayer::PaintableRegion() { scoped_refptr<DisplayItemList> PictureImageLayer::PaintContentsToDisplayList( ContentLayerClient::PaintingControlSetting painting_control) { - DCHECK(image_); - DCHECK_GT(image_->width(), 0); - DCHECK_GT(image_->height(), 0); + DCHECK(image_.sk_image()); + DCHECK_GT(image_.sk_image()->width(), 0); + DCHECK_GT(image_.sk_image()->height(), 0); DCHECK(layer_tree_host()); auto display_list = make_scoped_refptr(new DisplayItemList); @@ -66,10 +66,10 @@ scoped_refptr<DisplayItemList> PictureImageLayer::PaintContentsToDisplayList( PaintCanvas* canvas = recorder.beginRecording(gfx::RectToSkRect(PaintableRegion())); - SkScalar content_to_layer_scale_x = - SkFloatToScalar(static_cast<float>(bounds().width()) / image_->width()); - SkScalar content_to_layer_scale_y = - SkFloatToScalar(static_cast<float>(bounds().height()) / image_->height()); + SkScalar content_to_layer_scale_x = SkFloatToScalar( + static_cast<float>(bounds().width()) / image_.sk_image()->width()); + SkScalar content_to_layer_scale_y = SkFloatToScalar( + static_cast<float>(bounds().height()) / image_.sk_image()->height()); canvas->scale(content_to_layer_scale_x, content_to_layer_scale_y); // Because Android WebView resourceless software draw mode rasters directly @@ -78,7 +78,8 @@ scoped_refptr<DisplayItemList> PictureImageLayer::PaintContentsToDisplayList( canvas->drawImage(image_, 0, 0); display_list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - PaintableRegion(), recorder.finishRecordingAsPicture()); + PaintableRegion(), recorder.finishRecordingAsPicture(), + gfx::RectToSkRect(PaintableRegion())); display_list->Finalize(); return display_list; diff --git a/chromium/cc/layers/picture_image_layer.h b/chromium/cc/layers/picture_image_layer.h index d07e6cae731..faa62f41e8e 100644 --- a/chromium/cc/layers/picture_image_layer.h +++ b/chromium/cc/layers/picture_image_layer.h @@ -11,18 +11,17 @@ #include "cc/cc_export.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/picture_layer.h" +#include "cc/paint/paint_image.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "ui/gfx/geometry/size.h" -class SkImage; - namespace cc { class CC_EXPORT PictureImageLayer : public PictureLayer, ContentLayerClient { public: static scoped_refptr<PictureImageLayer> Create(); - void SetImage(sk_sp<const SkImage> image); + void SetImage(PaintImage image); // Layer implementation. std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; @@ -42,7 +41,7 @@ class CC_EXPORT PictureImageLayer : public PictureLayer, ContentLayerClient { PictureImageLayer(); ~PictureImageLayer() override; - sk_sp<const SkImage> image_; + PaintImage image_; DISALLOW_COPY_AND_ASSIGN(PictureImageLayer); }; diff --git a/chromium/cc/layers/picture_image_layer_unittest.cc b/chromium/cc/layers/picture_image_layer_unittest.cc index 8701d316cdf..e17cc6c4677 100644 --- a/chromium/cc/layers/picture_image_layer_unittest.cc +++ b/chromium/cc/layers/picture_image_layer_unittest.cc @@ -40,7 +40,8 @@ TEST(PictureImageLayerTest, PaintContentsToDisplayList) { image_canvas->drawRect(SkRect::MakeWH(100, 100), blue_paint); image_canvas->drawRect(SkRect::MakeLTRB(100, 100, 200, 200), blue_paint); - layer->SetImage(image_surface->makeImageSnapshot()); + layer->SetImage( + PaintImage(PaintImage::GetNextId(), image_surface->makeImageSnapshot())); layer->SetBounds(gfx::Size(layer_rect.width(), layer_rect.height())); scoped_refptr<DisplayItemList> display_list = diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc index d853f13ae1a..d2ce7ccd954 100644 --- a/chromium/cc/layers/picture_layer.cc +++ b/chromium/cc/layers/picture_layer.cc @@ -50,8 +50,7 @@ void PictureLayer::PushPropertiesTo(LayerImpl* base_layer) { Layer::PushPropertiesTo(base_layer); TRACE_EVENT0("cc", "PictureLayer::PushPropertiesTo"); PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer); - // TODO(danakj): Make mask_type_ a constructor parameter for PictureLayer. - DCHECK_EQ(layer_impl->mask_type(), mask_type()); + layer_impl->SetLayerMaskType(mask_type()); DropRecordingSourceContentIfInvalid(); layer_impl->SetNearestNeighbor(picture_layer_inputs_.nearest_neighbor); @@ -83,11 +82,10 @@ void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) { recording_source_.reset(new RecordingSource); recording_source_->SetSlowdownRasterScaleFactor( host->GetDebugState().slow_down_raster_scale_factor); - // If we need to enable image decode tasks, then we have to generate the - // discardable images metadata. - const LayerTreeSettings& settings = layer_tree_host()->GetSettings(); - recording_source_->SetGenerateDiscardableImagesMetadata( - settings.image_decode_tasks_enabled); + + // Source frame numbers are relative the LayerTreeHost, so this needs + // to be reset. + update_source_frame_number_ = -1; } void PictureLayer::SetNeedsDisplayRect(const gfx::Rect& layer_rect) { @@ -101,7 +99,7 @@ bool PictureLayer::Update() { update_source_frame_number_ = layer_tree_host()->SourceFrameNumber(); bool updated = Layer::Update(); - gfx::Size layer_size = paint_properties().bounds; + gfx::Size layer_size = bounds(); recording_source_->SetBackgroundColor(SafeOpaqueBackgroundColor()); recording_source_->SetRequiresClear( @@ -227,8 +225,6 @@ void PictureLayer::DropRecordingSourceContentIfInvalid() { gfx::Size recording_source_bounds = recording_source_->GetSize(); gfx::Size layer_bounds = bounds(); - if (paint_properties().source_frame_number == source_frame_number) - layer_bounds = paint_properties().bounds; // If update called, then recording source size must match bounds pushed to // impl layer. diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index a85580bf3cd..f43c977f684 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -120,6 +120,17 @@ PictureLayerImpl::~PictureLayerImpl() { layer_tree_impl()->UnregisterPictureLayerImpl(this); } +void PictureLayerImpl::SetLayerMaskType(Layer::LayerMaskType mask_type) { + if (mask_type_ == mask_type) + return; + // It is expected that a layer can never change from being a mask to not being + // one and vice versa. Only changes that make mask layer single <-> multi are + // expected. + DCHECK(mask_type_ != Layer::LayerMaskType::NOT_MASK && + mask_type != Layer::LayerMaskType::NOT_MASK); + mask_type_ = mask_type; +} + const char* PictureLayerImpl::LayerTypeAsString() const { return "cc::PictureLayerImpl"; } @@ -131,10 +142,10 @@ std::unique_ptr<LayerImpl> PictureLayerImpl::CreateLayerImpl( void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer); - DCHECK_EQ(layer_impl->mask_type_, mask_type_); LayerImpl::PushPropertiesTo(base_layer); + layer_impl->SetLayerMaskType(mask_type()); // Twin relationships should never change once established. DCHECK(!twin_layer_ || twin_layer_ == layer_impl); DCHECK(!twin_layer_ || layer_impl->twin_layer_ == this); @@ -215,8 +226,9 @@ void PictureLayerImpl::AppendQuads(RenderPass* render_pass, if (current_draw_mode_ == DRAW_MODE_RESOURCELESS_SOFTWARE) { AppendDebugBorderQuad( - render_pass, shared_quad_state->quad_layer_bounds, shared_quad_state, - append_quads_data, DebugColors::DirectPictureBorderColor(), + render_pass, shared_quad_state->quad_layer_rect.size(), + shared_quad_state, append_quads_data, + DebugColors::DirectPictureBorderColor(), DebugColors::DirectPictureBorderWidth(device_scale_factor)); gfx::Rect geometry_rect = shared_quad_state->visible_quad_layer_rect; @@ -250,7 +262,7 @@ void PictureLayerImpl::AppendQuads(RenderPass* render_pass, return; } - AppendDebugBorderQuad(render_pass, shared_quad_state->quad_layer_bounds, + AppendDebugBorderQuad(render_pass, shared_quad_state->quad_layer_rect.size(), shared_quad_state, append_quads_data); if (ShowDebugBorders(DebugBorderType::LAYER)) { @@ -365,7 +377,8 @@ void PictureLayerImpl::AppendQuads(RenderPass* render_pass, float alpha = (SkColorGetA(draw_info.solid_color()) * (1.0f / 255.0f)) * shared_quad_state->opacity; - if (alpha >= std::numeric_limits<float>::epsilon()) { + if (mask_type_ == Layer::LayerMaskType::MULTI_TEXTURE_MASK || + alpha >= std::numeric_limits<float>::epsilon()) { SolidColorDrawQuad* quad = render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); quad->SetNew(shared_quad_state, geometry_rect, @@ -731,10 +744,10 @@ const PictureLayerTiling* PictureLayerImpl::GetPendingOrActiveTwinTiling( const PictureLayerTiling* twin_tiling = twin_layer->tilings_->FindTilingWithScaleKey( tiling->contents_scale_key()); - DCHECK(tiling->raster_transform().translation() == gfx::Vector2dF()); - DCHECK(!twin_tiling || - twin_tiling->raster_transform().translation() == gfx::Vector2dF()); - return twin_tiling; + if (twin_tiling && + twin_tiling->raster_transform() == tiling->raster_transform()) + return twin_tiling; + return nullptr; } bool PictureLayerImpl::RequiresHighResToDraw() const { @@ -838,8 +851,10 @@ gfx::Size PictureLayerImpl::CalculateTileSize( return gfx::Size(tile_width, tile_height); } -void PictureLayerImpl::GetContentsResourceId(ResourceId* resource_id, - gfx::Size* resource_size) const { +void PictureLayerImpl::GetContentsResourceId( + ResourceId* resource_id, + gfx::Size* resource_size, + gfx::SizeF* resource_uv_size) const { // The bounds and the pile size may differ if the pile wasn't updated (ie. // PictureLayer::Update didn't happen). In that case the pile will be empty. DCHECK(raster_source_->GetSize().IsEmpty() || @@ -872,6 +887,18 @@ void PictureLayerImpl::GetContentsResourceId(ResourceId* resource_id, *resource_id = draw_info.resource_id(); *resource_size = draw_info.resource_size(); + // |resource_uv_size| represents the range of UV coordinates that map to the + // content being drawn. Typically, we draw to the entire texture, so these + // coordinates are (1.0f, 1.0f). However, if we are rasterizing to an + // over-large texture, this size will be smaller, mapping to the subset of the + // texture being used. + gfx::SizeF requested_tile_size = + gfx::SizeF(iter->tiling()->tiling_data()->tiling_size()); + DCHECK_LE(requested_tile_size.width(), draw_info.resource_size().width()); + DCHECK_LE(requested_tile_size.height(), draw_info.resource_size().height()); + *resource_uv_size = gfx::SizeF( + requested_tile_size.width() / draw_info.resource_size().width(), + requested_tile_size.height() / draw_info.resource_size().height()); } void PictureLayerImpl::SetNearestNeighbor(bool nearest_neighbor) { @@ -890,13 +917,13 @@ void PictureLayerImpl::SetUseTransformedRasterization(bool use) { NoteLayerPropertyChanged(); } -PictureLayerTiling* PictureLayerImpl::AddTiling(float contents_scale) { +PictureLayerTiling* PictureLayerImpl::AddTiling( + const gfx::AxisTransform2d& contents_transform) { DCHECK(CanHaveTilings()); - DCHECK_GE(contents_scale, MinimumContentsScale()); - DCHECK_LE(contents_scale, MaximumContentsScale()); + DCHECK_GE(contents_transform.scale(), MinimumContentsScale()); + DCHECK_LE(contents_transform.scale(), MaximumContentsScale()); DCHECK(raster_source_->HasRecordings()); - return tilings_->AddTiling( - gfx::AxisTransform2d(contents_scale, gfx::Vector2dF()), raster_source_); + return tilings_->AddTiling(contents_transform, raster_source_); } void PictureLayerImpl::RemoveAllTilings() { @@ -912,9 +939,21 @@ void PictureLayerImpl::AddTilingsForRasterScale() { PictureLayerTiling* high_res = tilings_->FindTilingWithScaleKey(raster_contents_scale_); + // Note: This function is always invoked when raster scale is recomputed, + // but not necessarily changed. This means raster translation update is also + // always done when there are significant changes that triggered raster scale + // recomputation. + gfx::Vector2dF raster_translation = + CalculateRasterTranslation(raster_contents_scale_); + if (high_res && + high_res->raster_transform().translation() != raster_translation) { + tilings_->Remove(high_res); + high_res = nullptr; + } if (!high_res) { // We always need a high res tiling, so create one if it doesn't exist. - high_res = AddTiling(raster_contents_scale_); + high_res = AddTiling( + gfx::AxisTransform2d(raster_contents_scale_, raster_translation)); } else if (high_res->may_contain_low_resolution_tiles()) { // If the tiling we find here was LOW_RESOLUTION previously, it may not be // fully rastered, so destroy the old tiles. @@ -1011,7 +1050,8 @@ void PictureLayerImpl::AddLowResolutionTilingIfNeeded() { bool is_animating = draw_properties().screen_space_transform_is_animating; if (!is_pinching && !is_animating) { if (!low_res) - low_res = AddTiling(low_res_raster_contents_scale_); + low_res = AddTiling(gfx::AxisTransform2d(low_res_raster_contents_scale_, + gfx::Vector2dF())); low_res->set_resolution(LOW_RESOLUTION); } } @@ -1177,6 +1217,34 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer( SanityCheckTilingState(); } +gfx::Vector2dF PictureLayerImpl::CalculateRasterTranslation( + float raster_scale) { + if (!use_transformed_rasterization_) + return gfx::Vector2dF(); + + DCHECK(!draw_properties().screen_space_transform_is_animating); + gfx::Transform draw_transform = DrawTransform(); + DCHECK(draw_transform.IsScaleOrTranslation()); + + // It is only useful to align the content space to the target space if their + // relative pixel ratio is some small rational number. Currently we only + // align if the relative pixel ratio is 1:1. + // Good match if the maximum alignment error on a layer of size 10000px + // does not exceed 0.001px. + static constexpr float kErrorThreshold = 0.0000001f; + if (std::abs(draw_transform.matrix().getFloat(0, 0) - raster_scale) > + kErrorThreshold || + std::abs(draw_transform.matrix().getFloat(1, 1) - raster_scale) > + kErrorThreshold) + return gfx::Vector2dF(); + + // Extract the fractional part of layer origin in the target space. + float origin_x = draw_transform.matrix().getFloat(0, 3); + float origin_y = draw_transform.matrix().getFloat(1, 3); + return gfx::Vector2dF(origin_x - floorf(origin_x), + origin_y - floorf(origin_y)); +} + float PictureLayerImpl::MinimumContentsScale() const { float setting_min = layer_tree_impl()->settings().minimum_contents_scale; @@ -1390,12 +1458,12 @@ bool PictureLayerImpl::IsOnActiveOrPendingTree() const { } bool PictureLayerImpl::HasValidTilePriorities() const { - return IsOnActiveOrPendingTree() && - is_drawn_render_surface_layer_list_member(); + return IsOnActiveOrPendingTree() && (contributes_to_drawn_render_surface() || + raster_even_if_not_in_rsll()); } void PictureLayerImpl::InvalidateRegionForImages( - const ImageIdFlatSet& images_to_invalidate) { + const PaintImageIdFlatSet& images_to_invalidate) { TRACE_EVENT_BEGIN0("cc", "PictureLayerImpl::InvalidateRegionForImages"); InvalidationRegion image_invalidation; diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index 0e85d8d4208..96ac75c45d5 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -38,6 +38,7 @@ class CC_EXPORT PictureLayerImpl ~PictureLayerImpl() override; Layer::LayerMaskType mask_type() const { return mask_type_; } + void SetLayerMaskType(Layer::LayerMaskType type); // LayerImpl overrides. const char* LayerTypeAsString() const override; @@ -76,7 +77,8 @@ class CC_EXPORT PictureLayerImpl // Mask-related functions. void GetContentsResourceId(ResourceId* resource_id, - gfx::Size* resource_size) const override; + gfx::Size* resource_size, + gfx::SizeF* resource_uv_size) const override; void SetNearestNeighbor(bool nearest_neighbor); @@ -101,18 +103,20 @@ class CC_EXPORT PictureLayerImpl is_directly_composited_image_ = is_directly_composited_image; } - void InvalidateRegionForImages(const ImageIdFlatSet& images_to_invalidate); + void InvalidateRegionForImages( + const PaintImageIdFlatSet& images_to_invalidate); protected: PictureLayerImpl(LayerTreeImpl* tree_impl, int id, Layer::LayerMaskType mask_type); - PictureLayerTiling* AddTiling(float contents_scale); + PictureLayerTiling* AddTiling(const gfx::AxisTransform2d& contents_transform); void RemoveAllTilings(); void AddTilingsForRasterScale(); void AddLowResolutionTilingIfNeeded(); bool ShouldAdjustRasterScale() const; void RecalculateRasterScales(); + gfx::Vector2dF CalculateRasterTranslation(float raster_scale); void CleanUpTilingsOnActiveLayer( const std::vector<PictureLayerTiling*>& used_tilings); float MinimumContentsScale() const; @@ -150,7 +154,7 @@ class CC_EXPORT PictureLayerImpl bool was_screen_space_transform_animating_; bool only_used_low_res_last_append_quads_; - const Layer::LayerMaskType mask_type_; + Layer::LayerMaskType mask_type_; bool nearest_neighbor_; bool use_transformed_rasterization_; diff --git a/chromium/cc/layers/picture_layer_impl_perftest.cc b/chromium/cc/layers/picture_layer_impl_perftest.cc index a434297fbc9..270978d8cff 100644 --- a/chromium/cc/layers/picture_layer_impl_perftest.cc +++ b/chromium/cc/layers/picture_layer_impl_perftest.cc @@ -28,7 +28,8 @@ static const int kTimeCheckInterval = 10; void AddTiling(float scale, FakePictureLayerImpl* layer, std::vector<Tile*>* all_tiles) { - PictureLayerTiling* tiling = layer->AddTiling(scale); + PictureLayerTiling* tiling = + layer->AddTiling(gfx::AxisTransform2d(scale, gfx::Vector2dF())); tiling->set_resolution(HIGH_RESOLUTION); tiling->CreateAllTilesForTesting(); @@ -186,11 +187,12 @@ TEST_F(PictureLayerImplPerfTest, TilingSetRasterQueueConstructAndIterate) { float low_res_factor = host_impl_.settings().low_res_contents_scale_factor; - pending_layer_->AddTiling(low_res_factor); - pending_layer_->AddTiling(0.3f); - pending_layer_->AddTiling(0.7f); - pending_layer_->AddTiling(1.0f); - pending_layer_->AddTiling(2.0f); + pending_layer_->AddTiling( + gfx::AxisTransform2d(low_res_factor, gfx::Vector2dF())); + pending_layer_->AddTiling(gfx::AxisTransform2d(0.3f, gfx::Vector2dF())); + pending_layer_->AddTiling(gfx::AxisTransform2d(0.7f, gfx::Vector2dF())); + pending_layer_->AddTiling(gfx::AxisTransform2d(1.0f, gfx::Vector2dF())); + pending_layer_->AddTiling(gfx::AxisTransform2d(2.0f, gfx::Vector2dF())); RunRasterQueueConstructAndIterateTest("32_100x100", 32, gfx::Size(100, 100)); RunRasterQueueConstructAndIterateTest("32_500x500", 32, gfx::Size(500, 500)); @@ -203,11 +205,12 @@ TEST_F(PictureLayerImplPerfTest, TilingSetRasterQueueConstruct) { float low_res_factor = host_impl_.settings().low_res_contents_scale_factor; - pending_layer_->AddTiling(low_res_factor); - pending_layer_->AddTiling(0.3f); - pending_layer_->AddTiling(0.7f); - pending_layer_->AddTiling(1.0f); - pending_layer_->AddTiling(2.0f); + pending_layer_->AddTiling( + gfx::AxisTransform2d(low_res_factor, gfx::Vector2dF())); + pending_layer_->AddTiling(gfx::AxisTransform2d(0.3f, gfx::Vector2dF())); + pending_layer_->AddTiling(gfx::AxisTransform2d(0.7f, gfx::Vector2dF())); + pending_layer_->AddTiling(gfx::AxisTransform2d(1.0f, gfx::Vector2dF())); + pending_layer_->AddTiling(gfx::AxisTransform2d(2.0f, gfx::Vector2dF())); RunRasterQueueConstructTest("0_0_100x100", gfx::Rect(0, 0, 100, 100)); RunRasterQueueConstructTest("5000_0_100x100", gfx::Rect(5000, 0, 100, 100)); diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index 6ccf44a18d5..d9c825906cc 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -161,7 +161,7 @@ class PictureLayerImplTest : public TestLayerTreeHostBase { gfx::Transform scale_transform; scale_transform.Scale(ideal_contents_scale, ideal_contents_scale); layer->draw_properties().screen_space_transform = scale_transform; - layer->set_is_drawn_render_surface_layer_list_member(true); + layer->set_contributes_to_drawn_render_surface(true); DCHECK_EQ(layer->GetIdealContentsScale(), ideal_contents_scale); layer->layer_tree_impl()->property_trees()->SetAnimationScalesForTesting( layer->transform_tree_index(), maximum_animation_contents_scale, @@ -417,7 +417,8 @@ TEST_F(PictureLayerImplTest, ClonePartialInvalidation) { Region()); ActivateTree(); // Add a unique tiling on the active tree. - PictureLayerTiling* tiling = active_layer()->AddTiling(3.f); + PictureLayerTiling* tiling = + active_layer()->AddTiling(gfx::AxisTransform2d(3.f, gfx::Vector2dF())); tiling->set_resolution(HIGH_RESOLUTION); tiling->CreateAllTilesForTesting(); @@ -1230,9 +1231,12 @@ TEST_F(PictureLayerImplTest, HugeMasksGetScaledDown) { // The mask resource exists. ResourceId mask_resource_id; gfx::Size mask_texture_size; - active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size); + gfx::SizeF mask_uv_size; + active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size, + &mask_uv_size); EXPECT_NE(0u, mask_resource_id); EXPECT_EQ(active_mask->bounds(), mask_texture_size); + EXPECT_EQ(gfx::SizeF(1.0f, 1.0f), mask_uv_size); // Drop resources and recreate them, still the same. pending_mask->ReleaseTileResources(); @@ -1243,8 +1247,6 @@ TEST_F(PictureLayerImplTest, HugeMasksGetScaledDown) { false); active_mask->HighResTiling()->CreateAllTilesForTesting(); EXPECT_EQ(1u, active_mask->HighResTiling()->AllTilesForTesting().size()); - EXPECT_NE(0u, mask_resource_id); - EXPECT_EQ(active_mask->bounds(), mask_texture_size); // Resize larger than the max texture size. int max_texture_size = host_impl()->resource_provider()->max_texture_size(); @@ -1271,11 +1273,13 @@ TEST_F(PictureLayerImplTest, HugeMasksGetScaledDown) { // Mask layers have a tiling with a single tile in it. EXPECT_EQ(1u, active_mask->HighResTiling()->AllTilesForTesting().size()); // The mask resource exists. - active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size); + active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size, + &mask_uv_size); EXPECT_NE(0u, mask_resource_id); gfx::Size expected_size = active_mask->bounds(); expected_size.SetToMin(gfx::Size(max_texture_size, max_texture_size)); EXPECT_EQ(expected_size, mask_texture_size); + EXPECT_EQ(gfx::SizeF(1.0f, 1.0f), mask_uv_size); // Drop resources and recreate them, still the same. pending_mask->ReleaseTileResources(); @@ -1293,9 +1297,11 @@ TEST_F(PictureLayerImplTest, HugeMasksGetScaledDown) { SetupPendingTree(huge_raster_source); ActivateTree(); EXPECT_EQ(1u, active_mask->HighResTiling()->AllTilesForTesting().size()); - active_layer()->GetContentsResourceId(&mask_resource_id, &mask_texture_size); + active_layer()->GetContentsResourceId(&mask_resource_id, &mask_texture_size, + &mask_uv_size); EXPECT_EQ(expected_size, mask_texture_size); EXPECT_EQ(0u, mask_resource_id); + EXPECT_EQ(gfx::SizeF(1.0f, 1.0f), mask_uv_size); // Resize even larger, so that the scale would be smaller than the minimum // contents scale. Then the layer should no longer have any tiling. @@ -1360,11 +1366,14 @@ TEST_F(PictureLayerImplTest, ScaledMaskLayer) { // The mask resource exists. ResourceId mask_resource_id; gfx::Size mask_texture_size; - active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size); + gfx::SizeF mask_uv_size; + active_mask->GetContentsResourceId(&mask_resource_id, &mask_texture_size, + &mask_uv_size); EXPECT_NE(0u, mask_resource_id); gfx::Size expected_mask_texture_size = gfx::ScaleToCeiledSize(active_mask->bounds(), 1.3f); EXPECT_EQ(mask_texture_size, expected_mask_texture_size); + EXPECT_EQ(gfx::SizeF(1.0f, 1.0f), mask_uv_size); } TEST_F(PictureLayerImplTest, ReleaseTileResources) { @@ -2133,7 +2142,7 @@ TEST_F(PictureLayerImplTest, ActivateUninitializedLayer) { pending_raster_source); pending_layer->SetDrawsContent(true); pending_tree->SetRootLayerForTesting(std::move(pending_layer)); - pending_tree->BuildLayerListForTesting(); + pending_tree->BuildLayerListAndPropertyTreesForTesting(); FakePictureLayerImpl* raw_pending_layer = static_cast<FakePictureLayerImpl*>( host_impl()->pending_tree()->LayerById(layer_id())); @@ -2352,7 +2361,7 @@ TEST_F(PictureLayerImplTest, SyncTilingAfterGpuRasterizationToggles) { host_impl()->SetHasGpuRasterizationTrigger(true); host_impl()->SetContentIsSuitableForGpuRasterization(false); host_impl()->CommitComplete(); - EXPECT_EQ(GpuRasterizationStatus::OFF_CONTENT, + EXPECT_EQ(GpuRasterizationStatus::ON, host_impl()->gpu_rasterization_status()); } @@ -3570,9 +3579,9 @@ TEST_F(PictureLayerImplTest, SharedQuadStateContainsMaxTilingScale) { ->quad_to_target_transform.ToString()); // The content_bounds should be scaled by the // MaximumTilingContentsScale on the layer. - EXPECT_EQ(gfx::Size(2500u, 5000u).ToString(), - render_pass->shared_quad_state_list.front() - ->quad_layer_bounds.ToString()); + EXPECT_EQ( + gfx::Rect(2500u, 5000u).ToString(), + render_pass->shared_quad_state_list.front()->quad_layer_rect.ToString()); // The visible_layer_rect should be scaled by the // MaximumTilingContentsScale on the layer. EXPECT_EQ(gfx::Rect(0u, 0u, 2500u, 5000u).ToString(), @@ -3929,11 +3938,21 @@ TEST_F(OcclusionTrackingPictureLayerImplTest, OcclusionForDifferentScales) { pending_layer()->tilings()->RemoveAllTilings(); float low_res_factor = host_impl()->settings().low_res_contents_scale_factor; - pending_layer()->AddTiling(low_res_factor)->set_resolution(LOW_RESOLUTION); - pending_layer()->AddTiling(0.3f)->set_resolution(HIGH_RESOLUTION); - pending_layer()->AddTiling(0.7f)->set_resolution(HIGH_RESOLUTION); - pending_layer()->AddTiling(1.0f)->set_resolution(HIGH_RESOLUTION); - pending_layer()->AddTiling(2.0f)->set_resolution(HIGH_RESOLUTION); + pending_layer() + ->AddTiling(gfx::AxisTransform2d(low_res_factor, gfx::Vector2dF())) + ->set_resolution(LOW_RESOLUTION); + pending_layer() + ->AddTiling(gfx::AxisTransform2d(0.3f, gfx::Vector2dF())) + ->set_resolution(HIGH_RESOLUTION); + pending_layer() + ->AddTiling(gfx::AxisTransform2d(0.7f, gfx::Vector2dF())) + ->set_resolution(HIGH_RESOLUTION); + pending_layer() + ->AddTiling(gfx::AxisTransform2d(1.0f, gfx::Vector2dF())) + ->set_resolution(HIGH_RESOLUTION); + pending_layer() + ->AddTiling(gfx::AxisTransform2d(2.0f, gfx::Vector2dF())) + ->set_resolution(HIGH_RESOLUTION); RebuildPropertyTreesOnPendingTree(); host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1)); @@ -5000,5 +5019,135 @@ TEST_F(PictureLayerImplTest, CompositedImageRasterScaleChanges) { } } +TEST_F(PictureLayerImplTest, ChangeRasterTranslationNukePendingLayerTiles) { + gfx::Size layer_bounds(200, 200); + gfx::Size tile_size(256, 256); + SetupDefaultTreesWithFixedTileSize(layer_bounds, tile_size, Region()); + pending_layer()->SetUseTransformedRasterization(true); + + // Start with scale & translation of * 2.25 + (0.25, 0.5). + SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false); + gfx::Transform translate1; + translate1.Translate(0.25f, 0.5f); + pending_layer()->draw_properties().screen_space_transform.ConcatTransform( + translate1); + pending_layer()->draw_properties().target_space_transform = + pending_layer()->draw_properties().screen_space_transform; + pending_layer()->UpdateTiles(); + ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); + { + PictureLayerTiling* tiling = pending_layer()->tilings()->tiling_at(0); + EXPECT_EQ(gfx::AxisTransform2d(2.25f, gfx::Vector2dF(0.25f, 0.5f)), + tiling->raster_transform()); + EXPECT_EQ(4u, tiling->AllTilesForTesting().size()); + for (auto* tile : tiling->AllTilesForTesting()) + EXPECT_EQ(tile->raster_transform(), tiling->raster_transform()); + } + + // Change to scale & translation of * 2.25 + (0.75, 0.25). + // Verifies there is a hysteresis that simple layer movement doesn't update + // raster translation. + SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false); + gfx::Transform translate2; + translate2.Translate(0.75f, 0.25f); + pending_layer()->draw_properties().screen_space_transform.ConcatTransform( + translate2); + pending_layer()->draw_properties().target_space_transform = + pending_layer()->draw_properties().screen_space_transform; + pending_layer()->UpdateTiles(); + ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); + { + PictureLayerTiling* tiling = pending_layer()->tilings()->tiling_at(0); + EXPECT_EQ(gfx::AxisTransform2d(2.25f, gfx::Vector2dF(0.25f, 0.5f)), + tiling->raster_transform()); + EXPECT_EQ(4u, tiling->AllTilesForTesting().size()); + for (auto* tile : tiling->AllTilesForTesting()) + EXPECT_EQ(tile->raster_transform(), tiling->raster_transform()); + } + + // Now change the device scale factor but keep the same total scale. + // Our policy recomputes raster translation only if raster scale is + // recomputed. Even if the recomputed scale remains the same, we still + // updates to new translation value. Old tiles with the same scale but + // different translation would become non-ideal and deleted on pending + // layers (in fact, delete ahead due to slot conflict with the new tiling). + SetupDrawProperties(pending_layer(), 2.25f, 1.0f, 1.f, 2.25f, 2.25f, false); + pending_layer()->draw_properties().screen_space_transform.ConcatTransform( + translate2); + pending_layer()->draw_properties().target_space_transform = + pending_layer()->draw_properties().screen_space_transform; + pending_layer()->UpdateTiles(); + ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); + { + PictureLayerTiling* tiling = pending_layer()->tilings()->tiling_at(0); + EXPECT_EQ(gfx::AxisTransform2d(2.25f, gfx::Vector2dF(0.75f, 0.25f)), + tiling->raster_transform()); + EXPECT_EQ(4u, tiling->AllTilesForTesting().size()); + for (auto* tile : tiling->AllTilesForTesting()) + EXPECT_EQ(tile->raster_transform(), tiling->raster_transform()); + } +} + +TEST_F(PictureLayerImplTest, ChangeRasterTranslationNukeActiveLayerTiles) { + gfx::Size layer_bounds(200, 200); + gfx::Size tile_size(256, 256); + SetupDefaultTreesWithFixedTileSize(layer_bounds, tile_size, Region()); + active_layer()->SetUseTransformedRasterization(true); + pending_layer()->SetUseTransformedRasterization(true); + + // Start with scale & translation of * 2.25 + (0.25, 0.5) on the active layer. + SetupDrawProperties(active_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false); + gfx::Transform translate1; + translate1.Translate(0.25f, 0.5f); + active_layer()->draw_properties().screen_space_transform.ConcatTransform( + translate1); + active_layer()->draw_properties().target_space_transform = + active_layer()->draw_properties().screen_space_transform; + active_layer()->UpdateTiles(); + ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); + { + PictureLayerTiling* tiling = + active_layer()->tilings()->FindTilingWithScaleKey(2.25f); + EXPECT_EQ(gfx::AxisTransform2d(2.25f, gfx::Vector2dF(0.25f, 0.5f)), + tiling->raster_transform()); + EXPECT_EQ(4u, tiling->AllTilesForTesting().size()); + for (auto* tile : tiling->AllTilesForTesting()) + EXPECT_EQ(tile->raster_transform(), tiling->raster_transform()); + } + + // Create a pending layer with the same scale but different translation. + SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false); + gfx::Transform translate2; + translate2.Translate(0.75f, 0.25f); + pending_layer()->draw_properties().screen_space_transform.ConcatTransform( + translate2); + pending_layer()->draw_properties().target_space_transform = + pending_layer()->draw_properties().screen_space_transform; + pending_layer()->UpdateTiles(); + ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings()); + { + PictureLayerTiling* tiling = pending_layer()->tilings()->tiling_at(0); + EXPECT_EQ(gfx::AxisTransform2d(2.25f, gfx::Vector2dF(0.75f, 0.25f)), + tiling->raster_transform()); + EXPECT_EQ(4u, tiling->AllTilesForTesting().size()); + for (auto* tile : tiling->AllTilesForTesting()) + EXPECT_EQ(tile->raster_transform(), tiling->raster_transform()); + } + + // Now push to the active layer. + // Verifies the active tiles get evicted due to slot conflict. + host_impl()->ActivateSyncTree(); + ASSERT_EQ(3u, active_layer()->tilings()->num_tilings()); + { + PictureLayerTiling* tiling = + active_layer()->tilings()->FindTilingWithScaleKey(2.25f); + EXPECT_EQ(gfx::AxisTransform2d(2.25f, gfx::Vector2dF(0.75f, 0.25f)), + tiling->raster_transform()); + EXPECT_EQ(4u, tiling->AllTilesForTesting().size()); + for (auto* tile : tiling->AllTilesForTesting()) + EXPECT_EQ(tile->raster_transform(), tiling->raster_transform()); + } +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/picture_layer_unittest.cc b/chromium/cc/layers/picture_layer_unittest.cc index 1ba4387702c..f976ab9f206 100644 --- a/chromium/cc/layers/picture_layer_unittest.cc +++ b/chromium/cc/layers/picture_layer_unittest.cc @@ -45,7 +45,6 @@ TEST(PictureLayerTest, NoTilesIfEmptyBounds) { &host_client, &task_graph_runner, animation_host.get()); host->SetRootLayer(layer); layer->SetIsDrawable(true); - layer->SavePaintProperties(); layer->Update(); EXPECT_EQ(0, host->SourceFrameNumber()); @@ -53,7 +52,6 @@ TEST(PictureLayerTest, NoTilesIfEmptyBounds) { EXPECT_EQ(1, host->SourceFrameNumber()); layer->SetBounds(gfx::Size(0, 0)); - layer->SavePaintProperties(); // Intentionally skipping Update since it would normally be skipped on // a layer with empty bounds. @@ -89,7 +87,6 @@ TEST(PictureLayerTest, InvalidateRasterAfterUpdate) { &host_client, &task_graph_runner, animation_host.get()); host->SetRootLayer(layer); layer->SetIsDrawable(true); - layer->SavePaintProperties(); gfx::Rect invalidation_bounds(layer_size); @@ -102,7 +99,6 @@ TEST(PictureLayerTest, InvalidateRasterAfterUpdate) { std::unique_ptr<CompositorFrameSink> compositor_frame_sink( FakeCompositorFrameSink::Create3d()); LayerTreeSettings layer_tree_settings = LayerTreeSettings(); - layer_tree_settings.image_decode_tasks_enabled = true; FakeLayerTreeHostImpl host_impl( layer_tree_settings, &impl_task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); @@ -133,7 +129,6 @@ TEST(PictureLayerTest, InvalidateRasterWithoutUpdate) { &host_client, &task_graph_runner, animation_host.get()); host->SetRootLayer(layer); layer->SetIsDrawable(true); - layer->SavePaintProperties(); gfx::Rect invalidation_bounds(layer_size); @@ -145,7 +140,6 @@ TEST(PictureLayerTest, InvalidateRasterWithoutUpdate) { std::unique_ptr<CompositorFrameSink> compositor_frame_sink( FakeCompositorFrameSink::Create3d()); LayerTreeSettings layer_tree_settings = LayerTreeSettings(); - layer_tree_settings.image_decode_tasks_enabled = true; FakeLayerTreeHostImpl host_impl( layer_tree_settings, &impl_task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); @@ -177,22 +171,20 @@ TEST(PictureLayerTest, ClearVisibleRectWhenNoTiling) { &host_client, &task_graph_runner, animation_host.get()); host->SetRootLayer(layer); layer->SetIsDrawable(true); - layer->SavePaintProperties(); layer->Update(); EXPECT_EQ(0, host->SourceFrameNumber()); host->CommitComplete(); EXPECT_EQ(1, host->SourceFrameNumber()); - layer->SavePaintProperties(); layer->Update(); + host->BuildPropertyTreesForTesting(); FakeImplTaskRunnerProvider impl_task_runner_provider; std::unique_ptr<CompositorFrameSink> compositor_frame_sink( FakeCompositorFrameSink::Create3d()); LayerTreeSettings layer_tree_settings = LayerTreeSettings(); - layer_tree_settings.image_decode_tasks_enabled = true; FakeLayerTreeHostImpl host_impl( layer_tree_settings, &impl_task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); @@ -201,7 +193,7 @@ TEST(PictureLayerTest, ClearVisibleRectWhenNoTiling) { host_impl.CreatePendingTree(); host_impl.pending_tree()->SetRootLayerForTesting( FakePictureLayerImpl::Create(host_impl.pending_tree(), 1)); - host_impl.pending_tree()->BuildLayerListForTesting(); + host_impl.pending_tree()->BuildLayerListAndPropertyTreesForTesting(); FakePictureLayerImpl* layer_impl = static_cast<FakePictureLayerImpl*>( host_impl.pending_tree()->root_layer_for_testing()); @@ -220,7 +212,6 @@ TEST(PictureLayerTest, ClearVisibleRectWhenNoTiling) { host_impl.active_tree()->UpdateDrawProperties(can_use_lcd_text); layer->SetBounds(gfx::Size(11, 11)); - layer->SavePaintProperties(); host_impl.CreatePendingTree(); layer_impl = static_cast<FakePictureLayerImpl*>( @@ -317,7 +308,6 @@ TEST(PictureLayerTest, NonMonotonicSourceFrameNumber) { auto animation_host2 = AnimationHost::CreateForTesting(ThreadInstance::MAIN); - // TODO(sad): InitParams will be movable. LayerTreeHost::InitParams params2; params2.client = &host_client1; params2.settings = &settings; @@ -360,5 +350,81 @@ TEST(PictureLayerTest, NonMonotonicSourceFrameNumber) { animation_host2->SetMutatorHostClient(nullptr); } +// Verify that PictureLayer::DropRecordingSourceContentIfInvalid does not +// assert when changing frames. +TEST(PictureLayerTest, ChangingHostsWithCollidingFrames) { + LayerTreeSettings settings = LayerTreeSettings(); + settings.single_thread_proxy_scheduler = false; + + StubLayerTreeHostSingleThreadClient single_thread_client; + FakeLayerTreeHostClient host_client1; + FakeLayerTreeHostClient host_client2; + TestTaskGraphRunner task_graph_runner; + + FakeContentLayerClient client; + client.set_bounds(gfx::Size()); + scoped_refptr<FakePictureLayer> layer = FakePictureLayer::Create(&client); + + auto animation_host = AnimationHost::CreateForTesting(ThreadInstance::MAIN); + + LayerTreeHost::InitParams params; + params.client = &host_client1; + params.settings = &settings; + params.task_graph_runner = &task_graph_runner; + params.main_task_runner = base::ThreadTaskRunnerHandle::Get(); + params.mutator_host = animation_host.get(); + std::unique_ptr<LayerTreeHost> host1 = + LayerTreeHost::CreateSingleThreaded(&single_thread_client, ¶ms); + host1->SetVisible(true); + host_client1.SetLayerTreeHost(host1.get()); + + auto animation_host2 = AnimationHost::CreateForTesting(ThreadInstance::MAIN); + + LayerTreeHost::InitParams params2; + params2.client = &host_client1; + params2.settings = &settings; + params2.task_graph_runner = &task_graph_runner; + params2.main_task_runner = base::ThreadTaskRunnerHandle::Get(); + params2.client = &host_client2; + params2.mutator_host = animation_host2.get(); + std::unique_ptr<LayerTreeHost> host2 = + LayerTreeHost::CreateSingleThreaded(&single_thread_client, ¶ms2); + host2->SetVisible(true); + host_client2.SetLayerTreeHost(host2.get()); + + // The PictureLayer is put in one LayerTreeHost. + host1->SetRootLayer(layer); + // Do a main frame, record the picture layers. + EXPECT_EQ(0, layer->update_count()); + layer->SetBounds(gfx::Size(500, 500)); + host1->Composite(base::TimeTicks::Now()); + EXPECT_EQ(1, layer->update_count()); + EXPECT_EQ(1, host1->SourceFrameNumber()); + EXPECT_EQ(gfx::Size(500, 500), layer->bounds()); + + // Then moved to another LayerTreeHost. + host1->SetRootLayer(nullptr); + scoped_refptr<Layer> root = Layer::Create(); + host2->SetRootLayer(root); + root->AddChild(layer); + + // Make the layer not update. + layer->SetHideLayerAndSubtree(true); + EXPECT_EQ(gfx::Size(500, 500), + layer->GetRecordingSourceForTesting()->GetSize()); + + // Change its bounds while it's in a state that can't update. + layer->SetBounds(gfx::Size(600, 600)); + host2->Composite(base::TimeTicks::Now()); + + // This layer should not have been updated because it is invisible. + EXPECT_EQ(1, layer->update_count()); + EXPECT_EQ(1, host2->SourceFrameNumber()); + + // This layer should also drop its recording source because it was resized + // and not recorded. + EXPECT_EQ(gfx::Size(), layer->GetRecordingSourceForTesting()->GetSize()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/recording_source.cc b/chromium/cc/layers/recording_source.cc index 9b1efbd7137..e3a477a5b83 100644 --- a/chromium/cc/layers/recording_source.cc +++ b/chromium/cc/layers/recording_source.cc @@ -29,7 +29,6 @@ namespace cc { RecordingSource::RecordingSource() : slow_down_raster_scale_factor_for_debug_(0), - generate_discardable_images_metadata_(false), requires_clear_(false), is_solid_color_(false), clear_canvas_with_debug_color_(kDefaultClearCanvasSetting), @@ -56,8 +55,7 @@ void RecordingSource::FinishDisplayItemListUpdate() { TRACE_EVENT0("cc", "RecordingSource::FinishDisplayItemListUpdate"); DetermineIfSolidColor(); display_list_->EmitTraceSnapshot(); - if (generate_discardable_images_metadata_) - display_list_->GenerateDiscardableImagesMetadata(); + display_list_->GenerateDiscardableImagesMetadata(); } void RecordingSource::SetNeedsDisplayRect(const gfx::Rect& layer_rect) { @@ -121,11 +119,6 @@ void RecordingSource::SetSlowdownRasterScaleFactor(int factor) { slow_down_raster_scale_factor_for_debug_ = factor; } -void RecordingSource::SetGenerateDiscardableImagesMetadata( - bool generate_metadata) { - generate_discardable_images_metadata_ = generate_metadata; -} - void RecordingSource::SetBackgroundColor(SkColor background_color) { background_color_ = background_color; } @@ -153,10 +146,10 @@ void RecordingSource::DetermineIfSolidColor() { return; TRACE_EVENT1("cc", "RecordingSource::DetermineIfSolidColor", "opcount", - display_list_->ApproximateOpCount()); + display_list_->OpCount()); gfx::Size layer_size = GetSize(); skia::AnalysisCanvas canvas(layer_size.width(), layer_size.height()); - display_list_->Raster(&canvas, nullptr, gfx::Rect(layer_size), 1.f); + display_list_->Raster(&canvas); is_solid_color_ = canvas.GetColorIfSolid(&solid_color_); } diff --git a/chromium/cc/layers/recording_source.h b/chromium/cc/layers/recording_source.h index dadb0e77548..32550fe2004 100644 --- a/chromium/cc/layers/recording_source.h +++ b/chromium/cc/layers/recording_source.h @@ -46,7 +46,6 @@ class CC_EXPORT RecordingSource { gfx::Size GetSize() const; void SetEmptyBounds(); void SetSlowdownRasterScaleFactor(int factor); - void SetGenerateDiscardableImagesMetadata(bool generate_metadata); void SetBackgroundColor(SkColor background_color); void SetRequiresClear(bool requires_clear); @@ -63,7 +62,6 @@ class CC_EXPORT RecordingSource { gfx::Rect recorded_viewport_; gfx::Size size_; int slow_down_raster_scale_factor_for_debug_; - bool generate_discardable_images_metadata_; bool requires_clear_; bool is_solid_color_; bool clear_canvas_with_debug_color_; diff --git a/chromium/cc/layers/recording_source_unittest.cc b/chromium/cc/layers/recording_source_unittest.cc index 2a034a77125..d090ccdd567 100644 --- a/chromium/cc/layers/recording_source_unittest.cc +++ b/chromium/cc/layers/recording_source_unittest.cc @@ -68,7 +68,6 @@ TEST(RecordingSourceTest, DiscardableImagesWithTransform) { translate_transform); recording_source->add_draw_image_with_transform(discardable_image[1][1], rotate_transform); - recording_source->SetGenerateDiscardableImagesMetadata(true); recording_source->Rerecord(); bool can_use_lcd_text = true; @@ -127,33 +126,11 @@ TEST(RecordingSourceTest, DiscardableImagesWithTransform) { } } -TEST(RecordingSourceTest, NoGatherImageEmptyImages) { - gfx::Rect recorded_viewport(0, 0, 256, 256); - - std::unique_ptr<FakeRecordingSource> recording_source = - CreateRecordingSource(recorded_viewport); - recording_source->SetGenerateDiscardableImagesMetadata(false); - recording_source->Rerecord(); - - scoped_refptr<RasterSource> raster_source = - CreateRasterSource(recording_source.get()); - - // If recording source do not gather images, raster source is not going to - // get images. - { - std::vector<DrawImage> images; - raster_source->GetDiscardableImagesInRect(recorded_viewport, 1.f, - DefaultColorSpace(), &images); - EXPECT_TRUE(images.empty()); - } -} - TEST(RecordingSourceTest, EmptyImages) { gfx::Rect recorded_viewport(0, 0, 256, 256); std::unique_ptr<FakeRecordingSource> recording_source = CreateRecordingSource(recorded_viewport); - recording_source->SetGenerateDiscardableImagesMetadata(true); recording_source->Rerecord(); scoped_refptr<RasterSource> raster_source = @@ -208,7 +185,6 @@ TEST(RecordingSourceTest, NoDiscardableImages) { recording_source->add_draw_image(non_discardable_image, gfx::Point(128, 0)); recording_source->add_draw_image(non_discardable_image, gfx::Point(0, 128)); recording_source->add_draw_image(non_discardable_image, gfx::Point(150, 150)); - recording_source->SetGenerateDiscardableImagesMetadata(true); recording_source->Rerecord(); scoped_refptr<RasterSource> raster_source = @@ -258,7 +234,6 @@ TEST(RecordingSourceTest, DiscardableImages) { recording_source->add_draw_image(discardable_image[1][0], gfx::Point(0, 130)); recording_source->add_draw_image(discardable_image[1][1], gfx::Point(140, 140)); - recording_source->SetGenerateDiscardableImagesMetadata(true); recording_source->Rerecord(); scoped_refptr<RasterSource> raster_source = @@ -331,7 +306,6 @@ TEST(RecordingSourceTest, DiscardableImagesBaseNonDiscardable) { recording_source->add_draw_image(discardable_image[0][1], gfx::Point(260, 0)); recording_source->add_draw_image(discardable_image[1][1], gfx::Point(260, 260)); - recording_source->SetGenerateDiscardableImagesMetadata(true); recording_source->Rerecord(); scoped_refptr<RasterSource> raster_source = diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc index a03fb006fec..e1330f14f8a 100644 --- a/chromium/cc/layers/render_surface_impl.cc +++ b/chromium/cc/layers/render_surface_impl.cc @@ -38,10 +38,12 @@ RenderSurfaceImpl::RenderSurfaceImpl(LayerTreeImpl* layer_tree_impl, : layer_tree_impl_(layer_tree_impl), stable_effect_id_(stable_effect_id), effect_tree_index_(EffectTree::kInvalidNodeId), + num_contributors_(0), has_contributing_layer_that_escapes_clip_(false), surface_property_changed_(false), ancestor_property_changed_(false), contributes_to_drawn_surface_(false), + is_render_surface_list_member_(false), nearest_occlusion_immune_ancestor_(nullptr) { damage_tracker_ = DamageTracker::Create(); } @@ -125,7 +127,7 @@ LayerImpl* RenderSurfaceImpl::MaskLayer() { } bool RenderSurfaceImpl::HasMask() const { - return OwningEffectNode()->mask_layer_id != EffectTree::kInvalidNodeId; + return OwningEffectNode()->mask_layer_id != Layer::INVALID_ID; } const FilterOperations& RenderSurfaceImpl::Filters() const { @@ -160,10 +162,6 @@ int RenderSurfaceImpl::ClipTreeIndex() const { } int RenderSurfaceImpl::EffectTreeIndex() const { - DCHECK_EQ( - effect_tree_index_, - layer_tree_impl_->property_trees() - ->effect_tree.FindNodeIndexFromOwningLayerId(stable_effect_id_)); return effect_tree_index_; } @@ -339,7 +337,7 @@ void RenderSurfaceImpl::NoteAncestorPropertyChanged() { ancestor_property_changed_ = true; } -gfx::Rect RenderSurfaceImpl::GetDamageRect() { +gfx::Rect RenderSurfaceImpl::GetDamageRect() const { gfx::Rect damage_rect; bool is_valid_rect = damage_tracker_->GetDamageRectIfValid(&damage_rect); if (!is_valid_rect) @@ -352,16 +350,12 @@ void RenderSurfaceImpl::ResetPropertyChangedFlags() { ancestor_property_changed_ = false; } -void RenderSurfaceImpl::ClearLayerLists() { - layer_list_.clear(); -} - int RenderSurfaceImpl::GetRenderPassId() { return id(); } std::unique_ptr<RenderPass> RenderSurfaceImpl::CreateRenderPass() { - std::unique_ptr<RenderPass> pass = RenderPass::Create(layer_list_.size()); + std::unique_ptr<RenderPass> pass = RenderPass::Create(num_contributors_); gfx::Rect damage_rect = GetDamageRect(); damage_rect.Intersect(content_rect()); pass->SetNew(id(), content_rect(), damage_rect, @@ -385,7 +379,7 @@ void RenderSurfaceImpl::AppendQuads(RenderPass* render_pass, SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); shared_quad_state->SetAll( - draw_transform(), content_rect().size(), content_rect(), + draw_transform(), content_rect(), content_rect(), draw_properties_.clip_rect, draw_properties_.is_clipped, draw_properties_.draw_opacity, BlendMode(), sorting_context_id); @@ -416,15 +410,18 @@ void RenderSurfaceImpl::AppendQuads(RenderPass* render_pass, TileMaskLayer(render_pass, shared_quad_state, visible_layer_rect); return; } - mask_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size); + gfx::SizeF mask_uv_size; + mask_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size, + &mask_uv_size); gfx::SizeF unclipped_mask_target_size = gfx::ScaleSize( gfx::SizeF(OwningEffectNode()->unscaled_mask_target_size), surface_contents_scale.x(), surface_contents_scale.y()); - // Convert content_rect from target space to normalized space. - // Where unclipped_mask_target_size maps to gfx::Size(1, 1). - mask_uv_rect = gfx::ScaleRect(gfx::RectF(content_rect()), - 1.0f / unclipped_mask_target_size.width(), - 1.0f / unclipped_mask_target_size.height()); + // Convert content_rect from target space to normalized mask UV space. + // Where |unclipped_mask_target_size| maps to |mask_uv_size|. + mask_uv_rect = gfx::ScaleRect( + gfx::RectF(content_rect()), + mask_uv_size.width() / unclipped_mask_target_size.width(), + mask_uv_size.height() / unclipped_mask_target_size.height()); } gfx::RectF tex_coord_rect(gfx::Rect(content_rect().size())); @@ -461,18 +458,18 @@ void RenderSurfaceImpl::TileMaskLayer(RenderPass* render_pass, shared_quad_state->quad_to_target_transform.matrix().preScale( mask_quad_to_surface_contents_scale.x(), mask_quad_to_surface_contents_scale.y(), 1.f); - shared_quad_state->quad_layer_bounds = - gfx::ScaleToCeiledSize(shared_quad_state->quad_layer_bounds, - 1.f / mask_quad_to_surface_contents_scale.x(), - 1.f / mask_quad_to_surface_contents_scale.y()); + shared_quad_state->quad_layer_rect = + gfx::ScaleToEnclosingRect(shared_quad_state->quad_layer_rect, + 1.f / mask_quad_to_surface_contents_scale.x(), + 1.f / mask_quad_to_surface_contents_scale.y()); shared_quad_state->visible_quad_layer_rect = - gfx::ScaleToEnclosedRect(shared_quad_state->visible_quad_layer_rect, - mask_quad_to_surface_contents_scale.x(), - mask_quad_to_surface_contents_scale.y()); - gfx::Rect content_rect_in_coverage_space = gfx::ScaleToEnclosedRect( + gfx::ScaleToEnclosingRect(shared_quad_state->visible_quad_layer_rect, + mask_quad_to_surface_contents_scale.x(), + mask_quad_to_surface_contents_scale.y()); + gfx::Rect content_rect_in_coverage_space = gfx::ScaleToEnclosingRect( content_rect(), 1.f / mask_quad_to_surface_contents_scale.x(), 1.f / mask_quad_to_surface_contents_scale.y()); - gfx::Rect visible_layer_rect_in_coverage_space = gfx::ScaleToEnclosedRect( + gfx::Rect visible_layer_rect_in_coverage_space = gfx::ScaleToEnclosingRect( visible_layer_rect, 1.f / mask_quad_to_surface_contents_scale.x(), 1.f / mask_quad_to_surface_contents_scale.y()); diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h index 5cf15e86af2..f02f3b74e27 100644 --- a/chromium/cc/layers/render_surface_impl.h +++ b/chromium/cc/layers/render_surface_impl.h @@ -104,6 +104,13 @@ class CC_EXPORT RenderSurfaceImpl { return has_contributing_layer_that_escapes_clip_; } + void set_is_render_surface_list_member(bool is_render_surface_list_member) { + is_render_surface_list_member_ = is_render_surface_list_member; + } + bool is_render_surface_list_member() const { + return is_render_surface_list_member_; + } + void CalculateContentRectFromAccumulatedContentRect(int max_texture_size); void SetContentRectToViewport(); void SetContentRectForTesting(const gfx::Rect& rect); @@ -119,6 +126,14 @@ class CC_EXPORT RenderSurfaceImpl { return accumulated_content_rect_; } + void increment_num_contributors() { num_contributors_++; } + void decrement_num_contributors() { + num_contributors_--; + DCHECK_GE(num_contributors_, 0); + } + void reset_num_contributors() { num_contributors_ = 0; } + int num_contributors() const { return num_contributors_; } + const Occlusion& occlusion_in_content_space() const { return occlusion_in_content_space_; } @@ -126,9 +141,6 @@ class CC_EXPORT RenderSurfaceImpl { occlusion_in_content_space_ = occlusion; } - LayerImplList& layer_list() { return layer_list_; } - void ClearLayerLists(); - int id() const { return stable_effect_id_; } LayerImpl* MaskLayer(); @@ -148,7 +160,7 @@ class CC_EXPORT RenderSurfaceImpl { void NoteAncestorPropertyChanged(); DamageTracker* damage_tracker() const { return damage_tracker_.get(); } - gfx::Rect GetDamageRect(); + gfx::Rect GetDamageRect() const; int GetRenderPassId(); @@ -204,14 +216,15 @@ class CC_EXPORT RenderSurfaceImpl { // Is used to calculate the content rect from property trees. gfx::Rect accumulated_content_rect_; + int num_contributors_; // Is used to decide if the surface is clipped. bool has_contributing_layer_that_escapes_clip_ : 1; bool surface_property_changed_ : 1; bool ancestor_property_changed_ : 1; bool contributes_to_drawn_surface_ : 1; + bool is_render_surface_list_member_ : 1; - LayerImplList layer_list_; Occlusion occlusion_in_content_space_; // The nearest ancestor target surface that will contain the contents of this diff --git a/chromium/cc/layers/render_surface_impl_unittest.cc b/chromium/cc/layers/render_surface_impl_unittest.cc index 49f2f7d6eb9..6422ffa19fd 100644 --- a/chromium/cc/layers/render_surface_impl_unittest.cc +++ b/chromium/cc/layers/render_surface_impl_unittest.cc @@ -29,8 +29,7 @@ TEST(RenderSurfaceLayerImplTest, Occlusion) { impl.CalcDrawProps(viewport_size); - RenderSurfaceImpl* render_surface_impl = - owning_layer_impl->GetRenderSurface(); + RenderSurfaceImpl* render_surface_impl = GetRenderSurface(owning_layer_impl); ASSERT_TRUE(render_surface_impl); { @@ -69,6 +68,7 @@ TEST(RenderSurfaceLayerImplTest, Occlusion) { TEST(RenderSurfaceLayerImplTest, AppendQuadsWithScaledMask) { gfx::Size layer_size(1000, 1000); gfx::Size viewport_size(1000, 1000); + float scale_factor = 2; scoped_refptr<FakeRasterSource> raster_source = FakeRasterSource::CreateFilledSolidColor(layer_size); @@ -83,14 +83,17 @@ TEST(RenderSurfaceLayerImplTest, AppendQuadsWithScaledMask) { surface->test_properties()->force_render_surface = true; gfx::Transform scale; - scale.Scale(2, 2); + scale.Scale(scale_factor, scale_factor); surface->test_properties()->transform = scale; - surface->test_properties()->SetMaskLayer(FakeMaskLayerImpl::Create( + std::unique_ptr<FakeMaskLayerImpl> mask_layer = FakeMaskLayerImpl::Create( impl.host_impl()->active_tree(), 4, raster_source, - Layer::LayerMaskType::SINGLE_TEXTURE_MASK)); - surface->test_properties()->mask_layer->SetDrawsContent(true); - surface->test_properties()->mask_layer->SetBounds(layer_size); + Layer::LayerMaskType::SINGLE_TEXTURE_MASK); + mask_layer->set_resource_size( + gfx::ScaleToCeiledSize(layer_size, scale_factor)); + mask_layer->SetDrawsContent(true); + mask_layer->SetBounds(layer_size); + surface->test_properties()->SetMaskLayer(std::move(mask_layer)); std::unique_ptr<LayerImpl> child = LayerImpl::Create(impl.host_impl()->active_tree(), 5); @@ -110,7 +113,7 @@ TEST(RenderSurfaceLayerImplTest, AppendQuadsWithScaledMask) { ->root_layer_for_testing() ->test_properties() ->children[0]; - RenderSurfaceImpl* render_surface_impl = surface_raw->GetRenderSurface(); + RenderSurfaceImpl* render_surface_impl = GetRenderSurface(surface_raw); std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); AppendQuadsData append_quads_data; render_surface_impl->AppendQuads(render_pass.get(), &append_quads_data); diff --git a/chromium/cc/layers/render_surface_unittest.cc b/chromium/cc/layers/render_surface_unittest.cc index a8662d8aab0..05412a98f24 100644 --- a/chromium/cc/layers/render_surface_unittest.cc +++ b/chromium/cc/layers/render_surface_unittest.cc @@ -10,6 +10,7 @@ #include "cc/test/fake_impl_task_runner_provider.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" +#include "cc/test/layer_test_common.h" #include "cc/test/mock_occlusion_tracker.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_impl.h" @@ -53,7 +54,7 @@ TEST(RenderSurfaceTest, VerifySurfaceChangesAreTrackedProperly) { host_impl.active_tree()->UpdateDrawProperties(false /* update_lcd_text */); RenderSurfaceImpl* render_surface = - host_impl.active_tree()->root_layer_for_testing()->GetRenderSurface(); + GetRenderSurface(host_impl.active_tree()->root_layer_for_testing()); ASSERT_TRUE(render_surface); // Currently, the content_rect, clip_rect, and @@ -86,7 +87,6 @@ TEST(RenderSurfaceTest, VerifySurfaceChangesAreTrackedProperly) { render_surface->SetDrawOpacity(0.5f)); EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE( render_surface->SetDrawTransform(dummy_matrix)); - EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(render_surface->ClearLayerLists()); } TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectSharedQuadState) { @@ -114,9 +114,9 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectSharedQuadState) { host_impl.active_tree()->UpdateDrawProperties(false /* update_lcd_text */); ASSERT_TRUE( - host_impl.active_tree()->LayerById(owning_layer_id)->GetRenderSurface()); + GetRenderSurface(host_impl.active_tree()->LayerById(owning_layer_id))); RenderSurfaceImpl* render_surface = - host_impl.active_tree()->LayerById(owning_layer_id)->GetRenderSurface(); + GetRenderSurface(host_impl.active_tree()->LayerById(owning_layer_id)); gfx::Rect content_rect(0, 0, 50, 50); gfx::Rect clip_rect(5, 5, 40, 40); @@ -172,9 +172,9 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectRenderPass) { host_impl.active_tree()->UpdateDrawProperties(false /* update_lcd_text */); ASSERT_TRUE( - host_impl.active_tree()->LayerById(owning_layer_id)->GetRenderSurface()); + GetRenderSurface(host_impl.active_tree()->LayerById(owning_layer_id))); RenderSurfaceImpl* render_surface = - host_impl.active_tree()->LayerById(owning_layer_id)->GetRenderSurface(); + GetRenderSurface(host_impl.active_tree()->LayerById(owning_layer_id)); gfx::Rect content_rect(0, 0, 50, 50); gfx::Transform origin; diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.cc b/chromium/cc/layers/scrollbar_layer_impl_base.cc index da53036fea0..681a0401f42 100644 --- a/chromium/cc/layers/scrollbar_layer_impl_base.cc +++ b/chromium/cc/layers/scrollbar_layer_impl_base.cc @@ -18,7 +18,6 @@ ScrollbarLayerImplBase::ScrollbarLayerImplBase( bool is_left_side_vertical_scrollbar, bool is_overlay) : LayerImpl(tree_impl, id), - scroll_layer_id_(Layer::INVALID_ID), is_overlay_scrollbar_(is_overlay), thumb_thickness_scale_factor_(1.f), current_pos_(0.f), @@ -36,21 +35,19 @@ void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) { LayerImpl::PushPropertiesTo(layer); DCHECK(layer->ToScrollbarLayer()); layer->ToScrollbarLayer()->set_is_overlay_scrollbar(is_overlay_scrollbar_); - layer->ToScrollbarLayer()->SetScrollLayerId(ScrollLayerId()); + layer->ToScrollbarLayer()->SetScrollElementId(scroll_element_id()); } ScrollbarLayerImplBase* ScrollbarLayerImplBase::ToScrollbarLayer() { return this; } -void ScrollbarLayerImplBase::SetScrollLayerId(int scroll_layer_id) { - if (scroll_layer_id_ == scroll_layer_id) +void ScrollbarLayerImplBase::SetScrollElementId(ElementId scroll_element_id) { + if (scroll_element_id_ == scroll_element_id) return; layer_tree_impl()->UnregisterScrollbar(this); - - scroll_layer_id_ = scroll_layer_id; - + scroll_element_id_ = scroll_element_id; layer_tree_impl()->RegisterScrollbar(this); } @@ -63,10 +60,18 @@ bool ScrollbarLayerImplBase::SetCurrentPos(float current_pos) { } bool ScrollbarLayerImplBase::CanScrollOrientation() const { - LayerImpl* scroll_layer = layer_tree_impl()->LayerById(scroll_layer_id_); + // TODO(pdr): Refactor this to not depend on layers by using the associated + // scroll node's user_scrollable values. + LayerImpl* scroll_layer = + layer_tree_impl()->LayerByElementId(scroll_element_id_); if (!scroll_layer) return false; + return scroll_layer->user_scrollable(orientation()) && + // Ensure clip_layer_length_ smaller than scroll_layer_length_ not + // caused by floating error. + !MathUtil::IsFloatNearlyTheSame(clip_layer_length_, + scroll_layer_length_) && clip_layer_length_ < scroll_layer_length_; } @@ -102,7 +107,8 @@ bool ScrollbarLayerImplBase::SetThumbThicknessScaleFactor(float factor) { return true; } -gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const { +gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRectWithThumbThicknessScale( + float thumb_thickness_scale_factor) const { // Thumb extent is the length of the thumb in the scrolling direction, thumb // thickness is in the perpendicular direction. Here's an example of a // horizontal scrollbar - inputs are above the scrollbar, computed values @@ -180,7 +186,7 @@ gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const { } float thumb_thickness_adjustment = - thumb_thickness * (1.f - thumb_thickness_scale_factor_); + thumb_thickness * (1.f - thumb_thickness_scale_factor); gfx::RectF thumb_rect; if (orientation_ == HORIZONTAL) { @@ -201,6 +207,16 @@ gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const { return gfx::ToEnclosingRect(thumb_rect); } +gfx::Rect ScrollbarLayerImplBase::ComputeExpandedThumbQuadRect() const { + DCHECK(is_overlay_scrollbar()); + return ComputeThumbQuadRectWithThumbThicknessScale(1.f); +} + +gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const { + return ComputeThumbQuadRectWithThumbThicknessScale( + thumb_thickness_scale_factor_); +} + void ScrollbarLayerImplBase::SetOverlayScrollbarLayerOpacityAnimated( float opacity) { DCHECK(is_overlay_scrollbar()); @@ -208,23 +224,11 @@ void ScrollbarLayerImplBase::SetOverlayScrollbarLayerOpacityAnimated( return; PropertyTrees* property_trees = layer_tree_impl()->property_trees(); - int effect_node_index = - property_trees->effect_tree.FindNodeIndexFromOwningLayerId(id()); - // If this method is called during LayerImpl::PushPropertiesTo, we may not yet - // have valid owning_layer_id_to_node_index entries in effect tree as property - // trees are pushed after layers during activation. We can skip updating - // opacity in that case as we are only registering a scrollbar and because - // opacity will be overwritten anyway when property trees are pushed. - if (effect_node_index == EffectTree::kInvalidNodeId || - effect_node_index != effect_tree_index()) - return; EffectNode* node = property_trees->effect_tree.Node(effect_tree_index()); if (node->opacity == opacity) return; - layer_tree_impl()->AddToOpacityAnimationsMap(id(), opacity); - node->opacity = opacity; node->effect_changed = true; property_trees->changed = true; @@ -232,4 +236,9 @@ void ScrollbarLayerImplBase::SetOverlayScrollbarLayerOpacityAnimated( layer_tree_impl()->set_needs_update_draw_properties(); } +LayerTreeSettings::ScrollbarAnimator +ScrollbarLayerImplBase::GetScrollbarAnimator() const { + return layer_tree_impl()->settings().scrollbar_animator; +} + } // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.h b/chromium/cc/layers/scrollbar_layer_impl_base.h index 0b9b403950b..e6bb9b4e482 100644 --- a/chromium/cc/layers/scrollbar_layer_impl_base.h +++ b/chromium/cc/layers/scrollbar_layer_impl_base.h @@ -5,11 +5,13 @@ #ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_ #define CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_ +#include "base/containers/flat_set.h" #include "base/macros.h" #include "cc/cc_export.h" #include "cc/input/scrollbar.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" +#include "cc/trees/layer_tree_settings.h" namespace cc { @@ -17,9 +19,8 @@ class LayerTreeImpl; class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { public: - int ScrollLayerId() const { return scroll_layer_id_; } - - void SetScrollLayerId(int scroll_layer_id); + ElementId scroll_element_id() const { return scroll_element_id_; } + void SetScrollElementId(ElementId scroll_element_id); float current_pos() const { return current_pos_; } bool SetCurrentPos(float current_pos); @@ -47,7 +48,8 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { ScrollbarLayerImplBase* ToScrollbarLayer() override; // Thumb quad rect in layer space. - virtual gfx::Rect ComputeThumbQuadRect() const; + gfx::Rect ComputeThumbQuadRect() const; + gfx::Rect ComputeExpandedThumbQuadRect() const; float thumb_thickness_scale_factor() { return thumb_thickness_scale_factor_; @@ -60,6 +62,8 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { // on overlay scrollbar layers. void SetOverlayScrollbarLayerOpacityAnimated(float opacity); + virtual LayerTreeSettings::ScrollbarAnimator GetScrollbarAnimator() const; + protected: ScrollbarLayerImplBase(LayerTreeImpl* tree_impl, int id, @@ -76,7 +80,10 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { virtual bool IsThumbResizable() const = 0; private: - int scroll_layer_id_; + gfx::Rect ComputeThumbQuadRectWithThumbThicknessScale( + float thumb_thickness_scale_factor) const; + + ElementId scroll_element_id_; bool is_overlay_scrollbar_; float thumb_thickness_scale_factor_; @@ -93,7 +100,7 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerImplBase); }; -typedef std::set<ScrollbarLayerImplBase*> ScrollbarSet; +using ScrollbarSet = base::flat_set<ScrollbarLayerImplBase*>; } // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_interface.h b/chromium/cc/layers/scrollbar_layer_interface.h index 30c335ba011..7839c8e68cb 100644 --- a/chromium/cc/layers/scrollbar_layer_interface.h +++ b/chromium/cc/layers/scrollbar_layer_interface.h @@ -13,8 +13,8 @@ namespace cc { class CC_EXPORT ScrollbarLayerInterface { public: - virtual int ScrollLayerId() const = 0; - virtual void SetScrollLayer(int layer_id) = 0; + virtual ElementId scroll_element_id() const = 0; + virtual void SetScrollElementId(ElementId element_id) = 0; virtual ScrollbarOrientation orientation() const = 0; diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc index 3e6e56ec142..ff5d38a5f55 100644 --- a/chromium/cc/layers/scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/scrollbar_layer_unittest.cc @@ -26,6 +26,7 @@ #include "cc/test/fake_painted_scrollbar_layer.h" #include "cc/test/fake_scrollbar.h" #include "cc/test/geometry_test_utils.h" +#include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" #include "cc/test/mock_occlusion_tracker.h" #include "cc/test/stub_layer_tree_host_single_thread_client.h" @@ -150,10 +151,10 @@ class ScrollbarLayerTest : public testing::Test { const bool kIsLeftSideVerticalScrollbar = false; child2 = SolidColorScrollbarLayer::Create( scrollbar->Orientation(), thumb_thickness, track_start, - kIsLeftSideVerticalScrollbar, child1->id()); + kIsLeftSideVerticalScrollbar, child1->element_id()); } else { - child2 = - PaintedScrollbarLayer::Create(std::move(scrollbar), child1->id()); + child2 = PaintedScrollbarLayer::Create(std::move(scrollbar), + child1->element_id()); } layer_tree_root->AddChild(child1); layer_tree_root->InsertChild(child2, reverse_order ? 0 : 1); @@ -197,7 +198,7 @@ TEST_F(ScrollbarLayerTest, RepaintOverlayWhenResourceDisposed) { FakePaintedOverlayScrollbar* fake_scrollbar = scrollbar.get(); scoped_refptr<PaintedOverlayScrollbarLayer> scrollbar_layer = PaintedOverlayScrollbarLayer::Create(std::move(scrollbar), - layer_tree_root->id()); + layer_tree_root->element_id()); // Setup. { @@ -209,7 +210,6 @@ TEST_F(ScrollbarLayerTest, RepaintOverlayWhenResourceDisposed) { layer_tree_root->SetBounds(gfx::Size(100, 200)); content_layer->SetBounds(gfx::Size(100, 200)); scrollbar_layer->set_visible_layer_rect(gfx::Rect(0, 0, 100, 200)); - scrollbar_layer->SavePaintProperties(); } // First call to update should create a resource. The scrollbar itself thinks @@ -290,9 +290,10 @@ TEST_F(ScrollbarLayerTest, ScrollOffsetSynchronization) { std::unique_ptr<Scrollbar> scrollbar(new FakeScrollbar); scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> scroll_layer = Layer::Create(); + scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id())); scoped_refptr<Layer> content_layer = Layer::Create(); - scoped_refptr<Layer> scrollbar_layer = - PaintedScrollbarLayer::Create(std::move(scrollbar), scroll_layer->id()); + scoped_refptr<Layer> scrollbar_layer = PaintedScrollbarLayer::Create( + std::move(scrollbar), scroll_layer->element_id()); // Choose bounds to give max_scroll_offset = (30, 50). layer_tree_root->SetBounds(gfx::Size(70, 150)); @@ -306,8 +307,6 @@ TEST_F(ScrollbarLayerTest, ScrollOffsetSynchronization) { scroll_layer->AddChild(content_layer); layer_tree_root->AddChild(scrollbar_layer); - layer_tree_root->SavePaintProperties(); - content_layer->SavePaintProperties(); layer_tree_host_->UpdateLayers(); LayerImpl* layer_impl_tree_root = layer_tree_host_->CommitAndCreateLayerImplTree(); @@ -322,12 +321,9 @@ TEST_F(ScrollbarLayerTest, ScrollOffsetSynchronization) { cc_scrollbar_layer->clip_layer_length()); layer_tree_root->SetBounds(gfx::Size(700, 1500)); - layer_tree_root->SavePaintProperties(); scroll_layer->SetBounds(gfx::Size(1000, 2000)); scroll_layer->SetScrollOffset(gfx::ScrollOffset(100, 200)); - scroll_layer->SavePaintProperties(); content_layer->SetBounds(gfx::Size(1000, 2000)); - content_layer->SavePaintProperties(); layer_tree_host_->UpdateLayers(); layer_impl_tree_root = layer_tree_host_->CommitAndCreateLayerImplTree(); @@ -360,7 +356,7 @@ TEST_F(ScrollbarLayerTest, UpdatePropertiesOfScrollBarWhenThumbRemoved) { scoped_refptr<Layer> root_layer = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = - FakePaintedScrollbarLayer::Create(false, true, root_layer->id()); + FakePaintedScrollbarLayer::Create(false, true, root_layer->element_id()); root_layer->SetScrollClipLayerId(root_clip_layer->id()); // Give the root-clip a size that will result in MaxScrollOffset = (80, 0). @@ -375,7 +371,7 @@ TEST_F(ScrollbarLayerTest, UpdatePropertiesOfScrollBarWhenThumbRemoved) { root_layer->SetScrollOffset(gfx::ScrollOffset(0, 0)); scrollbar_layer->SetBounds(gfx::Size(70, 10)); - scrollbar_layer->SetScrollLayer(root_layer->id()); + scrollbar_layer->SetScrollElementId(root_layer->element_id()); scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10)); scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10)); scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10); @@ -383,6 +379,7 @@ TEST_F(ScrollbarLayerTest, UpdatePropertiesOfScrollBarWhenThumbRemoved) { LayerImpl* root_clip_layer_impl = nullptr; PaintedScrollbarLayerImpl* scrollbar_layer_impl = nullptr; + layer_tree_host_->BuildPropertyTreesForTesting(); UPDATE_AND_EXTRACT_LAYER_POINTERS(); EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(), scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); @@ -399,8 +396,9 @@ TEST_F(ScrollbarLayerTest, ThumbRect) { scoped_refptr<Layer> root_layer = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = - FakePaintedScrollbarLayer::Create(false, true, root_layer->id()); + FakePaintedScrollbarLayer::Create(false, true, root_layer->element_id()); + root_layer->SetElementId(LayerIdToElementIdForTesting(root_layer->id())); root_layer->SetScrollClipLayerId(root_clip_layer->id()); // Give the root-clip a size that will result in MaxScrollOffset = (80, 0). root_clip_layer->SetBounds(gfx::Size(20, 50)); @@ -414,7 +412,7 @@ TEST_F(ScrollbarLayerTest, ThumbRect) { root_layer->SetScrollOffset(gfx::ScrollOffset(0, 0)); scrollbar_layer->SetBounds(gfx::Size(70, 10)); - scrollbar_layer->SetScrollLayer(root_layer->id()); + scrollbar_layer->SetScrollElementId(root_layer->element_id()); scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10)); scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10)); scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10); @@ -439,6 +437,7 @@ TEST_F(ScrollbarLayerTest, ThumbRect) { // Over-scroll (thumb position should clamp on the far side). root_layer->SetScrollOffset(gfx::ScrollOffset(85, 0)); + layer_tree_host_->UpdateLayers(); UPDATE_AND_EXTRACT_LAYER_POINTERS(); EXPECT_EQ(gfx::Rect(56, 0, 4, 10).ToString(), @@ -477,7 +476,7 @@ TEST_F(ScrollbarLayerTest, ThumbRectForOverlayLeftSideVerticalScrollbar) { // Create an overlay left side vertical scrollbar. scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = FakePaintedScrollbarLayer::Create(false, true, VERTICAL, true, true, - root_layer->id()); + root_layer->element_id()); root_layer->SetScrollClipLayerId(root_clip_layer->id()); root_clip_layer->SetBounds(gfx::Size(50, 20)); root_layer->SetBounds(gfx::Size(50, 100)); @@ -488,7 +487,7 @@ TEST_F(ScrollbarLayerTest, ThumbRectForOverlayLeftSideVerticalScrollbar) { root_layer->SetScrollOffset(gfx::ScrollOffset(0, 0)); scrollbar_layer->SetBounds(gfx::Size(10, 20)); - scrollbar_layer->SetScrollLayer(root_layer->id()); + scrollbar_layer->SetScrollElementId(root_layer->element_id()); scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(0, 0, 10, 20)); scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10); scrollbar_layer->fake_scrollbar()->set_thumb_length(4); @@ -591,13 +590,14 @@ TEST_F(ScrollbarLayerTest, LayerDrivenSolidColorDrawQuads) { scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> scroll_layer = Layer::Create(); + scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id())); scroll_layer->SetScrollClipLayerId(layer_tree_root->id()); scoped_refptr<Layer> child1 = Layer::Create(); scoped_refptr<Layer> child2; const bool kIsLeftSideVerticalScrollbar = false; child2 = SolidColorScrollbarLayer::Create( scrollbar->Orientation(), kThumbThickness, kTrackStart, - kIsLeftSideVerticalScrollbar, scroll_layer->id()); + kIsLeftSideVerticalScrollbar, scroll_layer->element_id()); scroll_layer->AddChild(child1); scroll_layer->InsertChild(child2, 1); layer_tree_root->AddChild(scroll_layer); @@ -644,16 +644,19 @@ TEST_F(ScrollbarLayerTest, ScrollbarLayerOpacity) { scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> scroll_layer = Layer::Create(); scroll_layer->SetScrollClipLayerId(layer_tree_root->id()); + scroll_layer->SetElementId(ElementId(200)); scoped_refptr<Layer> child1 = Layer::Create(); - scoped_refptr<Layer> scrollbar_layer; + scoped_refptr<SolidColorScrollbarLayer> scrollbar_layer; const bool kIsLeftSideVerticalScrollbar = false; scrollbar_layer = SolidColorScrollbarLayer::Create( scrollbar->Orientation(), kThumbThickness, kTrackStart, - kIsLeftSideVerticalScrollbar, scroll_layer->id()); + kIsLeftSideVerticalScrollbar, scroll_layer->element_id()); + scrollbar_layer->SetElementId(ElementId(300)); scroll_layer->AddChild(child1); scroll_layer->InsertChild(scrollbar_layer, 1); layer_tree_root->AddChild(scroll_layer); layer_tree_host_->SetRootLayer(layer_tree_root); + scrollbar_layer->SetScrollElementId(scroll_layer->element_id()); // Choose layer bounds to give max_scroll_offset = (8, 8). layer_tree_root->SetBounds(gfx::Size(2, 2)); @@ -662,14 +665,8 @@ TEST_F(ScrollbarLayerTest, ScrollbarLayerOpacity) { // Building property trees twice shouldn't change the size of // PropertyTrees::always_use_active_tree_opacity_effect_ids. layer_tree_host_->BuildPropertyTreesForTesting(); - EXPECT_EQ(layer_tree_host_->property_trees() - ->always_use_active_tree_opacity_effect_ids.size(), - 1u); layer_tree_host_->property_trees()->needs_rebuild = true; layer_tree_host_->BuildPropertyTreesForTesting(); - EXPECT_EQ(layer_tree_host_->property_trees() - ->always_use_active_tree_opacity_effect_ids.size(), - 1u); // A solid color scrollbar layer's opacity is initialized to 0 on main thread layer_tree_host_->UpdateLayers(); @@ -724,14 +721,14 @@ TEST_F(ScrollbarLayerTest, ScrollbarLayerPushProperties) { scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> scroll_layer = Layer::Create(); + scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id())); scroll_layer->SetScrollClipLayerId(layer_tree_root->id()); scoped_refptr<Layer> child1 = Layer::Create(); scoped_refptr<Layer> scrollbar_layer; const bool kIsLeftSideVerticalScrollbar = false; scrollbar_layer = SolidColorScrollbarLayer::Create( scrollbar->Orientation(), kThumbThickness, kTrackStart, - kIsLeftSideVerticalScrollbar, scroll_layer->id()); - scroll_layer->SetScrollClipLayerId(layer_tree_root->id()); + kIsLeftSideVerticalScrollbar, scroll_layer->element_id()); scroll_layer->AddChild(child1); scroll_layer->InsertChild(scrollbar_layer, 1); layer_tree_root->AddChild(scroll_layer); @@ -740,11 +737,15 @@ TEST_F(ScrollbarLayerTest, ScrollbarLayerPushProperties) { layer_tree_root->SetBounds(gfx::Size(2, 2)); scroll_layer->SetBounds(gfx::Size(10, 10)); layer_tree_host_->UpdateLayers(); - layer_tree_host_->CommitAndCreateLayerImplTree(); LayerTreeHostImpl* host_impl = layer_tree_host_->host_impl(); - EXPECT_TRUE(host_impl->ScrollbarAnimationControllerForId(scroll_layer->id())); + host_impl->CreatePendingTree(); + layer_tree_host_->CommitAndCreatePendingTree(); + host_impl->ActivateSyncTree(); + EXPECT_TRUE(host_impl->ScrollbarAnimationControllerForElementId( + scroll_layer->element_id())); scroll_layer->SetBounds(gfx::Size(20, 20)); + scroll_layer->ShowScrollbars(); scroll_layer->SetForceRenderSurfaceForTesting(true); layer_tree_host_->UpdateLayers(); host_impl->CreatePendingTree(); @@ -756,6 +757,44 @@ TEST_F(ScrollbarLayerTest, ScrollbarLayerPushProperties) { EXPECT_EQ(node->opacity, 1.f); } +TEST_F(ScrollbarLayerTest, SubPixelCanScrollOrientation) { + gfx::Size viewport_size(980, 980); + + LayerTestCommon::LayerImplTest impl; + + LayerImpl* clip_layer = impl.AddChildToRoot<LayerImpl>(); + LayerImpl* scroll_layer = impl.AddChild<LayerImpl>(clip_layer); + + scroll_layer->SetScrollClipLayer(clip_layer->id()); + scroll_layer->SetElementId(LayerIdToElementIdForTesting(scroll_layer->id())); + + const int kTrackStart = 0; + const int kThumbThickness = 10; + const bool kIsLeftSideVerticalScrollbar = false; + const bool kIsOverlayScrollbar = false; + + SolidColorScrollbarLayerImpl* scrollbar_layer = + impl.AddChild<SolidColorScrollbarLayerImpl>( + scroll_layer, HORIZONTAL, kThumbThickness, kTrackStart, + kIsLeftSideVerticalScrollbar, kIsOverlayScrollbar); + + scrollbar_layer->SetScrollElementId(scroll_layer->element_id()); + clip_layer->SetBounds(gfx::Size(980, 980)); + scroll_layer->SetBounds(gfx::Size(980, 980)); + + impl.CalcDrawProps(viewport_size); + + // Fake clip layer length to scrollbar to mock rounding error. + scrollbar_layer->SetClipLayerLength(979.999939f); + + EXPECT_FALSE(scrollbar_layer->CanScrollOrientation()); + + // Fake clip layer length to scrollable. + scrollbar_layer->SetClipLayerLength(979.0f); + + EXPECT_TRUE(scrollbar_layer->CanScrollOrientation()); +} + class ScrollbarLayerSolidColorThumbTest : public testing::Test { public: ScrollbarLayerSolidColorThumbTest() { @@ -868,6 +907,7 @@ class ScrollbarLayerTestResourceCreationAndRelease : public ScrollbarLayerTest { int expected_deleted, bool use_solid_color_scrollbar) { std::unique_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false)); + scoped_refptr<Layer> root_clip_layer = Layer::Create(); scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); scoped_refptr<Layer> scrollbar_layer; @@ -877,10 +917,10 @@ class ScrollbarLayerTestResourceCreationAndRelease : public ScrollbarLayerTest { const bool kIsLeftSideVerticalScrollbar = false; scrollbar_layer = SolidColorScrollbarLayer::Create( scrollbar->Orientation(), kThumbThickness, kTrackStart, - kIsLeftSideVerticalScrollbar, layer_tree_root->id()); + kIsLeftSideVerticalScrollbar, layer_tree_root->element_id()); } else { - scrollbar_layer = PaintedScrollbarLayer::Create(std::move(scrollbar), - layer_tree_root->id()); + scrollbar_layer = PaintedScrollbarLayer::Create( + std::move(scrollbar), layer_tree_root->element_id()); } layer_tree_root->AddChild(content_layer); layer_tree_root->AddChild(scrollbar_layer); @@ -889,6 +929,7 @@ class ScrollbarLayerTestResourceCreationAndRelease : public ScrollbarLayerTest { scrollbar_layer->SetIsDrawable(true); scrollbar_layer->SetBounds(gfx::Size(100, 100)); + layer_tree_root->SetScrollClipLayerId(root_clip_layer->id()); layer_tree_root->SetScrollOffset(gfx::ScrollOffset(10, 20)); layer_tree_root->SetBounds(gfx::Size(100, 200)); content_layer->SetBounds(gfx::Size(100, 200)); @@ -898,7 +939,6 @@ class ScrollbarLayerTestResourceCreationAndRelease : public ScrollbarLayerTest { EXPECT_EQ(scrollbar_layer->GetLayerTreeHostForTesting(), layer_tree_host_.get()); - scrollbar_layer->SavePaintProperties(); for (int update_counter = 0; update_counter < num_updates; update_counter++) scrollbar_layer->Update(); @@ -939,7 +979,8 @@ TEST_F(ScrollbarLayerTestResourceCreationAndRelease, TestResourceUpdate) { scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = - FakePaintedScrollbarLayer::Create(false, true, layer_tree_root->id()); + FakePaintedScrollbarLayer::Create(false, true, + layer_tree_root->element_id()); layer_tree_root->AddChild(content_layer); layer_tree_root->AddChild(scrollbar_layer); @@ -959,7 +1000,6 @@ TEST_F(ScrollbarLayerTestResourceCreationAndRelease, TestResourceUpdate) { size_t resource_count; int expected_created, expected_deleted; - scrollbar_layer->SavePaintProperties(); resource_count = 2; expected_created = 2; @@ -1099,7 +1139,8 @@ class ScaledScrollbarLayerTestResourceCreation : public ScrollbarLayerTest { scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = - FakePaintedScrollbarLayer::Create(false, true, layer_tree_root->id()); + FakePaintedScrollbarLayer::Create(false, true, + layer_tree_root->element_id()); layer_tree_root->AddChild(content_layer); layer_tree_root->AddChild(scrollbar_layer); @@ -1119,7 +1160,6 @@ class ScaledScrollbarLayerTestResourceCreation : public ScrollbarLayerTest { layer_tree_host_->SetDeviceScaleFactor(test_scale); - scrollbar_layer->SavePaintProperties(); scrollbar_layer->Update(); // Verify that we have not generated any content uploads that are larger @@ -1169,7 +1209,7 @@ class ScaledScrollbarLayerTestScaledRasterization : public ScrollbarLayerTest { scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = FakePaintedScrollbarLayer::Create(paint_during_update, has_thumb, - layer_tree_root->id()); + layer_tree_root->element_id()); layer_tree_root->AddChild(scrollbar_layer); @@ -1184,7 +1224,6 @@ class ScaledScrollbarLayerTestScaledRasterization : public ScrollbarLayerTest { layer_tree_host_->SetDeviceScaleFactor(test_scale); gfx::Rect screen_space_clip_rect; - scrollbar_layer->SavePaintProperties(); scrollbar_layer->Update(); @@ -1193,10 +1232,8 @@ class ScaledScrollbarLayerTestScaledRasterization : public ScrollbarLayerTest { DCHECK(bitmap); - AutoLockUIResourceBitmap locked_bitmap(*bitmap); - const SkColor* pixels = - reinterpret_cast<const SkColor*>(locked_bitmap.GetPixels()); + reinterpret_cast<const SkColor*>(bitmap->GetPixels()); SkColor color = argb_to_skia( scrollbar_layer->fake_scrollbar()->paint_fill_color()); int width = bitmap->GetSize().width(); diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.cc b/chromium/cc/layers/solid_color_scrollbar_layer.cc index 2eb5484c855..926f4efee38 100644 --- a/chromium/cc/layers/solid_color_scrollbar_layer.cc +++ b/chromium/cc/layers/solid_color_scrollbar_layer.cc @@ -27,10 +27,10 @@ scoped_refptr<SolidColorScrollbarLayer> SolidColorScrollbarLayer::Create( int thumb_thickness, int track_start, bool is_left_side_vertical_scrollbar, - int scroll_layer_id) { + ElementId scroll_element_id) { return make_scoped_refptr(new SolidColorScrollbarLayer( orientation, thumb_thickness, track_start, - is_left_side_vertical_scrollbar, scroll_layer_id)); + is_left_side_vertical_scrollbar, scroll_element_id)); } SolidColorScrollbarLayer::SolidColorScrollbarLayerInputs:: @@ -38,8 +38,8 @@ SolidColorScrollbarLayer::SolidColorScrollbarLayerInputs:: int thumb_thickness, int track_start, bool is_left_side_vertical_scrollbar, - int scroll_layer_id) - : scroll_layer_id(scroll_layer_id), + ElementId scroll_element_id) + : scroll_element_id(scroll_element_id), orientation(orientation), thumb_thickness(thumb_thickness), track_start(track_start), @@ -53,12 +53,12 @@ SolidColorScrollbarLayer::SolidColorScrollbarLayer( int thumb_thickness, int track_start, bool is_left_side_vertical_scrollbar, - int scroll_layer_id) + ElementId scroll_element_id) : solid_color_scrollbar_layer_inputs_(orientation, thumb_thickness, track_start, is_left_side_vertical_scrollbar, - scroll_layer_id) { + scroll_element_id) { Layer::SetOpacity(0.f); } @@ -79,8 +79,8 @@ void SolidColorScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { SolidColorScrollbarLayerImpl* scrollbar_layer = static_cast<SolidColorScrollbarLayerImpl*>(layer); - scrollbar_layer->SetScrollLayerId( - solid_color_scrollbar_layer_inputs_.scroll_layer_id); + scrollbar_layer->SetScrollElementId( + solid_color_scrollbar_layer_inputs_.scroll_element_id); } void SolidColorScrollbarLayer::SetNeedsDisplayRect(const gfx::Rect& rect) { @@ -91,19 +91,15 @@ bool SolidColorScrollbarLayer::OpacityCanAnimateOnImplThread() const { return true; } -bool SolidColorScrollbarLayer::AlwaysUseActiveTreeOpacity() const { - return true; -} - -int SolidColorScrollbarLayer::ScrollLayerId() const { - return solid_color_scrollbar_layer_inputs_.scroll_layer_id; +ElementId SolidColorScrollbarLayer::scroll_element_id() const { + return solid_color_scrollbar_layer_inputs_.scroll_element_id; } -void SolidColorScrollbarLayer::SetScrollLayer(int layer_id) { - if (layer_id == solid_color_scrollbar_layer_inputs_.scroll_layer_id) +void SolidColorScrollbarLayer::SetScrollElementId(ElementId element_id) { + if (element_id == solid_color_scrollbar_layer_inputs_.scroll_element_id) return; - solid_color_scrollbar_layer_inputs_.scroll_layer_id = layer_id; + solid_color_scrollbar_layer_inputs_.scroll_element_id = element_id; SetNeedsFullTreeSync(); } diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.h b/chromium/cc/layers/solid_color_scrollbar_layer.h index 319674c22ad..c3994b38349 100644 --- a/chromium/cc/layers/solid_color_scrollbar_layer.h +++ b/chromium/cc/layers/solid_color_scrollbar_layer.h @@ -22,11 +22,10 @@ class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface, int thumb_thickness, int track_start, bool is_left_side_vertical_scrollbar, - int scroll_layer_id); + ElementId scroll_element_id); // Layer overrides. bool OpacityCanAnimateOnImplThread() const override; - bool AlwaysUseActiveTreeOpacity() const override; ScrollbarLayerInterface* ToScrollbarLayer() override; void SetOpacity(float opacity) override; @@ -35,8 +34,8 @@ class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface, void SetNeedsDisplayRect(const gfx::Rect& rect) override; // ScrollbarLayerInterface - int ScrollLayerId() const override; - void SetScrollLayer(int layer_id) override; + ElementId scroll_element_id() const override; + void SetScrollElementId(ElementId element_id) override; ScrollbarOrientation orientation() const override; @@ -57,7 +56,7 @@ class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface, int thumb_thickness, int track_start, bool is_left_side_vertical_scrollbar, - int scroll_layer_id); + ElementId scroll_element_id); ~SolidColorScrollbarLayer() override; private: @@ -69,10 +68,10 @@ class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface, int thumb_thickness, int track_start, bool is_left_side_vertical_scrollbar, - int scroll_layer_id); + ElementId scroll_element_id); ~SolidColorScrollbarLayerInputs(); - int scroll_layer_id; + ElementId scroll_element_id; ScrollbarOrientation orientation; int thumb_thickness; int track_start; diff --git a/chromium/cc/layers/surface_layer.cc b/chromium/cc/layers/surface_layer.cc index 000832fe116..573f97b9b13 100644 --- a/chromium/cc/layers/surface_layer.cc +++ b/chromium/cc/layers/surface_layer.cc @@ -8,6 +8,7 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/single_thread_task_runner.h" #include "base/trace_event/trace_event.h" #include "cc/layers/surface_layer_impl.h" #include "cc/output/swap_promise.h" diff --git a/chromium/cc/layers/surface_layer_impl.cc b/chromium/cc/layers/surface_layer_impl.cc index 7a1bfbd9795..e42bca07ed5 100644 --- a/chromium/cc/layers/surface_layer_impl.cc +++ b/chromium/cc/layers/surface_layer_impl.cc @@ -65,15 +65,28 @@ void SurfaceLayerImpl::PushPropertiesTo(LayerImpl* layer) { void SurfaceLayerImpl::AppendQuads(RenderPass* render_pass, AppendQuadsData* append_quads_data) { AppendRainbowDebugBorder(render_pass); - auto* primary = CreateSurfaceDrawQuad( - render_pass, SurfaceDrawQuadType::PRIMARY, primary_surface_info_, - &append_quads_data->embedded_surfaces); + SharedQuadState* common_shared_quad_state = nullptr; + auto* primary = + CreateSurfaceDrawQuad(render_pass, SurfaceDrawQuadType::PRIMARY, + primary_surface_info_, &common_shared_quad_state); // Emitting a fallback SurfaceDrawQuad is unnecessary if the primary and // fallback surface Ids match. - if (primary && fallback_surface_info_.id() != primary_surface_info_.id()) { + bool needs_fallback = + fallback_surface_info_.is_valid() && + (fallback_surface_info_.id() != primary_surface_info_.id()); + if (primary && needs_fallback) { + // Add the primary surface ID as a dependency. + append_quads_data->activation_dependencies.push_back( + primary_surface_info_.id()); + // We can use the same SharedQuadState as the primary SurfaceDrawQuad if + // we don't need a different transform on the fallback. + bool use_common_shared_quad_state = + !stretch_content_to_fill_bounds_ && + primary_surface_info_.device_scale_factor() == + fallback_surface_info_.device_scale_factor(); primary->fallback_quad = CreateSurfaceDrawQuad( render_pass, SurfaceDrawQuadType::FALLBACK, fallback_surface_info_, - nullptr /* embedded_surfaces */); + use_common_shared_quad_state ? &common_shared_quad_state : nullptr); } } @@ -81,7 +94,7 @@ SurfaceDrawQuad* SurfaceLayerImpl::CreateSurfaceDrawQuad( RenderPass* render_pass, SurfaceDrawQuadType surface_draw_quad_type, const SurfaceInfo& surface_info, - std::vector<SurfaceId>* embedded_surfaces) { + SharedQuadState** common_shared_quad_state) { if (!surface_info.is_valid()) return nullptr; @@ -105,24 +118,31 @@ SurfaceDrawQuad* SurfaceLayerImpl::CreateSurfaceDrawQuad( surface_info.device_scale_factor(); } - visible_quad_rect = gfx::ScaleToEnclosedRect( + visible_quad_rect = gfx::ScaleToEnclosingRect( visible_quad_rect, layer_to_content_scale_x, layer_to_content_scale_y); visible_quad_rect = gfx::IntersectRects(quad_rect, visible_quad_rect); if (visible_quad_rect.IsEmpty()) return nullptr; + // If a |common_shared_quad_state| is provided then use that. Otherwise, + // allocate a new SharedQuadState. Assign the new SharedQuadState to + // *|common_shared_quad_state| so that it may be reused by another emitted + // SurfaceDrawQuad. SharedQuadState* shared_quad_state = - render_pass->CreateAndAppendSharedQuadState(); - PopulateScaledSharedQuadState(shared_quad_state, layer_to_content_scale_x, - layer_to_content_scale_y); + common_shared_quad_state ? *common_shared_quad_state : nullptr; + if (!shared_quad_state) { + shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); + PopulateScaledSharedQuadState(shared_quad_state, layer_to_content_scale_x, + layer_to_content_scale_y); + } + if (common_shared_quad_state) + *common_shared_quad_state = shared_quad_state; SurfaceDrawQuad* surface_draw_quad = render_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>(); surface_draw_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, surface_info.id(), surface_draw_quad_type, nullptr); - if (embedded_surfaces) - embedded_surfaces->push_back(surface_info.id()); return surface_draw_quad; } diff --git a/chromium/cc/layers/surface_layer_impl.h b/chromium/cc/layers/surface_layer_impl.h index 9b853df5253..3c518827799 100644 --- a/chromium/cc/layers/surface_layer_impl.h +++ b/chromium/cc/layers/surface_layer_impl.h @@ -30,6 +30,11 @@ class CC_EXPORT SurfaceLayerImpl : public LayerImpl { return primary_surface_info_; } + // A fallback Surface is a Surface that is already known to exist in the + // display compositor. If surface synchronization is enabled, the display + // compositor will use the fallback if the primary surface is unavailable + // at the time of surface aggregation. If surface synchronization is not + // enabled, then a fallback surface will not be specified. void SetFallbackSurfaceInfo(const SurfaceInfo& surface_info); const SurfaceInfo& fallback_surface_info() const { return fallback_surface_info_; @@ -51,7 +56,7 @@ class CC_EXPORT SurfaceLayerImpl : public LayerImpl { RenderPass* render_pass, SurfaceDrawQuadType surface_draw_quad_type, const SurfaceInfo& surface_info, - std::vector<SurfaceId>* embedded_surfaces); + SharedQuadState** common_shared_quad_state); void GetDebugBorderProperties(SkColor* color, float* width) const override; void AppendRainbowDebugBorder(RenderPass* render_pass); diff --git a/chromium/cc/layers/surface_layer_impl_unittest.cc b/chromium/cc/layers/surface_layer_impl_unittest.cc index 5c6f942a66f..8aa8f8fa64e 100644 --- a/chromium/cc/layers/surface_layer_impl_unittest.cc +++ b/chromium/cc/layers/surface_layer_impl_unittest.cc @@ -20,12 +20,12 @@ namespace { static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1); TEST(SurfaceLayerImplTest, OcclusionWithDeviceScaleFactor) { - float device_scale_factor = 1.25f; + float device_scale_factor = 1.33f; - gfx::Size layer_size(1000, 1000); + gfx::Size layer_size(512, 512); gfx::Size scaled_surface_size( gfx::ScaleToCeiledSize(layer_size, device_scale_factor)); - gfx::Size viewport_size(1250, 1325); + gfx::Size viewport_size(681, 750); const LocalSurfaceId kArbitraryLocalSurfaceId( 9, base::UnguessableToken::Create()); @@ -40,10 +40,10 @@ TEST(SurfaceLayerImplTest, OcclusionWithDeviceScaleFactor) { surface_layer_impl->SetPrimarySurfaceInfo( SurfaceInfo(surface_id, device_scale_factor, scaled_surface_size)); - LayerImplList layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( impl.root_layer_for_testing(), viewport_size, device_scale_factor, - &layer_list); + &render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); { @@ -67,7 +67,7 @@ TEST(SurfaceLayerImplTest, OcclusionWithDeviceScaleFactor) { { SCOPED_TRACE("Partial occlusion"); - gfx::Rect occluded(gfx::ScaleToEnclosingRect(gfx::Rect(200, 0, 800, 1000), + gfx::Rect occluded(gfx::ScaleToEnclosingRect(gfx::Rect(200, 0, 312, 512), device_scale_factor)); impl.AppendQuadsWithOcclusion(surface_layer_impl, occluded); @@ -80,7 +80,7 @@ TEST(SurfaceLayerImplTest, OcclusionWithDeviceScaleFactor) { } { SCOPED_TRACE("No outside occlusion"); - gfx::Rect occluded(gfx::ScaleToEnclosingRect(gfx::Rect(0, 1000, 1250, 300), + gfx::Rect occluded(gfx::ScaleToEnclosingRect(gfx::Rect(0, 681, 681, 69), device_scale_factor)); impl.AppendQuadsWithOcclusion(surface_layer_impl, occluded); @@ -147,6 +147,8 @@ TEST(SurfaceLayerImplTest, SurfaceStretchedToLayerBounds) { impl.AddChildToRoot<SurfaceLayerImpl>(); const LocalSurfaceId kArbitraryLocalSurfaceId( 9, base::UnguessableToken::Create()); + const LocalSurfaceId kArbitraryLocalSurfaceId2( + 10, base::UnguessableToken::Create()); // Given condition: layer and surface have different size and different // aspect ratios. @@ -162,8 +164,11 @@ TEST(SurfaceLayerImplTest, SurfaceStretchedToLayerBounds) { surface_layer_impl->SetBounds(layer_size); surface_layer_impl->SetDrawsContent(true); SurfaceId surface_id(kArbitraryFrameSinkId, kArbitraryLocalSurfaceId); + SurfaceId surface_id2(kArbitraryFrameSinkId, kArbitraryLocalSurfaceId2); surface_layer_impl->SetPrimarySurfaceInfo( SurfaceInfo(surface_id, surface_scale, surface_size)); + surface_layer_impl->SetFallbackSurfaceInfo( + SurfaceInfo(surface_id2, surface_scale, surface_size)); surface_layer_impl->SetStretchContentToFillBounds(true); impl.CalcDrawProps(viewport_size); @@ -171,10 +176,10 @@ TEST(SurfaceLayerImplTest, SurfaceStretchedToLayerBounds) { std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); AppendQuadsData data; surface_layer_impl->AppendQuads(render_pass.get(), &data); - EXPECT_THAT(data.embedded_surfaces, UnorderedElementsAre(surface_id)); + EXPECT_THAT(data.activation_dependencies, UnorderedElementsAre(surface_id)); const QuadList& quads = render_pass->quad_list; - ASSERT_EQ(1u, quads.size()); + ASSERT_EQ(2u, quads.size()); const SharedQuadState* shared_quad_state = quads.front()->shared_quad_state; // We expect that the transform for the quad stretches the quad to cover the @@ -224,6 +229,8 @@ TEST(SurfaceLayerImplTest, SurfaceLayerImplEmitsTwoDrawQuadsIfUniqueFallback) { float surface_scale2 = 2.f; gfx::Size surface_size2(400, 400); SurfaceInfo fallback_surface_info(surface_id2, surface_scale2, surface_size2); + SurfaceInfo fallback_surface_info2(surface_id2, surface_scale1, + surface_size2); gfx::Size layer_size(400, 100); @@ -238,18 +245,37 @@ TEST(SurfaceLayerImplTest, SurfaceLayerImplEmitsTwoDrawQuadsIfUniqueFallback) { impl.CalcDrawProps(viewport_size); std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); - AppendQuadsData data; - surface_layer_impl->AppendQuads(render_pass.get(), &data); - // The the primary SurfaceInfo will be added to embedded_surfaces. - EXPECT_THAT(data.embedded_surfaces, UnorderedElementsAre(surface_id1)); + { + AppendQuadsData data; + surface_layer_impl->AppendQuads(render_pass.get(), &data); + // The the primary SurfaceInfo will be added to activation_dependencies. + EXPECT_THAT(data.activation_dependencies, + UnorderedElementsAre(surface_id1)); + } - ASSERT_EQ(2u, render_pass->quad_list.size()); + // Update the fallback SurfaceInfo and re-emit DrawQuads. + { + AppendQuadsData data; + surface_layer_impl->SetFallbackSurfaceInfo(fallback_surface_info2); + surface_layer_impl->AppendQuads(render_pass.get(), &data); + // The the primary SurfaceInfo will be added to activation_dependencies. + EXPECT_THAT(data.activation_dependencies, + UnorderedElementsAre(surface_id1)); + } + + ASSERT_EQ(4u, render_pass->quad_list.size()); const SurfaceDrawQuad* surface_draw_quad1 = SurfaceDrawQuad::MaterialCast(render_pass->quad_list.ElementAt(0)); ASSERT_TRUE(surface_draw_quad1); const SurfaceDrawQuad* surface_draw_quad2 = SurfaceDrawQuad::MaterialCast(render_pass->quad_list.ElementAt(1)); ASSERT_TRUE(surface_draw_quad2); + const SurfaceDrawQuad* surface_draw_quad3 = + SurfaceDrawQuad::MaterialCast(render_pass->quad_list.ElementAt(2)); + ASSERT_TRUE(surface_draw_quad3); + const SurfaceDrawQuad* surface_draw_quad4 = + SurfaceDrawQuad::MaterialCast(render_pass->quad_list.ElementAt(3)); + ASSERT_TRUE(surface_draw_quad4); EXPECT_EQ(SurfaceDrawQuadType::PRIMARY, surface_draw_quad1->surface_draw_quad_type); @@ -258,6 +284,22 @@ TEST(SurfaceLayerImplTest, SurfaceLayerImplEmitsTwoDrawQuadsIfUniqueFallback) { EXPECT_EQ(SurfaceDrawQuadType::FALLBACK, surface_draw_quad2->surface_draw_quad_type); EXPECT_EQ(surface_id2, surface_draw_quad2->surface_id); + // If the device scale factor of the primary and fallback are different then + // they do not share a SharedQuadState. + EXPECT_NE(surface_draw_quad1->shared_quad_state, + surface_draw_quad2->shared_quad_state); + + EXPECT_EQ(SurfaceDrawQuadType::PRIMARY, + surface_draw_quad3->surface_draw_quad_type); + EXPECT_EQ(surface_id1, surface_draw_quad3->surface_id); + EXPECT_EQ(surface_draw_quad4, surface_draw_quad3->fallback_quad); + EXPECT_EQ(SurfaceDrawQuadType::FALLBACK, + surface_draw_quad4->surface_draw_quad_type); + EXPECT_EQ(surface_id2, surface_draw_quad4->surface_id); + // If the device scale factor of the primary and fallback are the same then + // they share a SharedQuadState. + EXPECT_EQ(surface_draw_quad3->shared_quad_state, + surface_draw_quad4->shared_quad_state); } // This test verifies that one SurfaceDrawQuad is emitted if a @@ -292,7 +334,11 @@ TEST(SurfaceLayerImplTest, std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); AppendQuadsData data; surface_layer_impl->AppendQuads(render_pass.get(), &data); - EXPECT_THAT(data.embedded_surfaces, UnorderedElementsAre(surface_id1)); + // As the primary and fallback SurfaceInfos match, there is no reason to + // add the primary surface ID to |activation_dependencies| because it is not + // an unresolved dependency. The fallback surface will already be added as a + // reference in referenced_surfaces. + EXPECT_THAT(data.activation_dependencies, testing::IsEmpty()); ASSERT_EQ(1u, render_pass->quad_list.size()); const SurfaceDrawQuad* surface_draw_quad1 = diff --git a/chromium/cc/layers/surface_layer_unittest.cc b/chromium/cc/layers/surface_layer_unittest.cc index 0ee4e836500..4c354a7a5f6 100644 --- a/chromium/cc/layers/surface_layer_unittest.cc +++ b/chromium/cc/layers/surface_layer_unittest.cc @@ -32,6 +32,9 @@ namespace cc { namespace { +using testing::_; +using testing::Eq; + static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1); class SurfaceLayerTest : public testing::Test { @@ -63,33 +66,20 @@ class SurfaceLayerTest : public testing::Test { FakeLayerTreeHostImpl host_impl_; }; -class TestSurfaceReferenceFactory : public SequenceSurfaceReferenceFactory { - protected: - void SatisfySequence(const SurfaceSequence& seq) const override { - *out_seq_ = seq; - } - - void RequireSequence(const SurfaceId& id, - const SurfaceSequence& seq) const override { - *out_id_ = id; - out_set_->insert(seq); - } - +class MockSurfaceReferenceFactory : public SequenceSurfaceReferenceFactory { public: - TestSurfaceReferenceFactory(SurfaceSequence* out_seq, - SurfaceId* out_id, - std::set<SurfaceSequence>* out_set) - : out_seq_(out_seq), out_id_(out_id), out_set_(out_set) {} + MockSurfaceReferenceFactory() {} + + // SequenceSurfaceReferenceFactory implementation. + MOCK_CONST_METHOD1(SatisfySequence, void(const SurfaceSequence&)); + MOCK_CONST_METHOD2(RequireSequence, + void(const SurfaceId&, const SurfaceSequence&)); protected: - ~TestSurfaceReferenceFactory() override = default; + ~MockSurfaceReferenceFactory() override = default; private: - SurfaceSequence* out_seq_; - SurfaceId* out_id_; - std::set<SurfaceSequence>* out_set_; - - DISALLOW_COPY_AND_ASSIGN(TestSurfaceReferenceFactory); + DISALLOW_COPY_AND_ASSIGN(MockSurfaceReferenceFactory); }; // Check that one surface can be referenced by multiple LayerTreeHosts, and @@ -97,17 +87,26 @@ class TestSurfaceReferenceFactory : public SequenceSurfaceReferenceFactory { TEST_F(SurfaceLayerTest, MultipleFramesOneSurface) { const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create(); - SurfaceSequence blank_change; // Receives sequence if commit doesn't happen. - - SurfaceId required_id; - std::set<SurfaceSequence> required_seq; - scoped_refptr<SurfaceReferenceFactory> ref_factory = - new TestSurfaceReferenceFactory(&blank_change, &required_id, - &required_seq); - auto layer = SurfaceLayer::Create(ref_factory); - SurfaceInfo info( + const SurfaceInfo info( SurfaceId(kArbitraryFrameSinkId, LocalSurfaceId(1, kArbitraryToken)), 1.f, gfx::Size(1, 1)); + const SurfaceSequence expected_seq1(FrameSinkId(1, 1), 1u); + const SurfaceSequence expected_seq2(FrameSinkId(2, 2), 1u); + const SurfaceId expected_id(kArbitraryFrameSinkId, + LocalSurfaceId(1, kArbitraryToken)); + + scoped_refptr<MockSurfaceReferenceFactory> ref_factory = + new testing::StrictMock<MockSurfaceReferenceFactory>(); + + // We are going to set up the SurfaceLayers and LayerTreeHosts. Each layer + // will require a sequence and no sequence should be satisfied for now. + EXPECT_CALL(*ref_factory, RequireSequence(Eq(expected_id), Eq(expected_seq1))) + .Times(1); + EXPECT_CALL(*ref_factory, RequireSequence(Eq(expected_id), Eq(expected_seq2))) + .Times(1); + EXPECT_CALL(*ref_factory, SatisfySequence(_)).Times(0); + + auto layer = SurfaceLayer::Create(ref_factory); layer->SetPrimarySurfaceInfo(info); layer_tree_host_->GetSurfaceSequenceGenerator()->set_frame_sink_id( FrameSinkId(1, 1)); @@ -117,57 +116,40 @@ TEST_F(SurfaceLayerTest, MultipleFramesOneSurface) { std::unique_ptr<FakeLayerTreeHost> layer_tree_host2 = FakeLayerTreeHost::Create(&fake_client_, &task_graph_runner_, animation_host2.get()); - auto layer2 = SurfaceLayer::Create(std::move(ref_factory)); + auto layer2 = SurfaceLayer::Create(ref_factory); layer2->SetPrimarySurfaceInfo(info); layer_tree_host2->GetSurfaceSequenceGenerator()->set_frame_sink_id( FrameSinkId(2, 2)); layer_tree_host2->SetRootLayer(layer2); - // Layers haven't been removed, so no sequence should be satisfied. - EXPECT_FALSE(blank_change.is_valid()); - - SurfaceSequence expected1(FrameSinkId(1, 1), 1u); - SurfaceSequence expected2(FrameSinkId(2, 2), 1u); + testing::Mock::VerifyAndClearExpectations(ref_factory.get()); + // Destroy the second LayerTreeHost. The sequence generated by its + // SurfaceLayer must be satisfied and no new sequences must be required. + EXPECT_CALL(*ref_factory, SatisfySequence(Eq(expected_seq2))).Times(1); layer_tree_host2->SetRootLayer(nullptr); layer_tree_host2.reset(); animation_host2 = nullptr; - - // Layer was removed so sequence from second LayerTreeHost should be - // satisfied. base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(blank_change == expected2); - - // Set of sequences that need to be satisfied should include sequences from - // both trees. - EXPECT_TRUE(required_id == SurfaceId(kArbitraryFrameSinkId, - LocalSurfaceId(1, kArbitraryToken))); - EXPECT_EQ(2u, required_seq.size()); - EXPECT_TRUE(required_seq.count(expected1)); - EXPECT_TRUE(required_seq.count(expected2)); + testing::Mock::VerifyAndClearExpectations(ref_factory.get()); + // Destroy the first LayerTreeHost. The sequence generated by its + // SurfaceLayer must be satisfied and no new sequences must be required. + EXPECT_CALL(*ref_factory, SatisfySequence(expected_seq1)).Times(1); + EXPECT_CALL(*ref_factory, RequireSequence(_, _)).Times(0); layer_tree_host_->SetRootLayer(nullptr); layer_tree_host_.reset(); - - // Layer was removed so sequence from first LayerTreeHost should be - // satisfied. base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(blank_change == expected1); - - // No more SurfaceSequences should have been generated that need to have be - // satisfied. - EXPECT_EQ(2u, required_seq.size()); + testing::Mock::VerifyAndClearExpectations(ref_factory.get()); } // This test verifies that the primary and fallback SurfaceInfo are pushed // across from SurfaceLayer to SurfaceLayerImpl. TEST_F(SurfaceLayerTest, SurfaceInfoPushProperties) { - SurfaceSequence blank_change; - SurfaceId required_id; - std::set<SurfaceSequence> required_sequences; + // We use a nice mock here because we are not really interested in calls to + // MockSurfaceReferenceFactory and we don't want warnings printed. scoped_refptr<SurfaceReferenceFactory> ref_factory = - new TestSurfaceReferenceFactory(&blank_change, &required_id, - &required_sequences); + new testing::NiceMock<MockSurfaceReferenceFactory>(); scoped_refptr<SurfaceLayer> layer = SurfaceLayer::Create(ref_factory); layer_tree_host_->SetRootLayer(layer); @@ -208,24 +190,34 @@ class SurfaceLayerSwapPromise : public LayerTreeTest { void BeginTest() override { layer_tree_host()->GetSurfaceSequenceGenerator()->set_frame_sink_id( FrameSinkId(1, 1)); - layer_ = SurfaceLayer::Create(new TestSurfaceReferenceFactory( - &satisfied_sequence_, &required_id_, &required_set_)); + ref_factory_ = new testing::StrictMock<MockSurfaceReferenceFactory>(); + + // Create a SurfaceLayer but don't add it to the tree yet. No sequence + // should be required / satisfied. + EXPECT_CALL(*ref_factory_, SatisfySequence(_)).Times(0); + EXPECT_CALL(*ref_factory_, RequireSequence(_, _)).Times(0); + layer_ = SurfaceLayer::Create(ref_factory_); SurfaceInfo info( SurfaceId(kArbitraryFrameSinkId, LocalSurfaceId(1, kArbitraryToken)), 1.f, gfx::Size(1, 1)); layer_->SetPrimarySurfaceInfo(info); - - // Layer hasn't been added to tree so no SurfaceSequence generated yet. - EXPECT_EQ(0u, required_set_.size()); - + testing::Mock::VerifyAndClearExpectations(ref_factory_.get()); + + // Add the layer to the tree. A sequence must be required. + SurfaceSequence expected_seq(kArbitraryFrameSinkId, 1u); + SurfaceId expected_id(kArbitraryFrameSinkId, + LocalSurfaceId(1, kArbitraryToken)); + EXPECT_CALL(*ref_factory_, SatisfySequence(_)).Times(0); + EXPECT_CALL(*ref_factory_, + RequireSequence(Eq(expected_id), Eq(expected_seq))) + .Times(1); layer_tree_host()->SetRootLayer(layer_); + testing::Mock::VerifyAndClearExpectations(ref_factory_.get()); - // Should have SurfaceSequence from first tree. - SurfaceSequence expected(kArbitraryFrameSinkId, 1u); - EXPECT_TRUE(required_id_ == SurfaceId(kArbitraryFrameSinkId, - LocalSurfaceId(1, kArbitraryToken))); - EXPECT_EQ(1u, required_set_.size()); - EXPECT_TRUE(required_set_.count(expected)); + // By the end of the test, the required sequence must be satisfied and no + // more sequence must be required. + EXPECT_CALL(*ref_factory_, SatisfySequence(Eq(expected_seq))).Times(1); + EXPECT_CALL(*ref_factory_, RequireSequence(_, _)).Times(0); gfx::Size bounds(100, 100); layer_tree_host()->SetViewportSize(bounds); @@ -245,15 +237,15 @@ class SurfaceLayerSwapPromise : public LayerTreeTest { base::Unretained(this))); } + void AfterTest() override {} + protected: int commit_count_; bool sequence_was_satisfied_; scoped_refptr<SurfaceLayer> layer_; scoped_refptr<Layer> blank_layer_; - SurfaceSequence satisfied_sequence_; + scoped_refptr<MockSurfaceReferenceFactory> ref_factory_; - SurfaceId required_id_; - std::set<SurfaceSequence> required_set_; const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create(); }; @@ -276,14 +268,6 @@ class SurfaceLayerSwapPromiseWithDraw : public SurfaceLayerSwapPromise { break; } } - - void AfterTest() override { - EXPECT_TRUE(required_id_ == SurfaceId(kArbitraryFrameSinkId, - LocalSurfaceId(1, kArbitraryToken))); - EXPECT_EQ(1u, required_set_.size()); - EXPECT_TRUE(satisfied_sequence_ == - SurfaceSequence(kArbitraryFrameSinkId, 1u)); - } }; SINGLE_AND_MULTI_THREAD_TEST_F(SurfaceLayerSwapPromiseWithDraw); @@ -315,14 +299,6 @@ class SurfaceLayerSwapPromiseWithoutDraw : public SurfaceLayerSwapPromise { break; } } - - void AfterTest() override { - EXPECT_TRUE(required_id_ == SurfaceId(kArbitraryFrameSinkId, - LocalSurfaceId(1, kArbitraryToken))); - EXPECT_EQ(1u, required_set_.size()); - EXPECT_TRUE(satisfied_sequence_ == - SurfaceSequence(kArbitraryFrameSinkId, 1u)); - } }; MULTI_THREAD_TEST_F(SurfaceLayerSwapPromiseWithoutDraw); diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc index dc9c60d3795..a13964af827 100644 --- a/chromium/cc/layers/texture_layer_unittest.cc +++ b/chromium/cc/layers/texture_layer_unittest.cc @@ -638,18 +638,15 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { std::unique_ptr<TestCompositorFrameSink> CreateCompositorFrameSink( scoped_refptr<ContextProvider> compositor_context_provider, scoped_refptr<ContextProvider> worker_context_provider) override { + constexpr bool kDisableDisplayVsync = false; bool synchronous_composite = !HasImplThread() && !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; - // Allow relaim resources for this test so that mailboxes in the display - // will be returned inside the commit that replaces them. - bool force_disable_reclaim_resources = false; return base::MakeUnique<TestCompositorFrameSink>( compositor_context_provider, std::move(worker_context_provider), shared_bitmap_manager(), gpu_memory_buffer_manager(), layer_tree_host()->GetSettings().renderer_settings, - ImplThreadTaskRunner(), synchronous_composite, - force_disable_reclaim_resources); + ImplThreadTaskRunner(), synchronous_composite, kDisableDisplayVsync); } void AdvanceTestCase() { @@ -1188,16 +1185,6 @@ class TextureLayerChangeInvisibleMailboxTest void MailboxReleased(const gpu::SyncToken& sync_token, bool lost_resource) { EXPECT_TRUE(sync_token.HasData()); ++mailbox_returned_; - switch (mailbox_returned_) { - case 1: - break; - case 2: - EXPECT_EQ(commit_count_, 5); - EndTest(); - break; - default: - NOTREACHED(); - } } void SetupTree() override { @@ -1227,7 +1214,7 @@ class TextureLayerChangeInvisibleMailboxTest void BeginTest() override { PostSetNeedsCommitToMainThread(); } - void DidCommitAndDrawFrame() override { + void DidReceiveCompositorFrameAck() override { ++commit_count_; switch (commit_count_) { case 1: @@ -1262,6 +1249,8 @@ class TextureLayerChangeInvisibleMailboxTest texture_layer_->ClearClient(); break; case 5: + EXPECT_EQ(2, mailbox_returned_); + EndTest(); break; default: NOTREACHED(); @@ -1284,8 +1273,7 @@ class TextureLayerChangeInvisibleMailboxTest int commit_count_; }; -// Flaky when multi-threaded. crbug.com/702868 -SINGLE_THREAD_TEST_F(TextureLayerChangeInvisibleMailboxTest); +SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerChangeInvisibleMailboxTest); // Test that TextureLayerImpl::ReleaseResources can be called which releases // the mailbox back to TextureLayerClient. diff --git a/chromium/cc/layers/ui_resource_layer_unittest.cc b/chromium/cc/layers/ui_resource_layer_unittest.cc index 17dc4dadfe7..6c05ec16246 100644 --- a/chromium/cc/layers/ui_resource_layer_unittest.cc +++ b/chromium/cc/layers/ui_resource_layer_unittest.cc @@ -60,7 +60,6 @@ TEST_F(UIResourceLayerTest, SetBitmap) { Mock::VerifyAndClearExpectations(layer_tree_host()); EXPECT_EQ(test_layer->GetLayerTreeHostForTesting(), layer_tree_host()); - test_layer->SavePaintProperties(); test_layer->Update(); EXPECT_FALSE(test_layer->DrawsContent()); @@ -84,7 +83,6 @@ TEST_F(UIResourceLayerTest, SetUIResourceId) { Mock::VerifyAndClearExpectations(layer_tree_host()); EXPECT_EQ(test_layer->GetLayerTreeHostForTesting(), layer_tree_host()); - test_layer->SavePaintProperties(); test_layer->Update(); EXPECT_FALSE(test_layer->DrawsContent()); @@ -162,7 +160,6 @@ TEST_F(UIResourceLayerTest, SharedBitmap) { layer_tree_host()->SetRootLayer(layer1); layer1->SetBitmap(bitmap); bitmap.reset(); - layer1->SavePaintProperties(); layer1->Update(); EXPECT_TRUE(layer1->DrawsContent()); const auto resource_id = layer1->resource_id(); @@ -171,7 +168,6 @@ TEST_F(UIResourceLayerTest, SharedBitmap) { scoped_refptr<TestUIResourceLayer> layer2 = TestUIResourceLayer::Create(); layer_tree_host()->SetRootLayer(layer2); layer2->SetBitmap(bitmap_copy); - layer2->SavePaintProperties(); layer2->Update(); EXPECT_TRUE(layer2->DrawsContent()); EXPECT_EQ(resource_id, layer2->resource_id()); @@ -189,7 +185,6 @@ TEST_F(UIResourceLayerTest, SharedBitmap) { // change the shared bitmap to something else then back to the original. LayerTestCommon::LayerImplTest impl; impl.host()->SetRootLayer(layer1); - layer1->SavePaintProperties(); layer1->Update(); EXPECT_TRUE(layer1->DrawsContent()); const auto other_lth_resource_id = layer1->resource_id(); diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc index 53b90ee4fa5..7b29b323965 100644 --- a/chromium/cc/layers/video_layer_impl.cc +++ b/chromium/cc/layers/video_layer_impl.cc @@ -168,9 +168,10 @@ void VideoLayerImpl::AppendQuads(RenderPass* render_pass, SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); - shared_quad_state->SetAll(transform, rotated_size, visible_layer_rect(), - clip_rect(), is_clipped(), draw_opacity(), - SkBlendMode::kSrcOver, GetSortingContextId()); + shared_quad_state->SetAll(transform, gfx::Rect(rotated_size), + visible_layer_rect(), clip_rect(), is_clipped(), + draw_opacity(), SkBlendMode::kSrcOver, + GetSortingContextId()); AppendDebugBorderQuad( render_pass, rotated_size, shared_quad_state, append_quads_data); diff --git a/chromium/cc/output/bsp_tree_perftest.cc b/chromium/cc/output/bsp_tree_perftest.cc index 3b954d1d41e..f8f16d0d0ae 100644 --- a/chromium/cc/output/bsp_tree_perftest.cc +++ b/chromium/cc/output/bsp_tree_perftest.cc @@ -71,10 +71,8 @@ class BspTreePerfTest : public LayerTreeTest { LayerTreeImpl* active_tree = host_impl->active_tree(); // First build the tree and then we'll start running tests on layersorter // itself - bool can_render_to_separate_surface = true; int max_texture_size = 8096; - DoCalcDrawPropertiesImpl(can_render_to_separate_surface, max_texture_size, - active_tree, host_impl); + DoCalcDrawPropertiesImpl(max_texture_size, active_tree, host_impl); LayerImplList base_list; BuildLayerImplList(active_tree->root_layer_for_testing(), &base_list); @@ -104,23 +102,21 @@ class BspTreePerfTest : public LayerTreeTest { EndTest(); } - void DoCalcDrawPropertiesImpl(bool can_render_to_separate_surface, - int max_texture_size, + void DoCalcDrawPropertiesImpl(int max_texture_size, LayerTreeImpl* active_tree, LayerTreeHostImpl* host_impl) { - LayerImplList update_list; + RenderSurfaceList update_list; LayerTreeHostCommon::CalcDrawPropsImplInputs inputs( - active_tree->root_layer_for_testing(), active_tree->DrawViewportSize(), - host_impl->DrawTransform(), active_tree->device_scale_factor(), + active_tree->root_layer_for_testing(), + active_tree->DeviceViewport().size(), host_impl->DrawTransform(), + active_tree->device_scale_factor(), active_tree->current_page_scale_factor(), active_tree->InnerViewportContainerLayer(), active_tree->InnerViewportScrollLayer(), active_tree->OuterViewportScrollLayer(), active_tree->elastic_overscroll()->Current(active_tree->IsActiveTree()), active_tree->OverscrollElasticityLayer(), max_texture_size, - can_render_to_separate_surface, host_impl->settings().layer_transforms_should_scale_layer_contents, - false, // don't use layer lists for perf tests &update_list, active_tree->property_trees()); LayerTreeHostCommon::CalculateDrawProperties(&inputs); } diff --git a/chromium/cc/output/ca_layer_overlay.cc b/chromium/cc/output/ca_layer_overlay.cc index 1acd9528c56..972c8c405f7 100644 --- a/chromium/cc/output/ca_layer_overlay.cc +++ b/chromium/cc/output/ca_layer_overlay.cc @@ -74,34 +74,23 @@ bool FilterOperationSupported(const FilterOperation& operation) { } } -static const FilterOperations* FiltersForPass( - int render_pass_id, - const RenderPassFilterList& filter_list) { - auto it = std::lower_bound( - filter_list.begin(), filter_list.end(), - std::pair<int, FilterOperations*>(render_pass_id, nullptr)); - if (it != filter_list.end() && it->first == render_pass_id) - return it->second; - return nullptr; -} - CALayerResult FromRenderPassQuad( ResourceProvider* resource_provider, const RenderPassDrawQuad* quad, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, CALayerOverlay* ca_layer_overlay) { - if (FiltersForPass(quad->render_pass_id, render_pass_background_filters)) { + if (render_pass_background_filters.count(quad->render_pass_id)) { return CA_LAYER_FAILED_RENDER_PASS_BACKGROUND_FILTERS; } if (quad->shared_quad_state->sorting_context_id != 0) return CA_LAYER_FAILED_RENDER_PASS_SORTING_CONTEXT_ID; - const FilterOperations* filters = - FiltersForPass(quad->render_pass_id, render_pass_filters); - if (filters) { - for (const FilterOperation& operation : filters->operations()) { + auto it = render_pass_filters.find(quad->render_pass_id); + if (it != render_pass_filters.end()) { + for (const FilterOperation& operation : it->second->operations()) { bool success = FilterOperationSupported(operation); if (!success) return CA_LAYER_FAILED_RENDER_PASS_FILTER_OPERATION; @@ -188,8 +177,9 @@ class CALayerOverlayProcessor { ResourceProvider* resource_provider, const gfx::RectF& display_rect, const DrawQuad* quad, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, CALayerOverlay* ca_layer_overlay, bool* skip, bool* render_pass_draw_quad) { @@ -286,8 +276,9 @@ bool ProcessForCALayerOverlays( ResourceProvider* resource_provider, const gfx::RectF& display_rect, const QuadList& quad_list, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, CALayerOverlayList* ca_layer_overlays) { CALayerResult result = CA_LAYER_SUCCESS; ca_layer_overlays->reserve(quad_list.size()); diff --git a/chromium/cc/output/ca_layer_overlay.h b/chromium/cc/output/ca_layer_overlay.h index 513ba667d01..da003f9f02e 100644 --- a/chromium/cc/output/ca_layer_overlay.h +++ b/chromium/cc/output/ca_layer_overlay.h @@ -5,6 +5,7 @@ #ifndef CC_OUTPUT_CA_LAYER_OVERLAY_H_ #define CC_OUTPUT_CA_LAYER_OVERLAY_H_ +#include "base/containers/flat_map.h" #include "base/memory/ref_counted.h" #include "cc/quads/render_pass.h" #include "third_party/skia/include/core/SkColor.h" @@ -76,8 +77,9 @@ bool ProcessForCALayerOverlays( ResourceProvider* resource_provider, const gfx::RectF& display_rect, const QuadList& quad_list, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, CALayerOverlayList* ca_layer_overlays); } // namespace cc diff --git a/chromium/cc/output/compositor_frame.cc b/chromium/cc/output/compositor_frame.cc index b1438ee856b..82479cf3b43 100644 --- a/chromium/cc/output/compositor_frame.cc +++ b/chromium/cc/output/compositor_frame.cc @@ -13,4 +13,5 @@ CompositorFrame::CompositorFrame(CompositorFrame&& other) = default; CompositorFrame::~CompositorFrame() {} CompositorFrame& CompositorFrame::operator=(CompositorFrame&& other) = default; + } // namespace cc diff --git a/chromium/cc/output/compositor_frame_metadata.h b/chromium/cc/output/compositor_frame_metadata.h index a3ebfc26483..9e73fc5b926 100644 --- a/chromium/cc/output/compositor_frame_metadata.h +++ b/chromium/cc/output/compositor_frame_metadata.h @@ -85,8 +85,18 @@ class CC_EXPORT CompositorFrameMetadata { // retain. Thus, this field will likely go away. std::vector<SurfaceId> referenced_surfaces; - // This is the set of SurfaceIds embedded in DrawQuads. - std::vector<SurfaceId> embedded_surfaces; + // This is the set of dependent SurfaceIds that should be active in the + // display compositor before this CompositorFrame can be activated. Note + // that if |can_activate_before_dependencies| then the display compositor + // can choose to activate a CompositorFrame before all dependencies are + // available. + // Note: |activation_dependencies| and |referenced_surfaces| are disjoint + // sets of surface IDs. If a surface ID is known to exist and can be + // used without additional synchronization, then it is placed in + // |referenced_surfaces|. |activation_dependencies| is the set of + // surface IDs that this frame would like to block on until they + // become available or a deadline hits. + std::vector<SurfaceId> activation_dependencies; // This indicates whether this CompositorFrame can be activated before // dependencies have been resolved. diff --git a/chromium/cc/output/compositor_frame_sink.h b/chromium/cc/output/compositor_frame_sink.h index 650f7191c2e..28b72581b5e 100644 --- a/chromium/cc/output/compositor_frame_sink.h +++ b/chromium/cc/output/compositor_frame_sink.h @@ -25,6 +25,7 @@ class GpuMemoryBufferManager; namespace cc { +struct BeginFrameAck; class CompositorFrame; class CompositorFrameSinkClient; class LocalSurfaceId; @@ -41,9 +42,13 @@ class CC_EXPORT CompositorFrameSink { struct Capabilities { Capabilities() = default; - // Whether ForceReclaimResources can be called to reclaim all resources - // from the CompositorFrameSink. - bool can_force_reclaim_resources = false; + // True if we must always swap, even if there is no damage to the frame. + // Needed for both the browser compositor as well as layout tests. + // TODO(ericrk): This should be test-only for layout tests, but tab + // capture has issues capturing offscreen tabs whithout this. We should + // remove this dependency. crbug.com/680196 + bool must_always_swap = false; + // True if sync points for resources are needed when swapping delegated // frames. bool delegated_sync_points_required = true; @@ -102,10 +107,6 @@ class CC_EXPORT CompositorFrameSink { return shared_bitmap_manager_; } - // If supported, this causes a ReclaimResources for all resources that are - // currently in use. - virtual void ForceReclaimResources() {} - // If supported, this sets the LocalSurfaceId the CompositorFrameSink will use // to submit a CompositorFrame. virtual void SetLocalSurfaceId(const LocalSurfaceId& local_surface_id) {} @@ -121,6 +122,10 @@ class CC_EXPORT CompositorFrameSink { // processed in order to unthrottle the next frame. virtual void SubmitCompositorFrame(CompositorFrame frame) = 0; + // Signals that a BeginFrame issued by the BeginFrameSource provided to the + // client did not lead to a CompositorFrame submission. + virtual void DidNotProduceFrame(const BeginFrameAck& ack) = 0; + protected: // Bound to the ContextProvider to hear about when it is lost and inform the // |client_|. diff --git a/chromium/cc/output/compositor_frame_sink_unittest.cc b/chromium/cc/output/compositor_frame_sink_unittest.cc index 6a72c8d58bb..127eb43f9c9 100644 --- a/chromium/cc/output/compositor_frame_sink_unittest.cc +++ b/chromium/cc/output/compositor_frame_sink_unittest.cc @@ -28,6 +28,7 @@ class TestCompositorFrameSink : public CompositorFrameSink { void SubmitCompositorFrame(CompositorFrame frame) override { client_->DidReceiveCompositorFrameAck(); } + void DidNotProduceFrame(const BeginFrameAck& ack) override {} }; TEST(CompositorFrameSinkTest, ContextLossInformsClient) { diff --git a/chromium/cc/output/dc_layer_overlay.cc b/chromium/cc/output/dc_layer_overlay.cc index eda508e6e13..7cf68722011 100644 --- a/chromium/cc/output/dc_layer_overlay.cc +++ b/chromium/cc/output/dc_layer_overlay.cc @@ -4,6 +4,7 @@ #include "cc/output/dc_layer_overlay.h" +#include "base/metrics/histogram_macros.h" #include "cc/base/math_util.h" #include "cc/quads/solid_color_draw_quad.h" #include "cc/quads/yuv_video_draw_quad.h" @@ -68,6 +69,11 @@ gfx::RectF GetOcclusionBounds(const gfx::RectF& target_quad, return occlusion_bounding_box; } +void RecordDCLayerResult(DCLayerOverlayProcessor::DCLayerResult result) { + UMA_HISTOGRAM_ENUMERATION("GPU.DirectComposition.DCLayerResult", result, + DCLayerOverlayProcessor::DC_LAYER_FAILED_MAX); +} + } // namespace DCLayerOverlay::DCLayerOverlay() : filter(GL_LINEAR) {} @@ -85,7 +91,7 @@ DCLayerOverlayProcessor::DCLayerResult DCLayerOverlayProcessor::FromDrawQuad( if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver) return DC_LAYER_FAILED_QUAD_BLEND_MODE; - DCLayerResult result = DC_LAYER_FAILED_UNKNOWN; + DCLayerResult result; switch (quad->material) { case DrawQuad::YUV_VIDEO_CONTENT: result = @@ -93,7 +99,7 @@ DCLayerOverlayProcessor::DCLayerResult DCLayerOverlayProcessor::FromDrawQuad( ca_layer_overlay); break; default: - return DC_LAYER_FAILED_UNKNOWN; + return DC_LAYER_FAILED_UNSUPPORTED_QUAD; } if (result != DC_LAYER_SUCCESS) return result; @@ -128,13 +134,16 @@ void DCLayerOverlayProcessor::Process(ResourceProvider* resource_provider, DCLayerOverlay ca_layer; DCLayerResult result = FromDrawQuad(resource_provider, display_rect, quad_list->begin(), it, &ca_layer); - if (result != DC_LAYER_SUCCESS) + if (result != DC_LAYER_SUCCESS) { + RecordDCLayerResult(result); continue; + } if (!it->shared_quad_state->quad_to_target_transform .Preserves2dAxisAlignment() && !base::FeatureList::IsEnabled( features::kDirectCompositionComplexOverlays)) { + RecordDCLayerResult(DC_LAYER_FAILED_COMPLEX_TRANSFORM); continue; } @@ -152,6 +161,7 @@ void DCLayerOverlayProcessor::Process(ResourceProvider* resource_provider, quad_list->EraseAndInvalidateAllPointers(it); } else if (!base::FeatureList::IsEnabled( features::kDirectCompositionUnderlays)) { + RecordDCLayerResult(DC_LAYER_FAILED_OCCLUDED); continue; } else { // The quad is occluded, so replace it with a black solid color quad and diff --git a/chromium/cc/output/dc_layer_overlay.h b/chromium/cc/output/dc_layer_overlay.h index 5ae861a22d8..6daeedc02e7 100644 --- a/chromium/cc/output/dc_layer_overlay.h +++ b/chromium/cc/output/dc_layer_overlay.h @@ -66,12 +66,17 @@ typedef std::vector<DCLayerOverlay> DCLayerOverlayList; class DCLayerOverlayProcessor { public: + // This is used for a histogram to determine why overlays are or aren't + // used, so don't remove entries and make sure to update enums.xml if + // it changes. enum DCLayerResult { DC_LAYER_SUCCESS, + DC_LAYER_FAILED_UNSUPPORTED_QUAD, DC_LAYER_FAILED_QUAD_BLEND_MODE, DC_LAYER_FAILED_TEXTURE_NOT_CANDIDATE, DC_LAYER_FAILED_OCCLUDED, - DC_LAYER_FAILED_UNKNOWN + DC_LAYER_FAILED_COMPLEX_TRANSFORM, + DC_LAYER_FAILED_MAX, }; void Process(ResourceProvider* resource_provider, diff --git a/chromium/cc/output/direct_renderer.cc b/chromium/cc/output/direct_renderer.cc index 024f5653f36..5339f829c2b 100644 --- a/chromium/cc/output/direct_renderer.cc +++ b/chromium/cc/output/direct_renderer.cc @@ -6,7 +6,6 @@ #include <stddef.h> -#include <unordered_map> #include <utility> #include <vector> @@ -178,31 +177,31 @@ void DirectRenderer::DecideRenderPassAllocationsForFrame( const RenderPassList& render_passes_in_draw_order) { render_pass_bypass_quads_.clear(); - std::unordered_map<int, gfx::Size> render_passes_in_frame; - RenderPass* root_render_pass = render_passes_in_draw_order.back().get(); - for (size_t i = 0; i < render_passes_in_draw_order.size(); ++i) { - RenderPass* pass = render_passes_in_draw_order[i].get(); + auto& root_render_pass = render_passes_in_draw_order.back(); + + base::flat_map<int, gfx::Size> render_passes_in_frame; + for (const auto& pass : render_passes_in_draw_order) { if (pass != root_render_pass) { - if (const TileDrawQuad* tile_quad = CanPassBeDrawnDirectly(pass)) { + if (const TileDrawQuad* tile_quad = CanPassBeDrawnDirectly(pass.get())) { + // If the render pass is drawn directly, it will not be drawn from as + // a render pass so it's not added to the map. render_pass_bypass_quads_[pass->id] = *tile_quad; continue; } } - render_passes_in_frame.insert( - std::pair<int, gfx::Size>(pass->id, RenderPassTextureSize(pass))); + render_passes_in_frame[pass->id] = RenderPassTextureSize(pass.get()); } std::vector<int> passes_to_delete; - for (auto pass_iter = render_pass_textures_.begin(); - pass_iter != render_pass_textures_.end(); ++pass_iter) { - auto it = render_passes_in_frame.find(pass_iter->first); + for (const auto& pair : render_pass_textures_) { + auto it = render_passes_in_frame.find(pair.first); if (it == render_passes_in_frame.end()) { - passes_to_delete.push_back(pass_iter->first); + passes_to_delete.push_back(pair.first); continue; } gfx::Size required_size = it->second; - ScopedResource* texture = pass_iter->second.get(); + ScopedResource* texture = pair.second.get(); DCHECK(texture); bool size_appropriate = texture->size().width() >= required_size.width() && @@ -290,14 +289,9 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order, for (const auto& pass : *render_passes_in_draw_order) { if (!pass->filters.IsEmpty()) - render_pass_filters_.push_back(std::make_pair(pass->id, &pass->filters)); - if (!pass->background_filters.IsEmpty()) { - render_pass_background_filters_.push_back( - std::make_pair(pass->id, &pass->background_filters)); - } - std::sort(render_pass_filters_.begin(), render_pass_filters_.end()); - std::sort(render_pass_background_filters_.begin(), - render_pass_background_filters_.end()); + render_pass_filters_[pass->id] = &pass->filters; + if (!pass->background_filters.IsEmpty()) + render_pass_background_filters_[pass->id] = &pass->background_filters; } // Draw all non-root render passes except for the root render pass. @@ -462,24 +456,14 @@ void DirectRenderer::DoDrawPolygon(const DrawPolygon& poly, const FilterOperations* DirectRenderer::FiltersForPass( int render_pass_id) const { - auto it = std::lower_bound( - render_pass_filters_.begin(), render_pass_filters_.end(), - std::pair<int, FilterOperations*>(render_pass_id, nullptr)); - if (it != render_pass_filters_.end() && it->first == render_pass_id) - return it->second; - return nullptr; + auto it = render_pass_filters_.find(render_pass_id); + return it == render_pass_filters_.end() ? nullptr : it->second; } const FilterOperations* DirectRenderer::BackgroundFiltersForPass( int render_pass_id) const { - auto it = std::lower_bound( - render_pass_background_filters_.begin(), - render_pass_background_filters_.end(), - std::pair<int, FilterOperations*>(render_pass_id, nullptr)); - if (it != render_pass_background_filters_.end() && - it->first == render_pass_id) - return it->second; - return nullptr; + auto it = render_pass_background_filters_.find(render_pass_id); + return it == render_pass_background_filters_.end() ? nullptr : it->second; } void DirectRenderer::FlushPolygons( diff --git a/chromium/cc/output/direct_renderer.h b/chromium/cc/output/direct_renderer.h index 864c1863b29..b5e58e443b7 100644 --- a/chromium/cc/output/direct_renderer.h +++ b/chromium/cc/output/direct_renderer.h @@ -6,10 +6,10 @@ #define CC_OUTPUT_DIRECT_RENDERER_H_ #include <memory> -#include <unordered_map> #include <vector> #include "base/callback.h" +#include "base/containers/flat_map.h" #include "base/macros.h" #include "cc/base/filter_operations.h" #include "cc/cc_export.h" @@ -198,13 +198,15 @@ class CC_EXPORT DirectRenderer { // DirectComposition layers needed to be used. int frames_since_using_dc_layers_ = 0; - // TODO(danakj): Just use a vector of pairs here? Hash map is way overkill. - std::unordered_map<int, std::unique_ptr<ScopedResource>> - render_pass_textures_; - std::unordered_map<int, TileDrawQuad> render_pass_bypass_quads_; + // A map from RenderPass id to the texture used to draw the RenderPass from. + base::flat_map<int, std::unique_ptr<ScopedResource>> render_pass_textures_; + // A map from RenderPass id to the single quad present in and replacing the + // RenderPass. + base::flat_map<int, TileDrawQuad> render_pass_bypass_quads_; - RenderPassFilterList render_pass_filters_; - RenderPassFilterList render_pass_background_filters_; + // A map from RenderPass id to the filters used when drawing the RenderPass. + base::flat_map<int, FilterOperations*> render_pass_filters_; + base::flat_map<int, FilterOperations*> render_pass_background_filters_; bool visible_ = false; bool disable_color_checks_for_testing_ = false; diff --git a/chromium/cc/output/gl_renderer.cc b/chromium/cc/output/gl_renderer.cc index 3446cc7d721..03a5ae29725 100644 --- a/chromium/cc/output/gl_renderer.cc +++ b/chromium/cc/output/gl_renderer.cc @@ -375,14 +375,12 @@ class GLRenderer::SyncQuery { GLRenderer::GLRenderer(const RendererSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider, - TextureMailboxDeleter* texture_mailbox_deleter, - int highp_threshold_min) + TextureMailboxDeleter* texture_mailbox_deleter) : DirectRenderer(settings, output_surface, resource_provider), shared_geometry_quad_(QuadVertexRect()), gl_(output_surface->context_provider()->ContextGL()), context_support_(output_surface->context_provider()->ContextSupport()), texture_mailbox_deleter_(texture_mailbox_deleter), - highp_threshold_min_(highp_threshold_min), gl_composited_texture_quad_border_( settings->gl_composited_texture_quad_border), bound_geometry_(NO_BINDING), @@ -677,6 +675,9 @@ static sk_sp<SkImage> ApplyImageFilter( return nullptr; } + // Big filters can sometimes fallback to CPU. Therefore, we need + // to disable subnormal floats for performance and security reasons. + ScopedSubnormalFloatDisabler disabler; SkMatrix local_matrix; local_matrix.setTranslate(origin.x(), origin.y()); local_matrix.postScale(scale.x(), scale.y()); @@ -946,6 +947,9 @@ sk_sp<SkImage> GLRenderer::ApplyBackgroundFilters( return nullptr; } + // Big filters can sometimes fallback to CPU. Therefore, we need + // to disable subnormal floats for performance and security reasons. + ScopedSubnormalFloatDisabler disabler; SkMatrix local_matrix; local_matrix.setScale(quad->filters_scale.x(), quad->filters_scale.y()); @@ -1000,7 +1004,7 @@ const TileDrawQuad* GLRenderer::CanPassBeDrawnDirectly(const RenderPass* pass) { quad->rect != pass->output_rect) return nullptr; // The quad is expected to be the entire layer so that AA edges are correct. - if (gfx::Rect(quad->shared_quad_state->quad_layer_bounds) != quad->rect) + if (quad->shared_quad_state->quad_layer_rect != quad->rect) return nullptr; if (quad->material != DrawQuad::TILED_CONTENT) return nullptr; @@ -1102,8 +1106,11 @@ bool GLRenderer::InitializeRPDQParameters( static_cast<float>(dst_rect.width()), static_cast<float>(dst_rect.height())); gfx::Transform quad_rect_matrix; + gfx::Rect quad_layer_rect(quad->shared_quad_state->quad_layer_rect); + if (params->filters) + quad_layer_rect = params->filters->MapRect(quad_layer_rect, local_matrix); QuadRectTransform(&quad_rect_matrix, params->quad_to_target_transform, - params->dst_rect); + gfx::RectF(quad_layer_rect)); params->contents_device_transform = params->window_matrix * params->projection_matrix * quad_rect_matrix; params->contents_device_transform.FlattenTo2d(); @@ -1112,10 +1119,10 @@ bool GLRenderer::InitializeRPDQParameters( if (!params->contents_device_transform.IsInvertible()) return false; + // TODO(sunxd): unify the anti-aliasing logic of RPDQ and TileDrawQuad. params->surface_quad = SharedGeometryQuad(); - gfx::QuadF device_layer_quad; - if (settings_->allow_antialiasing) { + if (settings_->allow_antialiasing && quad->IsEdge()) { bool clipped = false; device_layer_quad = MathUtil::MapQuad(params->contents_device_transform, params->surface_quad, &clipped); @@ -1305,7 +1312,7 @@ void GLRenderer::UpdateRPDQBlendMode(DrawRenderPassDrawQuadParams* params) { void GLRenderer::ChooseRPDQProgram(DrawRenderPassDrawQuadParams* params) { TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, highp_threshold_min_, + gl_, &highp_threshold_cache_, settings_->highp_threshold_min, params->quad->shared_quad_state->visible_quad_layer_rect.bottom_right()); BlendMode shader_blend_mode = @@ -1481,10 +1488,10 @@ bool is_bottom(const gfx::QuadF* clip_region, const DrawQuad* quad) { return true; return std::abs(clip_region->p3().y() - - quad->shared_quad_state->quad_layer_bounds.height()) < + quad->shared_quad_state->quad_layer_rect.height()) < kAntiAliasingEpsilon && std::abs(clip_region->p4().y() - - quad->shared_quad_state->quad_layer_bounds.height()) < + quad->shared_quad_state->quad_layer_rect.height()) < kAntiAliasingEpsilon; } @@ -1505,10 +1512,10 @@ bool is_right(const gfx::QuadF* clip_region, const DrawQuad* quad) { return true; return std::abs(clip_region->p2().x() - - quad->shared_quad_state->quad_layer_bounds.width()) < + quad->shared_quad_state->quad_layer_rect.width()) < kAntiAliasingEpsilon && std::abs(clip_region->p3().x() - - quad->shared_quad_state->quad_layer_bounds.width()) < + quad->shared_quad_state->quad_layer_rect.width()) < kAntiAliasingEpsilon; } } // anonymous namespace @@ -1890,7 +1897,8 @@ void GLRenderer::DrawContentQuadAA(const ContentDrawQuadBase* quad, float vertex_tex_scale_y = tile_rect.height() / clamp_geom_rect.height(); TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, highp_threshold_min_, quad->texture_size); + gl_, &highp_threshold_cache_, settings_->highp_threshold_min, + quad->texture_size); auto local_quad = gfx::QuadF(gfx::RectF(tile_rect)); float edge[24]; @@ -1991,7 +1999,8 @@ void GLRenderer::DrawContentQuadNoAA(const ContentDrawQuadBase* quad, } TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, highp_threshold_min_, quad->texture_size); + gl_, &highp_threshold_cache_, settings_->highp_threshold_min, + quad->texture_size); SetUseProgram( ProgramKey::Tile(tex_coord_precision, sampler, NO_AA, @@ -2051,7 +2060,7 @@ void GLRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, SetBlendEnabled(quad->ShouldDrawWithBlending()); TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, highp_threshold_min_, + gl_, &highp_threshold_cache_, settings_->highp_threshold_min, quad->shared_quad_state->visible_quad_layer_rect.bottom_right()); YUVAlphaTextureMode alpha_texture_mode = quad->a_plane_resource_id() ? YUV_HAS_ALPHA_TEXTURE @@ -2222,7 +2231,7 @@ void GLRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad, .egl_image_external); TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, highp_threshold_min_, + gl_, &highp_threshold_cache_, settings_->highp_threshold_min, quad->shared_quad_state->visible_quad_layer_rect.bottom_right()); ResourceProvider::ScopedReadLockGL lock(resource_provider_, @@ -2360,7 +2369,7 @@ void GLRenderer::EnqueueTextureQuad(const TextureDrawQuad* quad, } TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, highp_threshold_min_, + gl_, &highp_threshold_cache_, settings_->highp_threshold_min, quad->shared_quad_state->visible_quad_layer_rect.bottom_right()); ResourceProvider::ScopedReadLockGL lock(resource_provider_, @@ -2830,7 +2839,6 @@ void GLRenderer::FinishedReadback(unsigned source_buffer, if (src_pixels) { bitmap.reset(new SkBitmap); bitmap->allocN32Pixels(size.width(), size.height()); - std::unique_ptr<SkAutoLockPixels> lock(new SkAutoLockPixels(*bitmap)); uint8_t* dest_pixels = static_cast<uint8_t*>(bitmap->getPixels()); size_t row_bytes = size.width() * 4; @@ -3230,15 +3238,19 @@ void GLRenderer::ScheduleDCLayers() { current_frame()->dc_layer_overlay_list) { DCHECK(!dc_layer_overlay.rpdq); - unsigned texture_id = 0; + int i = 0; + unsigned texture_ids[DrawQuad::Resources::kMaxResourceIdCount] = {}; + int ids_to_send = 0; + for (const auto& contents_resource_id : dc_layer_overlay.resources) { if (contents_resource_id) { pending_overlay_resources_.push_back( base::MakeUnique<ResourceProvider::ScopedReadLockGL>( resource_provider_, contents_resource_id)); - if (!texture_id) - texture_id = pending_overlay_resources_.back()->texture_id(); + texture_ids[i] = pending_overlay_resources_.back()->texture_id(); + ids_to_send = i + 1; } + i++; } GLfloat contents_rect[4] = { dc_layer_overlay.contents_rect.x(), dc_layer_overlay.contents_rect.y(), @@ -3266,9 +3278,10 @@ void GLRenderer::ScheduleDCLayers() { dc_layer_overlay.shared_state->opacity, is_clipped, clip_rect, z_order, transform); } - gl_->ScheduleDCLayerCHROMIUM( - texture_id, contents_rect, dc_layer_overlay.background_color, - dc_layer_overlay.edge_aa_mask, bounds_rect, filter); + gl_->ScheduleDCLayerCHROMIUM(ids_to_send, texture_ids, contents_rect, + dc_layer_overlay.background_color, + dc_layer_overlay.edge_aa_mask, bounds_rect, + filter); } // Take the number of copied render passes in this frame, and use 3 times that @@ -3450,7 +3463,8 @@ void GLRenderer::ScheduleRenderPassDrawQuad( if (!overlay_resource_pool_) { overlay_resource_pool_ = ResourcePool::CreateForGpuMemoryBufferResources( resource_provider_, base::ThreadTaskRunnerHandle::Get().get(), - gfx::BufferUsage::SCANOUT, base::TimeDelta::FromSeconds(3)); + gfx::BufferUsage::SCANOUT, base::TimeDelta::FromSeconds(3), + settings_->disallow_non_exact_resource_reuse); } Resource* resource = nullptr; diff --git a/chromium/cc/output/gl_renderer.h b/chromium/cc/output/gl_renderer.h index 3562271d64c..e00b475fcd7 100644 --- a/chromium/cc/output/gl_renderer.h +++ b/chromium/cc/output/gl_renderer.h @@ -6,6 +6,7 @@ #define CC_OUTPUT_GL_RENDERER_H_ #include <deque> +#include <unordered_map> #include <vector> #include "base/cancelable_callback.h" @@ -51,8 +52,7 @@ class CC_EXPORT GLRenderer : public DirectRenderer { GLRenderer(const RendererSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider, - TextureMailboxDeleter* texture_mailbox_deleter, - int highp_threshold_min); + TextureMailboxDeleter* texture_mailbox_deleter); ~GLRenderer() override; bool use_swap_with_bounds() const { return use_swap_with_bounds_; } @@ -322,7 +322,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer { bool blend_shadow_ = false; const Program* current_program_ = nullptr; TexturedQuadDrawCache draw_cache_; - int highp_threshold_min_ = 0; int highp_threshold_cache_ = 0; struct PendingAsyncReadPixels; diff --git a/chromium/cc/output/gl_renderer_unittest.cc b/chromium/cc/output/gl_renderer_unittest.cc index ff42c7e6d07..f5802441730 100644 --- a/chromium/cc/output/gl_renderer_unittest.cc +++ b/chromium/cc/output/gl_renderer_unittest.cc @@ -374,7 +374,7 @@ class FakeRendererGL : public GLRenderer { FakeRendererGL(const RendererSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider) - : GLRenderer(settings, output_surface, resource_provider, nullptr, 0) {} + : GLRenderer(settings, output_surface, resource_provider, nullptr) {} FakeRendererGL(const RendererSettings* settings, OutputSurface* output_surface, @@ -383,8 +383,7 @@ class FakeRendererGL : public GLRenderer { : GLRenderer(settings, output_surface, resource_provider, - texture_mailbox_deleter, - 0) {} + texture_mailbox_deleter) {} void SetOverlayProcessor(OverlayProcessor* processor) { overlay_processor_.reset(processor); @@ -1977,7 +1976,7 @@ TEST_F(GLRendererTest, OverlaySyncTokensAreProcessed) { TextureDrawQuad* overlay_quad = root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), viewport_size, + shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size), gfx::Rect(viewport_size), gfx::Rect(viewport_size), false, 1, SkBlendMode::kSrcOver, 0); overlay_quad->SetNew(shared_state, gfx::Rect(viewport_size), @@ -2179,7 +2178,7 @@ TEST_F(GLRendererTest, DCLayerOverlaySwitch) { gfx::RectF tex_coord_rect(0, 0, 1, 1); SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), rect.size(), rect, rect, false, 1, + shared_state->SetAll(gfx::Transform(), rect, rect, rect, false, 1, SkBlendMode::kSrcOver, 0); YUVVideoDrawQuad* quad = root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>(); @@ -2237,9 +2236,8 @@ class GLRendererWithMockContextTest : public ::testing::Test { output_surface_->BindToClient(&output_surface_client_); resource_provider_ = FakeResourceProvider::Create( output_surface_->context_provider(), nullptr); - renderer_ = - base::MakeUnique<GLRenderer>(&settings_, output_surface_.get(), - resource_provider_.get(), nullptr, 0); + renderer_ = base::MakeUnique<GLRenderer>(&settings_, output_surface_.get(), + resource_provider_.get(), nullptr); renderer_->Initialize(); } diff --git a/chromium/cc/output/overlay_processor.cc b/chromium/cc/output/overlay_processor.cc index 594111bd786..bd3097fc375 100644 --- a/chromium/cc/output/overlay_processor.cc +++ b/chromium/cc/output/overlay_processor.cc @@ -62,8 +62,9 @@ gfx::Rect OverlayProcessor::GetAndResetOverlayDamage() { bool OverlayProcessor::ProcessForCALayers( ResourceProvider* resource_provider, RenderPass* render_pass, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, OverlayCandidateList* overlay_candidates, CALayerOverlayList* ca_layer_overlays, gfx::Rect* damage_rect) { @@ -90,8 +91,9 @@ bool OverlayProcessor::ProcessForCALayers( bool OverlayProcessor::ProcessForDCLayers( ResourceProvider* resource_provider, RenderPass* render_pass, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, OverlayCandidateList* overlay_candidates, DCLayerOverlayList* dc_layer_overlays, gfx::Rect* damage_rect) { @@ -111,8 +113,9 @@ bool OverlayProcessor::ProcessForDCLayers( void OverlayProcessor::ProcessForOverlays( ResourceProvider* resource_provider, RenderPass* render_pass, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, OverlayCandidateList* candidates, CALayerOverlayList* ca_layer_overlays, DCLayerOverlayList* dc_layer_overlays, @@ -125,14 +128,16 @@ void OverlayProcessor::ProcessForOverlays( SendPromotionHintsBeforeReturning notifier(resource_provider, candidates); #endif + // Reset |previous_frame_underlay_rect_| in case UpdateDamageRect() not being + // invoked. + const gfx::Rect previous_frame_underlay_rect = previous_frame_underlay_rect_; + previous_frame_underlay_rect_ = gfx::Rect(); + // If we have any copy requests, we can't remove any quads for overlays or // CALayers because the framebuffer would be missing the removed quads' // contents. if (!render_pass->copy_requests.empty()) { dc_processor_.ClearOverlayState(); - // If overlay processing was skipped for a frame there's no way to be sure - // of the state of the previous frame, so reset. - previous_frame_underlay_rect_ = gfx::Rect(); return; } @@ -155,7 +160,7 @@ void OverlayProcessor::ProcessForOverlays( content_bounds)) continue; - UpdateDamageRect(candidates, damage_rect); + UpdateDamageRect(candidates, previous_frame_underlay_rect, damage_rect); return; } } @@ -168,8 +173,10 @@ void OverlayProcessor::ProcessForOverlays( // not to swap the framebuffer there will still be a transparent hole in the // previous frame. This only handles the common case of a single underlay quad // for fullscreen video. -void OverlayProcessor::UpdateDamageRect(OverlayCandidateList* candidates, - gfx::Rect* damage_rect) { +void OverlayProcessor::UpdateDamageRect( + OverlayCandidateList* candidates, + const gfx::Rect& previous_frame_underlay_rect, + gfx::Rect* damage_rect) { gfx::Rect output_surface_overlay_damage_rect; gfx::Rect this_frame_underlay_rect; for (const OverlayCandidate& overlay : *candidates) { @@ -186,7 +193,7 @@ void OverlayProcessor::UpdateDamageRect(OverlayCandidateList* candidates, } } - if (this_frame_underlay_rect == previous_frame_underlay_rect_) + if (this_frame_underlay_rect == previous_frame_underlay_rect) damage_rect->Subtract(this_frame_underlay_rect); previous_frame_underlay_rect_ = this_frame_underlay_rect; diff --git a/chromium/cc/output/overlay_processor.h b/chromium/cc/output/overlay_processor.h index a00574e45c1..6262893755b 100644 --- a/chromium/cc/output/overlay_processor.h +++ b/chromium/cc/output/overlay_processor.h @@ -7,6 +7,7 @@ #include <memory> +#include "base/containers/flat_map.h" #include "base/macros.h" #include "cc/cc_export.h" #include "cc/output/ca_layer_overlay.h" @@ -46,8 +47,9 @@ class CC_EXPORT OverlayProcessor { void ProcessForOverlays( ResourceProvider* resource_provider, RenderPass* root_render_pass, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, OverlayCandidateList* overlay_candidates, CALayerOverlayList* ca_layer_overlays, DCLayerOverlayList* dc_layer_overlays, @@ -64,21 +66,24 @@ class CC_EXPORT OverlayProcessor { bool ProcessForCALayers( ResourceProvider* resource_provider, RenderPass* render_pass, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, OverlayCandidateList* overlay_candidates, CALayerOverlayList* ca_layer_overlays, gfx::Rect* damage_rect); bool ProcessForDCLayers( ResourceProvider* resource_provider, RenderPass* render_pass, - const RenderPassFilterList& render_pass_filters, - const RenderPassFilterList& render_pass_background_filters, + const base::flat_map<int, FilterOperations*>& render_pass_filters, + const base::flat_map<int, FilterOperations*>& + render_pass_background_filters, OverlayCandidateList* overlay_candidates, DCLayerOverlayList* dc_layer_overlays, gfx::Rect* damage_rect); // Update |damage_rect| by removing damage casued by |candidates|. void UpdateDamageRect(OverlayCandidateList* candidates, + const gfx::Rect& previous_frame_underlay_rect, gfx::Rect* damage_rect); DCLayerOverlayProcessor dc_processor_; diff --git a/chromium/cc/output/overlay_strategy_fullscreen.cc b/chromium/cc/output/overlay_strategy_fullscreen.cc index 2dd3e474268..e9401ec25c1 100644 --- a/chromium/cc/output/overlay_strategy_fullscreen.cc +++ b/chromium/cc/output/overlay_strategy_fullscreen.cc @@ -47,10 +47,6 @@ bool OverlayStrategyFullscreen::Attempt( return false; } - if (candidate.transform != gfx::OVERLAY_TRANSFORM_NONE) { - return false; - } - if (!candidate.display_rect.origin().IsOrigin() || gfx::ToRoundedSize(candidate.display_rect.size()) != render_pass->output_rect.size() || diff --git a/chromium/cc/output/overlay_strategy_single_on_top.cc b/chromium/cc/output/overlay_strategy_single_on_top.cc index 0cc5d998564..8cfda2864da 100644 --- a/chromium/cc/output/overlay_strategy_single_on_top.cc +++ b/chromium/cc/output/overlay_strategy_single_on_top.cc @@ -37,8 +37,6 @@ bool OverlayStrategySingleOnTop::Attempt( for (auto it = quad_list->begin(); it != quad_list->end(); ++it) { OverlayCandidate candidate; if (OverlayCandidate::FromDrawQuad(resource_provider, *it, &candidate) && - // TODO(dcastagna): Remove this once drm platform supports transforms. - candidate.transform == gfx::OVERLAY_TRANSFORM_NONE && !OverlayCandidate::IsOccluded(candidate, quad_list->cbegin(), it)) { // We currently reject quads with alpha that do not request alpha blending // since the alpha channel might not be set to 1 and we're not disabling diff --git a/chromium/cc/output/overlay_unittest.cc b/chromium/cc/output/overlay_unittest.cc index 9c077e90564..3a6156455b7 100644 --- a/chromium/cc/output/overlay_unittest.cc +++ b/chromium/cc/output/overlay_unittest.cc @@ -7,6 +7,7 @@ #include <utility> #include <vector> +#include "base/containers/flat_map.h" #include "base/memory/ptr_util.h" #include "base/test/scoped_feature_list.h" #include "cc/base/filter_operation.h" @@ -417,8 +418,8 @@ static void CompareRenderPassLists(const RenderPassList& expected_list, exp_iter != expected->quad_list.cend(); ++exp_iter, ++act_iter) { EXPECT_EQ(exp_iter->rect.ToString(), act_iter->rect.ToString()); - EXPECT_EQ(exp_iter->shared_quad_state->quad_layer_bounds.ToString(), - act_iter->shared_quad_state->quad_layer_bounds.ToString()); + EXPECT_EQ(exp_iter->shared_quad_state->quad_layer_rect.ToString(), + act_iter->shared_quad_state->quad_layer_rect.ToString()); } } } @@ -502,8 +503,8 @@ TEST_F(FullscreenOverlayTest, SuccessfulOverlay) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -528,8 +529,8 @@ TEST_F(FullscreenOverlayTest, AlphaFail) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -551,8 +552,8 @@ TEST_F(FullscreenOverlayTest, ResourceSizeInPixelsFail) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -578,8 +579,8 @@ TEST_F(FullscreenOverlayTest, OnTopFail) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -601,8 +602,8 @@ TEST_F(FullscreenOverlayTest, NotCoveringFullscreenFail) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -631,8 +632,8 @@ TEST_F(FullscreenOverlayTest, RemoveFullscreenQuadFromQuadList) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -661,8 +662,8 @@ TEST_F(SingleOverlayOnTopTest, SuccessfulOverlay) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -709,8 +710,8 @@ TEST_F(SingleOverlayOnTopTest, PrioritizeBiggerOne) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -750,8 +751,8 @@ TEST_F(SingleOverlayOnTopTest, DamageRect) { output_surface_plane.overlay_handled = true; candidate_list.push_back(output_surface_plane); - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -773,8 +774,8 @@ TEST_F(SingleOverlayOnTopTest, NoCandidates) { RenderPass::CopyAll(pass_list, &original_pass_list); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass_list.back().get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -802,8 +803,8 @@ TEST_F(SingleOverlayOnTopTest, OccludedCandidates) { RenderPass::CopyAll(pass_list, &original_pass_list); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass_list.back().get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -828,8 +829,8 @@ TEST_F(SingleOverlayOnTopTest, MultipleRenderPasses) { // Check for potential candidates. OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -847,8 +848,8 @@ TEST_F(SingleOverlayOnTopTest, AcceptBlending) { quad->opaque_rect = gfx::Rect(0, 0, 0, 0); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; damage_rect_ = quad->rect; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, @@ -867,8 +868,8 @@ TEST_F(SingleOverlayOnTopTest, RejectBackgroundColor) { quad->background_color = SK_ColorBLACK; OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -884,8 +885,8 @@ TEST_F(SingleOverlayOnTopTest, RejectBlendMode) { pass->shared_quad_state_list.back()->blend_mode = SkBlendMode::kScreen; OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -901,8 +902,8 @@ TEST_F(SingleOverlayOnTopTest, RejectOpacity) { pass->shared_quad_state_list.back()->opacity = 0.5f; OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -919,8 +920,8 @@ TEST_F(SingleOverlayOnTopTest, RejectNonAxisAlignedTransform) { ->quad_to_target_transform.RotateAboutXAxis(45.f); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -937,8 +938,8 @@ TEST_F(SingleOverlayOnTopTest, AllowClipped) { pass->shared_quad_state_list.back()->clip_rect = kOverlayClipRect; OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -956,8 +957,8 @@ TEST_F(UnderlayTest, AllowVerticalFlip) { pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(2.0f, -1.0f); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -978,8 +979,8 @@ TEST_F(UnderlayTest, AllowHorizontalFlip) { 2.0f); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -998,8 +999,8 @@ TEST_F(SingleOverlayOnTopTest, AllowPositiveScaleTransform) { pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(2.0f, 1.0f); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1007,23 +1008,23 @@ TEST_F(SingleOverlayOnTopTest, AllowPositiveScaleTransform) { EXPECT_EQ(1U, candidate_list.size()); } -TEST_F(SingleOverlayOnTopTest, RejectTransform) { +TEST_F(SingleOverlayOnTopTest, AcceptMirrorYTransform) { gfx::Rect rect = kOverlayRect; rect.Offset(0, -rect.height()); std::unique_ptr<RenderPass> pass = CreateRenderPass(); CreateCandidateQuadAt(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get(), rect); - pass->shared_quad_state_list.back() - ->quad_to_target_transform.RotateAboutZAxis(90.f); + pass->shared_quad_state_list.back()->quad_to_target_transform.Scale(1.f, + -1.f); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_); - ASSERT_EQ(0U, candidate_list.size()); + ASSERT_EQ(1U, candidate_list.size()); } TEST_F(UnderlayTest, Allow90DegreeRotation) { @@ -1036,8 +1037,8 @@ TEST_F(UnderlayTest, Allow90DegreeRotation) { ->quad_to_target_transform.RotateAboutZAxis(90.f); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1056,8 +1057,8 @@ TEST_F(UnderlayTest, Allow180DegreeRotation) { ->quad_to_target_transform.RotateAboutZAxis(180.f); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1076,8 +1077,8 @@ TEST_F(UnderlayTest, Allow270DegreeRotation) { ->quad_to_target_transform.RotateAboutZAxis(270.f); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1100,8 +1101,8 @@ TEST_F(SingleOverlayOnTopTest, AllowNotTopIfNotOccluded) { kOverlayBottomRightRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1124,8 +1125,8 @@ TEST_F(SingleOverlayOnTopTest, AllowTransparentOnTop) { kOverlayBottomRightRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1146,8 +1147,8 @@ TEST_F(SingleOverlayOnTopTest, AllowTransparentColorOnTop) { kOverlayBottomRightRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1167,8 +1168,8 @@ TEST_F(SingleOverlayOnTopTest, RejectOpaqueColorOnTop) { kOverlayBottomRightRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1186,8 +1187,8 @@ TEST_F(SingleOverlayOnTopTest, RejectTransparentColorOnTopWithoutBlending) { kOverlayBottomRightRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1202,8 +1203,8 @@ TEST_F(SingleOverlayOnTopTest, RejectVideoSwapTransform) { pass.get(), kSwapTransform); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1218,8 +1219,8 @@ TEST_F(UnderlayTest, AllowVideoXMirrorTransform) { pass.get(), kXMirrorTransform); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1234,8 +1235,8 @@ TEST_F(UnderlayTest, AllowVideoBothMirrorTransform) { pass.get(), kBothMirrorTransform); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1250,8 +1251,8 @@ TEST_F(UnderlayTest, AllowVideoNormalTransform) { pass.get(), kNormalTransform); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1266,8 +1267,8 @@ TEST_F(SingleOverlayOnTopTest, AllowVideoYMirrorTransform) { pass.get(), kYMirrorTransform); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1287,8 +1288,8 @@ TEST_F(UnderlayTest, OverlayLayerUnderMainLayer) { kOverlayBottomRightRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1310,8 +1311,8 @@ TEST_F(UnderlayTest, AllowOnTop) { pass->shared_quad_state_list.back(), pass.get()); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1332,8 +1333,8 @@ TEST_F(UnderlayTest, InitialUnderlayDamageNotSubtracted) { damage_rect_ = kOverlayRect; OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1358,8 +1359,8 @@ TEST_F(UnderlayTest, DamageSubtractedForConsecutiveIdenticalUnderlays) { pass->shared_quad_state_list.back(), pass.get()); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1388,8 +1389,8 @@ TEST_F(UnderlayTest, DamageNotSubtractedForNonIdenticalConsecutiveUnderlays) { damage_rect_ = overlay_rects[i]; OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1399,6 +1400,38 @@ TEST_F(UnderlayTest, DamageNotSubtractedForNonIdenticalConsecutiveUnderlays) { } } +// Underlay damage can only be subtracted if the previous frame's underlay +// exists. +TEST_F(UnderlayTest, DamageNotSubtractedForNonConsecutiveIdenticalUnderlays) { + bool has_fullscreen_candidate[] = {true, false, true}; + + for (int i = 0; i < 3; ++i) { + std::unique_ptr<RenderPass> pass = CreateRenderPass(); + + if (has_fullscreen_candidate[i]) { + CreateFullscreenCandidateQuad(resource_provider_.get(), + pass->shared_quad_state_list.back(), + pass.get()); + } + + damage_rect_ = kOverlayRect; + + // Add something behind it. + CreateFullscreenOpaqueQuad(resource_provider_.get(), + pass->shared_quad_state_list.back(), pass.get()); + + OverlayCandidateList candidate_list; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; + overlay_processor_->ProcessForOverlays( + resource_provider_.get(), pass.get(), render_pass_filters, + render_pass_background_filters, &candidate_list, nullptr, nullptr, + &damage_rect_, &content_bounds_); + } + + EXPECT_EQ(kOverlayRect, damage_rect_); +} + TEST_F(UnderlayTest, DamageNotSubtractedWhenQuadsAboveOverlap) { for (int i = 0; i < 2; ++i) { std::unique_ptr<RenderPass> pass = CreateRenderPass(); @@ -1412,8 +1445,8 @@ TEST_F(UnderlayTest, DamageNotSubtractedWhenQuadsAboveOverlap) { damage_rect_ = kOverlayRect; OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1440,8 +1473,8 @@ TEST_F(UnderlayTest, DamageSubtractedWhenQuadsAboveDontOverlap) { damage_rect_ = kOverlayBottomRightRect; OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1459,8 +1492,8 @@ TEST_F(UnderlayCastTest, NoOverlayContentBounds) { kOverlayTopLeftRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1475,8 +1508,8 @@ TEST_F(UnderlayCastTest, FullScreenOverlayContentBounds) { kOverlayRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1505,8 +1538,8 @@ TEST_F(UnderlayCastTest, BlackOutsideOverlayContentBounds) { SK_ColorBLACK); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1526,8 +1559,8 @@ TEST_F(UnderlayCastTest, OverlayOccludedContentBounds) { kOverlayRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1550,8 +1583,8 @@ TEST_F(UnderlayCastTest, OverlayOccludedUnionContentBounds) { kOverlayRect); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1580,8 +1613,8 @@ TEST_F(UnderlayCastTest, RoundOverlayContentBounds) { gfx::Rect(0, 0, 10, 10), SK_ColorWHITE); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1611,8 +1644,8 @@ TEST_F(UnderlayCastTest, RoundContentBounds) { gfx::Rect(0, 0, 255, 255), SK_ColorWHITE); OverlayCandidateList candidate_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &candidate_list, nullptr, nullptr, @@ -1645,8 +1678,8 @@ TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) { gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &overlay_list, &ca_layer_list, nullptr, @@ -1668,8 +1701,8 @@ TEST_F(CALayerOverlayTest, ThreeDTransform) { gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &overlay_list, &ca_layer_list, nullptr, @@ -1694,8 +1727,8 @@ TEST_F(CALayerOverlayTest, AllowContainingClip) { gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &overlay_list, &ca_layer_list, nullptr, @@ -1717,8 +1750,8 @@ TEST_F(CALayerOverlayTest, NontrivialClip) { gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &overlay_list, &ca_layer_list, nullptr, @@ -1742,8 +1775,8 @@ TEST_F(CALayerOverlayTest, SkipTransparent) { gfx::Rect damage_rect; CALayerOverlayList ca_layer_list; OverlayCandidateList overlay_list(BackbufferOverlayList(pass.get())); - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, render_pass_background_filters, &overlay_list, &ca_layer_list, nullptr, @@ -1768,8 +1801,8 @@ TEST_F(DCLayerOverlayTest, AllowNonAxisAlignedTransform) { gfx::Rect damage_rect; DCLayerOverlayList dc_layer_list; OverlayCandidateList overlay_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; damage_rect_ = gfx::Rect(1, 1, 10, 10); overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, @@ -1798,8 +1831,8 @@ TEST_F(DCLayerOverlayTest, Occluded) { gfx::Rect damage_rect; DCLayerOverlayList dc_layer_list; OverlayCandidateList overlay_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; damage_rect_ = gfx::Rect(1, 1, 10, 10); overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, @@ -1825,8 +1858,8 @@ TEST_F(DCLayerOverlayTest, Occluded) { gfx::Rect damage_rect; DCLayerOverlayList dc_layer_list; OverlayCandidateList overlay_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; damage_rect_ = gfx::Rect(1, 1, 10, 10); overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, @@ -1853,8 +1886,8 @@ TEST_F(DCLayerOverlayTest, DamageRect) { gfx::Rect damage_rect; DCLayerOverlayList dc_layer_list; OverlayCandidateList overlay_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; damage_rect_ = gfx::Rect(1, 1, 10, 10); overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, @@ -1899,8 +1932,8 @@ TEST_F(DCLayerOverlayTest, ClipRect) { DCLayerOverlayList dc_layer_list; OverlayCandidateList overlay_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; damage_rect_ = gfx::Rect(1, 1, 10, 10); overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, @@ -1934,8 +1967,8 @@ TEST_F(DCLayerOverlayTest, TransparentOnTop) { DCLayerOverlayList dc_layer_list; OverlayCandidateList overlay_list; - RenderPassFilterList render_pass_filters; - RenderPassFilterList render_pass_background_filters; + base::flat_map<int, FilterOperations*> render_pass_filters; + base::flat_map<int, FilterOperations*> render_pass_background_filters; damage_rect_ = gfx::Rect(1, 1, 10, 10); overlay_processor_->ProcessForOverlays( resource_provider_.get(), pass.get(), render_pass_filters, @@ -1954,7 +1987,7 @@ class OverlayInfoRendererGL : public GLRenderer { OverlayInfoRendererGL(const RendererSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider) - : GLRenderer(settings, output_surface, resource_provider, NULL, 0), + : GLRenderer(settings, output_surface, resource_provider, NULL), expect_overlays_(false) {} MOCK_METHOD2(DoDrawQuad, @@ -2494,8 +2527,8 @@ class CALayerOverlayRPDQTest : public CALayerOverlayTest { int render_pass_id_; FilterOperations filters_; FilterOperations background_filters_; - RenderPassFilterList render_pass_filters_; - RenderPassFilterList render_pass_background_filters_; + base::flat_map<int, FilterOperations*> render_pass_filters_; + base::flat_map<int, FilterOperations*> render_pass_background_filters_; CALayerOverlayList ca_layer_list_; OverlayCandidateList overlay_list_; }; @@ -2521,7 +2554,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadAllValidFilters) { filters_.Append(FilterOperation::CreateBlurFilter(0.9f)); filters_.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(10, 20), 1.0f, SK_ColorGREEN)); - render_pass_filters_.push_back(std::make_pair(render_pass_id_, &filters_)); + render_pass_filters_[render_pass_id_] = &filters_; quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF()); @@ -2532,7 +2565,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadAllValidFilters) { TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadOpacityFilterScale) { filters_.Append(FilterOperation::CreateOpacityFilter(0.8f)); - render_pass_filters_.push_back(std::make_pair(render_pass_id_, &filters_)); + render_pass_filters_[render_pass_id_] = &filters_; quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF()); @@ -2542,7 +2575,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadOpacityFilterScale) { TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBlurFilterScale) { filters_.Append(FilterOperation::CreateBlurFilter(0.8f)); - render_pass_filters_.push_back(std::make_pair(render_pass_id_, &filters_)); + render_pass_filters_[render_pass_id_] = &filters_; quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF()); @@ -2553,7 +2586,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBlurFilterScale) { TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadDropShadowFilterScale) { filters_.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(10, 20), 1.0f, SK_ColorGREEN)); - render_pass_filters_.push_back(std::make_pair(render_pass_id_, &filters_)); + render_pass_filters_[render_pass_id_] = &filters_; quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF()); @@ -2563,8 +2596,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadDropShadowFilterScale) { TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBackgroundFilter) { background_filters_.Append(FilterOperation::CreateGrayscaleFilter(0.1f)); - render_pass_background_filters_.push_back( - std::make_pair(render_pass_id_, &background_filters_)); + render_pass_background_filters_[render_pass_id_] = &background_filters_; quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF()); @@ -2582,7 +2614,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadMask) { TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadUnsupportedFilter) { filters_.Append(FilterOperation::CreateZoomFilter(0.9f, 1)); - render_pass_filters_.push_back(std::make_pair(render_pass_id_, &filters_)); + render_pass_filters_[render_pass_id_] = &filters_; quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF()); diff --git a/chromium/cc/output/renderer_pixeltest.cc b/chromium/cc/output/renderer_pixeltest.cc index 6fc2ac45522..e8215673291 100644 --- a/chromium/cc/output/renderer_pixeltest.cc +++ b/chromium/cc/output/renderer_pixeltest.cc @@ -62,7 +62,7 @@ SharedQuadState* CreateTestSharedQuadState( gfx::Transform quad_to_target_transform, const gfx::Rect& rect, RenderPass* render_pass) { - const gfx::Size layer_bounds = rect.size(); + const gfx::Rect layer_rect = rect; const gfx::Rect visible_layer_rect = rect; const gfx::Rect clip_rect = rect; const bool is_clipped = false; @@ -70,9 +70,9 @@ SharedQuadState* CreateTestSharedQuadState( const SkBlendMode blend_mode = SkBlendMode::kSrcOver; int sorting_context_id = 0; SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(quad_to_target_transform, layer_bounds, - visible_layer_rect, clip_rect, is_clipped, opacity, - blend_mode, sorting_context_id); + shared_state->SetAll(quad_to_target_transform, layer_rect, visible_layer_rect, + clip_rect, is_clipped, opacity, blend_mode, + sorting_context_id); return shared_state; } @@ -81,16 +81,16 @@ SharedQuadState* CreateTestSharedQuadStateClipped( const gfx::Rect& rect, const gfx::Rect& clip_rect, RenderPass* render_pass) { - const gfx::Size layer_bounds = rect.size(); + const gfx::Rect layer_rect = rect; const gfx::Rect visible_layer_rect = clip_rect; const bool is_clipped = true; const float opacity = 1.0f; const SkBlendMode blend_mode = SkBlendMode::kSrcOver; int sorting_context_id = 0; SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(quad_to_target_transform, layer_bounds, - visible_layer_rect, clip_rect, is_clipped, opacity, - blend_mode, sorting_context_id); + shared_state->SetAll(quad_to_target_transform, layer_rect, visible_layer_rect, + clip_rect, is_clipped, opacity, blend_mode, + sorting_context_id); return shared_state; } @@ -1983,12 +1983,10 @@ TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad) { ResourceId mask_resource_id = this->resource_provider_->CreateResource( mask_rect.size(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888, gfx::ColorSpace()); - { - SkAutoLockPixels lock(bitmap); - this->resource_provider_->CopyToResource( - mask_resource_id, reinterpret_cast<uint8_t*>(bitmap.getPixels()), - mask_rect.size()); - } + + this->resource_provider_->CopyToResource( + mask_resource_id, reinterpret_cast<uint8_t*>(bitmap.getPixels()), + mask_rect.size()); // This RenderPassDrawQuad does not include the full |viewport_rect| which is // the size of the child render pass. @@ -2078,12 +2076,10 @@ TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad2) { ResourceId mask_resource_id = this->resource_provider_->CreateResource( mask_rect.size(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888, gfx::ColorSpace()); - { - SkAutoLockPixels lock(bitmap); - this->resource_provider_->CopyToResource( - mask_resource_id, reinterpret_cast<uint8_t*>(bitmap.getPixels()), - mask_rect.size()); - } + + this->resource_provider_->CopyToResource( + mask_resource_id, reinterpret_cast<uint8_t*>(bitmap.getPixels()), + mask_rect.size()); // This RenderPassDrawQuad does not include the full |viewport_rect| which is // the size of the child render pass. @@ -2806,25 +2802,19 @@ TYPED_TEST(RendererPixelTest, TileDrawQuadNearestNeighbor) { SkBitmap bitmap; bitmap.allocN32Pixels(2, 2); - { - SkAutoLockPixels lock(bitmap); - SkCanvas canvas(bitmap); - draw_point_color(&canvas, 0, 0, SK_ColorGREEN); - draw_point_color(&canvas, 0, 1, SK_ColorBLUE); - draw_point_color(&canvas, 1, 0, SK_ColorBLUE); - draw_point_color(&canvas, 1, 1, SK_ColorGREEN); - } + SkCanvas canvas(bitmap); + draw_point_color(&canvas, 0, 0, SK_ColorGREEN); + draw_point_color(&canvas, 0, 1, SK_ColorBLUE); + draw_point_color(&canvas, 1, 0, SK_ColorBLUE); + draw_point_color(&canvas, 1, 1, SK_ColorGREEN); gfx::Size tile_size(2, 2); ResourceId resource = this->resource_provider_->CreateResource( tile_size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888, gfx::ColorSpace()); - { - SkAutoLockPixels lock(bitmap); - this->resource_provider_->CopyToResource( - resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size); - } + this->resource_provider_->CopyToResource( + resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size); int id = 1; gfx::Transform transform_to_root; @@ -2857,25 +2847,19 @@ TYPED_TEST(SoftwareRendererPixelTest, TextureDrawQuadNearestNeighbor) { SkBitmap bitmap; bitmap.allocN32Pixels(2, 2); - { - SkAutoLockPixels lock(bitmap); - SkCanvas canvas(bitmap); - draw_point_color(&canvas, 0, 0, SK_ColorGREEN); - draw_point_color(&canvas, 0, 1, SK_ColorBLUE); - draw_point_color(&canvas, 1, 0, SK_ColorBLUE); - draw_point_color(&canvas, 1, 1, SK_ColorGREEN); - } + SkCanvas canvas(bitmap); + draw_point_color(&canvas, 0, 0, SK_ColorGREEN); + draw_point_color(&canvas, 0, 1, SK_ColorBLUE); + draw_point_color(&canvas, 1, 0, SK_ColorBLUE); + draw_point_color(&canvas, 1, 1, SK_ColorGREEN); gfx::Size tile_size(2, 2); ResourceId resource = this->resource_provider_->CreateResource( tile_size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888, gfx::ColorSpace()); - { - SkAutoLockPixels lock(bitmap); - this->resource_provider_->CopyToResource( - resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size); - } + this->resource_provider_->CopyToResource( + resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size); int id = 1; gfx::Transform transform_to_root; @@ -2910,7 +2894,6 @@ TYPED_TEST(SoftwareRendererPixelTest, TextureDrawQuadLinear) { SkBitmap bitmap; bitmap.allocN32Pixels(2, 2); { - SkAutoLockPixels lock(bitmap); SkCanvas canvas(bitmap); draw_point_color(&canvas, 0, 0, SK_ColorGREEN); draw_point_color(&canvas, 0, 1, SK_ColorBLUE); @@ -2923,11 +2906,8 @@ TYPED_TEST(SoftwareRendererPixelTest, TextureDrawQuadLinear) { tile_size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888, gfx::ColorSpace()); - { - SkAutoLockPixels lock(bitmap); - this->resource_provider_->CopyToResource( - resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size); - } + this->resource_provider_->CopyToResource( + resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size); int id = 1; gfx::Transform transform_to_root; @@ -3286,12 +3266,10 @@ TEST_F(GLRendererPixelTest, TextureQuadBatching) { ResourceId resource = this->resource_provider_->CreateResource( mask_rect.size(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888, gfx::ColorSpace()); - { - SkAutoLockPixels lock(bitmap); - this->resource_provider_->CopyToResource( - resource, reinterpret_cast<uint8_t*>(bitmap.getPixels()), - mask_rect.size()); - } + + this->resource_provider_->CopyToResource( + resource, reinterpret_cast<uint8_t*>(bitmap.getPixels()), + mask_rect.size()); // Arbitrary dividing lengths to divide up the resource into 16 quads. int widths[] = { diff --git a/chromium/cc/output/renderer_settings.h b/chromium/cc/output/renderer_settings.h index f6afb55467a..913aa2d3cfb 100644 --- a/chromium/cc/output/renderer_settings.h +++ b/chromium/cc/output/renderer_settings.h @@ -25,7 +25,6 @@ class CC_EXPORT RendererSettings { bool partial_swap_enabled = false; bool finish_rendering_on_resize = false; bool should_clear_root_render_pass = true; - bool disable_display_vsync = false; bool release_overlay_resources_after_gpu_query = false; bool gl_composited_texture_quad_border = false; bool show_overdraw_feedback = false; @@ -37,6 +36,11 @@ class CC_EXPORT RendererSettings { bool use_gpu_memory_buffer_resources = false; ResourceFormat preferred_tile_format; BufferToTextureTargetMap buffer_to_texture_target_map; + + // Determines whether we disallow non-exact matches when finding resources + // in ResourcePool. Only used for layout or pixel tests, as non-deterministic + // resource sizes can lead to floating point error and noise in these tests. + bool disallow_non_exact_resource_reuse = false; }; } // namespace cc diff --git a/chromium/cc/output/software_renderer.cc b/chromium/cc/output/software_renderer.cc index 75dbff4a6e8..6fc30a038f0 100644 --- a/chromium/cc/output/software_renderer.cc +++ b/chromium/cc/output/software_renderer.cc @@ -99,19 +99,10 @@ bool SoftwareRenderer::FlippedFramebuffer() const { void SoftwareRenderer::EnsureScissorTestEnabled() { is_scissor_enabled_ = true; - SetClipRect(scissor_rect_); } void SoftwareRenderer::EnsureScissorTestDisabled() { - // There is no explicit notion of enabling/disabling scissoring in software - // rendering, but the underlying effect we want is to clear any existing - // clipRect on the current SkCanvas. This is done by setting clipRect to - // the viewport's dimensions. - if (!current_canvas_) - return; is_scissor_enabled_ = false; - SkISize size = current_canvas_->getBaseLayerSize(); - SetClipRect(gfx::Rect(size.width(), size.height())); } void SoftwareRenderer::BindFramebufferToOutputSurface() { @@ -140,7 +131,6 @@ bool SoftwareRenderer::BindFramebufferToTexture( void SoftwareRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) { is_scissor_enabled_ = true; scissor_rect_ = scissor_rect; - SetClipRect(scissor_rect); } void SoftwareRenderer::SetClipRect(const gfx::Rect& rect) { @@ -149,16 +139,29 @@ void SoftwareRenderer::SetClipRect(const gfx::Rect& rect) { // Skia applies the current matrix to clip rects so we reset it temporary. SkMatrix current_matrix = current_canvas_->getTotalMatrix(); current_canvas_->resetMatrix(); - // TODO(fmalita) stop using kReplace (see crbug.com/673851) - current_canvas_->clipRect(gfx::RectToSkRect(rect), - SkClipOp::kReplace_deprecated); + // SetClipRect is assumed to be applied temporarily, on an + // otherwise-unclipped canvas. + DCHECK_EQ(current_canvas_->getDeviceClipBounds().width(), + current_canvas_->imageInfo().width()); + DCHECK_EQ(current_canvas_->getDeviceClipBounds().height(), + current_canvas_->imageInfo().height()); + current_canvas_->clipRect(gfx::RectToSkRect(rect)); current_canvas_->setMatrix(current_matrix); } void SoftwareRenderer::ClearCanvas(SkColor color) { if (!current_canvas_) return; - current_canvas_->clear(color); + + if (is_scissor_enabled_) { + // The same paint used by SkCanvas::clear, but applied to the scissor rect. + SkPaint clear_paint; + clear_paint.setColor(color); + clear_paint.setBlendMode(SkBlendMode::kSrc); + current_canvas_->drawRect(gfx::RectToSkRect(scissor_rect_), clear_paint); + } else { + current_canvas_->clear(color); + } } void SoftwareRenderer::ClearFramebuffer() { @@ -208,11 +211,14 @@ void SoftwareRenderer::DoDrawQuad(const DrawQuad* quad, const gfx::QuadF* draw_region) { if (!current_canvas_) return; - if (draw_region) { - current_canvas_->save(); - } TRACE_EVENT0("cc", "SoftwareRenderer::DoDrawQuad"); + bool do_save = draw_region || is_scissor_enabled_; + SkAutoCanvasRestore canvas_restore(current_canvas_, do_save); + if (is_scissor_enabled_) { + SetClipRect(scissor_rect_); + } + gfx::Transform quad_rect_matrix; QuadRectTransform(&quad_rect_matrix, quad->shared_quad_state->quad_to_target_transform, @@ -298,9 +304,6 @@ void SoftwareRenderer::DoDrawQuad(const DrawQuad* quad, } current_canvas_->resetMatrix(); - if (draw_region) { - current_canvas_->restore(); - } } void SoftwareRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) { @@ -576,10 +579,11 @@ void SoftwareRenderer::CopyCurrentRenderPassToBitmap( gfx::Rect window_copy_rect = MoveFromDrawToWindowSpace(copy_rect); std::unique_ptr<SkBitmap> bitmap(new SkBitmap); - bitmap->setInfo(SkImageInfo::MakeN32Premul(window_copy_rect.width(), - window_copy_rect.height())); - current_canvas_->readPixels( - bitmap.get(), window_copy_rect.x(), window_copy_rect.y()); + bitmap->allocPixels(SkImageInfo::MakeN32Premul(window_copy_rect.width(), + window_copy_rect.height())); + if (!current_canvas_->readPixels(*bitmap, window_copy_rect.x(), + window_copy_rect.y())) + bitmap->reset(); request->SendBitmapResult(std::move(bitmap)); } @@ -654,9 +658,11 @@ sk_sp<SkImage> SoftwareRenderer::ApplyImageFilter( SkBitmap SoftwareRenderer::GetBackdropBitmap( const gfx::Rect& bounding_rect) const { SkBitmap bitmap; - bitmap.setInfo(SkImageInfo::MakeN32Premul(bounding_rect.width(), - bounding_rect.height())); - current_canvas_->readPixels(&bitmap, bounding_rect.x(), bounding_rect.y()); + bitmap.allocPixels(SkImageInfo::MakeN32Premul(bounding_rect.width(), + bounding_rect.height())); + if (!current_canvas_->readPixels(bitmap, bounding_rect.x(), + bounding_rect.y())) + bitmap.reset(); return bitmap; } diff --git a/chromium/cc/output/software_renderer_unittest.cc b/chromium/cc/output/software_renderer_unittest.cc index 15da1b622d1..19a68ca6340 100644 --- a/chromium/cc/output/software_renderer_unittest.cc +++ b/chromium/cc/output/software_renderer_unittest.cc @@ -27,6 +27,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" #include "ui/gfx/skia_util.h" namespace cc { @@ -104,7 +105,7 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) { gfx::Transform()); SharedQuadState* shared_quad_state = root_render_pass->CreateAndAppendSharedQuadState(); - shared_quad_state->SetAll(gfx::Transform(), outer_size, outer_rect, + shared_quad_state->SetAll(gfx::Transform(), outer_rect, outer_rect, outer_rect, false, 1.0, SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* inner_quad = root_render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); @@ -170,7 +171,7 @@ TEST_F(SoftwareRendererTest, TileQuad) { gfx::Transform()); SharedQuadState* shared_quad_state = root_render_pass->CreateAndAppendSharedQuadState(); - shared_quad_state->SetAll(gfx::Transform(), outer_size, outer_rect, + shared_quad_state->SetAll(gfx::Transform(), outer_rect, outer_rect, outer_rect, false, 1.0, SkBlendMode::kSrcOver, 0); TileDrawQuad* inner_quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>(); @@ -230,7 +231,7 @@ TEST_F(SoftwareRendererTest, TileQuadVisibleRect) { gfx::Transform()); SharedQuadState* shared_quad_state = root_render_pass->CreateAndAppendSharedQuadState(); - shared_quad_state->SetAll(gfx::Transform(), tile_size, tile_rect, tile_rect, + shared_quad_state->SetAll(gfx::Transform(), tile_rect, tile_rect, tile_rect, false, 1.0, SkBlendMode::kSrcOver, 0); TileDrawQuad* quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>(); @@ -378,26 +379,45 @@ TEST_F(SoftwareRendererTest, RenderPassVisibleRect) { interior_visible_rect.bottom() - 1)); } +class ClipTrackingCanvas : public SkNWayCanvas { + public: + ClipTrackingCanvas(int width, int height) : SkNWayCanvas(width, height) {} + void onClipRect(const SkRect& rect, + SkClipOp op, + ClipEdgeStyle style) override { + last_clip_rect_ = rect; + SkNWayCanvas::onClipRect(rect, op, style); + } + + SkRect last_clip_rect() const { return last_clip_rect_; } + + private: + SkRect last_clip_rect_; +}; + class PartialSwapSoftwareOutputDevice : public SoftwareOutputDevice { public: // SoftwareOutputDevice overrides. SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override { damage_rect_at_start_ = damage_rect; - canvas_ = SoftwareOutputDevice::BeginPaint(damage_rect); - return canvas_; + canvas_.reset(new ClipTrackingCanvas(viewport_pixel_size_.width(), + viewport_pixel_size_.height())); + canvas_->addCanvas(SoftwareOutputDevice::BeginPaint(damage_rect)); + return canvas_.get(); } + void EndPaint() override { - clip_rect_at_end_ = gfx::SkIRectToRect(canvas_->getDeviceClipBounds()); + clip_rect_at_end_ = gfx::SkRectToRectF(canvas_->last_clip_rect()); SoftwareOutputDevice::EndPaint(); } gfx::Rect damage_rect_at_start() const { return damage_rect_at_start_; } - gfx::Rect clip_rect_at_end() const { return clip_rect_at_end_; } + gfx::RectF clip_rect_at_end() const { return clip_rect_at_end_; } private: - SkCanvas* canvas_ = nullptr; + std::unique_ptr<ClipTrackingCanvas> canvas_; gfx::Rect damage_rect_at_start_; - gfx::Rect clip_rect_at_end_; + gfx::RectF clip_rect_at_end_; }; TEST_F(SoftwareRendererTest, PartialSwap) { @@ -428,7 +448,7 @@ TEST_F(SoftwareRendererTest, PartialSwap) { // The damage rect should be reported to the SoftwareOutputDevice. EXPECT_EQ(gfx::Rect(2, 2, 3, 3), device->damage_rect_at_start()); // The SkCanvas should be clipped to the damage rect. - EXPECT_EQ(gfx::Rect(2, 2, 3, 3), device->clip_rect_at_end()); + EXPECT_EQ(gfx::RectF(2, 2, 3, 3), device->clip_rect_at_end()); } } // namespace diff --git a/chromium/cc/paint/BUILD.gn b/chromium/cc/paint/BUILD.gn index c442f045829..7669b28f3a8 100644 --- a/chromium/cc/paint/BUILD.gn +++ b/chromium/cc/paint/BUILD.gn @@ -15,6 +15,8 @@ cc_component("paint") { "compositing_display_item.h", "discardable_image_map.cc", "discardable_image_map.h", + "discardable_image_store.cc", + "discardable_image_store.h", "display_item.h", "display_item_list.cc", "display_item_list.h", @@ -32,11 +34,19 @@ cc_component("paint") { "paint_canvas.cc", "paint_canvas.h", "paint_export.h", + "paint_flags.cc", "paint_flags.h", + "paint_image.cc", + "paint_image.h", + "paint_op_buffer.cc", + "paint_op_buffer.h", + "paint_record.cc", "paint_record.h", "paint_recorder.cc", "paint_recorder.h", "paint_shader.h", + "record_paint_canvas.cc", + "record_paint_canvas.h", "skia_paint_canvas.cc", "skia_paint_canvas.h", "transform_display_item.cc", diff --git a/chromium/cc/paint/clip_display_item.h b/chromium/cc/paint/clip_display_item.h index a969d48dd5b..6a5bf0cf694 100644 --- a/chromium/cc/paint/clip_display_item.h +++ b/chromium/cc/paint/clip_display_item.h @@ -26,7 +26,7 @@ class CC_PAINT_EXPORT ClipDisplayItem : public DisplayItem { size_t ExternalMemoryUsage() const { return rounded_clip_rects.capacity() * sizeof(rounded_clip_rects[0]); } - int ApproximateOpCount() const { return 1; } + int OpCount() const { return 1; } const gfx::Rect clip_rect; const std::vector<SkRRect> rounded_clip_rects; @@ -38,7 +38,7 @@ class CC_PAINT_EXPORT EndClipDisplayItem : public DisplayItem { EndClipDisplayItem(); ~EndClipDisplayItem() override; - int ApproximateOpCount() const { return 0; } + int OpCount() const { return 0; } }; } // namespace cc diff --git a/chromium/cc/paint/clip_path_display_item.h b/chromium/cc/paint/clip_path_display_item.h index 54de9992233..960697dc62e 100644 --- a/chromium/cc/paint/clip_path_display_item.h +++ b/chromium/cc/paint/clip_path_display_item.h @@ -23,7 +23,7 @@ class CC_PAINT_EXPORT ClipPathDisplayItem : public DisplayItem { // may well be shared anyway). return 0; } - int ApproximateOpCount() const { return 1; } + int OpCount() const { return 1; } const SkPath clip_path; const bool antialias; @@ -34,7 +34,7 @@ class CC_PAINT_EXPORT EndClipPathDisplayItem : public DisplayItem { EndClipPathDisplayItem(); ~EndClipPathDisplayItem() override; - int ApproximateOpCount() const { return 0; } + int OpCount() const { return 0; } }; } // namespace cc diff --git a/chromium/cc/paint/compositing_display_item.h b/chromium/cc/paint/compositing_display_item.h index 266ab4d2ea8..ba14de004b8 100644 --- a/chromium/cc/paint/compositing_display_item.h +++ b/chromium/cc/paint/compositing_display_item.h @@ -29,7 +29,7 @@ class CC_PAINT_EXPORT CompositingDisplayItem : public DisplayItem { // TODO(pdr): Include color_filter's memory here. return 0; } - int ApproximateOpCount() const { return 1; } + int OpCount() const { return 1; } const uint8_t alpha; const SkBlendMode xfermode; @@ -44,7 +44,7 @@ class CC_PAINT_EXPORT EndCompositingDisplayItem : public DisplayItem { EndCompositingDisplayItem(); ~EndCompositingDisplayItem() override; - int ApproximateOpCount() const { return 0; } + int OpCount() const { return 0; } }; } // namespace cc diff --git a/chromium/cc/paint/discardable_image_map.cc b/chromium/cc/paint/discardable_image_map.cc index c1a3b57ccb5..4b6c5824089 100644 --- a/chromium/cc/paint/discardable_image_map.cc +++ b/chromium/cc/paint/discardable_image_map.cc @@ -9,260 +9,19 @@ #include <algorithm> #include <limits> -#include "base/containers/adapters.h" #include "base/memory/ptr_util.h" -#include "cc/base/math_util.h" -#include "cc/paint/display_item_list.h" -#include "third_party/skia/include/core/SkPath.h" -#include "third_party/skia/include/utils/SkNWayCanvas.h" -#include "ui/gfx/geometry/rect_conversions.h" -#include "ui/gfx/skia_util.h" +#include "cc/paint/discardable_image_store.h" namespace cc { -SkRect MapRect(const SkMatrix& matrix, const SkRect& src) { - SkRect dst; - matrix.mapRect(&dst, src); - return dst; -} - -// Returns a rect clamped to |max_size|. Note that |paint_rect| should intersect -// or be contained by a rect defined by (0, 0) and |max_size|. -gfx::Rect SafeClampPaintRectToSize(const SkRect& paint_rect, - const gfx::Size& max_size) { - // bounds_rect.x() + bounds_rect.width() (aka bounds_rect.right()) might - // overflow integer bounds, so do custom intersect, since gfx::Rect::Intersect - // uses bounds_rect.right(). - gfx::RectF bounds_rect = gfx::SkRectToRectF(paint_rect); - float x_offset_if_negative = bounds_rect.x() < 0.f ? bounds_rect.x() : 0.f; - float y_offset_if_negative = bounds_rect.y() < 0.f ? bounds_rect.y() : 0.f; - bounds_rect.set_x(std::max(0.f, bounds_rect.x())); - bounds_rect.set_y(std::max(0.f, bounds_rect.y())); - - // Verify that the rects intersect or that bound_rect is contained by - // max_size. - DCHECK_GE(bounds_rect.width(), -x_offset_if_negative); - DCHECK_GE(bounds_rect.height(), -y_offset_if_negative); - DCHECK_GE(max_size.width(), bounds_rect.x()); - DCHECK_GE(max_size.height(), bounds_rect.y()); - - bounds_rect.set_width(std::min(bounds_rect.width() + x_offset_if_negative, - max_size.width() - bounds_rect.x())); - bounds_rect.set_height(std::min(bounds_rect.height() + y_offset_if_negative, - max_size.height() - bounds_rect.y())); - return gfx::ToEnclosingRect(bounds_rect); -} - -namespace { - -// We're using an NWay canvas with no added canvases, so in effect -// non-overridden functions are no-ops. -class DiscardableImagesMetadataCanvas : public SkNWayCanvas { - public: - DiscardableImagesMetadataCanvas( - int width, - int height, - std::vector<std::pair<DrawImage, gfx::Rect>>* image_set, - std::unordered_map<ImageId, gfx::Rect>* image_id_to_rect) - : SkNWayCanvas(width, height), - image_set_(image_set), - image_id_to_rect_(image_id_to_rect), - canvas_bounds_(SkRect::MakeIWH(width, height)), - canvas_size_(width, height) {} - - protected: - // we need to "undo" the behavior of SkNWayCanvas, which will try to forward - // it. - void onDrawPicture(const SkPicture* picture, - const SkMatrix* matrix, - const SkPaint* paint) override { - SkCanvas::onDrawPicture(picture, matrix, paint); - } - - void onDrawImage(const SkImage* image, - SkScalar x, - SkScalar y, - const SkPaint* paint) override { - const SkMatrix& ctm = getTotalMatrix(); - AddImage( - sk_ref_sp(image), SkRect::MakeIWH(image->width(), image->height()), - MapRect(ctm, SkRect::MakeXYWH(x, y, image->width(), image->height())), - ctm, paint); - } - - void onDrawImageRect(const SkImage* image, - const SkRect* src, - const SkRect& dst, - const SkPaint* paint, - SrcRectConstraint) override { - const SkMatrix& ctm = getTotalMatrix(); - SkRect src_storage; - if (!src) { - src_storage = SkRect::MakeIWH(image->width(), image->height()); - src = &src_storage; - } - SkMatrix matrix; - matrix.setRectToRect(*src, dst, SkMatrix::kFill_ScaleToFit); - matrix.preConcat(ctm); - AddImage(sk_ref_sp(image), *src, MapRect(ctm, dst), matrix, paint); - } - - void onDrawImageNine(const SkImage* image, - const SkIRect& center, - const SkRect& dst, - const SkPaint* paint) override { - // No cc embedder issues image nine calls. - NOTREACHED(); - } - - void onDrawRect(const SkRect& r, const SkPaint& paint) override { - AddPaintImage(r, paint); - } - - void onDrawPath(const SkPath& path, const SkPaint& paint) override { - AddPaintImage(path.getBounds(), paint); - } - - void onDrawOval(const SkRect& r, const SkPaint& paint) override { - AddPaintImage(r, paint); - } - - void onDrawArc(const SkRect& r, - SkScalar start_angle, - SkScalar sweep_angle, - bool use_center, - const SkPaint& paint) override { - AddPaintImage(r, paint); - } - - void onDrawRRect(const SkRRect& rr, const SkPaint& paint) override { - AddPaintImage(rr.rect(), paint); - } - - SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { - saved_paints_.push_back(rec.fPaint ? *rec.fPaint : SkPaint()); - return SkNWayCanvas::getSaveLayerStrategy(rec); - } - - void willSave() override { - saved_paints_.push_back(SkPaint()); - return SkNWayCanvas::willSave(); - } - - void willRestore() override { - DCHECK_GT(saved_paints_.size(), 0u); - saved_paints_.pop_back(); - SkNWayCanvas::willRestore(); - } - - private: - bool ComputePaintBounds(const SkRect& rect, - const SkPaint* current_paint, - SkRect* paint_bounds) { - *paint_bounds = rect; - if (current_paint) { - if (!current_paint->canComputeFastBounds()) - return false; - *paint_bounds = - current_paint->computeFastBounds(*paint_bounds, paint_bounds); - } - - for (const auto& paint : base::Reversed(saved_paints_)) { - if (!paint.canComputeFastBounds()) - return false; - *paint_bounds = paint.computeFastBounds(*paint_bounds, paint_bounds); - } - - return true; - } - - void AddImage(sk_sp<const SkImage> image, - const SkRect& src_rect, - const SkRect& rect, - const SkMatrix& matrix, - const SkPaint* paint) { - if (!image->isLazyGenerated()) - return; - - SkRect paint_rect; - bool computed_paint_bounds = ComputePaintBounds(rect, paint, &paint_rect); - if (!computed_paint_bounds) { - // TODO(vmpstr): UMA this case. - paint_rect = canvas_bounds_; - } - - if (!paint_rect.intersects(canvas_bounds_)) - return; - - SkFilterQuality filter_quality = kNone_SkFilterQuality; - if (paint) { - filter_quality = paint->getFilterQuality(); - } - - SkIRect src_irect; - src_rect.roundOut(&src_irect); - gfx::Rect image_rect = SafeClampPaintRectToSize(paint_rect, canvas_size_); - - // During raster, we use the device clip bounds on the canvas, which outsets - // the actual clip by 1 due to the possibility of antialiasing. Account for - // this here by outsetting the image rect by 1. Note that this only affects - // queries into the rtree, which will now return images that only touch the - // bounds of the query rect. - // - // Note that it's not sufficient for us to inset the device clip bounds at - // raster time, since we might be sending a larger-than-one-item display - // item to skia, which means that skia will internally determine whether to - // raster the picture (using device clip bounds that are outset). - image_rect.Inset(-1, -1); - - // The true target color space will be assigned when it is known, in - // GetDiscardableImagesInRect. - gfx::ColorSpace target_color_space; - - (*image_id_to_rect_)[image->uniqueID()].Union(image_rect); - image_set_->push_back( - std::make_pair(DrawImage(std::move(image), src_irect, filter_quality, - matrix, target_color_space), - image_rect)); - } - - // Currently this function only handles extracting images from SkImageShaders - // embedded in SkPaints. Other embedded image cases, such as SkPictures, - // are not yet handled. - void AddPaintImage(const SkRect& rect, const SkPaint& paint) { - SkShader* shader = paint.getShader(); - if (shader) { - SkMatrix matrix; - SkShader::TileMode xy[2]; - SkImage* image = shader->isAImage(&matrix, xy); - if (image) { - const SkMatrix& ctm = getTotalMatrix(); - matrix.postConcat(ctm); - // TODO(ericrk): Handle cases where we only need a sub-rect from the - // image. crbug.com/671821 - AddImage(sk_ref_sp(image), SkRect::MakeFromIRect(image->bounds()), - MapRect(ctm, rect), matrix, &paint); - } - } - } - - std::vector<std::pair<DrawImage, gfx::Rect>>* image_set_; - std::unordered_map<ImageId, gfx::Rect>* image_id_to_rect_; - const SkRect canvas_bounds_; - const gfx::Size canvas_size_; - std::vector<SkPaint> saved_paints_; -}; - -} // namespace - DiscardableImageMap::DiscardableImageMap() {} DiscardableImageMap::~DiscardableImageMap() {} -std::unique_ptr<SkCanvas> DiscardableImageMap::BeginGeneratingMetadata( - const gfx::Size& bounds) { +std::unique_ptr<DiscardableImageStore> +DiscardableImageMap::BeginGeneratingMetadata(const gfx::Size& bounds) { DCHECK(all_images_.empty()); - return base::MakeUnique<DiscardableImagesMetadataCanvas>( + return base::MakeUnique<DiscardableImageStore>( bounds.width(), bounds.height(), &all_images_, &image_id_to_rect_); } @@ -278,16 +37,14 @@ void DiscardableImageMap::GetDiscardableImagesInRect( float contents_scale, const gfx::ColorSpace& target_color_space, std::vector<DrawImage>* images) const { - std::vector<size_t> indices; - images_rtree_.Search(rect, &indices); - for (size_t index : indices) { + for (size_t index : images_rtree_.Search(rect)) { images->push_back(all_images_[index] .first.ApplyScale(contents_scale) .ApplyTargetColorSpace(target_color_space)); } } -gfx::Rect DiscardableImageMap::GetRectForImage(ImageId image_id) const { +gfx::Rect DiscardableImageMap::GetRectForImage(PaintImage::Id image_id) const { const auto& it = image_id_to_rect_.find(image_id); return it == image_id_to_rect_.end() ? gfx::Rect() : it->second; } @@ -296,7 +53,7 @@ DiscardableImageMap::ScopedMetadataGenerator::ScopedMetadataGenerator( DiscardableImageMap* image_map, const gfx::Size& bounds) : image_map_(image_map), - metadata_canvas_(image_map->BeginGeneratingMetadata(bounds)) {} + image_store_(image_map->BeginGeneratingMetadata(bounds)) {} DiscardableImageMap::ScopedMetadataGenerator::~ScopedMetadataGenerator() { image_map_->EndGeneratingMetadata(); diff --git a/chromium/cc/paint/discardable_image_map.h b/chromium/cc/paint/discardable_image_map.h index 0e006838834..f705e89355d 100644 --- a/chromium/cc/paint/discardable_image_map.h +++ b/chromium/cc/paint/discardable_image_map.h @@ -5,23 +5,23 @@ #ifndef CC_PAINT_DISCARDABLE_IMAGE_MAP_H_ #define CC_PAINT_DISCARDABLE_IMAGE_MAP_H_ -#include <unordered_map> #include <utility> #include <vector> +#include "base/containers/flat_map.h" #include "cc/base/rtree.h" #include "cc/paint/draw_image.h" #include "cc/paint/image_id.h" #include "cc/paint/paint_export.h" +#include "cc/paint/paint_flags.h" +#include "cc/paint/paint_image.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" namespace cc { - -// Helper function to apply the matrix to the rect and return the result. -SkRect MapRect(const SkMatrix& matrix, const SkRect& src); +class DiscardableImageStore; // This class is used for generating discardable images data (see DrawImage // for the type of data it stores). It allows the client to query a particular @@ -34,11 +34,11 @@ class CC_PAINT_EXPORT DiscardableImageMap { const gfx::Size& bounds); ~ScopedMetadataGenerator(); - SkCanvas* canvas() { return metadata_canvas_.get(); } + DiscardableImageStore* image_store() { return image_store_.get(); } private: DiscardableImageMap* image_map_; - std::unique_ptr<SkCanvas> metadata_canvas_; + std::unique_ptr<DiscardableImageStore> image_store_; }; DiscardableImageMap(); @@ -49,17 +49,18 @@ class CC_PAINT_EXPORT DiscardableImageMap { float contents_scale, const gfx::ColorSpace& target_color_space, std::vector<DrawImage>* images) const; - gfx::Rect GetRectForImage(ImageId image_id) const; + gfx::Rect GetRectForImage(PaintImage::Id image_id) const; private: friend class ScopedMetadataGenerator; friend class DiscardableImageMapTest; - std::unique_ptr<SkCanvas> BeginGeneratingMetadata(const gfx::Size& bounds); + std::unique_ptr<DiscardableImageStore> BeginGeneratingMetadata( + const gfx::Size& bounds); void EndGeneratingMetadata(); std::vector<std::pair<DrawImage, gfx::Rect>> all_images_; - std::unordered_map<ImageId, gfx::Rect> image_id_to_rect_; + base::flat_map<PaintImage::Id, gfx::Rect> image_id_to_rect_; RTree images_rtree_; }; diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc index eb687ad27df..756a9c35d07 100644 --- a/chromium/cc/paint/discardable_image_map_unittest.cc +++ b/chromium/cc/paint/discardable_image_map_unittest.cc @@ -11,6 +11,10 @@ #include "base/memory/ref_counted.h" #include "base/values.h" #include "cc/base/region.h" +#include "cc/paint/clip_display_item.h" +#include "cc/paint/discardable_image_store.h" +#include "cc/paint/paint_flags.h" +#include "cc/paint/paint_recorder.h" #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_recording_source.h" #include "cc/test/skia_common.h" @@ -25,16 +29,30 @@ namespace cc { namespace { +PaintImage CreateDiscardablePaintImage(const gfx::Size& size) { + return PaintImage(PaintImage::GetNextId(), CreateDiscardableImage(size)); +} + struct PositionScaleDrawImage { - PositionScaleDrawImage(sk_sp<const SkImage> image, + PositionScaleDrawImage(const PaintImage& image, const gfx::Rect& image_rect, const SkSize& scale) - : image(std::move(image)), image_rect(image_rect), scale(scale) {} - sk_sp<const SkImage> image; + : image(image), image_rect(image_rect), scale(scale) {} + PaintImage image; gfx::Rect image_rect; SkSize scale; }; +sk_sp<PaintRecord> CreateRecording(const PaintImage& discardable_image, + const gfx::Rect& visible_rect) { + PaintRecorder recorder; + PaintCanvas* canvas = + recorder.beginRecording(visible_rect.width(), visible_rect.height()); + canvas->drawImage(discardable_image, 0, 0, nullptr); + sk_sp<PaintRecord> record = recorder.finishRecordingAsPicture(); + return record; +} + } // namespace class DiscardableImageMapTest : public testing::Test { @@ -49,19 +67,18 @@ class DiscardableImageMapTest : public testing::Test { image_map.GetDiscardableImagesInRect(rect, 1.f, target_color_space, &draw_images); - std::vector<size_t> indices; - image_map.images_rtree_.Search(rect, &indices); std::vector<PositionScaleDrawImage> position_draw_images; - for (size_t index : indices) { - position_draw_images.push_back( - PositionScaleDrawImage(image_map.all_images_[index].first.image(), - image_map.all_images_[index].second, - image_map.all_images_[index].first.scale())); + for (size_t index : image_map.images_rtree_.Search(rect)) { + position_draw_images.push_back(PositionScaleDrawImage( + image_map.all_images_[index].first.paint_image(), + image_map.all_images_[index].second, + image_map.all_images_[index].first.scale())); } EXPECT_EQ(draw_images.size(), position_draw_images.size()); for (size_t i = 0; i < draw_images.size(); ++i) { - EXPECT_TRUE(draw_images[i].image() == position_draw_images[i].image); + EXPECT_TRUE(draw_images[i].paint_image() == + position_draw_images[i].image); EXPECT_EQ(draw_images[i].target_color_space(), target_color_space); } return position_draw_images; @@ -95,11 +112,12 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectTest) { // |---|---|---|---| // | x | | x | | // |---|---|---|---| - sk_sp<SkImage> discardable_image[4][4]; + PaintImage discardable_image[4][4]; for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { if ((x + y) & 1) { - discardable_image[y][x] = CreateDiscardableImage(gfx::Size(500, 500)); + discardable_image[y][x] = + CreateDiscardablePaintImage(gfx::Size(500, 500)); PaintFlags flags; content_layer_client.add_draw_image( discardable_image[y][x], gfx::Point(x * 512 + 6, y * 512 + 6), @@ -111,13 +129,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectTest) { scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); - DiscardableImageMap image_map; - { - DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, - visible_rect.size()); - display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f); - } + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { @@ -131,7 +146,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectTest) { EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500), inset_rects[0]); EXPECT_EQ(images[0].image_rect, - image_map.GetRectForImage(images[0].image->uniqueID())); + image_map.GetRectForImage(images[0].image.stable_id())); } else { EXPECT_EQ(0u, images.size()) << x << " " << y; } @@ -147,22 +162,22 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectTest) { EXPECT_TRUE(images[0].image == discardable_image[1][2]); EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]); EXPECT_EQ(images[0].image_rect, - image_map.GetRectForImage(images[0].image->uniqueID())); + image_map.GetRectForImage(images[0].image.stable_id())); EXPECT_TRUE(images[1].image == discardable_image[2][1]); EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]); EXPECT_EQ(images[1].image_rect, - image_map.GetRectForImage(images[1].image->uniqueID())); + image_map.GetRectForImage(images[1].image.stable_id())); EXPECT_TRUE(images[2].image == discardable_image[2][3]); EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[2]); EXPECT_EQ(images[2].image_rect, - image_map.GetRectForImage(images[2].image->uniqueID())); + image_map.GetRectForImage(images[2].image.stable_id())); EXPECT_TRUE(images[3].image == discardable_image[3][2]); EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500), inset_rects[3]); EXPECT_EQ(images[3].image_rect, - image_map.GetRectForImage(images[3].image->uniqueID())); + image_map.GetRectForImage(images[3].image.stable_id())); } TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) { @@ -182,11 +197,12 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) { // |---|---|---|---| // | x | | x | | // |---|---|---|---| - sk_sp<SkImage> discardable_image[4][4]; + PaintImage discardable_image[4][4]; for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { if ((x + y) & 1) { - discardable_image[y][x] = CreateDiscardableImage(gfx::Size(500, 500)); + discardable_image[y][x] = + CreateDiscardablePaintImage(gfx::Size(500, 500)); PaintFlags flags; content_layer_client.add_draw_image( discardable_image[y][x], @@ -198,13 +214,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) { scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); - DiscardableImageMap image_map; - { - DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, - layer_size); - display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f); - } + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { @@ -218,7 +231,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) { EXPECT_EQ(gfx::Rect(1024 + x * 512 + 6, y * 512 + 6, 500, 500), inset_rects[0]); EXPECT_EQ(images[0].image_rect, - image_map.GetRectForImage(images[0].image->uniqueID())); + image_map.GetRectForImage(images[0].image.stable_id())); } else { EXPECT_EQ(0u, images.size()) << x << " " << y; } @@ -234,24 +247,24 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) { EXPECT_TRUE(images[0].image == discardable_image[1][2]); EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]); EXPECT_EQ(images[0].image_rect, - image_map.GetRectForImage(images[0].image->uniqueID())); + image_map.GetRectForImage(images[0].image.stable_id())); EXPECT_TRUE(images[1].image == discardable_image[2][1]); EXPECT_EQ(gfx::Rect(1024 + 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]); EXPECT_EQ(images[1].image_rect, - image_map.GetRectForImage(images[1].image->uniqueID())); + image_map.GetRectForImage(images[1].image.stable_id())); EXPECT_TRUE(images[2].image == discardable_image[2][3]); EXPECT_EQ(gfx::Rect(1024 + 3 * 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[2]); EXPECT_EQ(images[2].image_rect, - image_map.GetRectForImage(images[2].image->uniqueID())); + image_map.GetRectForImage(images[2].image.stable_id())); EXPECT_TRUE(images[3].image == discardable_image[3][2]); EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 3 * 512 + 6, 500, 500), inset_rects[3]); EXPECT_EQ(images[3].image_rect, - image_map.GetRectForImage(images[3].image->uniqueID())); + image_map.GetRectForImage(images[3].image.stable_id())); } // Non intersecting rects @@ -278,8 +291,8 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) { // Image not present in the list. { - sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(500, 500)); - EXPECT_EQ(gfx::Rect(), image_map.GetRectForImage(image->uniqueID())); + PaintImage image = CreateDiscardablePaintImage(gfx::Size(500, 500)); + EXPECT_EQ(gfx::Rect(), image_map.GetRectForImage(image.stable_id())); } } @@ -298,11 +311,12 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectOnePixelQuery) { // |---|---|---|---| // | x | | x | | // |---|---|---|---| - sk_sp<SkImage> discardable_image[4][4]; + PaintImage discardable_image[4][4]; for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { if ((x + y) & 1) { - discardable_image[y][x] = CreateDiscardableImage(gfx::Size(500, 500)); + discardable_image[y][x] = + CreateDiscardablePaintImage(gfx::Size(500, 500)); PaintFlags flags; content_layer_client.add_draw_image( discardable_image[y][x], gfx::Point(x * 512 + 6, y * 512 + 6), @@ -314,13 +328,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectOnePixelQuery) { scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); - DiscardableImageMap image_map; - { - DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, - visible_rect.size()); - display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f); - } + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { @@ -334,7 +345,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectOnePixelQuery) { EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500), inset_rects[0]); EXPECT_EQ(images[0].image_rect, - image_map.GetRectForImage(images[0].image->uniqueID())); + image_map.GetRectForImage(images[0].image.stable_id())); } else { EXPECT_EQ(0u, images.size()) << x << " " << y; } @@ -347,8 +358,8 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMassiveImage) { FakeContentLayerClient content_layer_client; content_layer_client.set_bounds(visible_rect.size()); - sk_sp<SkImage> discardable_image = - CreateDiscardableImage(gfx::Size(1 << 25, 1 << 25)); + PaintImage discardable_image = + CreateDiscardablePaintImage(gfx::Size(1 << 25, 1 << 25)); PaintFlags flags; content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0), flags); @@ -356,13 +367,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMassiveImage) { scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); - DiscardableImageMap image_map; - { - DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, - visible_rect.size()); - display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f); - } + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1)); std::vector<gfx::Rect> inset_rects = InsetImageRects(images); @@ -370,7 +378,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMassiveImage) { EXPECT_TRUE(images[0].image == discardable_image); EXPECT_EQ(gfx::Rect(0, 0, 2048, 2048), inset_rects[0]); EXPECT_EQ(images[0].image_rect, - image_map.GetRectForImage(images[0].image->uniqueID())); + image_map.GetRectForImage(images[0].image.stable_id())); } TEST_F(DiscardableImageMapTest, PaintDestroyedWhileImageIsDrawn) { @@ -378,19 +386,21 @@ TEST_F(DiscardableImageMapTest, PaintDestroyedWhileImageIsDrawn) { FakeContentLayerClient content_layer_client; content_layer_client.set_bounds(visible_rect.size()); - sk_sp<SkImage> discardable_image = CreateDiscardableImage(gfx::Size(10, 10)); + PaintImage discardable_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); + sk_sp<PaintRecord> record = CreateRecording(discardable_image, visible_rect); DiscardableImageMap image_map; { DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, visible_rect.size()); + DiscardableImageStore* image_store = generator.image_store(); { std::unique_ptr<SkPaint> paint(new SkPaint()); - generator.canvas()->saveLayer(gfx::RectToSkRect(visible_rect), - paint.get()); + image_store->GetNoDrawCanvas()->saveLayer(gfx::RectToSkRect(visible_rect), + paint.get()); } - generator.canvas()->drawImage(discardable_image, 0, 0, nullptr); - generator.canvas()->restore(); + image_store->GatherDiscardableImages(record.get()); + image_store->GetNoDrawCanvas()->restore(); } std::vector<PositionScaleDrawImage> images = @@ -404,16 +414,19 @@ TEST_F(DiscardableImageMapTest, NullPaintOnSaveLayer) { FakeContentLayerClient content_layer_client; content_layer_client.set_bounds(visible_rect.size()); - sk_sp<SkImage> discardable_image = CreateDiscardableImage(gfx::Size(10, 10)); + PaintImage discardable_image = CreateDiscardablePaintImage(gfx::Size(10, 10)); + sk_sp<PaintRecord> record = CreateRecording(discardable_image, visible_rect); DiscardableImageMap image_map; { DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, visible_rect.size()); + DiscardableImageStore* image_store = generator.image_store(); SkPaint* null_paint = nullptr; - generator.canvas()->saveLayer(gfx::RectToSkRect(visible_rect), null_paint); - generator.canvas()->drawImage(discardable_image, 0, 0, nullptr); - generator.canvas()->restore(); + image_store->GetNoDrawCanvas()->saveLayer(gfx::RectToSkRect(visible_rect), + null_paint); + image_store->GatherDiscardableImages(record.get()); + image_store->GetNoDrawCanvas()->restore(); } std::vector<PositionScaleDrawImage> images = @@ -428,8 +441,8 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImage) { content_layer_client.set_bounds(visible_rect.size()); int dimension = std::numeric_limits<int>::max(); - sk_sp<SkImage> discardable_image = - CreateDiscardableImage(gfx::Size(dimension, dimension)); + PaintImage discardable_image = + CreateDiscardablePaintImage(gfx::Size(dimension, dimension)); PaintFlags flags; content_layer_client.add_draw_image(discardable_image, gfx::Point(42, 42), flags); @@ -437,13 +450,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImage) { scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); - DiscardableImageMap image_map; - { - DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, - visible_rect.size()); - display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f); - } + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(image_map, gfx::Rect(42, 42, 1, 1)); std::vector<gfx::Rect> inset_rects = InsetImageRects(images); @@ -451,7 +461,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImage) { EXPECT_TRUE(images[0].image == discardable_image); EXPECT_EQ(gfx::Rect(42, 42, 2006, 2006), inset_rects[0]); EXPECT_EQ(images[0].image_rect, - image_map.GetRectForImage(images[0].image->uniqueID())); + image_map.GetRectForImage(images[0].image.stable_id())); } TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImageMaxLayer) { @@ -466,8 +476,8 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImageMaxLayer) { FakeContentLayerClient content_layer_client; content_layer_client.set_bounds(visible_rect.size()); - sk_sp<SkImage> discardable_image = - CreateDiscardableImage(gfx::Size(dimension, dimension)); + PaintImage discardable_image = + CreateDiscardablePaintImage(gfx::Size(dimension, dimension)); PaintFlags flags; content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0), flags); @@ -479,13 +489,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImageMaxLayer) { scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); - DiscardableImageMap image_map; - { - DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, - visible_rect.size()); - display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f); - } + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1)); std::vector<gfx::Rect> inset_rects = InsetImageRects(images); @@ -510,7 +517,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImageMaxLayer) { EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), inset_rects[0]); EXPECT_EQ(images[0].image_rect, - image_map.GetRectForImage(discardable_image->uniqueID())); + image_map.GetRectForImage(discardable_image.stable_id())); } TEST_F(DiscardableImageMapTest, GetDiscardableImagesRectInBounds) { @@ -518,10 +525,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesRectInBounds) { FakeContentLayerClient content_layer_client; content_layer_client.set_bounds(visible_rect.size()); - sk_sp<SkImage> discardable_image = - CreateDiscardableImage(gfx::Size(100, 100)); - sk_sp<SkImage> long_discardable_image = - CreateDiscardableImage(gfx::Size(10000, 100)); + PaintImage discardable_image = + CreateDiscardablePaintImage(gfx::Size(100, 100)); + PaintImage long_discardable_image = + CreateDiscardablePaintImage(gfx::Size(10000, 100)); PaintFlags flags; content_layer_client.add_draw_image(discardable_image, gfx::Point(-10, -11), @@ -534,13 +541,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesRectInBounds) { scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); - DiscardableImageMap image_map; - { - DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, - visible_rect.size()); - display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f); - } + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1)); std::vector<gfx::Rect> inset_rects = InsetImageRects(images); @@ -562,10 +566,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesRectInBounds) { discardable_image_rect.Union(gfx::Rect(950, 951, 50, 49)); discardable_image_rect.Inset(-1, -1, -1, -1); EXPECT_EQ(discardable_image_rect, - image_map.GetRectForImage(discardable_image->uniqueID())); + image_map.GetRectForImage(discardable_image.stable_id())); EXPECT_EQ(gfx::Rect(-1, 499, 1002, 102), - image_map.GetRectForImage(long_discardable_image->uniqueID())); + image_map.GetRectForImage(long_discardable_image.stable_id())); } TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) { @@ -607,13 +611,10 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) { scoped_refptr<DisplayItemList> display_list = content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); - DiscardableImageMap image_map; - { - DiscardableImageMap::ScopedMetadataGenerator generator(&image_map, - visible_rect.size()); - display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f); - } + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { @@ -622,7 +623,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) { std::vector<gfx::Rect> inset_rects = InsetImageRects(images); if ((x + y) & 1) { EXPECT_EQ(1u, images.size()) << x << " " << y; - EXPECT_TRUE(images[0].image == discardable_image[y][x]) + EXPECT_TRUE(images[0].image.sk_image() == discardable_image[y][x]) << x << " " << y; EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500), inset_rects[0]); @@ -639,14 +640,79 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) { GetDiscardableImagesInRect(image_map, gfx::Rect(512, 512, 2048, 2048)); std::vector<gfx::Rect> inset_rects = InsetImageRects(images); EXPECT_EQ(4u, images.size()); - EXPECT_TRUE(images[0].image == discardable_image[1][2]); + EXPECT_TRUE(images[0].image.sk_image() == discardable_image[1][2]); EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]); - EXPECT_TRUE(images[1].image == discardable_image[2][1]); + EXPECT_TRUE(images[1].image.sk_image() == discardable_image[2][1]); EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]); - EXPECT_TRUE(images[2].image == discardable_image[2][3]); + EXPECT_TRUE(images[2].image.sk_image() == discardable_image[2][3]); EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[2]); - EXPECT_TRUE(images[3].image == discardable_image[3][2]); + EXPECT_TRUE(images[3].image.sk_image() == discardable_image[3][2]); EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500), inset_rects[3]); } +TEST_F(DiscardableImageMapTest, ClipsImageRects) { + gfx::Rect visible_rect(500, 500); + + PaintImage discardable_image = + CreateDiscardablePaintImage(gfx::Size(500, 500)); + sk_sp<PaintRecord> record = CreateRecording(discardable_image, visible_rect); + + scoped_refptr<DisplayItemList> display_list = new DisplayItemList; + display_list->CreateAndAppendPairedBeginItem<ClipDisplayItem>( + gfx::Rect(250, 250), std::vector<SkRRect>(), false); + display_list->CreateAndAppendDrawingItem<DrawingDisplayItem>( + gfx::Rect(500, 500), record, SkRect::MakeWH(500, 500)); + display_list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); + display_list->Finalize(); + display_list->GenerateDiscardableImagesMetadata(); + + const DiscardableImageMap& image_map = + display_list->discardable_image_map_for_testing(); + std::vector<PositionScaleDrawImage> images = + GetDiscardableImagesInRect(image_map, visible_rect); + std::vector<gfx::Rect> inset_rects = InsetImageRects(images); + EXPECT_EQ(1u, images.size()); + EXPECT_TRUE(images[0].image == discardable_image); + EXPECT_EQ(gfx::Rect(250, 250), inset_rects[0]); +} + +TEST_F(DiscardableImageMapTest, GathersDiscardableImagesFromNestedOps) { + sk_sp<PaintRecord> internal_record = sk_make_sp<PaintRecord>(); + PaintImage discardable_image = + CreateDiscardablePaintImage(gfx::Size(100, 100)); + internal_record->push<DrawImageOp>(discardable_image, 0.f, 0.f, nullptr); + + sk_sp<PaintRecord> list_record = sk_make_sp<PaintRecord>(); + PaintImage discardable_image2 = + CreateDiscardablePaintImage(gfx::Size(100, 100)); + list_record->push<DrawImageOp>(discardable_image2, 100.f, 100.f, nullptr); + scoped_refptr<DisplayItemList> display_list = new DisplayItemList; + display_list->CreateAndAppendDrawingItem<DrawingDisplayItem>( + gfx::Rect(100, 100, 100, 100), list_record, SkRect::MakeWH(100, 100)); + display_list->Finalize(); + + PaintOpBuffer buffer; + buffer.push<DrawRecordOp>(internal_record); + buffer.push<DrawDisplayItemListOp>(display_list); + DiscardableImageMap image_map_; + { + DiscardableImageMap::ScopedMetadataGenerator generator(&image_map_, + gfx::Size(200, 200)); + generator.image_store()->GatherDiscardableImages(&buffer); + } + + gfx::ColorSpace target_color_space; + std::vector<DrawImage> images; + image_map_.GetDiscardableImagesInRect(gfx::Rect(0, 0, 5, 95), 1.f, + target_color_space, &images); + EXPECT_EQ(1u, images.size()); + EXPECT_TRUE(discardable_image == images[0].paint_image()); + + images.clear(); + image_map_.GetDiscardableImagesInRect(gfx::Rect(105, 105, 5, 95), 1.f, + target_color_space, &images); + EXPECT_EQ(1u, images.size()); + EXPECT_TRUE(discardable_image2 == images[0].paint_image()); +} + } // namespace cc diff --git a/chromium/cc/paint/discardable_image_store.cc b/chromium/cc/paint/discardable_image_store.cc new file mode 100644 index 00000000000..1545e32aba4 --- /dev/null +++ b/chromium/cc/paint/discardable_image_store.cc @@ -0,0 +1,259 @@ +// Copyright 2017 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/paint/discardable_image_store.h" + +#include "base/containers/adapters.h" +#include "base/memory/ptr_util.h" +#include "cc/paint/display_item_list.h" +#include "third_party/skia/include/utils/SkNoDrawCanvas.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/skia_util.h" + +namespace cc { +namespace { + +SkRect MapRect(const SkMatrix& matrix, const SkRect& src) { + SkRect dst; + matrix.mapRect(&dst, src); + return dst; +} + +} // namespace + +class DiscardableImageStore::PaintTrackingCanvas : public SkNoDrawCanvas { + public: + PaintTrackingCanvas(int width, int height) : SkNoDrawCanvas(width, height) {} + ~PaintTrackingCanvas() override = default; + + bool ComputePaintBounds(const SkRect& rect, + const SkPaint* current_paint, + SkRect* paint_bounds) { + *paint_bounds = rect; + if (current_paint) { + if (!current_paint->canComputeFastBounds()) + return false; + *paint_bounds = + current_paint->computeFastBounds(*paint_bounds, paint_bounds); + } + + for (const auto& paint : base::Reversed(saved_paints_)) { + if (!paint.canComputeFastBounds()) + return false; + *paint_bounds = paint.computeFastBounds(*paint_bounds, paint_bounds); + } + + return true; + } + + protected: + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { + saved_paints_.push_back(rec.fPaint ? *rec.fPaint : SkPaint()); + return SkNoDrawCanvas::getSaveLayerStrategy(rec); + } + + void willSave() override { + saved_paints_.push_back(SkPaint()); + return SkNoDrawCanvas::willSave(); + } + + void willRestore() override { + DCHECK_GT(saved_paints_.size(), 0u); + saved_paints_.pop_back(); + SkNoDrawCanvas::willRestore(); + } + + private: + std::vector<SkPaint> saved_paints_; +}; + +DiscardableImageStore::DiscardableImageStore( + int width, + int height, + std::vector<std::pair<DrawImage, gfx::Rect>>* image_set, + base::flat_map<PaintImage::Id, gfx::Rect>* image_id_to_rect) + : canvas_(base::MakeUnique<PaintTrackingCanvas>(width, height)), + image_set_(image_set), + image_id_to_rect_(image_id_to_rect) {} + +DiscardableImageStore::~DiscardableImageStore() = default; + +SkNoDrawCanvas* DiscardableImageStore::GetNoDrawCanvas() { + return canvas_.get(); +} + +void DiscardableImageStore::GatherDiscardableImages( + const PaintOpBuffer* buffer) { + if (!buffer->HasDiscardableImages()) + return; + + SkMatrix original = canvas_->getTotalMatrix(); + canvas_->save(); + // TODO(khushalsagar): Optimize out save/restore blocks if there are no images + // in the draw ops between them. + for (auto* op : PaintOpBuffer::Iterator(buffer)) { + if (op->IsDrawOp()) { + switch (op->GetType()) { + case PaintOpType::DrawArc: { + auto* arc_op = static_cast<DrawArcOp*>(op); + AddImageFromFlags(arc_op->oval, arc_op->flags); + } break; + case PaintOpType::DrawCircle: { + auto* circle_op = static_cast<DrawCircleOp*>(op); + SkRect rect = + SkRect::MakeXYWH(circle_op->cx - circle_op->radius, + circle_op->cy - circle_op->radius, + 2 * circle_op->radius, 2 * circle_op->radius); + AddImageFromFlags(rect, circle_op->flags); + } break; + case PaintOpType::DrawDisplayItemList: { + auto* list_op = static_cast<DrawDisplayItemListOp*>(op); + list_op->list->GatherDiscardableImages(this); + } break; + case PaintOpType::DrawImage: { + auto* image_op = static_cast<DrawImageOp*>(op); + const SkImage* sk_image = image_op->image.sk_image().get(); + AddImage(image_op->image, + SkRect::MakeIWH(sk_image->width(), sk_image->height()), + SkRect::MakeXYWH(image_op->left, image_op->top, + sk_image->width(), sk_image->height()), + nullptr, image_op->flags); + } break; + case PaintOpType::DrawImageRect: { + auto* image_rect_op = static_cast<DrawImageRectOp*>(op); + SkMatrix matrix; + matrix.setRectToRect(image_rect_op->src, image_rect_op->dst, + SkMatrix::kFill_ScaleToFit); + AddImage(image_rect_op->image, image_rect_op->src, image_rect_op->dst, + &matrix, image_rect_op->flags); + } break; + case PaintOpType::DrawIRect: { + auto* rect_op = static_cast<DrawIRectOp*>(op); + AddImageFromFlags(SkRect::Make(rect_op->rect), rect_op->flags); + } break; + case PaintOpType::DrawOval: { + auto* oval_op = static_cast<DrawOvalOp*>(op); + AddImageFromFlags(oval_op->oval, oval_op->flags); + } break; + case PaintOpType::DrawPath: { + auto* path_op = static_cast<DrawPathOp*>(op); + AddImageFromFlags(path_op->path.getBounds(), path_op->flags); + } break; + case PaintOpType::DrawRecord: { + auto* record_op = static_cast<DrawRecordOp*>(op); + GatherDiscardableImages(record_op->record.get()); + } break; + case PaintOpType::DrawRect: { + auto* rect_op = static_cast<DrawRectOp*>(op); + AddImageFromFlags(rect_op->rect, rect_op->flags); + } break; + case PaintOpType::DrawRRect: { + auto* rect_op = static_cast<DrawRRectOp*>(op); + AddImageFromFlags(rect_op->rrect.rect(), rect_op->flags); + } break; + // TODO(khushalsagar): Check if we should be querying images from any of + // the following ops. + case PaintOpType::DrawPosText: + case PaintOpType::DrawLine: + case PaintOpType::DrawDRRect: + case PaintOpType::DrawText: + case PaintOpType::DrawTextBlob: + case PaintOpType::DrawColor: + break; + default: + NOTREACHED(); + } + } else { + op->Raster(canvas_.get(), original); + } + } + canvas_->restore(); +} + +// Currently this function only handles extracting images from SkImageShaders +// embedded in SkPaints. Other embedded image cases, such as SkPictures, +// are not yet handled. +void DiscardableImageStore::AddImageFromFlags(const SkRect& rect, + const PaintFlags& flags) { + SkShader* shader = flags.getShader(); + if (shader) { + SkMatrix matrix; + SkShader::TileMode xy[2]; + SkImage* image = shader->isAImage(&matrix, xy); + if (image) { + // We currently use the wrong id for images that come from shaders. We + // don't know what the stable id is, but since the completion and + // animation states are both unknown, this value doesn't matter as it + // won't be used in checker imaging anyway. Keep this value the same to + // avoid id churn. + // TODO(vmpstr): Remove this when we can add paint images into shaders + // directly. + PaintImage paint_image(PaintImage::kUnknownStableId, sk_ref_sp(image), + PaintImage::AnimationType::UNKNOWN, + PaintImage::CompletionState::UNKNOWN); + // TODO(ericrk): Handle cases where we only need a sub-rect from the + // image. crbug.com/671821 + AddImage(std::move(paint_image), SkRect::MakeFromIRect(image->bounds()), + rect, &matrix, flags); + } + } +} + +void DiscardableImageStore::AddImage(PaintImage paint_image, + const SkRect& src_rect, + const SkRect& rect, + const SkMatrix* local_matrix, + const PaintFlags& flags) { + if (!paint_image.sk_image()->isLazyGenerated()) + return; + + const SkRect& clip_rect = SkRect::Make(canvas_->getDeviceClipBounds()); + const SkMatrix& ctm = canvas_->getTotalMatrix(); + + SkRect paint_rect = MapRect(ctm, rect); + bool computed_paint_bounds = + canvas_->ComputePaintBounds(paint_rect, ToSkPaint(&flags), &paint_rect); + if (!computed_paint_bounds) { + // TODO(vmpstr): UMA this case. + paint_rect = clip_rect; + } + + // Clamp the image rect by the current clip rect. + if (!paint_rect.intersect(clip_rect)) + return; + + SkFilterQuality filter_quality = flags.getFilterQuality(); + + SkIRect src_irect; + src_rect.roundOut(&src_irect); + gfx::Rect image_rect = gfx::ToEnclosingRect(gfx::SkRectToRectF(paint_rect)); + + // During raster, we use the device clip bounds on the canvas, which outsets + // the actual clip by 1 due to the possibility of antialiasing. Account for + // this here by outsetting the image rect by 1. Note that this only affects + // queries into the rtree, which will now return images that only touch the + // bounds of the query rect. + // + // Note that it's not sufficient for us to inset the device clip bounds at + // raster time, since we might be sending a larger-than-one-item display + // item to skia, which means that skia will internally determine whether to + // raster the picture (using device clip bounds that are outset). + image_rect.Inset(-1, -1); + + // The true target color space will be assigned when it is known, in + // GetDiscardableImagesInRect. + gfx::ColorSpace target_color_space; + + SkMatrix matrix = ctm; + if (local_matrix) + matrix.postConcat(*local_matrix); + + (*image_id_to_rect_)[paint_image.stable_id()].Union(image_rect); + image_set_->push_back( + std::make_pair(DrawImage(std::move(paint_image), src_irect, + filter_quality, matrix, target_color_space), + image_rect)); +} + +} // namespace cc diff --git a/chromium/cc/paint/discardable_image_store.h b/chromium/cc/paint/discardable_image_store.h new file mode 100644 index 00000000000..ea1e828fc5e --- /dev/null +++ b/chromium/cc/paint/discardable_image_store.h @@ -0,0 +1,52 @@ +// Copyright 2017 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. + +#ifndef CC_PAINT_DISCARDABLE_IMAGE_STORE_H_ +#define CC_PAINT_DISCARDABLE_IMAGE_STORE_H_ + +#include "base/containers/flat_map.h" +#include "cc/paint/draw_image.h" +#include "cc/paint/image_id.h" +#include "cc/paint/paint_export.h" +#include "cc/paint/paint_op_buffer.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/utils/SkNoDrawCanvas.h" + +namespace cc { +class PaintFlags; +class PaintImage; + +class CC_PAINT_EXPORT DiscardableImageStore { + public: + DiscardableImageStore( + int width, + int height, + std::vector<std::pair<DrawImage, gfx::Rect>>* image_set, + base::flat_map<PaintImage::Id, gfx::Rect>* image_id_to_rect); + ~DiscardableImageStore(); + + void GatherDiscardableImages(const PaintOpBuffer* buffer); + SkNoDrawCanvas* GetNoDrawCanvas(); + + private: + class PaintTrackingCanvas; + + void AddImageFromFlags(const SkRect& rect, const PaintFlags& flags); + void AddImage(PaintImage paint_image, + const SkRect& src_rect, + const SkRect& rect, + const SkMatrix* local_matrix, + const PaintFlags& flags); + + // This canvas is used only for tracking transform/clip/filter state from the + // non-drawing ops. + std::unique_ptr<PaintTrackingCanvas> canvas_; + std::vector<std::pair<DrawImage, gfx::Rect>>* image_set_; + base::flat_map<PaintImage::Id, gfx::Rect>* image_id_to_rect_; +}; + +} // namespace cc + +#endif // CC_PAINT_DISCARDABLE_IMAGE_STORE_H_ diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc index 83599482f53..b947403a738 100644 --- a/chromium/cc/paint/display_item_list.cc +++ b/chromium/cc/paint/display_item_list.cc @@ -19,6 +19,7 @@ #include "cc/paint/clip_display_item.h" #include "cc/paint/clip_path_display_item.h" #include "cc/paint/compositing_display_item.h" +#include "cc/paint/discardable_image_store.h" #include "cc/paint/drawing_display_item.h" #include "cc/paint/filter_display_item.h" #include "cc/paint/float_clip_display_item.h" @@ -97,12 +98,13 @@ NOINLINE DISABLE_CFI_PERF void RasterItem(const DisplayItem& base_item, break; case DisplayItem::DRAWING: { const auto& item = static_cast<const DrawingDisplayItem&>(base_item); - if (canvas->quickReject(item.picture->cullRect())) - break; - - // SkPicture always does a wrapping save/restore on the canvas, so it is - // not necessary here. + // TODO(enne): Maybe the PaintRecord itself could know whether this + // was needed? It's not clear whether these save/restore semantics + // that SkPicture handles during playback are things that should be + // kept around. + canvas->save(); item.picture->playback(canvas, callback); + canvas->restore(); break; } case DisplayItem::FLOAT_CLIP: { @@ -158,22 +160,31 @@ DisplayItemList::DisplayItemList() DisplayItemList::~DisplayItemList() = default; -void DisplayItemList::Raster(SkCanvas* canvas, - SkPicture::AbortCallback* callback, - const gfx::Rect& canvas_target_playback_rect, - float contents_scale) const { - canvas->save(); - if (!canvas_target_playback_rect.IsEmpty()) { - // canvas_target_playback_rect is specified in device space. We can't - // use clipRect because canvas CTM will be applied on it. Use clipRegion - // instead because it ignores canvas CTM. - SkRegion device_clip; - device_clip.setRect(gfx::RectToSkIRect(canvas_target_playback_rect)); - canvas->clipRegion(device_clip); - } - canvas->scale(contents_scale, contents_scale); - Raster(canvas, callback); - canvas->restore(); +// Atttempts to merge a CompositingDisplayItem and DrawingDisplayItem +// into a single "draw with alpha". This function returns true if +// it was successful. If false, then the caller is responsible for +// drawing these items. This is a DisplayItemList version of the +// SkRecord optimization SkRecordNoopSaveLayerDrawRestores. +static bool MergeAndDrawIfPossible(const CompositingDisplayItem& save_item, + const DrawingDisplayItem& draw_item, + SkCanvas* canvas) { + if (save_item.color_filter) + return false; + if (save_item.xfermode != SkBlendMode::kSrcOver) + return false; + // TODO(enne): I believe that lcd_text_requires_opaque_layer is not + // relevant here and that lcd text is preserved post merge, but I haven't + // tested that. + const PaintRecord* record = draw_item.picture.get(); + if (record->size() != 1u) + return false; + + const PaintOp* op = record->GetFirstOp(); + if (!op->IsDrawOp()) + return false; + + op->RasterWithAlpha(canvas, save_item.alpha); + return true; } void DisplayItemList::Raster(SkCanvas* canvas, @@ -182,16 +193,34 @@ void DisplayItemList::Raster(SkCanvas* canvas, if (!GetCanvasClipBounds(canvas, &canvas_playback_rect)) return; - std::vector<size_t> indices; - rtree_.Search(canvas_playback_rect, &indices); - for (size_t index : indices) { - RasterItem(items_[index], canvas, callback); - + std::vector<size_t> indices = rtree_.Search(canvas_playback_rect); + for (size_t i = 0; i < indices.size(); ++i) { // We use a callback during solid color analysis on the compositor thread to // break out early. Since we're handling a sequence of pictures via rtree // query results ourselves, we have to respect the callback and early out. if (callback && callback->abort()) break; + + const DisplayItem& item = items_[indices[i]]; + // Optimize empty begin/end compositing and merge begin/draw/end compositing + // where possible. + // TODO(enne): remove empty clips here too? + // TODO(enne): does this happen recursively? Or is this good enough? + if (i < indices.size() - 2 && item.type == DisplayItem::COMPOSITING) { + const DisplayItem& second = items_[indices[i + 1]]; + const DisplayItem& third = items_[indices[i + 2]]; + if (second.type == DisplayItem::DRAWING && + third.type == DisplayItem::END_COMPOSITING) { + if (MergeAndDrawIfPossible( + static_cast<const CompositingDisplayItem&>(item), + static_cast<const DrawingDisplayItem&>(second), canvas)) { + i += 2; + continue; + } + } + } + + RasterItem(item, canvas, callback); } } @@ -222,8 +251,8 @@ bool DisplayItemList::IsSuitableForGpuRasterization() const { return all_items_are_suitable_for_gpu_rasterization_; } -int DisplayItemList::ApproximateOpCount() const { - return approximate_op_count_; +size_t DisplayItemList::OpCount() const { + return op_count_; } size_t DisplayItemList::ApproximateMemoryUsage() const { @@ -282,7 +311,7 @@ size_t DisplayItemList::ApproximateMemoryUsage() const { } bool DisplayItemList::ShouldBeAnalyzedForSolidColor() const { - return ApproximateOpCount() <= kOpCountThatIsOkToAnalyze; + return OpCount() <= kOpCountThatIsOkToAnalyze; } void DisplayItemList::EmitTraceSnapshot() const { @@ -395,15 +424,15 @@ DisplayItemList::CreateTracedValue(bool include_items) const { state->EndArray(); state->BeginArray("cullRect"); - state->AppendInteger(item.picture->cullRect().x()); - state->AppendInteger(item.picture->cullRect().y()); - state->AppendInteger(item.picture->cullRect().width()); - state->AppendInteger(item.picture->cullRect().height()); + state->AppendInteger(item.bounds.x()); + state->AppendInteger(item.bounds.y()); + state->AppendInteger(item.bounds.width()); + state->AppendInteger(item.bounds.height()); state->EndArray(); std::string b64_picture; - PictureDebugUtil::SerializeAsBase64(ToSkPicture(item.picture).get(), - &b64_picture); + PictureDebugUtil::SerializeAsBase64( + ToSkPicture(item.picture, item.bounds).get(), &b64_picture); state->SetString("skp64", b64_picture); state->EndDictionary(); break; @@ -462,7 +491,7 @@ DisplayItemList::CreateTracedValue(bool include_items) const { SkCanvas* canvas = recorder.beginRecording(bounds.width(), bounds.height()); canvas->translate(-bounds.x(), -bounds.y()); canvas->clipRect(gfx::RectToSkRect(bounds)); - Raster(canvas, nullptr, gfx::Rect(), 1.f); + Raster(canvas); sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); std::string b64_picture; @@ -476,13 +505,28 @@ DisplayItemList::CreateTracedValue(bool include_items) const { void DisplayItemList::GenerateDiscardableImagesMetadata() { // This should be only called once. DCHECK(image_map_.empty()); + if (!has_discardable_images_) + return; gfx::Rect bounds = rtree_.GetBounds(); DiscardableImageMap::ScopedMetadataGenerator generator( &image_map_, gfx::Size(bounds.right(), bounds.bottom())); - auto* canvas = generator.canvas(); - for (const auto& item : items_) - RasterItem(item, canvas, nullptr); + GatherDiscardableImages(generator.image_store()); +} + +void DisplayItemList::GatherDiscardableImages( + DiscardableImageStore* image_store) const { + // TODO(khushalsagar): Could we avoid this if the data was already stored in + // the |image_map_|? + SkCanvas* canvas = image_store->GetNoDrawCanvas(); + for (const auto& item : items_) { + if (item.type == DisplayItem::DRAWING) { + const auto& drawing_item = static_cast<const DrawingDisplayItem&>(item); + image_store->GatherDiscardableImages(drawing_item.picture.get()); + } else { + RasterItem(item, canvas, nullptr); + } + } } void DisplayItemList::GetDiscardableImagesInRect( @@ -494,7 +538,7 @@ void DisplayItemList::GetDiscardableImagesInRect( target_color_space, images); } -gfx::Rect DisplayItemList::GetRectForImage(ImageId image_id) const { +gfx::Rect DisplayItemList::GetRectForImage(PaintImage::Id image_id) const { return image_map_.GetRectForImage(image_id); } diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h index b65707dc6e0..893dc9fbc4f 100644 --- a/chromium/cc/paint/display_item_list.h +++ b/chromium/cc/paint/display_item_list.h @@ -18,6 +18,7 @@ #include "cc/base/rtree.h" #include "cc/paint/discardable_image_map.h" #include "cc/paint/display_item.h" +#include "cc/paint/drawing_display_item.h" #include "cc/paint/image_id.h" #include "cc/paint/paint_export.h" #include "third_party/skia/include/core/SkPicture.h" @@ -41,13 +42,8 @@ class CC_PAINT_EXPORT DisplayItemList public: DisplayItemList(); - // TODO(trchen): Deprecated. Apply clip and scale on the canvas instead. void Raster(SkCanvas* canvas, - SkPicture::AbortCallback* callback, - const gfx::Rect& canvas_target_playback_rect, - float contents_scale) const; - - void Raster(SkCanvas* canvas, SkPicture::AbortCallback* callback) const; + SkPicture::AbortCallback* callback = nullptr) const; // Because processing happens in these CreateAndAppend functions, all the set // up for the item should be done via the args, which is why the return type @@ -119,7 +115,10 @@ class CC_PAINT_EXPORT DisplayItemList visual_rects_.push_back(visual_rect); GrowCurrentBeginItemVisualRect(visual_rect); - return AllocateAndConstruct<DisplayItemType>(std::forward<Args>(args)...); + const auto& item = + AllocateAndConstruct<DisplayItemType>(std::forward<Args>(args)...); + has_discardable_images_ |= item.picture->HasDiscardableImages(); + return item; } // Called after all items are appended, to process the items and, if @@ -131,7 +130,7 @@ class CC_PAINT_EXPORT DisplayItemList } bool IsSuitableForGpuRasterization() const; - int ApproximateOpCount() const; + size_t OpCount() const; size_t ApproximateMemoryUsage() const; bool ShouldBeAnalyzedForSolidColor() const; @@ -142,7 +141,7 @@ class CC_PAINT_EXPORT DisplayItemList float contents_scale, const gfx::ColorSpace& target_color_space, std::vector<DrawImage>* images); - gfx::Rect GetRectForImage(ImageId image_id) const; + gfx::Rect GetRectForImage(PaintImage::Id image_id) const; void SetRetainVisualRectsForTesting(bool retain) { retain_visual_rects_ = retain; @@ -160,6 +159,13 @@ class CC_PAINT_EXPORT DisplayItemList return items_.end(); } + void GatherDiscardableImages(DiscardableImageStore* image_store) const; + const DiscardableImageMap& discardable_image_map_for_testing() const { + return image_map_; + } + + bool has_discardable_images() const { return has_discardable_images_; } + private: FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, AsValueWithNoItems); FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, AsValueWithItems); @@ -177,7 +183,7 @@ class CC_PAINT_EXPORT DisplayItemList const DisplayItemType& AllocateAndConstruct(Args&&... args) { auto* item = &items_.AllocateAndConstruct<DisplayItemType>( std::forward<Args>(args)...); - approximate_op_count_ += item->ApproximateOpCount(); + op_count_ += item->OpCount(); return *item; } @@ -193,11 +199,12 @@ class CC_PAINT_EXPORT DisplayItemList std::vector<gfx::Rect> visual_rects_; std::vector<size_t> begin_item_indices_; - int approximate_op_count_ = 0; + size_t op_count_ = 0u; bool all_items_are_suitable_for_gpu_rasterization_ = true; // For testing purposes only. Whether to keep visual rects across calls to // Finalize(). bool retain_visual_rects_ = false; + bool has_discardable_images_ = false; friend class base::RefCountedThreadSafe<DisplayItemList>; FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, ApproximateMemoryUsage); diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc index f1b9e759f9b..e4682f3bc56 100644 --- a/chromium/cc/paint/display_item_list_unittest.cc +++ b/chromium/cc/paint/display_item_list_unittest.cc @@ -17,16 +17,17 @@ #include "cc/paint/compositing_display_item.h" #include "cc/paint/drawing_display_item.h" #include "cc/paint/filter_display_item.h" - #include "cc/paint/float_clip_display_item.h" #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_flags.h" #include "cc/paint/paint_record.h" #include "cc/paint/paint_recorder.h" +#include "cc/paint/skia_paint_canvas.h" #include "cc/paint/transform_display_item.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/pixel_test_utils.h" #include "cc/test/skia_common.h" +#include "cc/test/test_skcanvas.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -80,6 +81,19 @@ sk_sp<const PaintRecord> CreateRectPicture(const gfx::Rect& bounds) { return recorder.finishRecordingAsPicture(); } +sk_sp<const PaintRecord> CreateRectPictureWithAlpha(const gfx::Rect& bounds, + uint8_t alpha) { + PaintRecorder recorder; + PaintCanvas* canvas = + recorder.beginRecording(bounds.width(), bounds.height()); + PaintFlags flags; + flags.setAlpha(alpha); + canvas->drawRect( + SkRect::MakeXYWH(bounds.x(), bounds.y(), bounds.width(), bounds.height()), + flags); + return recorder.finishRecordingAsPicture(); +} + void AppendFirstSerializationTestPicture(scoped_refptr<DisplayItemList> list, const gfx::Size& layer_size) { gfx::PointF offset(2.f, 3.f); @@ -88,12 +102,13 @@ void AppendFirstSerializationTestPicture(scoped_refptr<DisplayItemList> list, PaintFlags red_paint; red_paint.setColor(SK_ColorRED); - PaintCanvas* canvas = recorder.beginRecording(SkRect::MakeXYWH( - offset.x(), offset.y(), layer_size.width(), layer_size.height())); + SkRect bounds = SkRect::MakeXYWH(offset.x(), offset.y(), layer_size.width(), + layer_size.height()); + PaintCanvas* canvas = recorder.beginRecording(bounds); canvas->translate(offset.x(), offset.y()); canvas->drawRect(SkRect::MakeWH(4, 4), red_paint); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - kVisualRect, recorder.finishRecordingAsPicture()); + kVisualRect, recorder.finishRecordingAsPicture(), bounds); } } // namespace @@ -116,7 +131,8 @@ TEST(DisplayItemListTest, SingleDrawingItem) { canvas->drawRect(SkRect::MakeLTRB(0.f, 0.f, 60.f, 60.f), red_paint); canvas->drawRect(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f), blue_flags); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - kVisualRect, recorder.finishRecordingAsPicture()); + kVisualRect, recorder.finishRecordingAsPicture(), + gfx::RectFToSkRect(recording_rect)); list->Finalize(); DrawDisplayList(pixels, layer_rect, list); @@ -156,7 +172,8 @@ TEST(DisplayItemListTest, ClipItem) { canvas->translate(first_offset.x(), first_offset.y()); canvas->drawRect(SkRect::MakeWH(60, 60), red_paint); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - kVisualRect, recorder.finishRecordingAsPicture()); + kVisualRect, recorder.finishRecordingAsPicture(), + gfx::RectFToSkRect(first_recording_rect)); gfx::Rect clip_rect(60, 60, 10, 10); list->CreateAndAppendPairedBeginItem<ClipDisplayItem>( @@ -169,7 +186,8 @@ TEST(DisplayItemListTest, ClipItem) { canvas->translate(second_offset.x(), second_offset.y()); canvas->drawRect(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f), blue_flags); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - kVisualRect, recorder.finishRecordingAsPicture()); + kVisualRect, recorder.finishRecordingAsPicture(), + gfx::RectFToSkRect(second_recording_rect)); list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); list->Finalize(); @@ -213,7 +231,8 @@ TEST(DisplayItemListTest, TransformItem) { canvas->translate(first_offset.x(), first_offset.y()); canvas->drawRect(SkRect::MakeWH(60, 60), red_paint); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - kVisualRect, recorder.finishRecordingAsPicture()); + kVisualRect, recorder.finishRecordingAsPicture(), + gfx::RectFToSkRect(first_recording_rect)); gfx::Transform transform; transform.Rotate(45.0); @@ -226,7 +245,8 @@ TEST(DisplayItemListTest, TransformItem) { canvas->translate(second_offset.x(), second_offset.y()); canvas->drawRect(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f), blue_flags); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - kVisualRect, recorder.finishRecordingAsPicture()); + kVisualRect, recorder.finishRecordingAsPicture(), + gfx::RectFToSkRect(second_recording_rect)); list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>(); list->Finalize(); @@ -289,14 +309,16 @@ TEST(DisplayItemListTest, FilterItem) { PaintFlags red_paint; red_paint.setColor(SK_ColorRED); - PaintCanvas* canvas = recorder.beginRecording( - SkRect::MakeXYWH(0, 0, layer_rect.width(), layer_rect.height())); + SkRect bounds = + SkRect::MakeXYWH(0, 0, layer_rect.width(), layer_rect.height()); + PaintCanvas* canvas = recorder.beginRecording(bounds); canvas->drawRect( SkRect::MakeLTRB(filter_bounds.x(), filter_bounds.y(), filter_bounds.right(), filter_bounds.bottom()), red_paint); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - ToNearestRect(filter_bounds), recorder.finishRecordingAsPicture()); + ToNearestRect(filter_bounds), recorder.finishRecordingAsPicture(), + bounds); } list->CreateAndAppendPairedEndItem<EndFilterDisplayItem>(); @@ -330,11 +352,12 @@ TEST(DisplayItemListTest, ApproximateMemoryUsage) { for (int i = 0; i < kNumCommandsInTestSkPicture; i++) canvas->drawRect(SkRect(), blue_flags); sk_sp<PaintRecord> record = recorder.finishRecordingAsPicture(); - size_t record_size = record->approximateBytesUsed(); + size_t record_size = record->bytes_used(); ASSERT_GE(record_size, kNumCommandsInTestSkPicture * sizeof(SkRect)); auto list = make_scoped_refptr(new DisplayItemList); - list->CreateAndAppendDrawingItem<DrawingDisplayItem>(kVisualRect, record); + list->CreateAndAppendDrawingItem<DrawingDisplayItem>( + kVisualRect, record, gfx::RectToSkRect(layer_rect)); list->Finalize(); memory_usage = list->ApproximateMemoryUsage(); EXPECT_GE(memory_usage, record_size); @@ -394,7 +417,8 @@ TEST(DisplayItemListTest, SizeOne) { auto list = make_scoped_refptr(new DisplayItemList); gfx::Rect drawing_bounds(5, 6, 1, 1); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_bounds, CreateRectPicture(drawing_bounds)); + drawing_bounds, CreateRectPicture(drawing_bounds), + gfx::RectToSkRect(drawing_bounds)); EXPECT_EQ(1u, list->size()); } @@ -414,7 +438,8 @@ TEST(DisplayItemListTest, AppendVisualRectSimple) { gfx::Rect drawing_bounds(5, 6, 7, 8); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_bounds, CreateRectPicture(drawing_bounds)); + drawing_bounds, CreateRectPicture(drawing_bounds), + gfx::RectToSkRect(drawing_bounds)); EXPECT_EQ(1u, list->size()); EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(0)); @@ -466,7 +491,8 @@ TEST(DisplayItemListTest, AppendVisualRectBlockContainingDrawing) { gfx::Rect drawing_bounds(5, 6, 1, 1); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_bounds, CreateRectPicture(drawing_bounds)); + drawing_bounds, CreateRectPicture(drawing_bounds), + gfx::RectToSkRect(drawing_bounds)); list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); @@ -487,7 +513,8 @@ TEST(DisplayItemListTest, AppendVisualRectBlockContainingEscapedDrawing) { gfx::Rect drawing_bounds(1, 2, 3, 4); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_bounds, CreateRectPicture(drawing_bounds)); + drawing_bounds, CreateRectPicture(drawing_bounds), + gfx::RectToSkRect(drawing_bounds)); list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); @@ -506,7 +533,8 @@ TEST(DisplayItemListTest, gfx::Rect drawing_a_bounds(1, 2, 3, 4); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_a_bounds, CreateRectPicture(drawing_a_bounds)); + drawing_a_bounds, CreateRectPicture(drawing_a_bounds), + gfx::RectToSkRect(drawing_a_bounds)); gfx::Rect clip_bounds(5, 6, 7, 8); list->CreateAndAppendPairedBeginItem<ClipDisplayItem>( @@ -514,7 +542,8 @@ TEST(DisplayItemListTest, gfx::Rect drawing_b_bounds(13, 14, 1, 1); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_b_bounds, CreateRectPicture(drawing_b_bounds)); + drawing_b_bounds, CreateRectPicture(drawing_b_bounds), + gfx::RectToSkRect(drawing_b_bounds)); list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); @@ -536,13 +565,15 @@ TEST(DisplayItemListTest, AppendVisualRectTwoBlocksTwoDrawings) { gfx::Rect drawing_a_bounds(5, 6, 1, 1); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_a_bounds, CreateRectPicture(drawing_a_bounds)); + drawing_a_bounds, CreateRectPicture(drawing_a_bounds), + gfx::RectToSkRect(drawing_a_bounds)); list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform()); gfx::Rect drawing_b_bounds(7, 8, 1, 1); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_b_bounds, CreateRectPicture(drawing_b_bounds)); + drawing_b_bounds, CreateRectPicture(drawing_b_bounds), + gfx::RectToSkRect(drawing_b_bounds)); list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>(); list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); @@ -571,13 +602,15 @@ TEST(DisplayItemListTest, gfx::Rect drawing_a_bounds(5, 6, 1, 1); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_a_bounds, CreateRectPicture(drawing_a_bounds)); + drawing_a_bounds, CreateRectPicture(drawing_a_bounds), + gfx::RectToSkRect(drawing_a_bounds)); list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform()); gfx::Rect drawing_b_bounds(1, 2, 3, 4); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_b_bounds, CreateRectPicture(drawing_b_bounds)); + drawing_b_bounds, CreateRectPicture(drawing_b_bounds), + gfx::RectToSkRect(drawing_b_bounds)); list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>(); list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); @@ -606,13 +639,15 @@ TEST(DisplayItemListTest, gfx::Rect drawing_a_bounds(1, 2, 3, 4); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_a_bounds, CreateRectPicture(drawing_a_bounds)); + drawing_a_bounds, CreateRectPicture(drawing_a_bounds), + gfx::RectToSkRect(drawing_a_bounds)); list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform()); gfx::Rect drawing_b_bounds(7, 8, 1, 1); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_b_bounds, CreateRectPicture(drawing_b_bounds)); + drawing_b_bounds, CreateRectPicture(drawing_b_bounds), + gfx::RectToSkRect(drawing_b_bounds)); list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>(); list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); @@ -641,13 +676,15 @@ TEST(DisplayItemListTest, gfx::Rect drawing_a_bounds(13, 14, 1, 1); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_a_bounds, CreateRectPicture(drawing_a_bounds)); + drawing_a_bounds, CreateRectPicture(drawing_a_bounds), + gfx::RectToSkRect(drawing_a_bounds)); list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform()); gfx::Rect drawing_b_bounds(1, 2, 3, 4); list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - drawing_b_bounds, CreateRectPicture(drawing_b_bounds)); + drawing_b_bounds, CreateRectPicture(drawing_b_bounds), + gfx::RectToSkRect(drawing_b_bounds)); list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>(); list->CreateAndAppendPairedEndItem<EndClipDisplayItem>(); @@ -704,4 +741,112 @@ TEST(DisplayItemListTest, AppendVisualRectBlockContainingFilterNoDrawings) { EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(3)); } +// Verify that raster time optimizations for compositing item / draw single op / +// end compositing item can be collapsed together into a single draw op +// with the opacity from the compositing item folded in. +TEST(DisplayItemListTest, SaveDrawRestore) { + auto list = make_scoped_refptr(new DisplayItemList); + + list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( + 80, SkBlendMode::kSrcOver, nullptr, nullptr, false); + list->CreateAndAppendDrawingItem<DrawingDisplayItem>( + kVisualRect, CreateRectPictureWithAlpha(kVisualRect, 40), + gfx::RectToSkRect(kVisualRect)); + list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); + list->Finalize(); + + SaveCountingCanvas canvas; + list->Raster(&canvas); + + EXPECT_EQ(0, canvas.save_count_); + EXPECT_EQ(0, canvas.restore_count_); + EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_); + + float expected_alpha = 80 * 40 / 255.f; + EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f); +} + +// Verify that compositing item / end compositing item is a noop. +// Here we're testing that Skia does an optimization that skips +// save/restore with nothing in between. If skia stops doing this +// then we should reimplement this optimization in display list raster. +TEST(DisplayItemListTest, SaveRestoreNoops) { + auto list = make_scoped_refptr(new DisplayItemList); + + list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( + 80, SkBlendMode::kSrcOver, nullptr, nullptr, false); + list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); + list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( + 255, SkBlendMode::kSrcOver, nullptr, nullptr, false); + list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); + list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( + 255, SkBlendMode::kSrc, nullptr, nullptr, false); + list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); + list->Finalize(); + + SaveCountingCanvas canvas; + list->Raster(&canvas); + + EXPECT_EQ(0, canvas.save_count_); + EXPECT_EQ(0, canvas.restore_count_); +} + +// The same as SaveDrawRestore, but with save flags that prevent the +// optimization. +TEST(DisplayItemListTest, SaveDrawRestoreFail_BadSaveFlags) { + auto list = make_scoped_refptr(new DisplayItemList); + + // Use a blend mode that's not compatible with the SaveDrawRestore + // optimization. + list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( + 80, SkBlendMode::kSrc, nullptr, nullptr, false); + list->CreateAndAppendDrawingItem<DrawingDisplayItem>( + kVisualRect, CreateRectPictureWithAlpha(kVisualRect, 40), + gfx::RectToSkRect(kVisualRect)); + list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); + list->Finalize(); + + SaveCountingCanvas canvas; + list->Raster(&canvas); + + EXPECT_EQ(1, canvas.save_count_); + EXPECT_EQ(1, canvas.restore_count_); + EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_); + EXPECT_LE(40, canvas.paint_.getAlpha()); +} + +// The same as SaveDrawRestore, but with too many ops in the PaintRecord. +TEST(DisplayItemListTest, SaveDrawRestoreFail_TooManyOps) { + sk_sp<const PaintRecord> record; + SkRect bounds = SkRect::MakeWH(kVisualRect.width(), kVisualRect.height()); + { + PaintRecorder recorder; + PaintCanvas* canvas = recorder.beginRecording(bounds); + PaintFlags flags; + flags.setAlpha(40); + canvas->drawRect(gfx::RectToSkRect(kVisualRect), flags); + // Add an extra op here. + canvas->drawRect(gfx::RectToSkRect(kVisualRect), flags); + record = recorder.finishRecordingAsPicture(); + } + EXPECT_GT(record->size(), 1u); + + auto list = make_scoped_refptr(new DisplayItemList); + + list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( + 80, SkBlendMode::kSrcOver, nullptr, nullptr, false); + list->CreateAndAppendDrawingItem<DrawingDisplayItem>( + kVisualRect, std::move(record), bounds); + list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); + list->Finalize(); + + SaveCountingCanvas canvas; + list->Raster(&canvas); + + EXPECT_EQ(1, canvas.save_count_); + EXPECT_EQ(1, canvas.restore_count_); + EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_); + EXPECT_LE(40, canvas.paint_.getAlpha()); +} + } // namespace cc diff --git a/chromium/cc/paint/draw_image.cc b/chromium/cc/paint/draw_image.cc index 6c0bb644864..6ffb3ae839b 100644 --- a/chromium/cc/paint/draw_image.cc +++ b/chromium/cc/paint/draw_image.cc @@ -23,19 +23,18 @@ bool ExtractScale(const SkMatrix& matrix, SkSize* scale) { } // namespace DrawImage::DrawImage() - : image_(nullptr), - src_rect_(SkIRect::MakeXYWH(0, 0, 0, 0)), + : src_rect_(SkIRect::MakeXYWH(0, 0, 0, 0)), filter_quality_(kNone_SkFilterQuality), matrix_(SkMatrix::I()), scale_(SkSize::Make(1.f, 1.f)), matrix_is_decomposable_(true) {} -DrawImage::DrawImage(sk_sp<const SkImage> image, +DrawImage::DrawImage(PaintImage image, const SkIRect& src_rect, SkFilterQuality filter_quality, const SkMatrix& matrix, const gfx::ColorSpace& target_color_space) - : image_(std::move(image)), + : paint_image_(std::move(image)), src_rect_(src_rect), filter_quality_(filter_quality), matrix_(matrix), diff --git a/chromium/cc/paint/draw_image.h b/chromium/cc/paint/draw_image.h index 78513b90d1e..fe84f0606ef 100644 --- a/chromium/cc/paint/draw_image.h +++ b/chromium/cc/paint/draw_image.h @@ -6,6 +6,7 @@ #define CC_PAINT_DRAW_IMAGE_H_ #include "cc/paint/paint_export.h" +#include "cc/paint/paint_image.h" #include "third_party/skia/include/core/SkFilterQuality.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkMatrix.h" @@ -21,7 +22,7 @@ namespace cc { class CC_PAINT_EXPORT DrawImage { public: DrawImage(); - DrawImage(sk_sp<const SkImage> image, + DrawImage(PaintImage image, const SkIRect& src_rect, SkFilterQuality filter_quality, const SkMatrix& matrix, @@ -29,7 +30,8 @@ class CC_PAINT_EXPORT DrawImage { DrawImage(const DrawImage& other); ~DrawImage(); - const sk_sp<const SkImage>& image() const { return image_; } + const PaintImage& paint_image() const { return paint_image_; } + const sk_sp<SkImage>& image() const { return paint_image_.sk_image(); } const SkSize& scale() const { return scale_; } const SkIRect& src_rect() const { return src_rect_; } SkFilterQuality filter_quality() const { return filter_quality_; } @@ -42,16 +44,16 @@ class CC_PAINT_EXPORT DrawImage { DrawImage ApplyScale(float scale) const { SkMatrix scaled_matrix = matrix_; scaled_matrix.preScale(scale, scale); - return DrawImage(image_, src_rect_, filter_quality_, scaled_matrix, + return DrawImage(paint_image_, src_rect_, filter_quality_, scaled_matrix, target_color_space_); } DrawImage ApplyTargetColorSpace(const gfx::ColorSpace& target_color_space) { - return DrawImage(image_, src_rect_, filter_quality_, matrix_, + return DrawImage(paint_image_, src_rect_, filter_quality_, matrix_, target_color_space); } private: - sk_sp<const SkImage> image_; + PaintImage paint_image_; SkIRect src_rect_; SkFilterQuality filter_quality_; SkMatrix matrix_; diff --git a/chromium/cc/paint/drawing_display_item.cc b/chromium/cc/paint/drawing_display_item.cc index 60b09ee0e27..5fc920bcd18 100644 --- a/chromium/cc/paint/drawing_display_item.cc +++ b/chromium/cc/paint/drawing_display_item.cc @@ -8,23 +8,25 @@ namespace cc { -DrawingDisplayItem::DrawingDisplayItem() : DisplayItem(DRAWING) {} +DrawingDisplayItem::DrawingDisplayItem() + : DisplayItem(DRAWING), bounds(SkRect::MakeEmpty()) {} -DrawingDisplayItem::DrawingDisplayItem(sk_sp<const PaintRecord> record) - : DisplayItem(DRAWING), picture(std::move(record)) {} +DrawingDisplayItem::DrawingDisplayItem(sk_sp<const PaintRecord> record, + const SkRect& bounds) + : DisplayItem(DRAWING), picture(std::move(record)), bounds(bounds) {} DrawingDisplayItem::DrawingDisplayItem(const DrawingDisplayItem& item) - : DisplayItem(DRAWING), picture(item.picture) {} + : DisplayItem(DRAWING), picture(item.picture), bounds(item.bounds) {} DrawingDisplayItem::~DrawingDisplayItem() = default; size_t DrawingDisplayItem::ExternalMemoryUsage() const { - return picture->approximateBytesUsed(); + return picture->bytes_used(); } DISABLE_CFI_PERF -int DrawingDisplayItem::ApproximateOpCount() const { - return picture->approximateOpCount(); +size_t DrawingDisplayItem::OpCount() const { + return picture->size(); } } // namespace cc diff --git a/chromium/cc/paint/drawing_display_item.h b/chromium/cc/paint/drawing_display_item.h index 56c7b17aef2..a746a0723f0 100644 --- a/chromium/cc/paint/drawing_display_item.h +++ b/chromium/cc/paint/drawing_display_item.h @@ -17,14 +17,16 @@ namespace cc { class CC_PAINT_EXPORT DrawingDisplayItem : public DisplayItem { public: DrawingDisplayItem(); - explicit DrawingDisplayItem(sk_sp<const PaintRecord> record); + explicit DrawingDisplayItem(sk_sp<const PaintRecord> record, + const SkRect& bounds); explicit DrawingDisplayItem(const DrawingDisplayItem& item); ~DrawingDisplayItem() override; size_t ExternalMemoryUsage() const; - int ApproximateOpCount() const; + size_t OpCount() const; const sk_sp<const PaintRecord> picture; + SkRect bounds; }; } // namespace cc diff --git a/chromium/cc/paint/filter_display_item.h b/chromium/cc/paint/filter_display_item.h index c4f18dcbc85..ad40ff977e2 100644 --- a/chromium/cc/paint/filter_display_item.h +++ b/chromium/cc/paint/filter_display_item.h @@ -25,7 +25,7 @@ class CC_PAINT_EXPORT FilterDisplayItem : public DisplayItem { // enough. return filters.size() * sizeof(filters.at(0)); } - int ApproximateOpCount() const { return 1; } + int OpCount() const { return 1; } const FilterOperations filters; const gfx::RectF bounds; @@ -37,7 +37,7 @@ class CC_PAINT_EXPORT EndFilterDisplayItem : public DisplayItem { EndFilterDisplayItem(); ~EndFilterDisplayItem() override; - int ApproximateOpCount() const { return 0; } + int OpCount() const { return 0; } }; } // namespace cc diff --git a/chromium/cc/paint/float_clip_display_item.h b/chromium/cc/paint/float_clip_display_item.h index 43d751b36ce..b2703c860ca 100644 --- a/chromium/cc/paint/float_clip_display_item.h +++ b/chromium/cc/paint/float_clip_display_item.h @@ -19,7 +19,7 @@ class CC_PAINT_EXPORT FloatClipDisplayItem : public DisplayItem { ~FloatClipDisplayItem() override; size_t ExternalMemoryUsage() const { return 0; } - int ApproximateOpCount() const { return 1; } + int OpCount() const { return 1; } const gfx::RectF clip_rect; }; @@ -29,7 +29,7 @@ class CC_PAINT_EXPORT EndFloatClipDisplayItem : public DisplayItem { EndFloatClipDisplayItem(); ~EndFloatClipDisplayItem() override; - int ApproximateOpCount() const { return 0; } + int OpCount() const { return 0; } }; } // namespace cc diff --git a/chromium/cc/paint/image_id.h b/chromium/cc/paint/image_id.h index 0ffaafd782f..015d6616eea 100644 --- a/chromium/cc/paint/image_id.h +++ b/chromium/cc/paint/image_id.h @@ -9,11 +9,16 @@ #include <unordered_set> #include "base/containers/flat_set.h" +#include "cc/paint/paint_image.h" namespace cc { -using ImageId = uint32_t; -using ImageIdFlatSet = base::flat_set<ImageId>; +using PaintImageIdFlatSet = base::flat_set<PaintImage::Id>; + +// TODO(khushalsagar): These are only used by the hijack canvas since it uses +// an SkCanvas to replace images. Remove once that moves to PaintOpBuffer. +using SkImageId = uint32_t; +using SkImageIdFlatSet = base::flat_set<SkImageId>; } // namespace cc diff --git a/chromium/cc/paint/paint_canvas.cc b/chromium/cc/paint/paint_canvas.cc index 4aab0652b19..a49b2b468fc 100644 --- a/chromium/cc/paint/paint_canvas.cc +++ b/chromium/cc/paint/paint_canvas.cc @@ -18,10 +18,6 @@ const char kIsPreviewMetafileKey[] = "CrIsPreviewMetafile"; namespace cc { -bool ToPixmap(PaintCanvas* canvas, SkPixmap* output) { - return canvas->ToPixmap(output); -} - #if defined(OS_MACOSX) void SetIsPreviewMetafile(PaintCanvas* canvas, bool is_preview) { SkMetaData& meta = canvas->getMetaData(); diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h index daacc301788..b6f4c58016f 100644 --- a/chromium/cc/paint/paint_canvas.h +++ b/chromium/cc/paint/paint_canvas.h @@ -10,19 +10,26 @@ #include "base/memory/ref_counted.h" #include "build/build_config.h" #include "cc/paint/paint_export.h" -#include "cc/paint/paint_record.h" +#include "cc/paint/paint_image.h" #include "third_party/skia/include/core/SkCanvas.h" namespace cc { class DisplayItemList; class PaintFlags; +class PaintOpBuffer; + +using PaintRecord = PaintOpBuffer; class CC_PAINT_EXPORT PaintCanvas { public: + PaintCanvas() {} virtual ~PaintCanvas() {} virtual SkMetaData& getMetaData() = 0; + + // TODO(enne): this only appears to mostly be used to determine if this is + // recording or not, so could be simplified or removed. virtual SkImageInfo imageInfo() const = 0; // TODO(enne): It would be nice to get rid of flush() entirely, as it @@ -33,15 +40,9 @@ class CC_PAINT_EXPORT PaintCanvas { // both recording and gpu work. virtual void flush() = 0; - virtual SkISize getBaseLayerSize() const = 0; - virtual bool writePixels(const SkImageInfo& info, - const void* pixels, - size_t row_bytes, - int x, - int y) = 0; virtual int save() = 0; virtual int saveLayer(const SkRect* bounds, const PaintFlags* flags) = 0; - virtual int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) = 0; + virtual int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) = 0; virtual void restore() = 0; virtual int getSaveCount() const = 0; @@ -92,6 +93,8 @@ class CC_PAINT_EXPORT PaintCanvas { virtual bool getDeviceClipBounds(SkIRect* bounds) const = 0; virtual void drawColor(SkColor color, SkBlendMode mode) = 0; void drawColor(SkColor color) { drawColor(color, SkBlendMode::kSrcOver); } + + // TODO(enne): This is a synonym for drawColor with kSrc. Remove it. virtual void clear(SkColor color) = 0; virtual void drawLine(SkScalar x0, @@ -120,11 +123,11 @@ class CC_PAINT_EXPORT PaintCanvas { SkScalar ry, const PaintFlags& flags) = 0; virtual void drawPath(const SkPath& path, const PaintFlags& flags) = 0; - virtual void drawImage(sk_sp<const SkImage> image, + virtual void drawImage(const PaintImage& image, SkScalar left, SkScalar top, const PaintFlags* flags) = 0; - void drawImage(sk_sp<const SkImage> image, SkScalar left, SkScalar top) { + void drawImage(const PaintImage& image, SkScalar left, SkScalar top) { drawImage(image, left, top, nullptr); } @@ -133,7 +136,7 @@ class CC_PAINT_EXPORT PaintCanvas { kFast_SrcRectConstraint = SkCanvas::kFast_SrcRectConstraint, }; - virtual void drawImageRect(sk_sp<const SkImage> image, + virtual void drawImageRect(const PaintImage& image, const SkRect& src, const SkRect& dst, const PaintFlags* flags, @@ -163,18 +166,14 @@ class CC_PAINT_EXPORT PaintCanvas { virtual void drawDisplayItemList( scoped_refptr<DisplayItemList> display_item_list) = 0; + // Unlike SkCanvas::drawPicture, this only plays back the PaintRecord and does + // not add an additional clip. This is closer to SkPicture::playback. virtual void drawPicture(sk_sp<const PaintRecord> record) = 0; virtual bool isClipEmpty() const = 0; virtual bool isClipRect() const = 0; virtual const SkMatrix& getTotalMatrix() const = 0; - // For GraphicsContextCanvas only. Maybe this could be rewritten? - virtual void temporary_internal_describeTopLayer(SkMatrix* matrix, - SkIRect* clip_bounds) = 0; - - virtual bool ToPixmap(SkPixmap* output) = 0; - enum class AnnotationType { URL, NAMED_DESTINATION, @@ -184,14 +183,8 @@ class CC_PAINT_EXPORT PaintCanvas { const SkRect& rect, sk_sp<SkData> data) = 0; - // TODO(enne): maybe this should live on PaintRecord, but that's not - // possible when PaintRecord is a typedef. - virtual void PlaybackPaintRecord(sk_sp<const PaintRecord> record) = 0; - - protected: - friend class PaintSurface; - friend class PaintRecorder; - friend CC_PAINT_EXPORT bool ToPixmap(PaintCanvas* canvas, SkPixmap* output); + private: + DISALLOW_COPY_AND_ASSIGN(PaintCanvas); }; class CC_PAINT_EXPORT PaintCanvasAutoRestore { @@ -223,14 +216,6 @@ class CC_PAINT_EXPORT PaintCanvasAutoRestore { int save_count_ = 0; }; -// TODO(enne): Move all these functions into PaintCanvas. These are only -// separate now to make the transition to concrete types easier by keeping -// the base PaintCanvas type equivalent to the SkCanvas interface and -// all these helper functions potentially operating on both. - -// PaintCanvas equivalent of skia::GetWritablePixels. -CC_PAINT_EXPORT bool ToPixmap(PaintCanvas* canvas, SkPixmap* output); - // Following routines are used in print preview workflow to mark the // preview metafile. #if defined(OS_MACOSX) diff --git a/chromium/cc/paint/paint_flags.cc b/chromium/cc/paint/paint_flags.cc new file mode 100644 index 00000000000..e16a8bb8073 --- /dev/null +++ b/chromium/cc/paint/paint_flags.cc @@ -0,0 +1,42 @@ +// Copyright 2017 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/paint/paint_flags.h" + +namespace cc { + +bool PaintFlags::IsSimpleOpacity() const { + uint32_t color = getColor(); + if (SK_ColorTRANSPARENT != SkColorSetA(color, SK_AlphaTRANSPARENT)) + return false; + if (!isSrcOver()) + return false; + if (getLooper()) + return false; + if (getPathEffect()) + return false; + if (getShader()) + return false; + if (getMaskFilter()) + return false; + if (getColorFilter()) + return false; + if (getImageFilter()) + return false; + return true; +} + +bool PaintFlags::SupportsFoldingAlpha() const { + if (!isSrcOver()) + return false; + if (getColorFilter()) + return false; + if (getImageFilter()) + return false; + if (getLooper()) + return false; + return true; +} + +} // namespace cc diff --git a/chromium/cc/paint/paint_flags.h b/chromium/cc/paint/paint_flags.h index b7e96c68e2e..c884098e555 100644 --- a/chromium/cc/paint/paint_flags.h +++ b/chromium/cc/paint/paint_flags.h @@ -7,7 +7,6 @@ #include "base/compiler_specific.h" #include "cc/paint/paint_export.h" -#include "cc/paint/paint_shader.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkDrawLooper.h" @@ -15,10 +14,13 @@ #include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPathEffect.h" +#include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/core/SkTypeface.h" namespace cc { +using PaintShader = SkShader; + class CC_PAINT_EXPORT PaintFlags { public: enum Style { @@ -198,6 +200,14 @@ class CC_PAINT_EXPORT PaintFlags { return paint_.computeFastBounds(orig, storage); } + bool operator==(const PaintFlags& flags) { return flags.paint_ == paint_; } + bool operator!=(const PaintFlags& flags) { return flags.paint_ != paint_; } + + // Returns true if this just represents an opacity blend when + // used as saveLayer flags. + bool IsSimpleOpacity() const; + bool SupportsFoldingAlpha() const; + private: friend const SkPaint& ToSkPaint(const PaintFlags& flags); friend const SkPaint* ToSkPaint(const PaintFlags* flags); diff --git a/chromium/cc/paint/paint_image.cc b/chromium/cc/paint/paint_image.cc new file mode 100644 index 00000000000..5575cbdad18 --- /dev/null +++ b/chromium/cc/paint/paint_image.cc @@ -0,0 +1,39 @@ +// Copyright 2017 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/paint/paint_image.h" +#include "base/atomic_sequence_num.h" + +namespace cc { +namespace { +base::StaticAtomicSequenceNumber s_next_id_; +} + +PaintImage::PaintImage() = default; +PaintImage::PaintImage(Id id, + sk_sp<SkImage> sk_image, + AnimationType animation_type, + CompletionState completion_state) + : id_(id), + sk_image_(std::move(sk_image)), + animation_type_(animation_type), + completion_state_(completion_state) {} +PaintImage::PaintImage(const PaintImage& other) = default; +PaintImage::PaintImage(PaintImage&& other) = default; +PaintImage::~PaintImage() = default; + +PaintImage& PaintImage::operator=(const PaintImage& other) = default; +PaintImage& PaintImage::operator=(PaintImage&& other) = default; + +bool PaintImage::operator==(const PaintImage& other) const { + return id_ == other.id_ && sk_image_ == other.sk_image_ && + animation_type_ == other.animation_type_ && + completion_state_ == other.completion_state_; +} + +PaintImage::Id PaintImage::GetNextId() { + return s_next_id_.GetNext(); +} + +} // namespace cc diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h new file mode 100644 index 00000000000..489cef27bd2 --- /dev/null +++ b/chromium/cc/paint/paint_image.h @@ -0,0 +1,66 @@ +// Copyright 2017 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. + +#ifndef CC_PAINT_PAINT_IMAGE_H_ +#define CC_PAINT_PAINT_IMAGE_H_ + +#include "base/logging.h" +#include "cc/paint/paint_export.h" +#include "third_party/skia/include/core/SkImage.h" + +namespace cc { + +// TODO(vmpstr): Add a persistent id to the paint image. +class CC_PAINT_EXPORT PaintImage { + public: + using Id = int; + + // An id that can be used for all non-lazy images. Note that if an image is + // not lazy, it does not mean that this id must be used; one can still use + // GetNextId to generate a stable id for such images. + static const Id kNonLazyStableId = -1; + + // This is the id used in places where we are currently not plumbing the + // correct image id from blink. + // TODO(khushalsagar): Eliminate these cases. See crbug.com/722559. + static const Id kUnknownStableId = -2; + + // TODO(vmpstr): Work towards removing "UNKNOWN" value. + enum class AnimationType { UNKNOWN, ANIMATED, VIDEO, STATIC }; + + // TODO(vmpstr): Work towards removing "UNKNOWN" value. + enum class CompletionState { UNKNOWN, DONE, PARTIALLY_DONE }; + + static Id GetNextId(); + + PaintImage(); + explicit PaintImage(Id id, + sk_sp<SkImage> sk_image, + AnimationType animation_type = AnimationType::STATIC, + CompletionState completion_state = CompletionState::DONE); + PaintImage(const PaintImage& other); + PaintImage(PaintImage&& other); + ~PaintImage(); + + PaintImage& operator=(const PaintImage& other); + PaintImage& operator=(PaintImage&& other); + + bool operator==(const PaintImage& other) const; + explicit operator bool() const { return sk_image_; } + + Id stable_id() const { return id_; } + const sk_sp<SkImage>& sk_image() const { return sk_image_; } + AnimationType animation_type() const { return animation_type_; } + CompletionState completion_state() const { return completion_state_; } + + private: + Id id_ = kUnknownStableId; + sk_sp<SkImage> sk_image_; + AnimationType animation_type_ = AnimationType::UNKNOWN; + CompletionState completion_state_ = CompletionState::UNKNOWN; +}; + +} // namespace cc + +#endif // CC_PAINT_PAINT_IMAGE_H_ diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc new file mode 100644 index 00000000000..a50c0cf38e2 --- /dev/null +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -0,0 +1,797 @@ +// Copyright 2017 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/paint/paint_op_buffer.h" + +#include "base/containers/stack_container.h" +#include "cc/paint/display_item_list.h" +#include "cc/paint/paint_record.h" +#include "third_party/skia/include/core/SkAnnotation.h" + +namespace cc { + +#define TYPES(M) \ + M(AnnotateOp) \ + M(ClipPathOp) \ + M(ClipRectOp) \ + M(ClipRRectOp) \ + M(ConcatOp) \ + M(DrawArcOp) \ + M(DrawCircleOp) \ + M(DrawColorOp) \ + M(DrawDisplayItemListOp) \ + M(DrawDRRectOp) \ + M(DrawImageOp) \ + M(DrawImageRectOp) \ + M(DrawIRectOp) \ + M(DrawLineOp) \ + M(DrawOvalOp) \ + M(DrawPathOp) \ + M(DrawPosTextOp) \ + M(DrawRecordOp) \ + M(DrawRectOp) \ + M(DrawRRectOp) \ + M(DrawTextOp) \ + M(DrawTextBlobOp) \ + M(NoopOp) \ + M(RestoreOp) \ + M(RotateOp) \ + M(SaveOp) \ + M(SaveLayerOp) \ + M(SaveLayerAlphaOp) \ + M(ScaleOp) \ + M(SetMatrixOp) \ + M(TranslateOp) + +using RasterFunction = void (*)(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); +using RasterWithFlagsFunction = void (*)(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + +NOINLINE static void RasterWithAlphaInternal(RasterFunction raster_fn, + const PaintOp* op, + SkCanvas* canvas, + uint8_t alpha) { + // TODO(enne): is it ok to just drop the bounds here? + canvas->saveLayerAlpha(nullptr, alpha); + SkMatrix unused_matrix; + raster_fn(op, canvas, unused_matrix); + canvas->restore(); +} + +// Helper template to share common code for RasterWithAlpha when paint ops +// have or don't have PaintFlags. +template <typename T, bool HasFlags> +struct Rasterizer { + static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) { + static_assert( + !T::kHasPaintFlags, + "This function should not be used for a PaintOp that has PaintFlags"); + DCHECK(T::kIsDrawOp); + RasterWithAlphaInternal(&T::Raster, op, canvas, alpha); + } +}; + +NOINLINE static void RasterWithAlphaInternalForFlags( + RasterWithFlagsFunction raster_fn, + const PaintOpWithFlags* op, + SkCanvas* canvas, + uint8_t alpha) { + SkMatrix unused_matrix; + if (alpha == 255) { + raster_fn(op, &op->flags, canvas, unused_matrix); + } else if (op->flags.SupportsFoldingAlpha()) { + PaintFlags flags = op->flags; + flags.setAlpha(SkMulDiv255Round(flags.getAlpha(), alpha)); + raster_fn(op, &flags, canvas, unused_matrix); + } else { + canvas->saveLayerAlpha(nullptr, alpha); + raster_fn(op, &op->flags, canvas, unused_matrix); + canvas->restore(); + } +} + +template <typename T> +struct Rasterizer<T, true> { + static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) { + static_assert(T::kHasPaintFlags, + "This function expects the PaintOp to have PaintFlags"); + DCHECK(T::kIsDrawOp); + RasterWithAlphaInternalForFlags(&T::RasterWithFlags, op, canvas, alpha); + } +}; + +template <> +struct Rasterizer<DrawRecordOp, false> { + static void RasterWithAlpha(const DrawRecordOp* op, + SkCanvas* canvas, + uint8_t alpha) { + // This "looking into records" optimization is done here instead of + // in the PaintOpBuffer::Raster function as DisplayItemList calls + // into RasterWithAlpha directly. + if (op->record->size() == 1u) { + PaintOp* single_op = op->record->GetFirstOp(); + // RasterWithAlpha only supported for draw ops. + if (single_op->IsDrawOp()) { + single_op->RasterWithAlpha(canvas, alpha); + return; + } + } + + canvas->saveLayerAlpha(nullptr, alpha); + SkMatrix unused_matrix; + DrawRecordOp::Raster(op, canvas, unused_matrix); + canvas->restore(); + } +}; + +// TODO(enne): partially specialize RasterWithAlpha for draw color? + +static constexpr size_t kNumOpTypes = + static_cast<size_t>(PaintOpType::LastPaintOpType) + 1; + +// Verify that every op is in the TYPES macro. +#define M(T) +1 +static_assert(kNumOpTypes == TYPES(M), "Missing op in list"); +#undef M + +using RasterFunction = void (*)(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); +#define M(T) &T::Raster, +static const RasterFunction g_raster_functions[kNumOpTypes] = {TYPES(M)}; +#undef M + +using RasterAlphaFunction = void (*)(const PaintOp* op, + SkCanvas* canvas, + uint8_t alpha); +#define M(T) \ + T::kIsDrawOp ? \ + [](const PaintOp* op, SkCanvas* canvas, uint8_t alpha) { \ + Rasterizer<T, T::kHasPaintFlags>::RasterWithAlpha( \ + static_cast<const T*>(op), canvas, alpha); \ + } : static_cast<RasterAlphaFunction>(nullptr), +static const RasterAlphaFunction g_raster_alpha_functions[kNumOpTypes] = { + TYPES(M)}; +#undef M + +// Most state ops (matrix, clip, save, restore) have a trivial destructor. +// TODO(enne): evaluate if we need the nullptr optimization or if +// we even need to differentiate trivial destructors here. +using VoidFunction = void (*)(PaintOp* op); +#define M(T) \ + !std::is_trivially_destructible<T>::value \ + ? [](PaintOp* op) { static_cast<T*>(op)->~T(); } \ + : static_cast<VoidFunction>(nullptr), +static const VoidFunction g_destructor_functions[kNumOpTypes] = {TYPES(M)}; +#undef M + +#define M(T) T::kIsDrawOp, +static bool g_is_draw_op[kNumOpTypes] = {TYPES(M)}; +#undef M + +#define M(T) \ + static_assert(sizeof(T) <= sizeof(LargestPaintOp), \ + #T " must be no bigger than LargestPaintOp"); +TYPES(M); +#undef M + +#define M(T) \ + static_assert(ALIGNOF(T) <= PaintOpBuffer::PaintOpAlign, \ + #T " must have alignment no bigger than PaintOpAlign"); +TYPES(M); +#undef M + +#undef TYPES + +SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0}; + +void AnnotateOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const AnnotateOp*>(base_op); + switch (op->annotation_type) { + case PaintCanvas::AnnotationType::URL: + SkAnnotateRectWithURL(canvas, op->rect, op->data.get()); + break; + case PaintCanvas::AnnotationType::LINK_TO_DESTINATION: + SkAnnotateLinkToDestination(canvas, op->rect, op->data.get()); + break; + case PaintCanvas::AnnotationType::NAMED_DESTINATION: { + SkPoint point = SkPoint::Make(op->rect.x(), op->rect.y()); + SkAnnotateNamedDestination(canvas, point, op->data.get()); + break; + } + } +} + +void ClipPathOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const ClipPathOp*>(base_op); + canvas->clipPath(op->path, op->op, op->antialias); +} + +void ClipRectOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const ClipRectOp*>(base_op); + canvas->clipRect(op->rect, op->op, op->antialias); +} + +void ClipRRectOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const ClipRRectOp*>(base_op); + canvas->clipRRect(op->rrect, op->op, op->antialias); +} + +void ConcatOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const ConcatOp*>(base_op); + canvas->concat(op->matrix); +} + +void DrawArcOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawArcOp*>(base_op); + canvas->drawArc(op->oval, op->start_angle, op->sweep_angle, op->use_center, + ToSkPaint(*flags)); +} + +void DrawCircleOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawCircleOp*>(base_op); + canvas->drawCircle(op->cx, op->cy, op->radius, ToSkPaint(*flags)); +} + +void DrawColorOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawColorOp*>(base_op); + canvas->drawColor(op->color, op->mode); +} + +void DrawDisplayItemListOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawDisplayItemListOp*>(base_op); + op->list->Raster(canvas); +} + +void DrawDRRectOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawDRRectOp*>(base_op); + canvas->drawDRRect(op->outer, op->inner, ToSkPaint(*flags)); +} + +void DrawImageOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawImageOp*>(base_op); + canvas->drawImage(op->image.sk_image().get(), op->left, op->top, + ToSkPaint(flags)); +} + +void DrawImageRectOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawImageRectOp*>(base_op); + // TODO(enne): Probably PaintCanvas should just use the skia enum directly. + SkCanvas::SrcRectConstraint skconstraint = + static_cast<SkCanvas::SrcRectConstraint>(op->constraint); + canvas->drawImageRect(op->image.sk_image().get(), op->src, op->dst, + ToSkPaint(flags), skconstraint); +} + +void DrawIRectOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawIRectOp*>(base_op); + canvas->drawIRect(op->rect, ToSkPaint(*flags)); +} + +void DrawLineOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawLineOp*>(base_op); + canvas->drawLine(op->x0, op->y0, op->x1, op->y1, ToSkPaint(*flags)); +} + +void DrawOvalOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawOvalOp*>(base_op); + canvas->drawOval(op->oval, ToSkPaint(*flags)); +} + +void DrawPathOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawPathOp*>(base_op); + canvas->drawPath(op->path, ToSkPaint(*flags)); +} + +void DrawPosTextOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawPosTextOp*>(base_op); + canvas->drawPosText(op->GetData(), op->bytes, op->GetArray(), + ToSkPaint(*flags)); +} + +void DrawRecordOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + // Don't use drawPicture here, as it adds an implicit clip. + auto* op = static_cast<const DrawRecordOp*>(base_op); + op->record->playback(canvas); +} + +void DrawRectOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawRectOp*>(base_op); + canvas->drawRect(op->rect, ToSkPaint(*flags)); +} + +void DrawRRectOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawRRectOp*>(base_op); + canvas->drawRRect(op->rrect, ToSkPaint(*flags)); +} + +void DrawTextOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawTextOp*>(base_op); + canvas->drawText(op->GetData(), op->bytes, op->x, op->y, ToSkPaint(*flags)); +} + +void DrawTextBlobOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const DrawTextBlobOp*>(base_op); + canvas->drawTextBlob(op->blob.get(), op->x, op->y, ToSkPaint(*flags)); +} + +void RestoreOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + canvas->restore(); +} + +void RotateOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const RotateOp*>(base_op); + canvas->rotate(op->degrees); +} + +void SaveOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + canvas->save(); +} + +void SaveLayerOp::RasterWithFlags(const PaintOpWithFlags* base_op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const SaveLayerOp*>(base_op); + // See PaintOp::kUnsetRect + bool unset = op->bounds.left() == SK_ScalarInfinity; + + canvas->saveLayer(unset ? nullptr : &op->bounds, ToSkPaint(flags)); +} + +void SaveLayerAlphaOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const SaveLayerAlphaOp*>(base_op); + // See PaintOp::kUnsetRect + bool unset = op->bounds.left() == SK_ScalarInfinity; + canvas->saveLayerAlpha(unset ? nullptr : &op->bounds, op->alpha); +} + +void ScaleOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const ScaleOp*>(base_op); + canvas->scale(op->sx, op->sy); +} + +void SetMatrixOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const SetMatrixOp*>(base_op); + canvas->setMatrix(SkMatrix::Concat(original_ctm, op->matrix)); +} + +void TranslateOp::Raster(const PaintOp* base_op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* op = static_cast<const TranslateOp*>(base_op); + canvas->translate(op->dx, op->dy); +} + +bool PaintOp::IsDrawOp() const { + return g_is_draw_op[type]; +} + +void PaintOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const { + g_raster_functions[type](this, canvas, original_ctm); +} + +void PaintOp::RasterWithAlpha(SkCanvas* canvas, uint8_t alpha) const { + g_raster_alpha_functions[type](this, canvas, alpha); +} + +int ClipPathOp::CountSlowPaths() const { + return antialias && !path.isConvex() ? 1 : 0; +} + +int DrawLineOp::CountSlowPaths() const { + if (const SkPathEffect* effect = flags.getPathEffect()) { + SkPathEffect::DashInfo info; + SkPathEffect::DashType dashType = effect->asADash(&info); + if (flags.getStrokeCap() != PaintFlags::kRound_Cap && + dashType == SkPathEffect::kDash_DashType && info.fCount == 2) { + // The PaintFlags will count this as 1, so uncount that here as + // this kind of line is special cased and not slow. + return -1; + } + } + return 0; +} + +int DrawPathOp::CountSlowPaths() const { + // This logic is copied from SkPathCounter instead of attempting to expose + // that from Skia. + if (!flags.isAntiAlias() || path.isConvex()) + return 0; + + PaintFlags::Style paintStyle = flags.getStyle(); + const SkRect& pathBounds = path.getBounds(); + if (paintStyle == PaintFlags::kStroke_Style && flags.getStrokeWidth() == 0) { + // AA hairline concave path is not slow. + return 0; + } else if (paintStyle == PaintFlags::kFill_Style && + pathBounds.width() < 64.f && pathBounds.height() < 64.f && + !path.isVolatile()) { + // AADF eligible concave path is not slow. + return 0; + } else { + return 1; + } +} + +int DrawRecordOp::CountSlowPaths() const { + return record->numSlowPaths(); +} + +AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type, + const SkRect& rect, + sk_sp<SkData> data) + : annotation_type(annotation_type), rect(rect), data(std::move(data)) {} + +AnnotateOp::~AnnotateOp() = default; + +DrawDisplayItemListOp::DrawDisplayItemListOp( + scoped_refptr<DisplayItemList> list) + : list(list) {} + +size_t DrawDisplayItemListOp::AdditionalBytesUsed() const { + return list->ApproximateMemoryUsage(); +} + +bool DrawDisplayItemListOp::HasDiscardableImages() const { + return list->has_discardable_images(); +} + +DrawDisplayItemListOp::DrawDisplayItemListOp(const DrawDisplayItemListOp& op) = + default; + +DrawDisplayItemListOp& DrawDisplayItemListOp::operator=( + const DrawDisplayItemListOp& op) = default; + +DrawDisplayItemListOp::~DrawDisplayItemListOp() = default; + +DrawImageOp::DrawImageOp(const PaintImage& image, + SkScalar left, + SkScalar top, + const PaintFlags* flags) + : PaintOpWithFlags(flags ? *flags : PaintFlags()), + image(image), + left(left), + top(top) {} + +bool DrawImageOp::HasDiscardableImages() const { + // TODO(khushalsagar): Callers should not be able to change the lazy generated + // state for a PaintImage. + return image.sk_image()->isLazyGenerated(); +} + +DrawImageOp::~DrawImageOp() = default; + +DrawImageRectOp::DrawImageRectOp(const PaintImage& image, + const SkRect& src, + const SkRect& dst, + const PaintFlags* flags, + PaintCanvas::SrcRectConstraint constraint) + : PaintOpWithFlags(flags ? *flags : PaintFlags()), + image(image), + src(src), + dst(dst), + constraint(constraint) {} + +bool DrawImageRectOp::HasDiscardableImages() const { + return image.sk_image()->isLazyGenerated(); +} + +DrawImageRectOp::~DrawImageRectOp() = default; + +DrawPosTextOp::DrawPosTextOp(size_t bytes, + size_t count, + const PaintFlags& flags) + : PaintOpWithArray(flags, bytes, count) {} + +DrawPosTextOp::~DrawPosTextOp() = default; + +DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record) + : record(std::move(record)) {} + +DrawRecordOp::~DrawRecordOp() = default; + +size_t DrawRecordOp::AdditionalBytesUsed() const { + return record->bytes_used(); +} + +bool DrawRecordOp::HasDiscardableImages() const { + return record->HasDiscardableImages(); +} + +DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob, + SkScalar x, + SkScalar y, + const PaintFlags& flags) + : PaintOpWithFlags(flags), blob(std::move(blob)), x(x), y(y) {} + +DrawTextBlobOp::~DrawTextBlobOp() = default; + +PaintOpBuffer::PaintOpBuffer() = default; + +PaintOpBuffer::~PaintOpBuffer() { + Reset(); +} + +void PaintOpBuffer::Reset() { + for (auto* op : Iterator(this)) { + auto func = g_destructor_functions[op->type]; + if (func) + func(op); + } + + // Leave data_ allocated, reserved_ unchanged. + used_ = 0; + op_count_ = 0; + num_slow_paths_ = 0; +} + +static const PaintOp* NextOp(const std::vector<size_t>& range_starts, + const std::vector<size_t>& range_indices, + base::StackVector<const PaintOp*, 3>* stack_ptr, + PaintOpBuffer::Iterator* iter, + size_t* range_index) { + auto& stack = *stack_ptr; + if (stack->size()) { + const PaintOp* op = stack->front(); + // Shift paintops forward + stack->erase(stack->begin()); + return op; + } + if (!*iter) + return nullptr; + + const size_t active_range = range_indices[*range_index]; + DCHECK_GE(iter->op_idx(), range_starts[active_range]); + + // This grabs the PaintOp from the current iterator position, and advances it + // to the next position immediately. We'll see we reached the end of the + // buffer on the next call to this method. + const PaintOp* op = **iter; + ++*iter; + + if (active_range + 1 == range_starts.size()) { + // In the last possible range, so let the iter go right to the end of the + // buffer. + return op; + } + + const size_t range_end = range_starts[active_range + 1]; + DCHECK_LE(iter->op_idx(), range_end); + if (iter->op_idx() < range_end) { + // Still inside the range, so let the iter be. + return op; + } + + if (*range_index + 1 == range_indices.size()) { + // We're now past the last range that we want to iterate. + *iter = iter->end(); + return op; + } + + // Move to the next range. + ++(*range_index); + size_t next_range_start = range_starts[range_indices[*range_index]]; + while (iter->op_idx() < next_range_start) + ++(*iter); + return op; +} + +void PaintOpBuffer::playback(SkCanvas* canvas, + SkPicture::AbortCallback* callback) const { + static auto* zero = new std::vector<size_t>({0}); + // Treats the entire PaintOpBuffer as a single range. + PlaybackRanges(*zero, *zero, canvas, callback); +} + +void PaintOpBuffer::PlaybackRanges(const std::vector<size_t>& range_starts, + const std::vector<size_t>& range_indices, + SkCanvas* canvas, + SkPicture::AbortCallback* callback) const { + if (!op_count_) + return; + if (callback && callback->abort()) + return; + +#if DCHECK_IS_ON() + DCHECK(!range_starts.empty()); // Don't call this then. + DCHECK(!range_indices.empty()); // Don't call this then. + DCHECK_EQ(0u, range_starts[0]); + for (size_t i = 1; i < range_starts.size(); ++i) { + DCHECK_GT(range_starts[i], range_starts[i - 1]); + DCHECK_LT(range_starts[i], op_count_); + } + DCHECK_LT(range_indices[0], range_starts.size()); + for (size_t i = 1; i < range_indices.size(); ++i) { + DCHECK_GT(range_indices[i], range_indices[i - 1]); + DCHECK_LT(range_indices[i], range_starts.size()); + } +#endif + + // TODO(enne): a PaintRecord that contains a SetMatrix assumes that the + // SetMatrix is local to that PaintRecord itself. Said differently, if you + // translate(x, y), then draw a paint record with a SetMatrix(identity), + // the translation should be preserved instead of clobbering the top level + // transform. This could probably be done more efficiently. + SkMatrix original = canvas->getTotalMatrix(); + + // FIFO queue of paint ops that have been peeked at. + base::StackVector<const PaintOp*, 3> stack; + + // The current offset into range_indices. range_indices[range_index] is the + // current offset into range_starts. + size_t range_index = 0; + + Iterator iter(this); + while (iter.op_idx() < range_starts[range_indices[range_index]]) + ++iter; + while (const PaintOp* op = + NextOp(range_starts, range_indices, &stack, &iter, &range_index)) { + // Optimize out save/restores or save/draw/restore that can be a single + // draw. See also: similar code in SkRecordOpts and cc's DisplayItemList. + // TODO(enne): consider making this recursive? + if (op->GetType() == PaintOpType::SaveLayerAlpha) { + const PaintOp* second = + NextOp(range_starts, range_indices, &stack, &iter, &range_index); + const PaintOp* third = nullptr; + if (second) { + if (second->GetType() == PaintOpType::Restore) { + continue; + } + if (second->IsDrawOp()) { + third = + NextOp(range_starts, range_indices, &stack, &iter, &range_index); + if (third && third->GetType() == PaintOpType::Restore) { + const SaveLayerAlphaOp* save_op = + static_cast<const SaveLayerAlphaOp*>(op); + second->RasterWithAlpha(canvas, save_op->alpha); + continue; + } + } + + // Store deferred ops for later. + stack->push_back(second); + if (third) + stack->push_back(third); + } + } + // TODO(enne): skip SaveLayer followed by restore with nothing in + // between, however SaveLayer with image filters on it (or maybe + // other PaintFlags options) are not a noop. Figure out what these + // are so we can skip them correctly. + + op->Raster(canvas, original); + if (callback && callback->abort()) + return; + } +} + +void PaintOpBuffer::ReallocBuffer(size_t new_size) { + DCHECK_GE(new_size, used_); + std::unique_ptr<char, base::AlignedFreeDeleter> new_data( + static_cast<char*>(base::AlignedAlloc(new_size, PaintOpAlign))); + memcpy(new_data.get(), data_.get(), used_); + data_ = std::move(new_data); + reserved_ = new_size; +} + +std::pair<void*, size_t> PaintOpBuffer::AllocatePaintOp(size_t sizeof_op, + size_t bytes) { + if (!op_count_) { + if (bytes) { + // Internal first_op buffer doesn't have room for extra data. + // If the op wants extra bytes, then we'll just store a Noop + // in the first_op and proceed from there. This seems unlikely + // to be a common case. + push<NoopOp>(); + } else { + op_count_++; + return std::make_pair(first_op_.void_data(), 0); + } + } + + // We've filled |first_op_| by now so we need to allocate space in |data_|. + DCHECK(op_count_); + + // Compute a skip such that all ops in the buffer are aligned to the + // maximum required alignment of all ops. + size_t skip = MathUtil::UncheckedRoundUp(sizeof_op + bytes, PaintOpAlign); + DCHECK_LT(skip, static_cast<size_t>(1) << 24); + if (used_ + skip > reserved_) { + // Start reserved_ at kInitialBufferSize and then double. + // ShrinkToFit can make this smaller afterwards. + size_t new_size = reserved_ ? reserved_ : kInitialBufferSize; + while (used_ + skip > new_size) + new_size *= 2; + ReallocBuffer(new_size); + } + DCHECK_LE(used_ + skip, reserved_); + + void* op = data_.get() + used_; + used_ += skip; + op_count_++; + return std::make_pair(op, skip); +} + +void PaintOpBuffer::ShrinkToFit() { + if (!used_ || used_ == reserved_) + return; + ReallocBuffer(used_); +} + +} // namespace cc diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h new file mode 100644 index 00000000000..bcc0c809964 --- /dev/null +++ b/chromium/cc/paint/paint_op_buffer.h @@ -0,0 +1,978 @@ +// Copyright 2017 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. + +#ifndef CC_PAINT_PAINT_OP_BUFFER_H_ +#define CC_PAINT_PAINT_OP_BUFFER_H_ + +#include <stdint.h> + +#include "base/logging.h" +#include "base/memory/aligned_memory.h" +#include "cc/base/math_util.h" +#include "cc/paint/paint_canvas.h" +#include "cc/paint/paint_export.h" +#include "cc/paint/paint_flags.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +// PaintOpBuffer is a reimplementation of SkLiteDL. +// See: third_party/skia/src/core/SkLiteDL.h. + +namespace cc { +class DisplayItemList; + +class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix { + public: + explicit ThreadsafeMatrix(const SkMatrix& matrix) : SkMatrix(matrix) { + (void)getType(); + } +}; + +class CC_PAINT_EXPORT ThreadsafePath : public SkPath { + public: + explicit ThreadsafePath(const SkPath& path) : SkPath(path) { + updateBoundsCache(); + } +}; + +enum class PaintOpType : uint8_t { + Annotate, + ClipPath, + ClipRect, + ClipRRect, + Concat, + DrawArc, + DrawCircle, + DrawColor, + DrawDisplayItemList, + DrawDRRect, + DrawImage, + DrawImageRect, + DrawIRect, + DrawLine, + DrawOval, + DrawPath, + DrawPosText, + DrawRecord, + DrawRect, + DrawRRect, + DrawText, + DrawTextBlob, + Noop, + Restore, + Rotate, + Save, + SaveLayer, + SaveLayerAlpha, + Scale, + SetMatrix, + Translate, + LastPaintOpType = Translate, +}; + +struct CC_PAINT_EXPORT PaintOp { + uint32_t type : 8; + uint32_t skip : 24; + + PaintOpType GetType() const { return static_cast<PaintOpType>(type); } + + // Subclasses should provide a static Raster() method which is called from + // here. The Raster method should take a const PaintOp* parameter. It is + // static with a pointer to the base type so that we can use it as a function + // pointer. + void Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const; + bool IsDrawOp() const; + + // Only valid for draw ops. + void RasterWithAlpha(SkCanvas* canvas, uint8_t alpha) const; + + int CountSlowPaths() const { return 0; } + int CountSlowPathsFromFlags() const { return 0; } + + bool HasDiscardableImages() const { return false; } + bool HasDiscardableImagesFromFlags() const { return false; } + + // Returns the number of bytes used by this op in referenced sub records + // and display lists. This doesn't count other objects like paths or blobs. + size_t AdditionalBytesUsed() const { return 0; } + + static constexpr bool kIsDrawOp = false; + static constexpr bool kHasPaintFlags = false; + static SkRect kUnsetRect; +}; + +struct CC_PAINT_EXPORT PaintOpWithFlags : PaintOp { + static constexpr bool kHasPaintFlags = true; + + explicit PaintOpWithFlags(const PaintFlags& flags) : flags(flags) {} + + int CountSlowPathsFromFlags() const { return flags.getPathEffect() ? 1 : 0; } + bool HasDiscardableImagesFromFlags() const { + if (!IsDrawOp()) + return false; + + SkShader* shader = flags.getShader(); + SkImage* image = shader ? shader->isAImage(nullptr, nullptr) : nullptr; + return image && image->isLazyGenerated(); + } + + // Subclasses should provide a static RasterWithFlags() method which is called + // from the Raster() method. The RasterWithFlags() should use the PaintFlags + // passed to it, instead of the |flags| member directly, as some callers may + // provide a modified PaintFlags. The RasterWithFlags() method is static with + // a const PaintOpWithFlags* parameter so that it can be used as a function + // pointer. + PaintFlags flags; +}; + +struct CC_PAINT_EXPORT PaintOpWithData : PaintOpWithFlags { + // Having data is just a helper for ops that have a varying amount of data and + // want a way to store that inline. This is for ops that pass in a + // void* and a length. The void* data is assumed to not have any alignment + // requirements. + PaintOpWithData(const PaintFlags& flags, size_t bytes) + : PaintOpWithFlags(flags), bytes(bytes) {} + + // Get data out by calling paint_op_data. This can't be part of the class + // because it needs to know the size of the derived type. + size_t bytes; + + protected: + // For some derived object T, return the internally stored data. + // This needs the fully derived type to know how much to offset + // from the start of the top to the data. + template <typename T> + const void* GetDataForThis(const T* op) const { + static_assert(std::is_convertible<T, PaintOpWithData>::value, + "T is not a PaintOpWithData"); + // Arbitrary data for a PaintOp is stored after the PaintOp itself + // in the PaintOpBuffer. Therefore, to access this data, it's + // pointer math to increment past the size of T. Accessing the + // next op in the buffer is ((char*)op) + op->skip, with the data + // fitting between. + return op + 1; + } + + template <typename T> + void* GetDataForThis(T* op) { + return const_cast<void*>( + const_cast<const PaintOpWithData*>(this)->GetDataForThis( + const_cast<const T*>(op))); + } +}; + +struct CC_PAINT_EXPORT PaintOpWithArrayBase : PaintOpWithFlags { + explicit PaintOpWithArrayBase(const PaintFlags& flags) + : PaintOpWithFlags(flags) {} +}; + +template <typename M> +struct CC_PAINT_EXPORT PaintOpWithArray : PaintOpWithArrayBase { + // Paint op that has a M[count] and a char[bytes]. + // Array data is stored first so that it can be aligned with T's alignment + // with the arbitrary unaligned char data after it. + // Memory layout here is: | op | M[count] | char[bytes] | padding | next op | + // Next op is located at (char*)(op) + op->skip. + PaintOpWithArray(const PaintFlags& flags, size_t bytes, size_t count) + : PaintOpWithArrayBase(flags), bytes(bytes), count(count) {} + + size_t bytes; + size_t count; + + protected: + template <typename T> + const void* GetDataForThis(const T* op) const { + static_assert(std::is_convertible<T, PaintOpWithArrayBase>::value, + "T is not a PaintOpWithData"); + const char* start_array = + reinterpret_cast<const char*>(GetArrayForThis(op)); + return start_array + sizeof(M) * count; + } + + template <typename T> + void* GetDataForThis(T* op) { + return const_cast<void*>( + const_cast<const PaintOpWithArray*>(this)->GetDataForThis( + const_cast<T*>(op))); + } + + template <typename T> + const M* GetArrayForThis(const T* op) const { + static_assert(std::is_convertible<T, PaintOpWithArrayBase>::value, + "T is not a PaintOpWithData"); + // As an optimization to not have to store an additional offset, + // assert that T has the same or more alignment requirements than M. Thus, + // if T is aligned, and M's alignment needs are a multiple of T's size, then + // M will also be aligned when placed immediately after T. + static_assert( + sizeof(T) % ALIGNOF(M) == 0, + "T must be padded such that an array of M is aligned after it"); + static_assert( + ALIGNOF(T) >= ALIGNOF(M), + "T must have not have less alignment requirements than the array data"); + return reinterpret_cast<const M*>(op + 1); + } + + template <typename T> + M* GetArrayForThis(T* op) { + return const_cast<M*>( + const_cast<const PaintOpWithArray*>(this)->GetArrayForThis( + const_cast<T*>(op))); + } +}; + +struct CC_PAINT_EXPORT AnnotateOp final : PaintOp { + enum class AnnotationType { + URL, + LinkToDestination, + NamedDestination, + }; + + static constexpr PaintOpType kType = PaintOpType::Annotate; + AnnotateOp(PaintCanvas::AnnotationType annotation_type, + const SkRect& rect, + sk_sp<SkData> data); + ~AnnotateOp(); + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + PaintCanvas::AnnotationType annotation_type; + SkRect rect; + sk_sp<SkData> data; +}; + +struct CC_PAINT_EXPORT ClipPathOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::ClipPath; + ClipPathOp(SkPath path, SkClipOp op, bool antialias) + : path(path), op(op), antialias(antialias) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + int CountSlowPaths() const; + + ThreadsafePath path; + SkClipOp op; + bool antialias; +}; + +struct CC_PAINT_EXPORT ClipRectOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::ClipRect; + ClipRectOp(const SkRect& rect, SkClipOp op, bool antialias) + : rect(rect), op(op), antialias(antialias) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRect rect; + SkClipOp op; + bool antialias; +}; + +struct CC_PAINT_EXPORT ClipRRectOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::ClipRRect; + ClipRRectOp(const SkRRect& rrect, SkClipOp op, bool antialias) + : rrect(rrect), op(op), antialias(antialias) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRRect rrect; + SkClipOp op; + bool antialias; +}; + +struct CC_PAINT_EXPORT ConcatOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::Concat; + explicit ConcatOp(const SkMatrix& matrix) : matrix(matrix) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + ThreadsafeMatrix matrix; +}; + +struct CC_PAINT_EXPORT DrawArcOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawArc; + static constexpr bool kIsDrawOp = true; + DrawArcOp(const SkRect& oval, + SkScalar start_angle, + SkScalar sweep_angle, + bool use_center, + const PaintFlags& flags) + : PaintOpWithFlags(flags), + oval(oval), + start_angle(start_angle), + sweep_angle(sweep_angle), + use_center(use_center) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRect oval; + SkScalar start_angle; + SkScalar sweep_angle; + bool use_center; +}; + +struct CC_PAINT_EXPORT DrawCircleOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawCircle; + static constexpr bool kIsDrawOp = true; + DrawCircleOp(SkScalar cx, + SkScalar cy, + SkScalar radius, + const PaintFlags& flags) + : PaintOpWithFlags(flags), cx(cx), cy(cy), radius(radius) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkScalar cx; + SkScalar cy; + SkScalar radius; +}; + +struct CC_PAINT_EXPORT DrawColorOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::DrawColor; + static constexpr bool kIsDrawOp = true; + DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkColor color; + SkBlendMode mode; +}; + +struct CC_PAINT_EXPORT DrawDisplayItemListOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::DrawDisplayItemList; + static constexpr bool kIsDrawOp = true; + explicit DrawDisplayItemListOp(scoped_refptr<DisplayItemList> list); + // Windows wants to generate these when types are exported, so + // provide them here explicitly so that DisplayItemList doesn't have + // to be defined in this header. + DrawDisplayItemListOp(const DrawDisplayItemListOp& op); + DrawDisplayItemListOp& operator=(const DrawDisplayItemListOp& op); + ~DrawDisplayItemListOp(); + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + size_t AdditionalBytesUsed() const; + bool HasDiscardableImages() const; + // TODO(enne): DisplayItemList should know number of slow paths. + + scoped_refptr<DisplayItemList> list; +}; + +struct CC_PAINT_EXPORT DrawDRRectOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawDRRect; + static constexpr bool kIsDrawOp = true; + DrawDRRectOp(const SkRRect& outer, + const SkRRect& inner, + const PaintFlags& flags) + : PaintOpWithFlags(flags), outer(outer), inner(inner) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRRect outer; + SkRRect inner; +}; + +struct CC_PAINT_EXPORT DrawImageOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawImage; + static constexpr bool kIsDrawOp = true; + DrawImageOp(const PaintImage& image, + SkScalar left, + SkScalar top, + const PaintFlags* flags); + ~DrawImageOp(); + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + bool HasDiscardableImages() const; + + PaintImage image; + SkScalar left; + SkScalar top; +}; + +struct CC_PAINT_EXPORT DrawImageRectOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawImageRect; + static constexpr bool kIsDrawOp = true; + DrawImageRectOp(const PaintImage& image, + const SkRect& src, + const SkRect& dst, + const PaintFlags* flags, + PaintCanvas::SrcRectConstraint constraint); + ~DrawImageRectOp(); + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + bool HasDiscardableImages() const; + + PaintImage image; + SkRect src; + SkRect dst; + PaintCanvas::SrcRectConstraint constraint; +}; + +struct CC_PAINT_EXPORT DrawIRectOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawIRect; + static constexpr bool kIsDrawOp = true; + DrawIRectOp(const SkIRect& rect, const PaintFlags& flags) + : PaintOpWithFlags(flags), rect(rect) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkIRect rect; +}; + +struct CC_PAINT_EXPORT DrawLineOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawLine; + static constexpr bool kIsDrawOp = true; + DrawLineOp(SkScalar x0, + SkScalar y0, + SkScalar x1, + SkScalar y1, + const PaintFlags& flags) + : PaintOpWithFlags(flags), x0(x0), y0(y0), x1(x1), y1(y1) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + int CountSlowPaths() const; + + SkScalar x0; + SkScalar y0; + SkScalar x1; + SkScalar y1; +}; + +struct CC_PAINT_EXPORT DrawOvalOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawOval; + static constexpr bool kIsDrawOp = true; + DrawOvalOp(const SkRect& oval, const PaintFlags& flags) + : PaintOpWithFlags(flags), oval(oval) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRect oval; +}; + +struct CC_PAINT_EXPORT DrawPathOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawPath; + static constexpr bool kIsDrawOp = true; + DrawPathOp(const SkPath& path, const PaintFlags& flags) + : PaintOpWithFlags(flags), path(path) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + int CountSlowPaths() const; + + ThreadsafePath path; +}; + +struct CC_PAINT_EXPORT DrawPosTextOp final : PaintOpWithArray<SkPoint> { + static constexpr PaintOpType kType = PaintOpType::DrawPosText; + static constexpr bool kIsDrawOp = true; + DrawPosTextOp(size_t bytes, size_t count, const PaintFlags& flags); + ~DrawPosTextOp(); + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + const void* GetData() const { return GetDataForThis(this); } + void* GetData() { return GetDataForThis(this); } + const SkPoint* GetArray() const { return GetArrayForThis(this); } + SkPoint* GetArray() { return GetArrayForThis(this); } +}; + +struct CC_PAINT_EXPORT DrawRecordOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::DrawRecord; + static constexpr bool kIsDrawOp = true; + explicit DrawRecordOp(sk_sp<const PaintRecord> record); + ~DrawRecordOp(); + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + size_t AdditionalBytesUsed() const; + bool HasDiscardableImages() const; + int CountSlowPaths() const; + + sk_sp<const PaintRecord> record; +}; + +struct CC_PAINT_EXPORT DrawRectOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawRect; + static constexpr bool kIsDrawOp = true; + DrawRectOp(const SkRect& rect, const PaintFlags& flags) + : PaintOpWithFlags(flags), rect(rect) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRect rect; +}; + +struct CC_PAINT_EXPORT DrawRRectOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawRRect; + static constexpr bool kIsDrawOp = true; + DrawRRectOp(const SkRRect& rrect, const PaintFlags& flags) + : PaintOpWithFlags(flags), rrect(rrect) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRRect rrect; +}; + +struct CC_PAINT_EXPORT DrawTextOp final : PaintOpWithData { + static constexpr PaintOpType kType = PaintOpType::DrawText; + static constexpr bool kIsDrawOp = true; + DrawTextOp(size_t bytes, SkScalar x, SkScalar y, const PaintFlags& flags) + : PaintOpWithData(flags, bytes), x(x), y(y) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + void* GetData() { return GetDataForThis(this); } + const void* GetData() const { return GetDataForThis(this); } + + SkScalar x; + SkScalar y; +}; + +struct CC_PAINT_EXPORT DrawTextBlobOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::DrawTextBlob; + static constexpr bool kIsDrawOp = true; + DrawTextBlobOp(sk_sp<SkTextBlob> blob, + SkScalar x, + SkScalar y, + const PaintFlags& flags); + ~DrawTextBlobOp(); + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + sk_sp<SkTextBlob> blob; + SkScalar x; + SkScalar y; +}; + +struct CC_PAINT_EXPORT NoopOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::Noop; + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) {} +}; + +struct CC_PAINT_EXPORT RestoreOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::Restore; + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); +}; + +struct CC_PAINT_EXPORT RotateOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::Rotate; + explicit RotateOp(SkScalar degrees) : degrees(degrees) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkScalar degrees; +}; + +struct CC_PAINT_EXPORT SaveOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::Save; + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); +}; + +struct CC_PAINT_EXPORT SaveLayerOp final : PaintOpWithFlags { + static constexpr PaintOpType kType = PaintOpType::SaveLayer; + SaveLayerOp(const SkRect* bounds, const PaintFlags* flags) + : PaintOpWithFlags(flags ? *flags : PaintFlags()), + bounds(bounds ? *bounds : kUnsetRect) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm) { + auto* flags_op = static_cast<const PaintOpWithFlags*>(op); + RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm); + } + static void RasterWithFlags(const PaintOpWithFlags* op, + const PaintFlags* flags, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRect bounds; +}; + +struct CC_PAINT_EXPORT SaveLayerAlphaOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::SaveLayerAlpha; + SaveLayerAlphaOp(const SkRect* bounds, uint8_t alpha) + : bounds(bounds ? *bounds : kUnsetRect), alpha(alpha) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkRect bounds; + uint8_t alpha; +}; + +struct CC_PAINT_EXPORT ScaleOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::Scale; + ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkScalar sx; + SkScalar sy; +}; + +struct CC_PAINT_EXPORT SetMatrixOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::SetMatrix; + explicit SetMatrixOp(const SkMatrix& matrix) : matrix(matrix) {} + // This is the only op that needs the original ctm of the SkCanvas + // used for raster (since SetMatrix is relative to the recording origin and + // shouldn't clobber the SkCanvas raster origin). + // + // TODO(enne): Find some cleaner way to do this, possibly by making + // all SetMatrix calls Concat?? + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + ThreadsafeMatrix matrix; +}; + +struct CC_PAINT_EXPORT TranslateOp final : PaintOp { + static constexpr PaintOpType kType = PaintOpType::Translate; + TranslateOp(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {} + static void Raster(const PaintOp* op, + SkCanvas* canvas, + const SkMatrix& original_ctm); + + SkScalar dx; + SkScalar dy; +}; + +using LargestPaintOp = DrawDRRectOp; + +class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { + public: + enum { kInitialBufferSize = 4096 }; + // It's not necessarily the case that the op with the maximum alignment + // requirements is also the biggest op, but for now that's true. + static constexpr size_t PaintOpAlign = ALIGNOF(DrawDRRectOp); + + PaintOpBuffer(); + ~PaintOpBuffer() override; + + void Reset(); + + void playback(SkCanvas* canvas, + SkPicture::AbortCallback* callback = nullptr) const; + // This can be used to play back a subset of the PaintOpBuffer. + // The |range_starts| array is an increasing set of positions in the + // PaintOpBuffer that break the buffer up into arbitrary consecutive chunks + // that together cover the entire buffer. The first value in |range_starts| + // must be 0. Each value after defines the end of the previous range + // (exclusive) and the beginning of the next range (inclusive). The last value + // in the array defines the last range which includes all ops to the end of + // the buffer. For example, given a PaintOpBuffer with the following ops: + // { A, B, C, D, E, F, G, H, I } + // And a |range_starts| with the following values: + // { 0, 4, 5 } + // This defines the following ranges in PaintOpBuffer: + // { A, B, C, D }, { E }, { F, G, H, I }. + // The |range_indices| is an increasing set of indices into the |range_starts| + // array. This defines the set of ranges that will be played back. + // Given the above example, if range_indices contains: + // { 1, 2 } + // Then the 1th and 2th (starting from base 0) ranges as defined in + // |range_starts| would be played back, which would be: + // { E, F, G, H, I }. + void PlaybackRanges(const std::vector<size_t>& range_starts, + const std::vector<size_t>& range_indices, + SkCanvas* canvas, + SkPicture::AbortCallback* callback = nullptr) const; + + // Returns the size of the paint op buffer. That is, the number of ops + // contained in it. + size_t size() const { return op_count_; } + // Returns the number of bytes used by the paint op buffer. + size_t bytes_used() const { + return sizeof(*this) + reserved_ + subrecord_bytes_used_; + } + int numSlowPaths() const { return num_slow_paths_; } + bool HasDiscardableImages() const { return has_discardable_images_; } + + // Resize the PaintOpBuffer to exactly fit the current amount of used space. + void ShrinkToFit(); + + PaintOp* GetFirstOp() const { + return const_cast<PaintOp*>(first_op_.data_as<PaintOp>()); + } + + template <typename T, typename... Args> + void push(Args&&... args) { + static_assert(std::is_convertible<T, PaintOp>::value, "T not a PaintOp."); + static_assert(!std::is_convertible<T, PaintOpWithData>::value, + "Type needs to use push_with_data"); + push_internal<T>(0, std::forward<Args>(args)...); + } + + template <typename T, typename... Args> + void push_with_data(const void* data, size_t bytes, Args&&... args) { + static_assert(std::is_convertible<T, PaintOpWithData>::value, + "T is not a PaintOpWithData"); + static_assert(!std::is_convertible<T, PaintOpWithArrayBase>::value, + "Type needs to use push_with_array"); + DCHECK_GE(bytes, 0u); + T* op = push_internal<T>(bytes, bytes, std::forward<Args>(args)...); + memcpy(op->GetData(), data, bytes); + +#if DCHECK_IS_ON() + // Double check the data fits between op and next op and doesn't clobber. + char* op_start = reinterpret_cast<char*>(op); + char* op_end = op_start + sizeof(T); + char* next_op = op_start + op->skip; + char* data_start = reinterpret_cast<char*>(op->GetData()); + char* data_end = data_start + bytes; + DCHECK_GE(data_start, op_end); + DCHECK_LT(data_start, next_op); + DCHECK_LE(data_end, next_op); +#endif + } + + template <typename T, typename M, typename... Args> + void push_with_array(const void* data, + size_t bytes, + const M* array, + size_t count, + Args&&... args) { + static_assert(std::is_convertible<T, PaintOpWithArray<M>>::value, + "T is not a PaintOpWithArray"); + DCHECK_GE(bytes, 0u); + DCHECK_GE(count, 0u); + size_t array_size = sizeof(M) * count; + size_t total_size = bytes + array_size; + T* op = + push_internal<T>(total_size, bytes, count, std::forward<Args>(args)...); + memcpy(op->GetData(), data, bytes); + memcpy(op->GetArray(), array, array_size); + +#if DCHECK_IS_ON() + // Double check data and array don't clobber op, next op, or each other + char* op_start = reinterpret_cast<char*>(op); + char* op_end = op_start + sizeof(T); + char* array_start = reinterpret_cast<char*>(op->GetArray()); + char* array_end = array_start + array_size; + char* data_start = reinterpret_cast<char*>(op->GetData()); + char* data_end = data_start + bytes; + char* next_op = op_start + op->skip; + DCHECK_GE(array_start, op_end); + DCHECK_LE(array_start, data_start); + DCHECK_GE(data_start, array_end); + DCHECK_LE(data_end, next_op); +#endif + } + + class Iterator { + public: + explicit Iterator(const PaintOpBuffer* buffer) + : buffer_(buffer), ptr_(buffer_->data_.get()) {} + + PaintOp* operator->() const { + return op_idx_ ? reinterpret_cast<PaintOp*>(ptr_) : buffer_->GetFirstOp(); + } + PaintOp* operator*() const { return operator->(); } + Iterator begin() { return Iterator(buffer_, buffer_->data_.get(), 0); } + Iterator end() { + return Iterator(buffer_, buffer_->data_.get() + buffer_->used_, + buffer_->size()); + } + bool operator!=(const Iterator& other) { + // Not valid to compare iterators on different buffers. + DCHECK_EQ(other.buffer_, buffer_); + return other.op_idx_ != op_idx_; + } + Iterator& operator++() { + if (!op_idx_++) + return *this; + PaintOp* op = **this; + uint32_t type = op->type; + CHECK_LE(type, static_cast<uint32_t>(PaintOpType::LastPaintOpType)); + ptr_ += op->skip; + return *this; + } + operator bool() const { return op_idx_ < buffer_->size(); } + size_t op_idx() const { return op_idx_; } + + private: + Iterator(const PaintOpBuffer* buffer, char* ptr, size_t op_idx) + : buffer_(buffer), ptr_(ptr), op_idx_(op_idx) {} + + const PaintOpBuffer* buffer_ = nullptr; + char* ptr_ = nullptr; + size_t op_idx_ = 0; + }; + + private: + void ReallocBuffer(size_t new_size); + // Returns the allocated op and the number of bytes to skip in |data_| to get + // to the next op. + std::pair<void*, size_t> AllocatePaintOp(size_t sizeof_op, size_t bytes); + + template <typename T, typename... Args> + T* push_internal(size_t bytes, Args&&... args) { + static_assert(ALIGNOF(T) <= PaintOpAlign, ""); + + auto pair = AllocatePaintOp(sizeof(T), bytes); + T* op = reinterpret_cast<T*>(pair.first); + size_t skip = pair.second; + + new (op) T{std::forward<Args>(args)...}; + op->type = static_cast<uint32_t>(T::kType); + op->skip = skip; + AnalyzeAddedOp(op); + return op; + } + + template <typename T> + void AnalyzeAddedOp(const T* op) { + num_slow_paths_ += op->CountSlowPathsFromFlags(); + num_slow_paths_ += op->CountSlowPaths(); + + has_discardable_images_ |= op->HasDiscardableImages(); + has_discardable_images_ |= op->HasDiscardableImagesFromFlags(); + + subrecord_bytes_used_ += op->AdditionalBytesUsed(); + } + + // As a performance optimization because n=1 is an extremely common case just + // store the first op in the PaintOpBuffer itself to avoid an extra alloc. + base::AlignedMemory<sizeof(LargestPaintOp), PaintOpAlign> first_op_; + std::unique_ptr<char, base::AlignedFreeDeleter> data_; + size_t used_ = 0; + size_t reserved_ = 0; + size_t op_count_ = 0; + + // Record paths for veto-to-msaa for gpu raster. + int num_slow_paths_ = 0; + // Record additional bytes used by referenced sub-records and display lists. + size_t subrecord_bytes_used_ = 0; + bool has_discardable_images_ = false; + + DISALLOW_COPY_AND_ASSIGN(PaintOpBuffer); +}; + +} // namespace cc + +#endif // CC_PAINT_PAINT_OP_BUFFER_H_ diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc new file mode 100644 index 00000000000..5751c2fa1e0 --- /dev/null +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -0,0 +1,901 @@ +// Copyright 2017 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/paint/paint_op_buffer.h" +#include "cc/paint/display_item_list.h" +#include "cc/test/skia_common.h" +#include "cc/test/test_skcanvas.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/effects/SkDashPathEffect.h" + +using testing::_; +using testing::Property; +using testing::Mock; + +namespace { + +template <typename T> +void CheckRefCnt(const T& obj, int32_t count) { +// Skia doesn't define getRefCnt in all builds. +#ifdef SK_DEBUG + EXPECT_EQ(obj->getRefCnt(), count); +#endif +} + +} // namespace + +namespace cc { + +TEST(PaintOpBufferTest, Empty) { + PaintOpBuffer buffer; + EXPECT_EQ(buffer.size(), 0u); + EXPECT_EQ(buffer.bytes_used(), sizeof(PaintOpBuffer)); + EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); + + buffer.Reset(); + EXPECT_EQ(buffer.size(), 0u); + EXPECT_EQ(buffer.bytes_used(), sizeof(PaintOpBuffer)); + EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); +} + +TEST(PaintOpBufferTest, SimpleAppend) { + SkRect rect = SkRect::MakeXYWH(2, 3, 4, 5); + PaintFlags flags; + flags.setColor(SK_ColorMAGENTA); + flags.setAlpha(100); + SkColor draw_color = SK_ColorRED; + SkBlendMode blend = SkBlendMode::kSrc; + + PaintOpBuffer buffer; + buffer.push<SaveLayerOp>(&rect, &flags); + buffer.push<SaveOp>(); + buffer.push<DrawColorOp>(draw_color, blend); + buffer.push<RestoreOp>(); + + EXPECT_EQ(buffer.size(), 4u); + + PaintOpBuffer::Iterator iter(&buffer); + ASSERT_EQ(iter->GetType(), PaintOpType::SaveLayer); + SaveLayerOp* save_op = static_cast<SaveLayerOp*>(*iter); + EXPECT_EQ(save_op->bounds, rect); + EXPECT_TRUE(save_op->flags == flags); + ++iter; + + ASSERT_EQ(iter->GetType(), PaintOpType::Save); + ++iter; + + ASSERT_EQ(iter->GetType(), PaintOpType::DrawColor); + DrawColorOp* op = static_cast<DrawColorOp*>(*iter); + EXPECT_EQ(op->color, draw_color); + EXPECT_EQ(op->mode, blend); + ++iter; + + ASSERT_EQ(iter->GetType(), PaintOpType::Restore); + ++iter; + + EXPECT_FALSE(iter); +} + +// PaintOpBuffer has a special case for first ops stored locally, so +// make sure that appending different kind of ops as a first op works +// properly, as well as resetting and reusing the first local op. +TEST(PaintOpBufferTest, FirstOpWithAndWithoutData) { + PaintOpBuffer buffer; + char text[] = "asdf"; + + // Use a color filter and its ref count to verify that the destructor + // is called on ops after reset. + PaintFlags flags; + sk_sp<SkColorFilter> filter = + SkColorFilter::MakeModeFilter(SK_ColorMAGENTA, SkBlendMode::kSrcOver); + flags.setColorFilter(filter); + CheckRefCnt(filter, 2); + + buffer.push_with_data<DrawTextOp>(text, arraysize(text), 0.f, 0.f, flags); + CheckRefCnt(filter, 3); + + // Verify that when the first op has data, which may not fit in the + // PaintRecord internal buffer, that it adds a noop as the first op + // and then appends the "op with data" into the heap buffer. + ASSERT_EQ(buffer.size(), 2u); + EXPECT_EQ(buffer.GetFirstOp()->GetType(), PaintOpType::Noop); + + // Verify iteration behavior and brief smoke test of op state. + { + PaintOpBuffer::Iterator iter(&buffer); + PaintOp* noop = *iter; + EXPECT_EQ(buffer.GetFirstOp(), noop); + ++iter; + + PaintOp* op = *iter; + ASSERT_EQ(op->GetType(), PaintOpType::DrawText); + DrawTextOp* draw_text_op = static_cast<DrawTextOp*>(op); + EXPECT_EQ(draw_text_op->bytes, arraysize(text)); + + const void* data = draw_text_op->GetData(); + EXPECT_EQ(memcmp(data, text, arraysize(text)), 0); + + ++iter; + EXPECT_FALSE(iter); + } + + // Reset, verify state, and append an op that will fit in the first slot. + buffer.Reset(); + CheckRefCnt(filter, 2); + + ASSERT_EQ(buffer.size(), 0u); + EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); + + SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); + buffer.push<DrawRectOp>(rect, flags); + CheckRefCnt(filter, 3); + + ASSERT_EQ(buffer.size(), 1u); + EXPECT_EQ(buffer.GetFirstOp()->GetType(), PaintOpType::DrawRect); + + PaintOpBuffer::Iterator iter(&buffer); + ASSERT_EQ(iter->GetType(), PaintOpType::DrawRect); + DrawRectOp* draw_rect_op = static_cast<DrawRectOp*>(*iter); + EXPECT_EQ(draw_rect_op->rect, rect); + + ++iter; + EXPECT_FALSE(iter); + + buffer.Reset(); + ASSERT_EQ(buffer.size(), 0u); + CheckRefCnt(filter, 2); +} + +// Verify that PaintOps with data are stored properly. +TEST(PaintOpBufferTest, PaintOpData) { + PaintOpBuffer buffer; + + buffer.push<SaveOp>(); + PaintFlags flags; + char text1[] = "asdfasdf"; + buffer.push_with_data<DrawTextOp>(text1, arraysize(text1), 0.f, 0.f, flags); + + char text2[] = "qwerty"; + buffer.push_with_data<DrawTextOp>(text2, arraysize(text2), 0.f, 0.f, flags); + + ASSERT_EQ(buffer.size(), 3u); + + // Verify iteration behavior and brief smoke test of op state. + PaintOpBuffer::Iterator iter(&buffer); + PaintOp* save_op = *iter; + EXPECT_EQ(save_op->GetType(), PaintOpType::Save); + ++iter; + + PaintOp* op1 = *iter; + ASSERT_EQ(op1->GetType(), PaintOpType::DrawText); + DrawTextOp* draw_text_op1 = static_cast<DrawTextOp*>(op1); + EXPECT_EQ(draw_text_op1->bytes, arraysize(text1)); + const void* data1 = draw_text_op1->GetData(); + EXPECT_EQ(memcmp(data1, text1, arraysize(text1)), 0); + ++iter; + + PaintOp* op2 = *iter; + ASSERT_EQ(op2->GetType(), PaintOpType::DrawText); + DrawTextOp* draw_text_op2 = static_cast<DrawTextOp*>(op2); + EXPECT_EQ(draw_text_op2->bytes, arraysize(text2)); + const void* data2 = draw_text_op2->GetData(); + EXPECT_EQ(memcmp(data2, text2, arraysize(text2)), 0); + ++iter; + + EXPECT_FALSE(iter); +} + +// Verify that PaintOps with arrays are stored properly. +TEST(PaintOpBufferTest, PaintOpArray) { + PaintOpBuffer buffer; + buffer.push<SaveOp>(); + + // arbitrary data + std::string texts[] = {"xyz", "abcdefg", "thingerdoo"}; + SkPoint point1[] = {SkPoint::Make(1, 2), SkPoint::Make(2, 3), + SkPoint::Make(3, 4)}; + SkPoint point2[] = {SkPoint::Make(8, -12)}; + SkPoint point3[] = {SkPoint::Make(0, 0), SkPoint::Make(5, 6), + SkPoint::Make(-1, -1), SkPoint::Make(9, 9), + SkPoint::Make(50, 50), SkPoint::Make(100, 100)}; + SkPoint* points[] = {point1, point2, point3}; + size_t counts[] = {arraysize(point1), arraysize(point2), arraysize(point3)}; + + for (size_t i = 0; i < arraysize(texts); ++i) { + PaintFlags flags; + flags.setAlpha(i); + buffer.push_with_array<DrawPosTextOp>(texts[i].c_str(), texts[i].length(), + points[i], counts[i], flags); + } + + PaintOpBuffer::Iterator iter(&buffer); + PaintOp* save_op = *iter; + EXPECT_EQ(save_op->GetType(), PaintOpType::Save); + ++iter; + + for (size_t i = 0; i < arraysize(texts); ++i) { + ASSERT_EQ(iter->GetType(), PaintOpType::DrawPosText); + DrawPosTextOp* op = static_cast<DrawPosTextOp*>(*iter); + + EXPECT_EQ(op->flags.getAlpha(), i); + + EXPECT_EQ(op->bytes, texts[i].length()); + const void* data = op->GetData(); + EXPECT_EQ(memcmp(data, texts[i].c_str(), op->bytes), 0); + + EXPECT_EQ(op->count, counts[i]); + const SkPoint* op_points = op->GetArray(); + for (size_t k = 0; k < op->count; ++k) + EXPECT_EQ(op_points[k], points[i][k]); + + ++iter; + } + + EXPECT_FALSE(iter); +} + +// Verify that a SaveLayerAlpha / Draw / Restore can be optimized to just +// a draw with opacity. +TEST(PaintOpBufferTest, SaveDrawRestore) { + PaintOpBuffer buffer; + + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + + PaintFlags draw_flags; + draw_flags.setColor(SK_ColorMAGENTA); + draw_flags.setAlpha(50); + EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); + SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); + buffer.push<DrawRectOp>(rect, draw_flags); + buffer.push<RestoreOp>(); + + SaveCountingCanvas canvas; + buffer.playback(&canvas); + + EXPECT_EQ(0, canvas.save_count_); + EXPECT_EQ(0, canvas.restore_count_); + EXPECT_EQ(rect, canvas.draw_rect_); + + // Expect the alpha from the draw and the save layer to be folded together. + // Since alpha is stored in a uint8_t and gets rounded, so use tolerance. + float expected_alpha = alpha * 50 / 255.f; + EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f); +} + +// The same as SaveDrawRestore, but test that the optimization doesn't apply +// when the drawing op's flags are not compatible with being folded into the +// save layer with opacity. +TEST(PaintOpBufferTest, SaveDrawRestoreFail_BadFlags) { + PaintOpBuffer buffer; + + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + + PaintFlags draw_flags; + draw_flags.setColor(SK_ColorMAGENTA); + draw_flags.setAlpha(50); + draw_flags.setBlendMode(SkBlendMode::kSrc); + EXPECT_FALSE(draw_flags.SupportsFoldingAlpha()); + SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); + buffer.push<DrawRectOp>(rect, draw_flags); + buffer.push<RestoreOp>(); + + SaveCountingCanvas canvas; + buffer.playback(&canvas); + + EXPECT_EQ(1, canvas.save_count_); + EXPECT_EQ(1, canvas.restore_count_); + EXPECT_EQ(rect, canvas.draw_rect_); + EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha()); +} + +// The same as SaveDrawRestore, but test that the optimization doesn't apply +// when there are more than one ops between the save and restore. +TEST(PaintOpBufferTest, SaveDrawRestoreFail_TooManyOps) { + PaintOpBuffer buffer; + + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + + PaintFlags draw_flags; + draw_flags.setColor(SK_ColorMAGENTA); + draw_flags.setAlpha(50); + draw_flags.setBlendMode(SkBlendMode::kSrcOver); + EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); + SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); + buffer.push<DrawRectOp>(rect, draw_flags); + buffer.push<NoopOp>(); + buffer.push<RestoreOp>(); + + SaveCountingCanvas canvas; + buffer.playback(&canvas); + + EXPECT_EQ(1, canvas.save_count_); + EXPECT_EQ(1, canvas.restore_count_); + EXPECT_EQ(rect, canvas.draw_rect_); + EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha()); +} + +// Verify that the save draw restore code works with a single op +// that's not a draw op, and the optimization does not kick in. +TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpNotADrawOp) { + PaintOpBuffer buffer; + + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + + buffer.push<NoopOp>(); + buffer.push<RestoreOp>(); + + SaveCountingCanvas canvas; + buffer.playback(&canvas); + + EXPECT_EQ(1, canvas.save_count_); + EXPECT_EQ(1, canvas.restore_count_); +} + +// Test that the save/draw/restore optimization applies if the single op +// is a DrawRecord that itself has a single draw op. +TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleOp) { + sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>(); + + PaintFlags draw_flags; + draw_flags.setColor(SK_ColorMAGENTA); + draw_flags.setAlpha(50); + EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); + SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); + record->push<DrawRectOp>(rect, draw_flags); + EXPECT_EQ(record->size(), 1u); + + PaintOpBuffer buffer; + + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + buffer.push<DrawRecordOp>(std::move(record)); + buffer.push<RestoreOp>(); + + SaveCountingCanvas canvas; + buffer.playback(&canvas); + + EXPECT_EQ(0, canvas.save_count_); + EXPECT_EQ(0, canvas.restore_count_); + EXPECT_EQ(rect, canvas.draw_rect_); + + float expected_alpha = alpha * 50 / 255.f; + EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f); +} + +// The same as the above SingleOpRecord test, but the single op is not +// a draw op. So, there's no way to fold in the save layer optimization. +// Verify that the optimization doesn't apply and that this doesn't crash. +// See: http://crbug.com/712093. +TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleNonDrawOp) { + sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>(); + record->push<NoopOp>(); + EXPECT_EQ(record->size(), 1u); + EXPECT_FALSE(record->GetFirstOp()->IsDrawOp()); + + PaintOpBuffer buffer; + + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + buffer.push<DrawRecordOp>(std::move(record)); + buffer.push<RestoreOp>(); + + SaveCountingCanvas canvas; + buffer.playback(&canvas); + + EXPECT_EQ(1, canvas.save_count_); + EXPECT_EQ(1, canvas.restore_count_); +} + +TEST(PaintOpBufferTest, DiscardableImagesTracking_EmptyBuffer) { + PaintOpBuffer buffer; + EXPECT_FALSE(buffer.HasDiscardableImages()); +} + +TEST(PaintOpBufferTest, DiscardableImagesTracking_NoImageOp) { + PaintOpBuffer buffer; + PaintFlags flags; + buffer.push<DrawRectOp>(SkRect::MakeWH(100, 100), flags); + EXPECT_FALSE(buffer.HasDiscardableImages()); +} + +TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImage) { + PaintOpBuffer buffer; + PaintImage image = PaintImage(PaintImage::GetNextId(), + CreateDiscardableImage(gfx::Size(100, 100))); + buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0), nullptr); + EXPECT_TRUE(buffer.HasDiscardableImages()); +} + +TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) { + PaintOpBuffer buffer; + PaintImage image = PaintImage(PaintImage::GetNextId(), + CreateDiscardableImage(gfx::Size(100, 100))); + buffer.push<DrawImageRectOp>( + image, SkRect::MakeWH(100, 100), SkRect::MakeWH(100, 100), nullptr, + PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint); + EXPECT_TRUE(buffer.HasDiscardableImages()); +} + +TEST(PaintOpBufferTest, DiscardableImagesTracking_NestedDrawOp) { + sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>(); + PaintImage image = PaintImage(PaintImage::GetNextId(), + CreateDiscardableImage(gfx::Size(100, 100))); + record->push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0), nullptr); + + PaintOpBuffer buffer; + buffer.push<DrawRecordOp>(record); + EXPECT_TRUE(buffer.HasDiscardableImages()); + + scoped_refptr<DisplayItemList> list = new DisplayItemList; + list->CreateAndAppendDrawingItem<DrawingDisplayItem>( + gfx::Rect(100, 100), record, SkRect::MakeWH(100, 100)); + list->Finalize(); + PaintOpBuffer new_buffer; + new_buffer.push<DrawDisplayItemListOp>(list); + EXPECT_TRUE(new_buffer.HasDiscardableImages()); +} + +TEST(PaintOpBufferTest, DiscardableImagesTracking_OpWithFlags) { + PaintOpBuffer buffer; + PaintFlags flags; + sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(100, 100)); + flags.setShader( + image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)); + buffer.push<DrawRectOp>(SkRect::MakeWH(100, 100), flags); + EXPECT_TRUE(buffer.HasDiscardableImages()); +} + +TEST(PaintOpBufferTest, SlowPaths) { + auto buffer = sk_make_sp<PaintOpBuffer>(); + EXPECT_EQ(buffer->numSlowPaths(), 0); + + // Op without slow paths + PaintFlags noop_flags; + SkRect rect = SkRect::MakeXYWH(2, 3, 4, 5); + buffer->push<SaveLayerOp>(&rect, &noop_flags); + + // Line op with a slow path + PaintFlags line_effect_slow; + line_effect_slow.setStrokeWidth(1.f); + line_effect_slow.setStyle(PaintFlags::kStroke_Style); + line_effect_slow.setStrokeCap(PaintFlags::kRound_Cap); + SkScalar intervals[] = {1.f, 1.f}; + line_effect_slow.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); + + buffer->push<DrawLineOp>(1.f, 2.f, 3.f, 4.f, line_effect_slow); + EXPECT_EQ(buffer->numSlowPaths(), 1); + + // Line effect special case that Skia handles specially. + PaintFlags line_effect = line_effect_slow; + line_effect.setStrokeCap(PaintFlags::kButt_Cap); + buffer->push<DrawLineOp>(1.f, 2.f, 3.f, 4.f, line_effect); + EXPECT_EQ(buffer->numSlowPaths(), 1); + + // Antialiased convex path is not slow. + SkPath path; + path.addCircle(2, 2, 5); + EXPECT_TRUE(path.isConvex()); + buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, true); + EXPECT_EQ(buffer->numSlowPaths(), 1); + + // Concave paths are slow only when antialiased. + SkPath concave = path; + concave.addCircle(3, 4, 2); + EXPECT_FALSE(concave.isConvex()); + buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, true); + EXPECT_EQ(buffer->numSlowPaths(), 2); + buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, false); + EXPECT_EQ(buffer->numSlowPaths(), 2); + + // Drawing a record with slow paths into another adds the same + // number of slow paths as the record. + auto buffer2 = sk_make_sp<PaintOpBuffer>(); + EXPECT_EQ(buffer2->numSlowPaths(), 0); + buffer2->push<DrawRecordOp>(buffer); + EXPECT_EQ(buffer->numSlowPaths(), buffer2->numSlowPaths()); +} + +TEST(PaintOpBufferTest, ContiguousRanges) { + PaintOpBuffer buffer; + MockCanvas canvas; + + buffer.push<DrawColorOp>(0u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(1u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(2u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(3u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(4u, SkBlendMode::kClear); + + // Ranges are {0, 1}, {2}, {3, 4}. + std::vector<size_t> ranges = {0, 2, 3}; + + // Plays all 3 ranges. + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2}, &canvas); +} + +TEST(PaintOpBufferTest, NonContiguousRanges) { + PaintOpBuffer buffer; + MockCanvas canvas; + + buffer.push<DrawColorOp>(0u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(1u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(2u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(3u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(4u, SkBlendMode::kClear); + + // Ranges are {0, 1}, {2}, {3, 4}. + std::vector<size_t> ranges = {0, 2, 3}; + + // Plays first and third ranges. + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 2}, &canvas); +} + +TEST(PaintOpBufferTest, FirstRange) { + PaintOpBuffer buffer; + MockCanvas canvas; + + buffer.push<DrawColorOp>(0u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(1u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(2u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(3u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(4u, SkBlendMode::kClear); + + // Ranges are {0, 1}, {2}, {3, 4}. + std::vector<size_t> ranges = {0, 2, 3}; + + // Plays first range. + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0}, &canvas); +} + +TEST(PaintOpBufferTest, MiddleRange) { + PaintOpBuffer buffer; + MockCanvas canvas; + + buffer.push<DrawColorOp>(0u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(1u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(2u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(3u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(4u, SkBlendMode::kClear); + + // Ranges are {0, 1}, {2}, {3, 4}. + std::vector<size_t> ranges = {0, 2, 3}; + + // Plays second range. + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s); + buffer.PlaybackRanges(ranges, {1}, &canvas); +} + +TEST(PaintOpBufferTest, LastRange) { + PaintOpBuffer buffer; + MockCanvas canvas; + + buffer.push<DrawColorOp>(0u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(1u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(2u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(3u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(4u, SkBlendMode::kClear); + + // Ranges are {0, 1}, {2}, {3, 4}. + std::vector<size_t> ranges = {0, 2, 3}; + + // Plays third range. + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {2}, &canvas); +} + +TEST(PaintOpBufferTest, ContiguousRangeWithSaveLayerAlphaRestore) { + PaintOpBuffer buffer; + MockCanvas canvas; + + buffer.push<DrawColorOp>(0u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(1u, SkBlendMode::kClear); + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + buffer.push<RestoreOp>(); + buffer.push<DrawColorOp>(2u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(3u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(4u, SkBlendMode::kClear); + + { + // Ranges are {0, 1, save, restore}, {2}, {3, 4}. + std::vector<size_t> ranges = {0, 4, 5}; + + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + // The empty SaveLayerAlpha/Restore is dropped. + EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + { + // Ranges are {0, 1}, {save, restore}, {2}, {3, 4}. + std::vector<size_t> ranges = {0, 2, 4, 5}; + + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + // The empty SaveLayerAlpha/Restore is dropped. + EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2, 3}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + { + // Ranges are {0, 1}, {save, restore, 2}, {3, 4}. + std::vector<size_t> ranges = {0, 2, 5}; + + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + // The empty SaveLayerAlpha/Restore is dropped. + EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + { + // Ranges are {0, 1, save}, {restore, 2}, {3, 4}. + std::vector<size_t> ranges = {0, 3, 5}; + + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + // The empty SaveLayerAlpha/Restore is dropped. + EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2}, &canvas); + } +} + +TEST(PaintOpBufferTest, NonContiguousRangeWithSaveLayerAlphaRestore) { + PaintOpBuffer buffer; + MockCanvas canvas; + + buffer.push<DrawColorOp>(0u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(1u, SkBlendMode::kClear); + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + buffer.push<DrawColorOp>(2u, SkBlendMode::kClear); + buffer.push<DrawColorOp>(3u, SkBlendMode::kClear); + buffer.push<RestoreOp>(); + buffer.push<DrawColorOp>(4u, SkBlendMode::kClear); + + // Ranges are {0, 1, save}, {2, 3}, {restore, 4}. + std::vector<size_t> ranges = {0, 3, 5}; + + // Plays back all ranges. + { + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + // The SaveLayerAlpha/Restore is not dropped if we draw the middle + // range, as we need them to represent the two draws inside the layer + // correctly. + EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, willRestore()).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + // Skips the middle range. + { + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + // The now-empty SaveLayerAlpha/Restore is dropped + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 2}, &canvas); + } + + // Repeat with ranges that contain just the save and restore, as + // {0, 1}, {save}, {2, 3}, {restore}, {4}. + ranges = {0, 2, 3, 5, 6}; + + // Plays back all ranges. + { + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + // The SaveLayerAlpha/Restore is not dropped if we draw the middle + // range, as we need them to represent the two draws inside the layer + // correctly. + EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, willRestore()).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2, 3, 4}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + // Skips the middle range. + { + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s); + // The now-empty SaveLayerAlpha/Restore is dropped + EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 3, 4}, &canvas); + } +} + +TEST(PaintOpBufferTest, ContiguousRangeWithSaveLayerAlphaDrawRestore) { + PaintOpBuffer buffer; + MockCanvas canvas; + + auto add_draw_rect = [](PaintOpBuffer* buffer, SkColor c) { + PaintFlags flags; + flags.setColor(c); + buffer->push<DrawRectOp>(SkRect::MakeWH(1, 1), flags); + }; + + add_draw_rect(&buffer, 0u); + add_draw_rect(&buffer, 1u); + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + add_draw_rect(&buffer, 2u); + buffer.push<RestoreOp>(); + add_draw_rect(&buffer, 3u); + add_draw_rect(&buffer, 4u); + + { + // Ranges are {0, 1, save, 2, restore}, {3, 4}. + std::vector<size_t> ranges = {0, 5}; + + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s); + // The empty SaveLayerAlpha/Restore is duropped, the containing + // operation can be drawn with alpha. + EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + { + // Ranges are {0, 1, save, 2}, {restore}, {3, 4}. + std::vector<size_t> ranges = {0, 4, 5}; + + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s); + // The empty SaveLayerAlpha/Restore is dropped, the containing + // operation can be drawn with alpha. + EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + { + // Ranges are {0, 1, save}, {2, restore}, {3, 4}. + std::vector<size_t> ranges = {0, 3, 5}; + + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s); + // The empty SaveLayerAlpha/Restore is dropped, the containing + // operation can be drawn with alpha. + EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + { + // Ranges are {0, 1, save}, {2}, {restore}, {3, 4}. + std::vector<size_t> ranges = {0, 3, 4, 5}; + + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s); + // The empty SaveLayerAlpha/Restore is dropped, the containing + // operation can be drawn with alpha. + EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2, 3}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); +} + +TEST(PaintOpBufferTest, NonContiguousRangeWithSaveLayerAlphaDrawRestore) { + PaintOpBuffer buffer; + MockCanvas canvas; + + auto add_draw_rect = [](PaintOpBuffer* buffer, SkColor c) { + PaintFlags flags; + flags.setColor(c); + buffer->push<DrawRectOp>(SkRect::MakeWH(1, 1), flags); + }; + + add_draw_rect(&buffer, 0u); + add_draw_rect(&buffer, 1u); + uint8_t alpha = 100; + buffer.push<SaveLayerAlphaOp>(nullptr, alpha); + add_draw_rect(&buffer, 2u); + add_draw_rect(&buffer, 3u); + add_draw_rect(&buffer, 4u); + buffer.push<RestoreOp>(); + + // Ranges are {0, 1, save}, {2, 3}, {4, restore}. + std::vector<size_t> ranges = {0, 3, 5}; + + // If the middle range is played, then the SaveLayerAlpha/Restore + // can't be dropped. + { + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s); + EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s); + EXPECT_CALL(canvas, willRestore()).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 1, 2}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + // If the middle range is not played, then the SaveLayerAlpha/Restore + // can be dropped. + { + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 2}, &canvas); + } + Mock::VerifyAndClearExpectations(&canvas); + + // Ranges are {0, 1, save, 2}, {3, 4}, {restore}. + ranges = {0, 4, 6}; + + // If the middle range is not played, then the SaveLayerAlpha/Restore + // can be dropped. + { + testing::Sequence s; + EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s); + EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s); + buffer.PlaybackRanges(ranges, {0, 2}, &canvas); + } +} + +} // namespace cc diff --git a/chromium/cc/paint/paint_record.cc b/chromium/cc/paint/paint_record.cc new file mode 100644 index 00000000000..24830068913 --- /dev/null +++ b/chromium/cc/paint/paint_record.cc @@ -0,0 +1,27 @@ +// Copyright 2017 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/paint/paint_record.h" + +#include "cc/paint/paint_op_buffer.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" + +namespace cc { + +sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record, const SkRect& bounds) { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(bounds); + record->playback(canvas); + return recorder.finishRecordingAsPicture(); +} + +sk_sp<const SkPicture> ToSkPicture(sk_sp<const PaintRecord> record, + const SkRect& bounds) { + SkPictureRecorder recorder; + SkCanvas* canvas = recorder.beginRecording(bounds); + record->playback(canvas); + return recorder.finishRecordingAsPicture(); +} + +} // namespace cc diff --git a/chromium/cc/paint/paint_record.h b/chromium/cc/paint/paint_record.h index 8506606b59f..1509bac0529 100644 --- a/chromium/cc/paint/paint_record.h +++ b/chromium/cc/paint/paint_record.h @@ -5,19 +5,24 @@ #ifndef CC_PAINT_PAINT_RECORD_H_ #define CC_PAINT_PAINT_RECORD_H_ +#include "cc/paint/paint_export.h" +#include "cc/paint/paint_op_buffer.h" #include "third_party/skia/include/core/SkPicture.h" namespace cc { -using PaintRecord = SkPicture; +// TODO(enne): Don't want to rename the world for this. Using these as the +// same types for now prevents an extra allocation. Probably PaintRecord +// will become an interface in the future. +using PaintRecord = PaintOpBuffer; -inline sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record) { - return record; -} +// TODO(enne): Remove these if possible, they are really expensive. +CC_PAINT_EXPORT sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record, + const SkRect& bounds); -inline sk_sp<const SkPicture> ToSkPicture(sk_sp<const PaintRecord> record) { - return record; -} +CC_PAINT_EXPORT sk_sp<const SkPicture> ToSkPicture( + sk_sp<const PaintRecord> record, + const SkRect& bounds); } // namespace cc diff --git a/chromium/cc/paint/paint_recorder.cc b/chromium/cc/paint/paint_recorder.cc index 672f0712725..20f5cacd2d6 100644 --- a/chromium/cc/paint/paint_recorder.cc +++ b/chromium/cc/paint/paint_recorder.cc @@ -4,9 +4,36 @@ #include "cc/paint/paint_recorder.h" +#include "cc/paint/paint_op_buffer.h" + namespace cc { PaintRecorder::PaintRecorder() = default; + PaintRecorder::~PaintRecorder() = default; +PaintCanvas* PaintRecorder::beginRecording(const SkRect& bounds) { + buffer_ = sk_make_sp<PaintOpBuffer>(); + canvas_.emplace(buffer_.get(), bounds); + return getRecordingCanvas(); +} + +sk_sp<PaintRecord> PaintRecorder::finishRecordingAsPicture() { + // SkPictureRecorder users expect that their saves are automatically + // closed for them. + // + // NOTE: Blink paint in general doesn't appear to need this, but the + // RecordingImageBufferSurface::fallBackToRasterCanvas finishing off the + // current frame depends on this. Maybe we could remove this assumption and + // just have callers do it. + canvas_->restoreToCount(1); + + // Some users (e.g. printing) use the existence of the recording canvas + // to know if recording is finished, so reset it here. + canvas_.reset(); + + buffer_->ShrinkToFit(); + return std::move(buffer_); +} + } // namespace cc diff --git a/chromium/cc/paint/paint_recorder.h b/chromium/cc/paint/paint_recorder.h index 2bbea83b981..7f582b85191 100644 --- a/chromium/cc/paint/paint_recorder.h +++ b/chromium/cc/paint/paint_recorder.h @@ -9,47 +9,36 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/optional.h" -#include "cc/paint/paint_canvas.h" #include "cc/paint/paint_record.h" -#include "cc/paint/skia_paint_canvas.h" -#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "cc/paint/record_paint_canvas.h" namespace cc { +class PaintOpBuffer; + class CC_PAINT_EXPORT PaintRecorder { public: PaintRecorder(); ~PaintRecorder(); - ALWAYS_INLINE PaintCanvas* beginRecording(const SkRect& bounds) { - uint32_t record_flags = 0; - canvas_.emplace(recorder_.beginRecording(bounds, nullptr, record_flags)); - return getRecordingCanvas(); - } + PaintCanvas* beginRecording(const SkRect& bounds); - ALWAYS_INLINE PaintCanvas* beginRecording(SkScalar width, SkScalar height) { - uint32_t record_flags = 0; - canvas_.emplace( - recorder_.beginRecording(width, height, nullptr, record_flags)); - return getRecordingCanvas(); + // TODO(enne): should make everything go through the non-rect version. + // See comments in RecordPaintCanvas ctor for why. + PaintCanvas* beginRecording(SkScalar width, SkScalar height) { + return beginRecording(SkRect::MakeWH(width, height)); } // Only valid between between and finish recording. - ALWAYS_INLINE PaintCanvas* getRecordingCanvas() { + ALWAYS_INLINE RecordPaintCanvas* getRecordingCanvas() { return canvas_.has_value() ? &canvas_.value() : nullptr; } - ALWAYS_INLINE sk_sp<PaintRecord> finishRecordingAsPicture() { - sk_sp<SkPicture> picture = recorder_.finishRecordingAsPicture(); - // Some users (e.g. printing) use the existence of the recording canvas - // to know if recording is finished, so reset it here. - canvas_.reset(); - return sk_ref_sp(static_cast<PaintRecord*>(picture.get())); - } + sk_sp<PaintRecord> finishRecordingAsPicture(); private: - SkPictureRecorder recorder_; - base::Optional<SkiaPaintCanvas> canvas_; + sk_sp<PaintOpBuffer> buffer_; + base::Optional<RecordPaintCanvas> canvas_; DISALLOW_COPY_AND_ASSIGN(PaintRecorder); }; diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h index 6afdaa650de..019174443bb 100644 --- a/chromium/cc/paint/paint_shader.h +++ b/chromium/cc/paint/paint_shader.h @@ -24,12 +24,12 @@ inline sk_sp<PaintShader> MakePaintShaderImage(sk_sp<const SkImage> image, } inline sk_sp<PaintShader> MakePaintShaderRecord(sk_sp<PaintRecord> record, + const SkRect& tile, SkShader::TileMode tx, SkShader::TileMode ty, - const SkMatrix* local_matrix, - const SkRect* tile) { - return SkShader::MakePictureShader(ToSkPicture(record), tx, ty, local_matrix, - tile); + const SkMatrix* local_matrix) { + return SkShader::MakePictureShader(ToSkPicture(record, tile), tx, ty, + local_matrix, nullptr); } } // namespace cc diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc new file mode 100644 index 00000000000..a635474a372 --- /dev/null +++ b/chromium/cc/paint/record_paint_canvas.cc @@ -0,0 +1,372 @@ +// Copyright 2017 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/paint/record_paint_canvas.h" + +#include "base/memory/ptr_util.h" +#include "cc/paint/display_item_list.h" +#include "cc/paint/paint_op_buffer.h" +#include "cc/paint/paint_record.h" +#include "cc/paint/paint_recorder.h" +#include "third_party/skia/include/core/SkAnnotation.h" +#include "third_party/skia/include/core/SkMetaData.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" + +namespace cc { + +RecordPaintCanvas::RecordPaintCanvas(PaintOpBuffer* buffer, + const SkRect& bounds) + : buffer_(buffer), recording_bounds_(bounds) { + DCHECK(buffer_); +} + +RecordPaintCanvas::~RecordPaintCanvas() = default; + +SkMetaData& RecordPaintCanvas::getMetaData() { + // This could just be SkMetaData owned by RecordPaintCanvas, but since + // SkCanvas already has one, we might as well use it directly. + return GetCanvas()->getMetaData(); +} + +SkImageInfo RecordPaintCanvas::imageInfo() const { + return GetCanvas()->imageInfo(); +} + +void RecordPaintCanvas::flush() { + // This is a noop when recording. +} + +int RecordPaintCanvas::save() { + buffer_->push<SaveOp>(); + return GetCanvas()->save(); +} + +int RecordPaintCanvas::saveLayer(const SkRect* bounds, + const PaintFlags* flags) { + if (flags) { + if (flags->IsSimpleOpacity()) { + // TODO(enne): maybe more callers should know this and call + // saveLayerAlpha instead of needing to check here. + uint8_t alpha = SkColorGetA(flags->getColor()); + return saveLayerAlpha(bounds, alpha); + } + + // TODO(enne): it appears that image filters affect matrices and color + // matrices affect transparent flags on SkCanvas layers, but it's not clear + // whether those are actually needed and we could just skip ToSkPaint here. + buffer_->push<SaveLayerOp>(bounds, flags); + const SkPaint& paint = ToSkPaint(*flags); + return GetCanvas()->saveLayer(bounds, &paint); + } + buffer_->push<SaveLayerOp>(bounds, flags); + return GetCanvas()->saveLayer(bounds, nullptr); +} + +int RecordPaintCanvas::saveLayerAlpha(const SkRect* bounds, uint8_t alpha) { + buffer_->push<SaveLayerAlphaOp>(bounds, alpha); + return GetCanvas()->saveLayerAlpha(bounds, alpha); +} + +void RecordPaintCanvas::restore() { + buffer_->push<RestoreOp>(); + GetCanvas()->restore(); +} + +int RecordPaintCanvas::getSaveCount() const { + return GetCanvas()->getSaveCount(); +} + +void RecordPaintCanvas::restoreToCount(int save_count) { + if (!canvas_) { + DCHECK_EQ(save_count, 1); + return; + } + + DCHECK_GE(save_count, 1); + int diff = GetCanvas()->getSaveCount() - save_count; + DCHECK_GE(diff, 0); + for (int i = 0; i < diff; ++i) + restore(); +} + +void RecordPaintCanvas::translate(SkScalar dx, SkScalar dy) { + buffer_->push<TranslateOp>(dx, dy); + GetCanvas()->translate(dx, dy); +} + +void RecordPaintCanvas::scale(SkScalar sx, SkScalar sy) { + buffer_->push<ScaleOp>(sx, sy); + GetCanvas()->scale(sx, sy); +} + +void RecordPaintCanvas::rotate(SkScalar degrees) { + buffer_->push<RotateOp>(degrees); + GetCanvas()->rotate(degrees); +} + +void RecordPaintCanvas::concat(const SkMatrix& matrix) { + buffer_->push<ConcatOp>(matrix); + GetCanvas()->concat(matrix); +} + +void RecordPaintCanvas::setMatrix(const SkMatrix& matrix) { + buffer_->push<SetMatrixOp>(matrix); + GetCanvas()->setMatrix(matrix); +} + +void RecordPaintCanvas::clipRect(const SkRect& rect, + SkClipOp op, + bool antialias) { + buffer_->push<ClipRectOp>(rect, op, antialias); + GetCanvas()->clipRect(rect, op, antialias); +} + +void RecordPaintCanvas::clipRRect(const SkRRect& rrect, + SkClipOp op, + bool antialias) { + // TODO(enne): does this happen? Should the caller know this? + if (rrect.isRect()) { + clipRect(rrect.getBounds(), op, antialias); + return; + } + buffer_->push<ClipRRectOp>(rrect, op, antialias); + GetCanvas()->clipRRect(rrect, op, antialias); +} + +void RecordPaintCanvas::clipPath(const SkPath& path, + SkClipOp op, + bool antialias) { + if (!path.isInverseFillType() && + GetCanvas()->getTotalMatrix().rectStaysRect()) { + // TODO(enne): do these cases happen? should the caller know that this isn't + // a path? + SkRect rect; + if (path.isRect(&rect)) { + clipRect(rect, op, antialias); + return; + } + SkRRect rrect; + if (path.isOval(&rect)) { + rrect.setOval(rect); + clipRRect(rrect, op, antialias); + return; + } + if (path.isRRect(&rrect)) { + clipRRect(rrect, op, antialias); + return; + } + } + + buffer_->push<ClipPathOp>(path, op, antialias); + GetCanvas()->clipPath(path, op, antialias); + return; +} + +bool RecordPaintCanvas::quickReject(const SkRect& rect) const { + return GetCanvas()->quickReject(rect); +} + +bool RecordPaintCanvas::quickReject(const SkPath& path) const { + return GetCanvas()->quickReject(path); +} + +SkRect RecordPaintCanvas::getLocalClipBounds() const { + return GetCanvas()->getLocalClipBounds(); +} + +bool RecordPaintCanvas::getLocalClipBounds(SkRect* bounds) const { + return GetCanvas()->getLocalClipBounds(bounds); +} + +SkIRect RecordPaintCanvas::getDeviceClipBounds() const { + return GetCanvas()->getDeviceClipBounds(); +} + +bool RecordPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const { + return GetCanvas()->getDeviceClipBounds(bounds); +} + +void RecordPaintCanvas::drawColor(SkColor color, SkBlendMode mode) { + buffer_->push<DrawColorOp>(color, mode); +} + +void RecordPaintCanvas::clear(SkColor color) { + buffer_->push<DrawColorOp>(color, SkBlendMode::kSrc); +} + +void RecordPaintCanvas::drawLine(SkScalar x0, + SkScalar y0, + SkScalar x1, + SkScalar y1, + const PaintFlags& flags) { + buffer_->push<DrawLineOp>(x0, y0, x1, y1, flags); +} + +void RecordPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) { + buffer_->push<DrawRectOp>(rect, flags); +} + +void RecordPaintCanvas::drawIRect(const SkIRect& rect, + const PaintFlags& flags) { + buffer_->push<DrawIRectOp>(rect, flags); +} + +void RecordPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) { + buffer_->push<DrawOvalOp>(oval, flags); +} + +void RecordPaintCanvas::drawRRect(const SkRRect& rrect, + const PaintFlags& flags) { + buffer_->push<DrawRRectOp>(rrect, flags); +} + +void RecordPaintCanvas::drawDRRect(const SkRRect& outer, + const SkRRect& inner, + const PaintFlags& flags) { + if (outer.isEmpty()) + return; + if (inner.isEmpty()) { + drawRRect(outer, flags); + return; + } + buffer_->push<DrawDRRectOp>(outer, inner, flags); +} + +void RecordPaintCanvas::drawCircle(SkScalar cx, + SkScalar cy, + SkScalar radius, + const PaintFlags& flags) { + buffer_->push<DrawCircleOp>(cx, cy, radius, flags); +} + +void RecordPaintCanvas::drawArc(const SkRect& oval, + SkScalar start_angle, + SkScalar sweep_angle, + bool use_center, + const PaintFlags& flags) { + buffer_->push<DrawArcOp>(oval, start_angle, sweep_angle, use_center, flags); +} + +void RecordPaintCanvas::drawRoundRect(const SkRect& rect, + SkScalar rx, + SkScalar ry, + const PaintFlags& flags) { + // TODO(enne): move this into base class? + if (rx > 0 && ry > 0) { + SkRRect rrect; + rrect.setRectXY(rect, rx, ry); + drawRRect(rrect, flags); + } else { + drawRect(rect, flags); + } +} + +void RecordPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) { + buffer_->push<DrawPathOp>(path, flags); +} + +void RecordPaintCanvas::drawImage(const PaintImage& image, + SkScalar left, + SkScalar top, + const PaintFlags* flags) { + buffer_->push<DrawImageOp>(image, left, top, flags); +} + +void RecordPaintCanvas::drawImageRect(const PaintImage& image, + const SkRect& src, + const SkRect& dst, + const PaintFlags* flags, + SrcRectConstraint constraint) { + buffer_->push<DrawImageRectOp>(image, src, dst, flags, constraint); +} + +void RecordPaintCanvas::drawBitmap(const SkBitmap& bitmap, + SkScalar left, + SkScalar top, + const PaintFlags* flags) { + // TODO(enne): Move into base class? + if (bitmap.drawsNothing()) + return; + drawImage( + PaintImage(PaintImage::kNonLazyStableId, SkImage::MakeFromBitmap(bitmap), + PaintImage::AnimationType::UNKNOWN, + PaintImage::CompletionState::UNKNOWN), + left, top, flags); +} + +void RecordPaintCanvas::drawText(const void* text, + size_t byte_length, + SkScalar x, + SkScalar y, + const PaintFlags& flags) { + buffer_->push_with_data<DrawTextOp>(text, byte_length, x, y, flags); +} + +void RecordPaintCanvas::drawPosText(const void* text, + size_t byte_length, + const SkPoint pos[], + const PaintFlags& flags) { + size_t count = ToSkPaint(flags).countText(text, byte_length); + buffer_->push_with_array<DrawPosTextOp>(text, byte_length, pos, count, flags); +} + +void RecordPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob, + SkScalar x, + SkScalar y, + const PaintFlags& flags) { + buffer_->push<DrawTextBlobOp>(blob, x, y, flags); +} + +void RecordPaintCanvas::drawDisplayItemList( + scoped_refptr<DisplayItemList> list) { + buffer_->push<DrawDisplayItemListOp>(list); +} + +void RecordPaintCanvas::drawPicture(sk_sp<const PaintRecord> record) { + // TODO(enne): If this is small, maybe flatten it? + buffer_->push<DrawRecordOp>(record); +} + +bool RecordPaintCanvas::isClipEmpty() const { + return GetCanvas()->isClipEmpty(); +} + +bool RecordPaintCanvas::isClipRect() const { + return GetCanvas()->isClipRect(); +} + +const SkMatrix& RecordPaintCanvas::getTotalMatrix() const { + return GetCanvas()->getTotalMatrix(); +} + +void RecordPaintCanvas::Annotate(AnnotationType type, + const SkRect& rect, + sk_sp<SkData> data) { + buffer_->push<AnnotateOp>(type, rect, data); +} + +const SkNoDrawCanvas* RecordPaintCanvas::GetCanvas() const { + return const_cast<RecordPaintCanvas*>(this)->GetCanvas(); +} + +SkNoDrawCanvas* RecordPaintCanvas::GetCanvas() { + if (canvas_) + return &*canvas_; + + // Size the canvas to be large enough to contain the |recording_bounds|, which + // may not be positioned at th origin. + SkIRect enclosing_rect = recording_bounds_.roundOut(); + canvas_.emplace(enclosing_rect.right(), enclosing_rect.bottom()); + + // This is part of the "recording canvases have a size, but why" dance. + // By creating a canvas of size (right x bottom) and then clipping it, + // It makes getDeviceClipBounds return the original cull rect, which code + // in GraphicsContextCanvas on Mac expects. (Just creating an SkNoDrawCanvas + // with the recording_bounds_ makes a canvas of size (width x height) instead + // which is incorrect. SkRecorder cheats with private resetForNextCanvas. + canvas_->clipRect(recording_bounds_, SkClipOp::kIntersect, false); + return &*canvas_; +} + +} // namespace cc diff --git a/chromium/cc/paint/record_paint_canvas.h b/chromium/cc/paint/record_paint_canvas.h new file mode 100644 index 00000000000..4eb391d747a --- /dev/null +++ b/chromium/cc/paint/record_paint_canvas.h @@ -0,0 +1,158 @@ +// Copyright 2017 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. + +#ifndef CC_PAINT_RECORD_PAINT_CANVAS_H_ +#define CC_PAINT_RECORD_PAINT_CANVAS_H_ + +#include <memory> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/optional.h" +#include "build/build_config.h" +#include "cc/paint/paint_canvas.h" +#include "cc/paint/paint_flags.h" +#include "cc/paint/paint_record.h" +#include "third_party/skia/include/utils/SkNoDrawCanvas.h" + +namespace cc { + +class PaintOpBuffer; +class PaintFlags; + +class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas { + public: + explicit RecordPaintCanvas(PaintOpBuffer* buffer, const SkRect& bounds); + ~RecordPaintCanvas() override; + + SkMetaData& getMetaData() override; + SkImageInfo imageInfo() const override; + + void flush() override; + + int save() override; + int saveLayer(const SkRect* bounds, const PaintFlags* flags) override; + int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) override; + + void restore() override; + int getSaveCount() const override; + void restoreToCount(int save_count) override; + void translate(SkScalar dx, SkScalar dy) override; + void scale(SkScalar sx, SkScalar sy) override; + void rotate(SkScalar degrees) override; + void concat(const SkMatrix& matrix) override; + void setMatrix(const SkMatrix& matrix) override; + + void clipRect(const SkRect& rect, SkClipOp op, bool antialias) override; + void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) override; + void clipPath(const SkPath& path, SkClipOp op, bool antialias) override; + bool quickReject(const SkRect& rect) const override; + bool quickReject(const SkPath& path) const override; + SkRect getLocalClipBounds() const override; + bool getLocalClipBounds(SkRect* bounds) const override; + SkIRect getDeviceClipBounds() const override; + bool getDeviceClipBounds(SkIRect* bounds) const override; + void drawColor(SkColor color, SkBlendMode mode) override; + void clear(SkColor color) override; + + void drawLine(SkScalar x0, + SkScalar y0, + SkScalar x1, + SkScalar y1, + const PaintFlags& flags) override; + void drawRect(const SkRect& rect, const PaintFlags& flags) override; + void drawIRect(const SkIRect& rect, const PaintFlags& flags) override; + void drawOval(const SkRect& oval, const PaintFlags& flags) override; + void drawRRect(const SkRRect& rrect, const PaintFlags& flags) override; + void drawDRRect(const SkRRect& outer, + const SkRRect& inner, + const PaintFlags& flags) override; + void drawCircle(SkScalar cx, + SkScalar cy, + SkScalar radius, + const PaintFlags& flags) override; + void drawArc(const SkRect& oval, + SkScalar start_angle, + SkScalar sweep_angle, + bool use_center, + const PaintFlags& flags) override; + void drawRoundRect(const SkRect& rect, + SkScalar rx, + SkScalar ry, + const PaintFlags& flags) override; + void drawPath(const SkPath& path, const PaintFlags& flags) override; + void drawImage(const PaintImage& image, + SkScalar left, + SkScalar top, + const PaintFlags* flags) override; + void drawImageRect(const PaintImage& image, + const SkRect& src, + const SkRect& dst, + const PaintFlags* flags, + SrcRectConstraint constraint) override; + void drawBitmap(const SkBitmap& bitmap, + SkScalar left, + SkScalar top, + const PaintFlags* flags) override; + + void drawText(const void* text, + size_t byte_length, + SkScalar x, + SkScalar y, + const PaintFlags& flags) override; + void drawPosText(const void* text, + size_t byte_length, + const SkPoint pos[], + const PaintFlags& flags) override; + void drawTextBlob(sk_sp<SkTextBlob> blob, + SkScalar x, + SkScalar y, + const PaintFlags& flags) override; + + void drawDisplayItemList( + scoped_refptr<DisplayItemList> display_item_list) override; + + void drawPicture(sk_sp<const PaintRecord> record) override; + + bool isClipEmpty() const override; + bool isClipRect() const override; + const SkMatrix& getTotalMatrix() const override; + + void Annotate(AnnotationType type, + const SkRect& rect, + sk_sp<SkData> data) override; + + // Don't shadow non-virtual helper functions. + using PaintCanvas::clipRect; + using PaintCanvas::clipRRect; + using PaintCanvas::clipPath; + using PaintCanvas::drawBitmap; + using PaintCanvas::drawColor; + using PaintCanvas::drawImage; + using PaintCanvas::drawPicture; + + private: + const SkNoDrawCanvas* GetCanvas() const; + SkNoDrawCanvas* GetCanvas(); + + PaintOpBuffer* buffer_; + + // TODO(enne): Although RecordPaintCanvas is mostly a write-only interface + // where paint commands are stored, occasionally users of PaintCanvas want + // to ask stateful questions mid-stream of clip and transform state. + // To avoid duplicating all this code (for now?), just forward to an SkCanvas + // that's not backed by anything but can answer these questions. + // + // This is mutable so that const functions (e.g. quickReject) that may + // lazy initialize the canvas can still be const. + mutable base::Optional<SkNoDrawCanvas> canvas_; + SkRect recording_bounds_; + + DISALLOW_COPY_AND_ASSIGN(RecordPaintCanvas); +}; + +} // namespace cc + +#endif // CC_PAINT_RECORD_PAINT_CANVAS_H_ diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc index 94b0cfb7a30..8c69d875fb8 100644 --- a/chromium/cc/paint/skia_paint_canvas.cc +++ b/chromium/cc/paint/skia_paint_canvas.cc @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "cc/paint/paint_canvas.h" +#include "cc/paint/skia_paint_canvas.h" #include "base/memory/ptr_util.h" #include "cc/paint/display_item_list.h" -#include "cc/paint/paint_record.h" #include "cc/paint/paint_recorder.h" #include "third_party/skia/include/core/SkAnnotation.h" #include "third_party/skia/include/core/SkMetaData.h" @@ -23,7 +22,6 @@ SkiaPaintCanvas::SkiaPaintCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props) : canvas_(new SkCanvas(bitmap, props)), owned_(canvas_) {} -SkiaPaintCanvas::SkiaPaintCanvas(SkiaPaintCanvas&& other) = default; SkiaPaintCanvas::~SkiaPaintCanvas() = default; SkMetaData& SkiaPaintCanvas::getMetaData() { @@ -38,18 +36,6 @@ void SkiaPaintCanvas::flush() { canvas_->flush(); } -SkISize SkiaPaintCanvas::getBaseLayerSize() const { - return canvas_->getBaseLayerSize(); -} - -bool SkiaPaintCanvas::writePixels(const SkImageInfo& info, - const void* pixels, - size_t row_bytes, - int x, - int y) { - return canvas_->writePixels(info, pixels, row_bytes, x, y); -} - int SkiaPaintCanvas::save() { return canvas_->save(); } @@ -58,7 +44,7 @@ int SkiaPaintCanvas::saveLayer(const SkRect* bounds, const PaintFlags* flags) { return canvas_->saveLayer(bounds, ToSkPaint(flags)); } -int SkiaPaintCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) { +int SkiaPaintCanvas::saveLayerAlpha(const SkRect* bounds, uint8_t alpha) { return canvas_->saveLayerAlpha(bounds, alpha); } @@ -201,19 +187,19 @@ void SkiaPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) { canvas_->drawPath(path, ToSkPaint(flags)); } -void SkiaPaintCanvas::drawImage(sk_sp<const SkImage> image, +void SkiaPaintCanvas::drawImage(const PaintImage& image, SkScalar left, SkScalar top, const PaintFlags* flags) { - canvas_->drawImage(image.get(), left, top, ToSkPaint(flags)); + canvas_->drawImage(image.sk_image().get(), left, top, ToSkPaint(flags)); } -void SkiaPaintCanvas::drawImageRect(sk_sp<const SkImage> image, +void SkiaPaintCanvas::drawImageRect(const PaintImage& image, const SkRect& src, const SkRect& dst, const PaintFlags* flags, SrcRectConstraint constraint) { - canvas_->drawImageRect(image.get(), src, dst, ToSkPaint(flags), + canvas_->drawImageRect(image.sk_image().get(), src, dst, ToSkPaint(flags), static_cast<SkCanvas::SrcRectConstraint>(constraint)); } @@ -248,7 +234,7 @@ void SkiaPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob, void SkiaPaintCanvas::drawDisplayItemList( scoped_refptr<DisplayItemList> display_item_list) { - display_item_list->Raster(canvas_, nullptr); + display_item_list->Raster(canvas_); } void SkiaPaintCanvas::drawPicture(sk_sp<const PaintRecord> record) { @@ -267,29 +253,6 @@ const SkMatrix& SkiaPaintCanvas::getTotalMatrix() const { return canvas_->getTotalMatrix(); } -void SkiaPaintCanvas::temporary_internal_describeTopLayer( - SkMatrix* matrix, - SkIRect* clip_bounds) { - return canvas_->temporary_internal_describeTopLayer(matrix, clip_bounds); -} - -void SkiaPaintCanvas::PlaybackPaintRecord(sk_sp<const PaintRecord> record) { - record->playback(canvas_); -} - -bool SkiaPaintCanvas::ToPixmap(SkPixmap* output) { - SkImageInfo info; - size_t row_bytes; - void* pixels = canvas_->accessTopLayerPixels(&info, &row_bytes); - if (!pixels) { - output->reset(); - return false; - } - - output->reset(info, pixels, row_bytes); - return true; -} - void SkiaPaintCanvas::Annotate(AnnotationType type, const SkRect& rect, sk_sp<SkData> data) { diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h index 669ca9b3e0b..1fd313b53ab 100644 --- a/chromium/cc/paint/skia_paint_canvas.h +++ b/chromium/cc/paint/skia_paint_canvas.h @@ -28,25 +28,16 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { explicit SkiaPaintCanvas(SkCanvas* canvas); explicit SkiaPaintCanvas(const SkBitmap& bitmap); explicit SkiaPaintCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props); - explicit SkiaPaintCanvas(SkiaPaintCanvas&& other); ~SkiaPaintCanvas() override; - SkiaPaintCanvas& operator=(SkiaPaintCanvas&& other) = default; - SkMetaData& getMetaData() override; SkImageInfo imageInfo() const override; void flush() override; - SkISize getBaseLayerSize() const override; - bool writePixels(const SkImageInfo& info, - const void* pixels, - size_t row_bytes, - int x, - int y) override; int save() override; int saveLayer(const SkRect* bounds, const PaintFlags* flags) override; - int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) override; + int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) override; void restore() override; int getSaveCount() const override; @@ -97,11 +88,11 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { SkScalar ry, const PaintFlags& flags) override; void drawPath(const SkPath& path, const PaintFlags& flags) override; - void drawImage(sk_sp<const SkImage> image, + void drawImage(const PaintImage& image, SkScalar left, SkScalar top, const PaintFlags* flags) override; - void drawImageRect(sk_sp<const SkImage> image, + void drawImageRect(const PaintImage& image, const SkRect& src, const SkRect& dst, const PaintFlags* flags, @@ -134,16 +125,10 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { bool isClipRect() const override; const SkMatrix& getTotalMatrix() const override; - void temporary_internal_describeTopLayer(SkMatrix* matrix, - SkIRect* clip_bounds) override; - - bool ToPixmap(SkPixmap* output) override; void Annotate(AnnotationType type, const SkRect& rect, sk_sp<SkData> data) override; - void PlaybackPaintRecord(sk_sp<const PaintRecord> record) override; - // Don't shadow non-virtual helper functions. using PaintCanvas::clipRect; using PaintCanvas::clipRRect; diff --git a/chromium/cc/paint/transform_display_item.cc b/chromium/cc/paint/transform_display_item.cc index 6cc3e7f2d1e..54c034e8502 100644 --- a/chromium/cc/paint/transform_display_item.cc +++ b/chromium/cc/paint/transform_display_item.cc @@ -8,7 +8,11 @@ namespace cc { TransformDisplayItem::TransformDisplayItem(const gfx::Transform& transform) - : DisplayItem(TRANSFORM), transform(transform) {} + : DisplayItem(TRANSFORM), transform(transform) { + // The underlying SkMatrix in gfx::Transform is not thread-safe, unless + // getType() has been called. + this->transform.matrix().getType(); +} TransformDisplayItem::~TransformDisplayItem() = default; diff --git a/chromium/cc/paint/transform_display_item.h b/chromium/cc/paint/transform_display_item.h index a75db2a4d87..50c2358d783 100644 --- a/chromium/cc/paint/transform_display_item.h +++ b/chromium/cc/paint/transform_display_item.h @@ -19,7 +19,7 @@ class CC_PAINT_EXPORT TransformDisplayItem : public DisplayItem { ~TransformDisplayItem() override; size_t ExternalMemoryUsage() const { return 0; } - int ApproximateOpCount() const { return 1; } + int OpCount() const { return 1; } const gfx::Transform transform; }; @@ -29,7 +29,7 @@ class CC_PAINT_EXPORT EndTransformDisplayItem : public DisplayItem { EndTransformDisplayItem(); ~EndTransformDisplayItem() override; - int ApproximateOpCount() const { return 0; } + int OpCount() const { return 0; } }; } // namespace cc diff --git a/chromium/cc/quads/draw_polygon_unittest.cc b/chromium/cc/quads/draw_polygon_unittest.cc index 5f9475360a5..6a5ff188adb 100644 --- a/chromium/cc/quads/draw_polygon_unittest.cc +++ b/chromium/cc/quads/draw_polygon_unittest.cc @@ -169,7 +169,7 @@ TEST(DrawPolygonConstructionTest, ManyVertexNormal) { EXPECT_NORMAL(polygon_c, 0.0f, 0.0f, 1.0f); CREATE_TEST_DRAW_FORWARD_POLYGON(polygon_d, vertices_d, 4); - EXPECT_NORMAL(polygon_c, 0.0f, 0.0f, 1.0f); + EXPECT_NORMAL(polygon_d, 0.0f, 0.0f, 1.0f); } // A simple rect being transformed. diff --git a/chromium/cc/quads/draw_quad.h b/chromium/cc/quads/draw_quad.h index 5e46ea4d2f6..1b5775c2ec1 100644 --- a/chromium/cc/quads/draw_quad.h +++ b/chromium/cc/quads/draw_quad.h @@ -95,13 +95,13 @@ class CC_EXPORT DrawQuad { // Is the right edge of this tile aligned with the originating layer's // right edge? bool IsRightEdge() const { - return rect.right() == shared_quad_state->quad_layer_bounds.width(); + return rect.right() == shared_quad_state->quad_layer_rect.right(); } // Is the bottom edge of this tile aligned with the originating layer's // bottom edge? bool IsBottomEdge() const { - return rect.bottom() == shared_quad_state->quad_layer_bounds.height(); + return rect.bottom() == shared_quad_state->quad_layer_rect.bottom(); } // Is any edge of this tile aligned with the originating layer's diff --git a/chromium/cc/quads/draw_quad_perftest.cc b/chromium/cc/quads/draw_quad_perftest.cc index 5d6c4c2ab33..669da217eeb 100644 --- a/chromium/cc/quads/draw_quad_perftest.cc +++ b/chromium/cc/quads/draw_quad_perftest.cc @@ -22,7 +22,7 @@ static const int kTimeCheckInterval = 10; SharedQuadState* CreateSharedQuadState(RenderPass* render_pass) { gfx::Transform quad_transform = gfx::Transform(1.0, 0.0, 0.5, 1.0, 0.5, 0.0); - gfx::Size content_bounds(26, 28); + gfx::Rect content_rect(26, 28); gfx::Rect visible_layer_rect(10, 12, 14, 16); gfx::Rect clip_rect(19, 21, 23, 25); bool is_clipped = false; @@ -31,7 +31,7 @@ SharedQuadState* CreateSharedQuadState(RenderPass* render_pass) { SkBlendMode blend_mode = SkBlendMode::kSrcOver; SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState(); - state->SetAll(quad_transform, content_bounds, visible_layer_rect, clip_rect, + state->SetAll(quad_transform, content_rect, visible_layer_rect, clip_rect, is_clipped, opacity, blend_mode, sorting_context_id); return state; } diff --git a/chromium/cc/quads/draw_quad_unittest.cc b/chromium/cc/quads/draw_quad_unittest.cc index fce24b153d7..7f559054337 100644 --- a/chromium/cc/quads/draw_quad_unittest.cc +++ b/chromium/cc/quads/draw_quad_unittest.cc @@ -36,7 +36,7 @@ static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1); TEST(DrawQuadTest, CopySharedQuadState) { gfx::Transform quad_transform = gfx::Transform(1.0, 0.0, 0.5, 1.0, 0.5, 0.0); - gfx::Size layer_bounds(26, 28); + gfx::Rect layer_rect(26, 28); gfx::Rect visible_layer_rect(10, 12, 14, 16); gfx::Rect clip_rect(19, 21, 23, 25); bool is_clipped = true; @@ -45,7 +45,7 @@ TEST(DrawQuadTest, CopySharedQuadState) { int sorting_context_id = 65536; std::unique_ptr<SharedQuadState> state(new SharedQuadState); - state->SetAll(quad_transform, layer_bounds, visible_layer_rect, clip_rect, + state->SetAll(quad_transform, layer_rect, visible_layer_rect, clip_rect, is_clipped, opacity, blend_mode, sorting_context_id); std::unique_ptr<SharedQuadState> copy(new SharedQuadState(*state)); @@ -59,7 +59,7 @@ TEST(DrawQuadTest, CopySharedQuadState) { SharedQuadState* CreateSharedQuadState(RenderPass* render_pass) { gfx::Transform quad_transform = gfx::Transform(1.0, 0.0, 0.5, 1.0, 0.5, 0.0); - gfx::Size layer_bounds(26, 28); + gfx::Rect layer_rect(26, 28); gfx::Rect visible_layer_rect(10, 12, 14, 16); gfx::Rect clip_rect(19, 21, 23, 25); bool is_clipped = false; @@ -68,7 +68,7 @@ SharedQuadState* CreateSharedQuadState(RenderPass* render_pass) { SkBlendMode blend_mode = SkBlendMode::kSrcOver; SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState(); - state->SetAll(quad_transform, layer_bounds, visible_layer_rect, clip_rect, + state->SetAll(quad_transform, layer_rect, visible_layer_rect, clip_rect, is_clipped, opacity, blend_mode, sorting_context_id); return state; } diff --git a/chromium/cc/quads/largest_draw_quad.cc b/chromium/cc/quads/largest_draw_quad.cc index 68221da17a9..de99f54dc7f 100644 --- a/chromium/cc/quads/largest_draw_quad.cc +++ b/chromium/cc/quads/largest_draw_quad.cc @@ -19,28 +19,53 @@ #include "cc/quads/yuv_video_draw_quad.h" namespace { + template <typename...> struct MaxSize {}; template <class T, class... Args> struct MaxSize<T, Args...> { - static const size_t value = sizeof(T) > MaxSize<Args...>::value - ? sizeof(T) - : MaxSize<Args...>::value; + static constexpr size_t value = sizeof(T) > MaxSize<Args...>::value + ? sizeof(T) + : MaxSize<Args...>::value; }; template <> struct MaxSize<> { - static const size_t value = 0; + static constexpr size_t value = 0; +}; + +constexpr size_t kLargestDrawQuadSize = MaxSize<cc::DebugBorderDrawQuad, + cc::PictureDrawQuad, + cc::RenderPassDrawQuad, + cc::SolidColorDrawQuad, + cc::StreamVideoDrawQuad, + cc::SurfaceDrawQuad, + cc::TextureDrawQuad, + cc::TileDrawQuad, + cc::YUVVideoDrawQuad>::value; + +template <typename...> +struct MaxAlign {}; +template <class T, class... Args> +struct MaxAlign<T, Args...> { + static constexpr size_t value = ALIGNOF(T) > MaxAlign<Args...>::value + ? ALIGNOF(T) + : MaxAlign<Args...>::value; +}; +template <> +struct MaxAlign<> { + static constexpr size_t value = 0; }; -const size_t kLargestDrawQuadSize = MaxSize<cc::DebugBorderDrawQuad, - cc::PictureDrawQuad, - cc::RenderPassDrawQuad, - cc::SolidColorDrawQuad, - cc::StreamVideoDrawQuad, - cc::SurfaceDrawQuad, - cc::TextureDrawQuad, - cc::TileDrawQuad, - cc::YUVVideoDrawQuad>::value; +constexpr size_t kLargestDrawQuadAlignment = + MaxAlign<cc::DebugBorderDrawQuad, + cc::PictureDrawQuad, + cc::RenderPassDrawQuad, + cc::SolidColorDrawQuad, + cc::StreamVideoDrawQuad, + cc::SurfaceDrawQuad, + cc::TextureDrawQuad, + cc::TileDrawQuad, + cc::YUVVideoDrawQuad>::value; } // namespace @@ -50,4 +75,8 @@ size_t LargestDrawQuadSize() { return kLargestDrawQuadSize; } +size_t LargestDrawQuadAlignment() { + return kLargestDrawQuadAlignment; +} + } // namespace cc diff --git a/chromium/cc/quads/largest_draw_quad.h b/chromium/cc/quads/largest_draw_quad.h index aefbd5ecee4..9069a3325eb 100644 --- a/chromium/cc/quads/largest_draw_quad.h +++ b/chromium/cc/quads/largest_draw_quad.h @@ -12,6 +12,7 @@ namespace cc { CC_EXPORT size_t LargestDrawQuadSize(); +CC_EXPORT size_t LargestDrawQuadAlignment(); } // namespace cc diff --git a/chromium/cc/quads/render_pass.cc b/chromium/cc/quads/render_pass.cc index 3ad5fb0cf83..de49a226a22 100644 --- a/chromium/cc/quads/render_pass.cc +++ b/chromium/cc/quads/render_pass.cc @@ -37,12 +37,14 @@ const size_t kDefaultNumQuadsToReserve = 128; namespace cc { QuadList::QuadList() - : ListContainer<DrawQuad>(LargestDrawQuadSize(), + : ListContainer<DrawQuad>(LargestDrawQuadAlignment(), + LargestDrawQuadSize(), kDefaultNumSharedQuadStatesToReserve) {} QuadList::QuadList(size_t default_size_to_reserve) - : ListContainer<DrawQuad>(LargestDrawQuadSize(), default_size_to_reserve) { -} + : ListContainer<DrawQuad>(LargestDrawQuadAlignment(), + LargestDrawQuadSize(), + default_size_to_reserve) {} std::unique_ptr<RenderPass> RenderPass::Create() { return base::WrapUnique(new RenderPass()); @@ -61,7 +63,8 @@ std::unique_ptr<RenderPass> RenderPass::Create( RenderPass::RenderPass() : quad_list(kDefaultNumQuadsToReserve), - shared_quad_state_list(sizeof(SharedQuadState), + shared_quad_state_list(ALIGNOF(SharedQuadState), + sizeof(SharedQuadState), kDefaultNumSharedQuadStatesToReserve) {} // Each layer usually produces one shared quad state, so the number of layers @@ -69,16 +72,17 @@ RenderPass::RenderPass() RenderPass::RenderPass(size_t num_layers) : has_transparent_background(true), quad_list(kDefaultNumQuadsToReserve), - shared_quad_state_list(sizeof(SharedQuadState), num_layers) { -} + shared_quad_state_list(ALIGNOF(SharedQuadState), + sizeof(SharedQuadState), + num_layers) {} RenderPass::RenderPass(size_t shared_quad_state_list_size, size_t quad_list_size) : has_transparent_background(true), quad_list(quad_list_size), - shared_quad_state_list(sizeof(SharedQuadState), - shared_quad_state_list_size) { -} + shared_quad_state_list(ALIGNOF(SharedQuadState), + sizeof(SharedQuadState), + shared_quad_state_list_size) {} RenderPass::~RenderPass() { TRACE_EVENT_OBJECT_DELETED_WITH_ID( diff --git a/chromium/cc/quads/render_pass.h b/chromium/cc/quads/render_pass.h index fbb43237b5a..0ad8debc892 100644 --- a/chromium/cc/quads/render_pass.h +++ b/chromium/cc/quads/render_pass.h @@ -7,7 +7,6 @@ #include <stddef.h> -#include <unordered_map> #include <utility> #include <vector> @@ -153,10 +152,6 @@ class CC_EXPORT RenderPass { using RenderPassList = std::vector<std::unique_ptr<RenderPass>>; -// List of pairs of render pass id and filter, sorted by render pass id so that -// it can be searched using std::lower_bound. -using RenderPassFilterList = std::vector<std::pair<int, FilterOperations*>>; - } // namespace cc #endif // CC_QUADS_RENDER_PASS_H_ diff --git a/chromium/cc/quads/render_pass_unittest.cc b/chromium/cc/quads/render_pass_unittest.cc index 9fced653723..485bf71946c 100644 --- a/chromium/cc/quads/render_pass_unittest.cc +++ b/chromium/cc/quads/render_pass_unittest.cc @@ -61,8 +61,8 @@ static void CompareRenderPassLists(const RenderPassList& expected_list, exp_iter != expected->quad_list.cend(); ++exp_iter, ++act_iter) { EXPECT_EQ(exp_iter->rect.ToString(), act_iter->rect.ToString()); - EXPECT_EQ(exp_iter->shared_quad_state->quad_layer_bounds.ToString(), - act_iter->shared_quad_state->quad_layer_bounds.ToString()); + EXPECT_EQ(exp_iter->shared_quad_state->quad_layer_rect.ToString(), + act_iter->shared_quad_state->quad_layer_rect.ToString()); } } } @@ -87,7 +87,7 @@ TEST(RenderPassTest, CopyShouldBeIdenticalExceptIdAndQuads) { // Stick a quad in the pass, this should not get copied. SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(gfx::Transform(), gfx::Size(), gfx::Rect(), gfx::Rect(), + shared_state->SetAll(gfx::Transform(), gfx::Rect(), gfx::Rect(), gfx::Rect(), false, 1, SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* color_quad = @@ -135,7 +135,7 @@ TEST(RenderPassTest, CopyAllShouldBeIdentical) { // Two quads using one shared state. SharedQuadState* shared_state1 = pass->CreateAndAppendSharedQuadState(); - shared_state1->SetAll(gfx::Transform(), gfx::Size(1, 1), gfx::Rect(), + shared_state1->SetAll(gfx::Transform(), gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Rect(), false, 1, SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* color_quad1 = @@ -152,7 +152,7 @@ TEST(RenderPassTest, CopyAllShouldBeIdentical) { // And two quads using another shared state. SharedQuadState* shared_state2 = pass->CreateAndAppendSharedQuadState(); - shared_state2->SetAll(gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), + shared_state2->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(), gfx::Rect(), false, 1, SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* color_quad3 = @@ -188,8 +188,9 @@ TEST(RenderPassTest, CopyAllShouldBeIdentical) { SharedQuadState* contrib_shared_state = contrib->CreateAndAppendSharedQuadState(); - contrib_shared_state->SetAll(gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), - gfx::Rect(), false, 1, SkBlendMode::kSrcOver, 0); + contrib_shared_state->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), + gfx::Rect(), gfx::Rect(), false, 1, + SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* contrib_quad = contrib->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); @@ -235,7 +236,7 @@ TEST(RenderPassTest, CopyAllWithCulledQuads) { // A shared state with a quad. SharedQuadState* shared_state1 = pass->CreateAndAppendSharedQuadState(); - shared_state1->SetAll(gfx::Transform(), gfx::Size(1, 1), gfx::Rect(), + shared_state1->SetAll(gfx::Transform(), gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Rect(), false, 1, SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* color_quad1 = @@ -246,17 +247,17 @@ TEST(RenderPassTest, CopyAllWithCulledQuads) { // A shared state with no quads, they were culled. SharedQuadState* shared_state2 = pass->CreateAndAppendSharedQuadState(); - shared_state2->SetAll(gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), + shared_state2->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(), gfx::Rect(), false, 1, SkBlendMode::kSrcOver, 0); // A second shared state with no quads. SharedQuadState* shared_state3 = pass->CreateAndAppendSharedQuadState(); - shared_state3->SetAll(gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), + shared_state3->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(), gfx::Rect(), false, 1, SkBlendMode::kSrcOver, 0); // A last shared state with a quad again. SharedQuadState* shared_state4 = pass->CreateAndAppendSharedQuadState(); - shared_state4->SetAll(gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), + shared_state4->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(), gfx::Rect(), false, 1, SkBlendMode::kSrcOver, 0); SolidColorDrawQuad* color_quad2 = diff --git a/chromium/cc/quads/shared_quad_state.cc b/chromium/cc/quads/shared_quad_state.cc index de2d75e722c..bc6e15e9ee6 100644 --- a/chromium/cc/quads/shared_quad_state.cc +++ b/chromium/cc/quads/shared_quad_state.cc @@ -28,7 +28,7 @@ SharedQuadState::~SharedQuadState() { } void SharedQuadState::SetAll(const gfx::Transform& quad_to_target_transform, - const gfx::Size& quad_layer_bounds, + const gfx::Rect& quad_layer_rect, const gfx::Rect& visible_quad_layer_rect, const gfx::Rect& clip_rect, bool is_clipped, @@ -36,7 +36,7 @@ void SharedQuadState::SetAll(const gfx::Transform& quad_to_target_transform, SkBlendMode blend_mode, int sorting_context_id) { this->quad_to_target_transform = quad_to_target_transform; - this->quad_layer_bounds = quad_layer_bounds; + this->quad_layer_rect = quad_layer_rect; this->visible_quad_layer_rect = visible_quad_layer_rect; this->clip_rect = clip_rect; this->is_clipped = is_clipped; @@ -47,7 +47,7 @@ void SharedQuadState::SetAll(const gfx::Transform& quad_to_target_transform, void SharedQuadState::AsValueInto(base::trace_event::TracedValue* value) const { MathUtil::AddToTracedValue("transform", quad_to_target_transform, value); - MathUtil::AddToTracedValue("layer_content_bounds", quad_layer_bounds, value); + MathUtil::AddToTracedValue("layer_content_rect", quad_layer_rect, value); MathUtil::AddToTracedValue("layer_visible_content_rect", visible_quad_layer_rect, value); diff --git a/chromium/cc/quads/shared_quad_state.h b/chromium/cc/quads/shared_quad_state.h index f3758fb0d96..c496b7b35b2 100644 --- a/chromium/cc/quads/shared_quad_state.h +++ b/chromium/cc/quads/shared_quad_state.h @@ -32,7 +32,7 @@ class CC_EXPORT SharedQuadState { ~SharedQuadState(); void SetAll(const gfx::Transform& quad_to_target_transform, - const gfx::Size& layer_bounds, + const gfx::Rect& layer_rect, const gfx::Rect& visible_layer_rect, const gfx::Rect& clip_rect, bool is_clipped, @@ -43,8 +43,8 @@ class CC_EXPORT SharedQuadState { // Transforms quad rects into the target content space. gfx::Transform quad_to_target_transform; - // The size of the quads' originating layer in the space of the quad rects. - gfx::Size quad_layer_bounds; + // The rect of the quads' originating layer in the space of the quad rects. + gfx::Rect quad_layer_rect; // The size of the visible area in the quads' originating layer, in the space // of the quad rects. gfx::Rect visible_quad_layer_rect; diff --git a/chromium/cc/raster/bitmap_raster_buffer_provider.cc b/chromium/cc/raster/bitmap_raster_buffer_provider.cc index 470e6e03354..b69531a3ea3 100644 --- a/chromium/cc/raster/bitmap_raster_buffer_provider.cc +++ b/chromium/cc/raster/bitmap_raster_buffer_provider.cc @@ -98,6 +98,8 @@ void BitmapRasterBufferProvider::OrderingBarrier() { // No need to sync resources as this provider does not use GL context. } +void BitmapRasterBufferProvider::Flush() {} + ResourceFormat BitmapRasterBufferProvider::GetResourceFormat( bool must_support_alpha) const { return resource_provider_->best_texture_format(); diff --git a/chromium/cc/raster/bitmap_raster_buffer_provider.h b/chromium/cc/raster/bitmap_raster_buffer_provider.h index d4e44e7a00d..4f1301365c3 100644 --- a/chromium/cc/raster/bitmap_raster_buffer_provider.h +++ b/chromium/cc/raster/bitmap_raster_buffer_provider.h @@ -34,6 +34,7 @@ class CC_EXPORT BitmapRasterBufferProvider : public RasterBufferProvider { uint64_t previous_content_id) override; void ReleaseBufferForRaster(std::unique_ptr<RasterBuffer> buffer) override; void OrderingBarrier() override; + void Flush() override; ResourceFormat GetResourceFormat(bool must_support_alpha) const override; bool IsResourceSwizzleRequired(bool must_support_alpha) const override; bool CanPartialRasterIntoProvidedResource() const override; diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc index de489ff8fde..1f2e8802e7d 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.cc +++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc @@ -175,6 +175,16 @@ void GpuRasterBufferProvider::OrderingBarrier() { pending_raster_buffers_.clear(); } +void GpuRasterBufferProvider::Flush() { + if (async_worker_context_enabled_) { + int32_t worker_stream_id = + worker_context_provider_->ContextSupport()->GetStreamId(); + + compositor_context_provider_->ContextSupport() + ->FlushOrderingBarrierOnStream(worker_stream_id); + } +} + ResourceFormat GpuRasterBufferProvider::GetResourceFormat( bool must_support_alpha) const { if (resource_provider_->IsRenderBufferFormatSupported( diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.h b/chromium/cc/raster/gpu_raster_buffer_provider.h index 0777db294c3..d699795e8b2 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.h +++ b/chromium/cc/raster/gpu_raster_buffer_provider.h @@ -33,6 +33,7 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { uint64_t previous_content_id) override; void ReleaseBufferForRaster(std::unique_ptr<RasterBuffer> buffer) override; void OrderingBarrier() override; + void Flush() override; ResourceFormat GetResourceFormat(bool must_support_alpha) const override; bool IsResourceSwizzleRequired(bool must_support_alpha) const override; bool CanPartialRasterIntoProvidedResource() const override; diff --git a/chromium/cc/raster/image_hijack_canvas.cc b/chromium/cc/raster/image_hijack_canvas.cc index 150be23fa5d..a0a8c847835 100644 --- a/chromium/cc/raster/image_hijack_canvas.cc +++ b/chromium/cc/raster/image_hijack_canvas.cc @@ -22,13 +22,15 @@ SkIRect RoundOutRect(const SkRect& rect) { class ScopedDecodedImageLock { public: ScopedDecodedImageLock(ImageDecodeCache* image_decode_cache, - sk_sp<const SkImage> image, + sk_sp<SkImage> image, const SkRect& src_rect, const SkMatrix& matrix, const SkPaint* paint, const gfx::ColorSpace& target_color_space) : image_decode_cache_(image_decode_cache), - draw_image_(std::move(image), + // TODO(khushalsagar): Using the wrong id should not be necessary once + // the hijack canvas is eliminated. + draw_image_(PaintImage(PaintImage::kUnknownStableId, std::move(image)), RoundOutRect(src_rect), paint ? paint->getFilterQuality() : kNone_SkFilterQuality, matrix, @@ -133,7 +135,7 @@ const SkImage* GetImageInPaint(const SkPaint& paint) { ImageHijackCanvas::ImageHijackCanvas(int width, int height, ImageDecodeCache* image_decode_cache, - const ImageIdFlatSet* images_to_skip, + const SkImageIdFlatSet* images_to_skip, const gfx::ColorSpace& target_color_space) : SkNWayCanvas(width, height), image_decode_cache_(image_decode_cache), @@ -156,6 +158,11 @@ void ImageHijackCanvas::onDrawImage(const SkImage* image, const SkPaint* paint) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ImageHijackCanvas::onDrawImage"); + SkRect rect = SkRect::MakeXYWH(x, y, SkIntToScalar(image->width()), + SkIntToScalar(image->height())); + if (QuickRejectDraw(rect, paint)) + return; + if (!image->isLazyGenerated()) { DCHECK(!ShouldSkipImage(image)); SkNWayCanvas::onDrawImage(image, x, y, paint); @@ -168,7 +175,7 @@ void ImageHijackCanvas::onDrawImage(const SkImage* image, SkMatrix ctm = getTotalMatrix(); ScopedDecodedImageLock scoped_lock( - image_decode_cache_, sk_ref_sp(image), + image_decode_cache_, sk_ref_sp(const_cast<SkImage*>(image)), SkRect::MakeIWH(image->width(), image->height()), ctm, paint, target_color_space_); const DecodedDrawImage& decoded_image = scoped_lock.decoded_image(); @@ -197,6 +204,9 @@ void ImageHijackCanvas::onDrawImageRect(const SkImage* image, SrcRectConstraint constraint) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ImageHijackCanvas::onDrawImageRect"); + if (QuickRejectDraw(dst, paint)) + return; + if (!image->isLazyGenerated()) { DCHECK(!ShouldSkipImage(image)); SkNWayCanvas::onDrawImageRect(image, src, dst, paint, constraint); @@ -215,7 +225,8 @@ void ImageHijackCanvas::onDrawImageRect(const SkImage* image, matrix.setRectToRect(*src, dst, SkMatrix::kFill_ScaleToFit); matrix.postConcat(getTotalMatrix()); - ScopedDecodedImageLock scoped_lock(image_decode_cache_, sk_ref_sp(image), + ScopedDecodedImageLock scoped_lock(image_decode_cache_, + sk_ref_sp(const_cast<SkImage*>(image)), *src, matrix, paint, target_color_space_); const DecodedDrawImage& decoded_image = scoped_lock.decoded_image(); if (!decoded_image.image()) @@ -240,6 +251,9 @@ void ImageHijackCanvas::onDrawImageRect(const SkImage* image, void ImageHijackCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ImageHijackCanvas::onDrawRect"); + if (QuickRejectDraw(r, &paint)) + return; + if (ShouldSkipImageInPaint(paint)) return; @@ -255,6 +269,9 @@ void ImageHijackCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) { void ImageHijackCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ImageHijackCanvas::onDrawPath"); + if (QuickRejectDraw(path.getBounds(), &paint)) + return; + if (ShouldSkipImageInPaint(paint)) return; @@ -270,6 +287,9 @@ void ImageHijackCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { void ImageHijackCanvas::onDrawOval(const SkRect& r, const SkPaint& paint) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ImageHijackCanvas::onDrawOval"); + if (QuickRejectDraw(r, &paint)) + return; + if (ShouldSkipImageInPaint(paint)) return; @@ -289,6 +309,9 @@ void ImageHijackCanvas::onDrawArc(const SkRect& r, const SkPaint& paint) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ImageHijackCanvas::onDrawArc"); + if (QuickRejectDraw(r, &paint)) + return; + if (ShouldSkipImageInPaint(paint)) return; @@ -305,6 +328,9 @@ void ImageHijackCanvas::onDrawArc(const SkRect& r, void ImageHijackCanvas::onDrawRRect(const SkRRect& rr, const SkPaint& paint) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "ImageHijackCanvas::onDrawRRect"); + if (QuickRejectDraw(rr.rect(), &paint)) + return; + if (ShouldSkipImageInPaint(paint)) return; @@ -339,4 +365,16 @@ bool ImageHijackCanvas::ShouldSkipImageInPaint(const SkPaint& paint) const { return image ? ShouldSkipImage(image) : false; } +bool ImageHijackCanvas::QuickRejectDraw(const SkRect& rect, + const SkPaint* paint) const { + if (nullptr == paint || paint->canComputeFastBounds()) { + SkRect tmp = rect; + if (paint) + paint->computeFastBounds(tmp, &tmp); + return quickReject(tmp); + } + + return false; +} + } // namespace cc diff --git a/chromium/cc/raster/image_hijack_canvas.h b/chromium/cc/raster/image_hijack_canvas.h index 4693e8d8622..a71c662f49a 100644 --- a/chromium/cc/raster/image_hijack_canvas.h +++ b/chromium/cc/raster/image_hijack_canvas.h @@ -22,7 +22,7 @@ class CC_EXPORT ImageHijackCanvas : public SkNWayCanvas { ImageHijackCanvas(int width, int height, ImageDecodeCache* image_decode_cache, - const ImageIdFlatSet* images_to_skip, + const SkImageIdFlatSet* images_to_skip, const gfx::ColorSpace& target_color_space); private: @@ -56,9 +56,10 @@ class CC_EXPORT ImageHijackCanvas : public SkNWayCanvas { bool ShouldSkipImage(const SkImage* image) const; bool ShouldSkipImageInPaint(const SkPaint& paint) const; + bool QuickRejectDraw(const SkRect& rect, const SkPaint* paint) const; ImageDecodeCache* image_decode_cache_; - const ImageIdFlatSet* images_to_skip_; + const SkImageIdFlatSet* images_to_skip_; const gfx::ColorSpace target_color_space_; DISALLOW_COPY_AND_ASSIGN(ImageHijackCanvas); diff --git a/chromium/cc/raster/image_hijack_canvas_unittest.cc b/chromium/cc/raster/image_hijack_canvas_unittest.cc index 070070f2632..8a9f4affdc3 100644 --- a/chromium/cc/raster/image_hijack_canvas_unittest.cc +++ b/chromium/cc/raster/image_hijack_canvas_unittest.cc @@ -32,13 +32,14 @@ class MockImageDecodeCache : public ImageDecodeCache { MOCK_METHOD0(ClearCache, void()); MOCK_METHOD2(GetOutOfRasterDecodeTaskForImageAndRef, bool(const DrawImage& image, scoped_refptr<TileTask>* task)); + MOCK_CONST_METHOD0(GetMaximumMemoryLimitBytes, size_t()); }; TEST(ImageHijackCanvasTest, NonLazyImagesSkipped) { // Use a strict mock so that if *any* ImageDecodeCache methods are called, we // will hit an error. testing::StrictMock<MockImageDecodeCache> image_decode_cache; - ImageIdFlatSet images_to_skip; + SkImageIdFlatSet images_to_skip; gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); ImageHijackCanvas canvas(100, 100, &image_decode_cache, &images_to_skip, target_color_space); @@ -71,7 +72,7 @@ TEST(ImageHijackCanvasTest, ImagesToSkipAreSkipped) { // Use a strict mock so that if *any* ImageDecodeCache methods are called, we // will hit an error. testing::StrictMock<MockImageDecodeCache> image_decode_cache; - ImageIdFlatSet images_to_skip; + SkImageIdFlatSet images_to_skip; sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(10, 10)); images_to_skip.insert(image->uniqueID()); gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); @@ -87,6 +88,29 @@ TEST(ImageHijackCanvasTest, ImagesToSkipAreSkipped) { canvas.drawRect(SkRect::MakeXYWH(10, 10, 10, 10), paint); } +TEST(ImageHijackCanvasTest, ClippedOpsAreSkipped) { + testing::StrictMock<MockImageDecodeCache> image_decode_cache; + SkImageIdFlatSet images_to_skip; + gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); + ImageHijackCanvas canvas(100, 100, &image_decode_cache, &images_to_skip, + target_color_space); + SkPaint paint; + SkRect draw_rect = SkRect::MakeXYWH(200, 200, 100, 100); + sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(10, 10)); + canvas.drawImage(image, 200, 200, &paint); + canvas.drawImageRect(image, SkRect::MakeXYWH(0, 0, 10, 10), draw_rect, + &paint); + paint.setShader(image->makeShader(SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, nullptr)); + canvas.drawRect(draw_rect, paint); + SkPath path; + path.addRect(draw_rect, SkPath::kCW_Direction); + canvas.drawPath(path, paint); + canvas.drawOval(draw_rect, paint); + canvas.drawArc(draw_rect, 0, 40, true, paint); + canvas.drawRRect(SkRRect::MakeRect(draw_rect), paint); +} + } // namespace } // namespace cc diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.cc b/chromium/cc/raster/one_copy_raster_buffer_provider.cc index abc63e46fc3..f662e6c1509 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.cc @@ -140,6 +140,16 @@ void OneCopyRasterBufferProvider::OrderingBarrier() { pending_raster_buffers_.clear(); } +void OneCopyRasterBufferProvider::Flush() { + if (async_worker_context_enabled_) { + int32_t worker_stream_id = + worker_context_provider_->ContextSupport()->GetStreamId(); + + compositor_context_provider_->ContextSupport() + ->FlushOrderingBarrierOnStream(worker_stream_id); + } +} + ResourceFormat OneCopyRasterBufferProvider::GetResourceFormat( bool must_support_alpha) const { if (resource_provider_->IsTextureFormatSupported(preferred_tile_format_) && @@ -240,7 +250,7 @@ void OneCopyRasterBufferProvider::PlaybackAndCopyOnWorkerThread( playback_settings, previous_content_id, new_content_id); CopyOnWorkerThread(staging_buffer.get(), resource_lock, sync_token, - raster_source, previous_content_id, new_content_id); + raster_source, raster_full_rect); staging_pool_.ReleaseStagingBuffer(std::move(staging_buffer)); } @@ -312,8 +322,7 @@ void OneCopyRasterBufferProvider::CopyOnWorkerThread( ResourceProvider::ScopedWriteLockGL* resource_lock, const gpu::SyncToken& sync_token, const RasterSource* raster_source, - uint64_t previous_content_id, - uint64_t new_content_id) { + const gfx::Rect& rect_to_copy) { ContextProvider::ScopedContextLock scoped_context(worker_context_provider_); gpu::gles2::GLES2Interface* gl = scoped_context.ContextGL(); DCHECK(gl); @@ -375,22 +384,21 @@ void OneCopyRasterBufferProvider::CopyOnWorkerThread( resource_texture_id); } else { int bytes_per_row = ResourceUtil::UncheckedWidthInBytes<int>( - resource_lock->size().width(), resource_lock->format()); + rect_to_copy.width(), resource_lock->format()); int chunk_size_in_rows = std::max(1, max_bytes_per_copy_operation_ / bytes_per_row); // Align chunk size to 4. Required to support compressed texture formats. chunk_size_in_rows = MathUtil::UncheckedRoundUp(chunk_size_in_rows, 4); int y = 0; - int height = resource_lock->size().height(); + int height = rect_to_copy.height(); while (y < height) { // Copy at most |chunk_size_in_rows|. int rows_to_copy = std::min(chunk_size_in_rows, height - y); DCHECK_GT(rows_to_copy, 0); - gl->CopySubTextureCHROMIUM(staging_buffer->texture_id, 0, GL_TEXTURE_2D, - resource_texture_id, 0, 0, y, 0, y, - resource_lock->size().width(), rows_to_copy, - false, false, false); + gl->CopySubTextureCHROMIUM( + staging_buffer->texture_id, 0, GL_TEXTURE_2D, resource_texture_id, 0, + 0, y, 0, y, rect_to_copy.width(), rows_to_copy, false, false, false); y += rows_to_copy; // Increment |bytes_scheduled_since_last_flush_| by the amount of memory diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.h b/chromium/cc/raster/one_copy_raster_buffer_provider.h index 5d82849cc6c..581bc493b40 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.h +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.h @@ -38,6 +38,7 @@ class CC_EXPORT OneCopyRasterBufferProvider : public RasterBufferProvider { uint64_t previous_content_id) override; void ReleaseBufferForRaster(std::unique_ptr<RasterBuffer> buffer) override; void OrderingBarrier() override; + void Flush() override; ResourceFormat GetResourceFormat(bool must_support_alpha) const override; bool IsResourceSwizzleRequired(bool must_support_alpha) const override; bool CanPartialRasterIntoProvidedResource() const override; @@ -110,8 +111,7 @@ class CC_EXPORT OneCopyRasterBufferProvider : public RasterBufferProvider { ResourceProvider::ScopedWriteLockGL* resource_lock, const gpu::SyncToken& sync_token, const RasterSource* raster_source, - uint64_t previous_content_id, - uint64_t new_content_id); + const gfx::Rect& rect_to_copy); gfx::BufferUsage StagingBufferUsage() const; ContextProvider* const compositor_context_provider_; diff --git a/chromium/cc/raster/raster_buffer_provider.cc b/chromium/cc/raster/raster_buffer_provider.cc index 7b3dfc6710f..f3e281063ad 100644 --- a/chromium/cc/raster/raster_buffer_provider.cc +++ b/chromium/cc/raster/raster_buffer_provider.cc @@ -12,6 +12,7 @@ #include "cc/resources/platform_color.h" #include "cc/resources/resource_format_utils.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkMath.h" #include "third_party/skia/include/core/SkSurface.h" #include "ui/gfx/geometry/axis_transform2d.h" @@ -23,6 +24,58 @@ RasterBufferProvider::~RasterBufferProvider() {} namespace { +// TODO(enne): http://crbug.com/721744. Add CHECKs for conditions that would +// cause Skia to not create a surface here to diagnose what's going wrong. This +// replicates SkSurfaceValidateRasterInfo and needs to be kept in sync with +// the corresponding Skia code. This code should be removed as quickly as +// possible once a diagnosis is made. +void CheckValidRasterInfo(const SkImageInfo& info, + void* pixels, + size_t row_bytes) { + CHECK(pixels); + CHECK(!info.isEmpty()); + + static const size_t kMaxTotalSize = SK_MaxS32; + + int shift = 0; + switch (info.colorType()) { + case kAlpha_8_SkColorType: + CHECK(!info.colorSpace()); + shift = 0; + break; + case kRGB_565_SkColorType: + CHECK(!info.colorSpace()); + shift = 1; + break; + case kN32_SkColorType: + if (info.colorSpace()) + CHECK(info.colorSpace()->gammaCloseToSRGB()); + shift = 2; + break; + case kRGBA_F16_SkColorType: + if (info.colorSpace()) + CHECK(info.colorSpace()->gammaIsLinear()); + shift = 3; + break; + default: + CHECK(false) << "Unknown color type"; + break; + } + + static constexpr size_t kIgnoreRowBytesValue = static_cast<size_t>(~0); + if (kIgnoreRowBytesValue == row_bytes) + return; + + uint64_t min_row_bytes = static_cast<uint64_t>(info.width()) << shift; + CHECK_LE(min_row_bytes, row_bytes); + + size_t aligned_row_bytes = row_bytes >> shift << shift; + CHECK_EQ(aligned_row_bytes, row_bytes); + + uint64_t size = sk_64_mul(info.height(), row_bytes); + CHECK_LE(size, kMaxTotalSize); +} + bool IsSupportedPlaybackToMemoryFormat(ResourceFormat format) { switch (format) { case RGBA_4444: @@ -80,8 +133,10 @@ void RasterBufferProvider::PlaybackToMemory( case RGBA_8888: case BGRA_8888: case RGBA_F16: { + CheckValidRasterInfo(info, memory, stride); sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(info, memory, stride, &surface_props); + CHECK(surface); raster_source->PlaybackToCanvas(surface->getCanvas(), target_color_space, canvas_bitmap_rect, canvas_playback_rect, transform, playback_settings); diff --git a/chromium/cc/raster/raster_buffer_provider.h b/chromium/cc/raster/raster_buffer_provider.h index 40611dfb96b..6055ccdacd6 100644 --- a/chromium/cc/raster/raster_buffer_provider.h +++ b/chromium/cc/raster/raster_buffer_provider.h @@ -54,6 +54,10 @@ class CC_EXPORT RasterBufferProvider { // Used for syncing resources to the worker context. virtual void OrderingBarrier() = 0; + // In addition to above, also ensures that pending work is sent to the GPU + // process. + virtual void Flush() = 0; + // Returns the format to use for the tiles. virtual ResourceFormat GetResourceFormat(bool must_support_alpha) const = 0; diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc index b1443e61770..409d214f407 100644 --- a/chromium/cc/raster/raster_source.cc +++ b/chromium/cc/raster/raster_source.cc @@ -101,7 +101,7 @@ void RasterSource::PlaybackToCanvas(SkCanvas* input_canvas, if (settings.skip_images) { SkipImageCanvas canvas(raster_canvas); - RasterCommon(&canvas, nullptr); + RasterCommon(&canvas); } else if (settings.use_image_hijack_canvas) { const SkImageInfo& info = raster_canvas->imageInfo(); ImageHijackCanvas canvas(info.width(), info.height(), image_decode_cache_, @@ -115,9 +115,9 @@ void RasterSource::PlaybackToCanvas(SkCanvas* input_canvas, canvas.setMatrix(raster_canvas->getTotalMatrix()); canvas.addCanvas(raster_canvas); - RasterCommon(&canvas, nullptr); + RasterCommon(&canvas); } else { - RasterCommon(raster_canvas, nullptr); + RasterCommon(raster_canvas); } } @@ -225,7 +225,7 @@ sk_sp<SkPicture> RasterSource::GetFlattenedPicture() { SkCanvas* canvas = recorder.beginRecording(size_.width(), size_.height()); if (!size_.IsEmpty()) { PrepareForPlaybackToCanvas(canvas); - RasterCommon(canvas, nullptr); + RasterCommon(canvas); } return recorder.finishRecordingAsPicture(); @@ -265,7 +265,7 @@ void RasterSource::GetDiscardableImagesInRect( target_color_space, images); } -gfx::Rect RasterSource::GetRectForImage(ImageId image_id) const { +gfx::Rect RasterSource::GetRectForImage(PaintImage::Id image_id) const { if (!display_list_) return gfx::Rect(); return display_list_->GetRectForImage(image_id); diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h index df899ab220c..92e429bd24f 100644 --- a/chromium/cc/raster/raster_source.h +++ b/chromium/cc/raster/raster_source.h @@ -51,7 +51,7 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { // during raster. // TODO(khushalsagar): Consolidate more settings for playback here? See // crbug.com/691076. - ImageIdFlatSet images_to_skip; + SkImageIdFlatSet images_to_skip; }; static scoped_refptr<RasterSource> CreateFromRecordingSource( @@ -121,7 +121,7 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { // Valid rectangle in which everything is recorded and can be rastered from. virtual gfx::Rect RecordedViewport() const; - gfx::Rect GetRectForImage(ImageId image_id) const; + gfx::Rect GetRectForImage(PaintImage::Id image_id) const; // Tracing functionality. virtual void DidBeginTracing(); @@ -168,7 +168,7 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { private: void RasterCommon(SkCanvas* canvas, - SkPicture::AbortCallback* callback) const; + SkPicture::AbortCallback* callback = nullptr) const; void PrepareForPlaybackToCanvas(SkCanvas* canvas) const; diff --git a/chromium/cc/raster/raster_source_unittest.cc b/chromium/cc/raster/raster_source_unittest.cc index 344205714c4..5cb06aaf7c3 100644 --- a/chromium/cc/raster/raster_source_unittest.cc +++ b/chromium/cc/raster/raster_source_unittest.cc @@ -207,7 +207,6 @@ TEST(RasterSourceTest, PixelRefIteratorDiscardableRefsOneTile) { recording_source->add_draw_image(discardable_image[0][1], gfx::Point(260, 0)); recording_source->add_draw_image(discardable_image[1][1], gfx::Point(260, 260)); - recording_source->SetGenerateDiscardableImagesMetadata(true); recording_source->Rerecord(); scoped_refptr<RasterSource> raster = @@ -593,7 +592,6 @@ TEST(RasterSourceTest, ImageHijackCanvasRespectsSharedCanvasTransform) { recording_source->add_draw_rect_with_flags( gfx::Rect(size.width() - 4, size.height() - 4, 4, 4), flags); - recording_source->SetGenerateDiscardableImagesMetadata(true); recording_source->Rerecord(); bool can_use_lcd = true; diff --git a/chromium/cc/raster/staging_buffer_pool.cc b/chromium/cc/raster/staging_buffer_pool.cc index 526e864ebdc..3cb4b14dfe6 100644 --- a/chromium/cc/raster/staging_buffer_pool.cc +++ b/chromium/cc/raster/staging_buffer_pool.cc @@ -114,8 +114,8 @@ void StagingBuffer::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, const uint64_t tracing_process_id = base::trace_event::MemoryDumpManager::GetInstance() ->GetTracingProcessId(); - MemoryAllocatorDumpGuid shared_buffer_guid = - gfx::GetGpuMemoryBufferGUIDForTracing(tracing_process_id, buffer_id); + auto shared_buffer_guid = + gpu_memory_buffer->GetGUIDForTracing(tracing_process_id); pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid); // By creating an edge with a higher |importance| (w.r.t. browser-side dumps) diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc index 85db2c4684f..9542fe4ac3e 100644 --- a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc @@ -100,6 +100,8 @@ void ZeroCopyRasterBufferProvider::OrderingBarrier() { // No need to sync resources as this provider does not use GL context. } +void ZeroCopyRasterBufferProvider::Flush() {} + ResourceFormat ZeroCopyRasterBufferProvider::GetResourceFormat( bool must_support_alpha) const { if (resource_provider_->IsTextureFormatSupported(preferred_tile_format_) && diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.h b/chromium/cc/raster/zero_copy_raster_buffer_provider.h index 686ba7ad5a2..16f2c6bdeef 100644 --- a/chromium/cc/raster/zero_copy_raster_buffer_provider.h +++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.h @@ -36,6 +36,7 @@ class CC_EXPORT ZeroCopyRasterBufferProvider : public RasterBufferProvider { uint64_t previous_content_id) override; void ReleaseBufferForRaster(std::unique_ptr<RasterBuffer> buffer) override; void OrderingBarrier() override; + void Flush() override; ResourceFormat GetResourceFormat(bool must_support_alpha) const override; bool IsResourceSwizzleRequired(bool must_support_alpha) const override; bool CanPartialRasterIntoProvidedResource() const override; diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc index ee516d50ebf..5ad2f6a5344 100644 --- a/chromium/cc/resources/resource_pool.cc +++ b/chromium/cc/resources/resource_pool.cc @@ -24,8 +24,39 @@ using base::trace_event::MemoryAllocatorDump; using base::trace_event::MemoryDumpLevelOfDetail; namespace cc { +namespace { +bool ResourceMeetsSizeRequirements(const gfx::Size& requested_size, + const gfx::Size& actual_size, + bool disallow_non_exact_reuse) { + const float kReuseThreshold = 2.0f; + + if (disallow_non_exact_reuse) + return requested_size == actual_size; + + // Allocating new resources is expensive, and we'd like to re-use our + // existing ones within reason. Allow a larger resource to be used for a + // smaller request. + if (actual_size.width() < requested_size.width() || + actual_size.height() < requested_size.height()) + return false; + + // GetArea will crash on overflow, however all sizes in use are tile sizes. + // These are capped at ResourceProvider::max_texture_size(), and will not + // overflow. + float actual_area = actual_size.GetArea(); + float requested_area = requested_size.GetArea(); + // Don't use a resource that is more than |kReuseThreshold| times the + // requested pixel area, as we want to free unnecessarily large resources. + if (actual_area / requested_area > kReuseThreshold) + return false; + + return true; +} + +} // namespace + base::TimeDelta ResourcePool::kDefaultExpirationDelay = - base::TimeDelta::FromSeconds(1); + base::TimeDelta::FromSeconds(5); void ResourcePool::PoolResource::OnMemoryDump( base::trace_event::ProcessMemoryDump* pmd, @@ -56,12 +87,14 @@ void ResourcePool::PoolResource::OnMemoryDump( ResourcePool::ResourcePool(ResourceProvider* resource_provider, base::SingleThreadTaskRunner* task_runner, gfx::BufferUsage usage, - const base::TimeDelta& expiration_delay) + const base::TimeDelta& expiration_delay, + bool disallow_non_exact_reuse) : resource_provider_(resource_provider), use_gpu_memory_buffers_(true), usage_(usage), task_runner_(task_runner), resource_expiration_delay_(expiration_delay), + disallow_non_exact_reuse_(disallow_non_exact_reuse), weak_ptr_factory_(this) { base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( this, "cc::ResourcePool", task_runner_.get()); @@ -73,12 +106,14 @@ ResourcePool::ResourcePool(ResourceProvider* resource_provider, ResourcePool::ResourcePool(ResourceProvider* resource_provider, base::SingleThreadTaskRunner* task_runner, ResourceProvider::TextureHint hint, - const base::TimeDelta& expiration_delay) + const base::TimeDelta& expiration_delay, + bool disallow_non_exact_reuse) : resource_provider_(resource_provider), use_gpu_memory_buffers_(false), hint_(hint), task_runner_(task_runner), resource_expiration_delay_(expiration_delay), + disallow_non_exact_reuse_(disallow_non_exact_reuse), weak_ptr_factory_(this) { base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( this, "cc::ResourcePool", task_runner_.get()); @@ -119,7 +154,8 @@ Resource* ResourcePool::ReuseResource(const gfx::Size& size, if (resource->format() != format) continue; - if (resource->size() != size) + if (!ResourceMeetsSizeRequirements(size, resource->size(), + disallow_non_exact_reuse_)) continue; if (resource->color_space() != color_space) continue; diff --git a/chromium/cc/resources/resource_pool.h b/chromium/cc/resources/resource_pool.h index 8932364ee4c..33291874d26 100644 --- a/chromium/cc/resources/resource_pool.h +++ b/chromium/cc/resources/resource_pool.h @@ -33,18 +33,22 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, ResourceProvider* resource_provider, base::SingleThreadTaskRunner* task_runner, gfx::BufferUsage usage, - const base::TimeDelta& expiration_delay) { + const base::TimeDelta& expiration_delay, + bool disallow_non_exact_reuse) { return base::WrapUnique(new ResourcePool(resource_provider, task_runner, - usage, expiration_delay)); + usage, expiration_delay, + disallow_non_exact_reuse)); } static std::unique_ptr<ResourcePool> Create( ResourceProvider* resource_provider, base::SingleThreadTaskRunner* task_runner, ResourceProvider::TextureHint hint, - const base::TimeDelta& expiration_delay) { + const base::TimeDelta& expiration_delay, + bool disallow_non_exact_reuse) { return base::WrapUnique(new ResourcePool(resource_provider, task_runner, - hint, expiration_delay)); + hint, expiration_delay, + disallow_non_exact_reuse)); } ~ResourcePool() override; @@ -102,16 +106,19 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, ResourcePool(ResourceProvider* resource_provider, base::SingleThreadTaskRunner* task_runner, gfx::BufferUsage usage, - const base::TimeDelta& expiration_delay); + const base::TimeDelta& expiration_delay, + bool disallow_non_exact_reuse); // Constructor for creating standard resources. ResourcePool(ResourceProvider* resource_provider, base::SingleThreadTaskRunner* task_runner, ResourceProvider::TextureHint hint, - const base::TimeDelta& expiration_delay); + const base::TimeDelta& expiration_delay, + bool disallow_non_exact_reuse); private: FRIEND_TEST_ALL_PREFIXES(ResourcePoolTest, ReuseResource); + FRIEND_TEST_ALL_PREFIXES(ResourcePoolTest, ExactRequestsRespected); class PoolResource : public ScopedResource { public: static std::unique_ptr<PoolResource> Create( @@ -186,6 +193,7 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, scoped_refptr<base::SingleThreadTaskRunner> task_runner_; bool evict_expired_resources_pending_ = false; const base::TimeDelta resource_expiration_delay_; + const bool disallow_non_exact_reuse_ = false; base::WeakPtrFactory<ResourcePool> weak_ptr_factory_; diff --git a/chromium/cc/resources/resource_pool_unittest.cc b/chromium/cc/resources/resource_pool_unittest.cc index aa8e853e0c4..34f7aca0c58 100644 --- a/chromium/cc/resources/resource_pool_unittest.cc +++ b/chromium/cc/resources/resource_pool_unittest.cc @@ -7,6 +7,7 @@ #include <stddef.h> #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/resources/resource_util.h" #include "cc/resources/scoped_resource.h" @@ -29,10 +30,16 @@ class ResourcePoolTest : public testing::Test { resource_pool_ = ResourcePool::Create(resource_provider_.get(), task_runner_.get(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, - ResourcePool::kDefaultExpirationDelay); + ResourcePool::kDefaultExpirationDelay, false); } protected: + void CheckAndReturnResource(Resource* resource) { + EXPECT_NE(nullptr, resource); + resource_pool_->ReleaseResource(resource); + resource_pool_->CheckBusyResources(); + } + scoped_refptr<TestContextProvider> context_provider_; std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_; std::unique_ptr<ResourceProvider> resource_provider_; @@ -105,32 +112,28 @@ TEST_F(ResourcePoolTest, SimpleResourceReuse) { gfx::ColorSpace color_space1; gfx::ColorSpace color_space2 = gfx::ColorSpace::CreateSRGB(); - Resource* resource = - resource_pool_->AcquireResource(size, format, color_space1); - resource_pool_->ReleaseResource(resource); - resource_pool_->CheckBusyResources(); + CheckAndReturnResource( + resource_pool_->AcquireResource(size, format, color_space1)); EXPECT_EQ(1u, resource_provider_->num_resources()); // Same size/format should re-use resource. - resource = resource_pool_->AcquireResource(size, format, color_space1); + Resource* resource = + resource_pool_->AcquireResource(size, format, color_space1); EXPECT_EQ(1u, resource_provider_->num_resources()); - resource_pool_->ReleaseResource(resource); - resource_pool_->CheckBusyResources(); + CheckAndReturnResource(resource); EXPECT_EQ(1u, resource_provider_->num_resources()); // Different size/format should allocate new resource. resource = resource_pool_->AcquireResource(gfx::Size(50, 50), LUMINANCE_8, color_space1); EXPECT_EQ(2u, resource_provider_->num_resources()); - resource_pool_->ReleaseResource(resource); - resource_pool_->CheckBusyResources(); + CheckAndReturnResource(resource); EXPECT_EQ(2u, resource_provider_->num_resources()); // Different color space should allocate new resource. resource = resource_pool_->AcquireResource(size, format, color_space2); EXPECT_EQ(3u, resource_provider_->num_resources()); - resource_pool_->ReleaseResource(resource); - resource_pool_->CheckBusyResources(); + CheckAndReturnResource(resource); EXPECT_EQ(3u, resource_provider_->num_resources()); } @@ -160,7 +163,7 @@ TEST_F(ResourcePoolTest, BusyResourcesEventuallyFreed) { resource_pool_ = ResourcePool::Create(resource_provider_.get(), task_runner_.get(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, - base::TimeDelta::FromMilliseconds(10)); + base::TimeDelta::FromMilliseconds(10), false); // Limits high enough to not be hit by this test. size_t bytes_limit = 10 * 1024 * 1024; @@ -201,7 +204,7 @@ TEST_F(ResourcePoolTest, UnusedResourcesEventuallyFreed) { resource_pool_ = ResourcePool::Create(resource_provider_.get(), task_runner_.get(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, - base::TimeDelta::FromMilliseconds(100)); + base::TimeDelta::FromMilliseconds(100), false); // Limits high enough to not be hit by this test. size_t bytes_limit = 10 * 1024 * 1024; @@ -317,25 +320,47 @@ TEST_F(ResourcePoolTest, ReuseResource) { ResourceFormat format = RGBA_8888; gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB(); - // Create unused resources with sizes close to 100, 100. - resource_pool_->ReleaseResource( - resource_pool_->CreateResource(gfx::Size(99, 100), format, color_space)); - resource_pool_->ReleaseResource( - resource_pool_->CreateResource(gfx::Size(99, 99), format, color_space)); - resource_pool_->ReleaseResource( - resource_pool_->CreateResource(gfx::Size(100, 99), format, color_space)); - resource_pool_->ReleaseResource( - resource_pool_->CreateResource(gfx::Size(101, 101), format, color_space)); - resource_pool_->CheckBusyResources(); - - gfx::Size size(100, 100); - Resource* resource = resource_pool_->ReuseResource(size, format, color_space); - EXPECT_EQ(nullptr, resource); - size = gfx::Size(100, 99); - resource = resource_pool_->ReuseResource(size, format, color_space); - EXPECT_NE(nullptr, resource); - ASSERT_EQ(nullptr, resource_pool_->ReuseResource(size, format, color_space)); - resource_pool_->ReleaseResource(resource); + // Create unused resource with size 100x100. + CheckAndReturnResource( + resource_pool_->CreateResource(gfx::Size(100, 100), format, color_space)); + + // Try some cases that are too large, none should succeed. + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(101, 100), format, + color_space)); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 101), format, + color_space)); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(90, 120), format, + color_space)); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(120, 120), format, + color_space)); + + // Try some cases that are more than 2x smaller than 100x100 in area and + // won't be re-used. + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(49, 100), format, + color_space)); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 49), format, + color_space)); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(50, 50), format, + color_space)); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(70, 70), format, + color_space)); + + // Try some cases that are smaller than 100x100, but within 2x area. Reuse + // should succeed. + CheckAndReturnResource( + resource_pool_->ReuseResource(gfx::Size(50, 100), format, color_space)); + CheckAndReturnResource( + resource_pool_->ReuseResource(gfx::Size(100, 50), format, color_space)); + CheckAndReturnResource( + resource_pool_->ReuseResource(gfx::Size(71, 71), format, color_space)); + + // 100x100 is an exact match and should succeed. A subsequent request for + // the same size should fail (the resource is already in use). + Resource* resource = + resource_pool_->ReuseResource(gfx::Size(100, 100), format, color_space); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 100), format, + color_space)); + CheckAndReturnResource(resource); } TEST_F(ResourcePoolTest, MemoryStateSuspended) { @@ -380,7 +405,7 @@ TEST_F(ResourcePoolTest, TextureHintRespected) { resource_pool_ = ResourcePool::Create(resource_provider_.get(), task_runner_.get(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, - base::TimeDelta::FromMilliseconds(100)); + base::TimeDelta::FromMilliseconds(100), false); Resource* resource = resource_pool_->AcquireResource(size, format, color_space); EXPECT_TRUE(resource_provider_->IsImmutable(resource->id())); @@ -389,10 +414,41 @@ TEST_F(ResourcePoolTest, TextureHintRespected) { resource_pool_ = ResourcePool::Create(resource_provider_.get(), task_runner_.get(), ResourceProvider::TEXTURE_HINT_DEFAULT, - base::TimeDelta::FromMilliseconds(100)); + base::TimeDelta::FromMilliseconds(100), false); resource = resource_pool_->AcquireResource(size, format, color_space); EXPECT_FALSE(resource_provider_->IsImmutable(resource->id())); resource_pool_->ReleaseResource(resource); } +TEST_F(ResourcePoolTest, ExactRequestsRespected) { + ResourceFormat format = RGBA_8888; + gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB(); + + resource_pool_ = + ResourcePool::Create(resource_provider_.get(), task_runner_.get(), + ResourceProvider::TEXTURE_HINT_DEFAULT, + base::TimeDelta::FromMilliseconds(100), true); + + // Create unused resource with size 100x100. + CheckAndReturnResource( + resource_pool_->CreateResource(gfx::Size(100, 100), format, color_space)); + + // Try some cases that are smaller than 100x100, but within 2x area which + // would typically allow reuse. Reuse should fail due to the . + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(50, 100), format, + color_space)); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 50), format, + color_space)); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(71, 71), format, + color_space)); + + // 100x100 is an exact match and should succeed. A subsequent request for + // the same size should fail (the resource is already in use). + Resource* resource = + resource_pool_->ReuseResource(gfx::Size(100, 100), format, color_space); + EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 100), format, + color_space)); + CheckAndReturnResource(resource); +} + } // namespace cc diff --git a/chromium/cc/resources/resource_provider.cc b/chromium/cc/resources/resource_provider.cc index 5275af92daf..8d5a3fce7fe 100644 --- a/chromium/cc/resources/resource_provider.cc +++ b/chromium/cc/resources/resource_provider.cc @@ -1623,7 +1623,7 @@ void ResourceProvider::ReceiveFromChild( // Don't allocate a texture for a child. resource->allocated = true; resource->imported_count = 1; - child_info.parent_to_child_map[local_id] = it->id; + resource->id_in_child = it->id; child_info.child_to_parent_map[it->id] = local_id; } } @@ -1835,9 +1835,8 @@ void ResourceProvider::DeleteAndReturnUnusedResourcesToChild( Resource& resource = it->second; DCHECK(!resource.locked_for_write); - DCHECK(child_info->parent_to_child_map.count(local_id)); - ResourceId child_id = child_info->parent_to_child_map[local_id]; + ResourceId child_id = resource.id_in_child; DCHECK(child_info->child_to_parent_map.count(child_id)); bool is_lost = resource.lost || @@ -1892,7 +1891,6 @@ void ResourceProvider::DeleteAndReturnUnusedResourcesToChild( } } - child_info->parent_to_child_map.erase(local_id); child_info->child_to_parent_map.erase(child_id); resource.imported_count = 0; DeleteResourceInternal(it, style); @@ -1924,8 +1922,7 @@ void ResourceProvider::DeleteAndReturnUnusedResourcesToChild( blocking_main_thread_task_runner_); if (child_info->marked_for_deletion && - child_info->parent_to_child_map.empty()) { - DCHECK(child_info->child_to_parent_map.empty()); + child_info->child_to_parent_map.empty()) { children_.erase(child_it); } } @@ -2177,8 +2174,8 @@ bool ResourceProvider::OnMemoryDump( base::trace_event::MemoryAllocatorDumpGuid guid; switch (resource.type) { case RESOURCE_TYPE_GPU_MEMORY_BUFFER: - guid = gfx::GetGpuMemoryBufferGUIDForTracing( - tracing_process_id, resource.gpu_memory_buffer->GetHandle().id); + guid = + resource.gpu_memory_buffer->GetGUIDForTracing(tracing_process_id); break; case RESOURCE_TYPE_GL_TEXTURE: DCHECK(resource.gl_id); diff --git a/chromium/cc/resources/resource_provider.h b/chromium/cc/resources/resource_provider.h index c323d4282ed..2b664ffd702 100644 --- a/chromium/cc/resources/resource_provider.h +++ b/chromium/cc/resources/resource_provider.h @@ -619,6 +619,7 @@ class CC_EXPORT ResourceProvider void WaitSyncToken(gpu::gles2::GLES2Interface* gl); int child_id; + ResourceId id_in_child; unsigned gl_id; // Pixel buffer used for set pixels without unnecessary copying. unsigned gl_pixel_buffer_id; @@ -696,7 +697,6 @@ class CC_EXPORT ResourceProvider ~Child(); ResourceIdMap child_to_parent_map; - ResourceIdMap parent_to_child_map; ReturnCallback return_callback; bool marked_for_deletion; bool needs_sync_tokens; @@ -800,7 +800,7 @@ class CC_EXPORT ResourceProvider // immediately. bool batch_return_resources_ = false; // Maps from a child id to the set of resources to be returned to it. - base::SmallMap<std::map<int, ResourceIdArray>> batched_returning_resources_; + base::small_map<std::map<int, ResourceIdArray>> batched_returning_resources_; base::ThreadChecker thread_checker_; diff --git a/chromium/cc/resources/ui_resource_bitmap.cc b/chromium/cc/resources/ui_resource_bitmap.cc index 4d8d2816175..0061e042118 100644 --- a/chromium/cc/resources/ui_resource_bitmap.cc +++ b/chromium/cc/resources/ui_resource_bitmap.cc @@ -36,23 +36,20 @@ UIResourceBitmap::UIResourceFormat SkColorTypeToUIResourceFormat( } // namespace void UIResourceBitmap::Create(sk_sp<SkPixelRef> pixel_ref, - const gfx::Size& size, + const SkImageInfo& info, UIResourceFormat format) { - DCHECK(size.width()); - DCHECK(size.height()); + DCHECK(info.width()); + DCHECK(info.height()); DCHECK(pixel_ref); DCHECK(pixel_ref->isImmutable()); format_ = format; - size_ = size; + info_ = info; pixel_ref_ = std::move(pixel_ref); - - // Default values for secondary parameters. - opaque_ = (format == ETC1); } void UIResourceBitmap::DrawToCanvas(SkCanvas* canvas, SkPaint* paint) { SkBitmap bitmap; - bitmap.setInfo(pixel_ref_.get()->info(), pixel_ref_.get()->rowBytes()); + bitmap.setInfo(info_, pixel_ref_.get()->rowBytes()); bitmap.setPixelRef(pixel_ref_, 0, 0); canvas->drawBitmap(bitmap, 0, 0, paint); canvas->flush(); @@ -63,11 +60,8 @@ UIResourceBitmap::UIResourceBitmap(const SkBitmap& skbitmap) { DCHECK(skbitmap.isImmutable()); sk_sp<SkPixelRef> pixel_ref = sk_ref_sp(skbitmap.pixelRef()); - const SkImageInfo& info = pixel_ref->info(); - Create(std::move(pixel_ref), gfx::Size(info.width(), info.height()), + Create(std::move(pixel_ref), skbitmap.info(), SkColorTypeToUIResourceFormat(skbitmap.colorType())); - - SetOpaque(skbitmap.isOpaque()); } UIResourceBitmap::UIResourceBitmap(const gfx::Size& size, bool is_opaque) { @@ -77,30 +71,17 @@ UIResourceBitmap::UIResourceBitmap(const gfx::Size& size, bool is_opaque) { sk_sp<SkPixelRef> pixel_ref( SkMallocPixelRef::MakeAllocate(info, info.minRowBytes(), NULL)); pixel_ref->setImmutable(); - Create(std::move(pixel_ref), size, UIResourceBitmap::RGBA8); - SetOpaque(is_opaque); + Create(std::move(pixel_ref), info, UIResourceBitmap::RGBA8); } UIResourceBitmap::UIResourceBitmap(sk_sp<SkPixelRef> pixel_ref, const gfx::Size& size) { - Create(std::move(pixel_ref), size, UIResourceBitmap::ETC1); + SkImageInfo info = + SkImageInfo::MakeN32(size.width(), size.height(), kOpaque_SkAlphaType); + Create(std::move(pixel_ref), info, UIResourceBitmap::ETC1); } UIResourceBitmap::UIResourceBitmap(const UIResourceBitmap& other) = default; UIResourceBitmap::~UIResourceBitmap() {} - -AutoLockUIResourceBitmap::AutoLockUIResourceBitmap( - const UIResourceBitmap& bitmap) : bitmap_(bitmap) { - bitmap_.pixel_ref_->lockPixels(); -} - -AutoLockUIResourceBitmap::~AutoLockUIResourceBitmap() { - bitmap_.pixel_ref_->unlockPixels(); -} - -const uint8_t* AutoLockUIResourceBitmap::GetPixels() const { - return static_cast<const uint8_t*>(bitmap_.pixel_ref_->pixels()); -} - } // namespace cc diff --git a/chromium/cc/resources/ui_resource_bitmap.h b/chromium/cc/resources/ui_resource_bitmap.h index 66a8a38d1cf..1c058e0a6f1 100644 --- a/chromium/cc/resources/ui_resource_bitmap.h +++ b/chromium/cc/resources/ui_resource_bitmap.h @@ -32,10 +32,9 @@ class CC_EXPORT UIResourceBitmap { ETC1 }; - gfx::Size GetSize() const { return size_; } + gfx::Size GetSize() const { return gfx::Size(info_.width(), info_.height()); } UIResourceFormat GetFormat() const { return format_; } - bool GetOpaque() const { return opaque_; } - void SetOpaque(bool opaque) { opaque_ = opaque; } + bool GetOpaque() const { return info_.isOpaque(); } // Draw the UIResourceBitmap onto the provided |canvas| using the style // information specified by |paint|. @@ -51,30 +50,23 @@ class CC_EXPORT UIResourceBitmap { // Returns the memory usage of the bitmap. size_t EstimateMemoryUsage() const { - return pixel_ref_ ? pixel_ref_->rowBytes() * size_.height() : 0; + return pixel_ref_ ? pixel_ref_->rowBytes() * info_.height() : 0; + } + + const uint8_t* GetPixels() const { + return static_cast<const uint8_t*>(pixel_ref_->pixels()); } private: friend class AutoLockUIResourceBitmap; void Create(sk_sp<SkPixelRef> pixel_ref, - const gfx::Size& size, + const SkImageInfo& info, UIResourceFormat format); sk_sp<SkPixelRef> pixel_ref_; UIResourceFormat format_; - gfx::Size size_; - bool opaque_; -}; - -class CC_EXPORT AutoLockUIResourceBitmap { - public: - explicit AutoLockUIResourceBitmap(const UIResourceBitmap& bitmap); - ~AutoLockUIResourceBitmap(); - const uint8_t* GetPixels() const; - - private: - const UIResourceBitmap& bitmap_; + SkImageInfo info_; }; } // namespace cc diff --git a/chromium/cc/resources/video_resource_updater.cc b/chromium/cc/resources/video_resource_updater.cc index 1c53b32da57..9e697580d3f 100644 --- a/chromium/cc/resources/video_resource_updater.cc +++ b/chromium/cc/resources/video_resource_updater.cc @@ -174,8 +174,8 @@ VideoFrameExternalResources::~VideoFrameExternalResources() {} VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider, ResourceProvider* resource_provider) : context_provider_(context_provider), - resource_provider_(resource_provider) { -} + resource_provider_(resource_provider), + weak_ptr_factory_(this) {} VideoResourceUpdater::~VideoResourceUpdater() { for (const PlaneResource& plane_resource : all_resources_) @@ -418,8 +418,9 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( if (software_compositor) { external_resources.software_resources.push_back( plane_resource.resource_id()); - external_resources.software_release_callback = base::Bind( - &RecycleResource, AsWeakPtr(), plane_resource.resource_id()); + external_resources.software_release_callback = + base::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(), + plane_resource.resource_id()); external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; } else { // VideoResourceUpdater shares a context with the compositor so @@ -429,8 +430,9 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( plane_resource.resource_id())); mailbox.set_color_space(output_color_space); external_resources.mailboxes.push_back(mailbox); - external_resources.release_callbacks.push_back(base::Bind( - &RecycleResource, AsWeakPtr(), plane_resource.resource_id())); + external_resources.release_callbacks.push_back( + base::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(), + plane_resource.resource_id())); external_resources.type = VideoFrameExternalResources::RGBA_RESOURCE; } return external_resources; @@ -532,8 +534,9 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( plane_resource.resource_id())); mailbox.set_color_space(output_color_space); external_resources.mailboxes.push_back(mailbox); - external_resources.release_callbacks.push_back(base::Bind( - &RecycleResource, AsWeakPtr(), plane_resource.resource_id())); + external_resources.release_callbacks.push_back( + base::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(), + plane_resource.resource_id())); } external_resources.type = VideoFrameExternalResources::YUV_RESOURCE; @@ -609,7 +612,8 @@ void VideoResourceUpdater::CopyPlaneTexture( external_resources->mailboxes.push_back(mailbox); external_resources->release_callbacks.push_back( - base::Bind(&RecycleResource, AsWeakPtr(), resource->resource_id())); + base::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(), + resource->resource_id())); } VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( @@ -660,8 +664,8 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( media::VideoFrameMetadata::WANTS_PROMOTION_HINT)); #endif external_resources.mailboxes.push_back(mailbox); - external_resources.release_callbacks.push_back( - base::Bind(&ReturnTexture, AsWeakPtr(), video_frame)); + external_resources.release_callbacks.push_back(base::Bind( + &ReturnTexture, weak_ptr_factory_.GetWeakPtr(), video_frame)); } } return external_resources; diff --git a/chromium/cc/resources/video_resource_updater.h b/chromium/cc/resources/video_resource_updater.h index 603b4a35d09..f5f949c7d6d 100644 --- a/chromium/cc/resources/video_resource_updater.h +++ b/chromium/cc/resources/video_resource_updater.h @@ -70,8 +70,7 @@ class CC_EXPORT VideoFrameExternalResources { // VideoResourceUpdater is used by the video system to produce frame content as // resources consumable by the compositor. -class CC_EXPORT VideoResourceUpdater - : public base::SupportsWeakPtr<VideoResourceUpdater> { +class CC_EXPORT VideoResourceUpdater { public: VideoResourceUpdater(ContextProvider* context_provider, ResourceProvider* resource_provider); @@ -183,6 +182,8 @@ class CC_EXPORT VideoResourceUpdater // data transfers. ResourceList all_resources_; + base::WeakPtrFactory<VideoResourceUpdater> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(VideoResourceUpdater); }; diff --git a/chromium/cc/scheduler/begin_frame_source.cc b/chromium/cc/scheduler/begin_frame_source.cc index 4db7202a02c..be6394e617a 100644 --- a/chromium/cc/scheduler/begin_frame_source.cc +++ b/chromium/cc/scheduler/begin_frame_source.cc @@ -27,9 +27,9 @@ static const double kDoubleTickDivisor = 2.0; } // BeginFrameObserverBase ------------------------------------------------- -BeginFrameObserverBase::BeginFrameObserverBase() - : last_begin_frame_args_(), dropped_begin_frame_args_(0) { -} +BeginFrameObserverBase::BeginFrameObserverBase() = default; + +BeginFrameObserverBase::~BeginFrameObserverBase() = default; const BeginFrameArgs& BeginFrameObserverBase::LastUsedBeginFrameArgs() const { return last_begin_frame_args_; @@ -50,6 +50,15 @@ void BeginFrameObserverBase::OnBeginFrame(const BeginFrameArgs& args) { } } +void BeginFrameObserverBase::AsValueInto( + base::trace_event::TracedValue* state) const { + state->SetInteger("dropped_begin_frame_args", dropped_begin_frame_args_); + + state->BeginDictionary("last_begin_frame_args"); + last_begin_frame_args_.AsValueInto(state); + state->EndDictionary(); +} + // BeginFrameSource ------------------------------------------------------- namespace { static base::StaticAtomicSequenceNumber g_next_source_id; @@ -57,8 +66,11 @@ static base::StaticAtomicSequenceNumber g_next_source_id; BeginFrameSource::BeginFrameSource() : source_id_(g_next_source_id.GetNext()) {} -uint32_t BeginFrameSource::source_id() const { - return source_id_; +BeginFrameSource::~BeginFrameSource() = default; + +void BeginFrameSource::AsValueInto( + base::trace_event::TracedValue* state) const { + state->SetInteger("source_id", source_id_); } // StubBeginFrameSource --------------------------------------------------- @@ -240,100 +252,6 @@ void DelayBasedBeginFrameSource::OnTimerTick() { } } -// BeginFrameObserverAckTracker ------------------------------------------- -BeginFrameObserverAckTracker::BeginFrameObserverAckTracker() - : current_source_id_(0), - current_sequence_number_(BeginFrameArgs::kStartingFrameNumber), - observers_had_damage_(false) {} - -BeginFrameObserverAckTracker::~BeginFrameObserverAckTracker() {} - -void BeginFrameObserverAckTracker::OnBeginFrame(const BeginFrameArgs& args) { - if (current_source_id_ != args.source_id) - SourceChanged(args); - - DCHECK_GE(args.sequence_number, current_sequence_number_); - // Reset for new BeginFrame. - current_sequence_number_ = args.sequence_number; - finished_observers_.clear(); - observers_had_damage_ = false; -} - -void BeginFrameObserverAckTracker::SourceChanged(const BeginFrameArgs& args) { - current_source_id_ = args.source_id; - current_sequence_number_ = args.sequence_number; - - // Mark all observers invalid: We report an invalid frame until every observer - // has confirmed the frame. - for (auto& entry : latest_confirmed_sequence_numbers_) - entry.second = BeginFrameArgs::kInvalidFrameNumber; -} - -void BeginFrameObserverAckTracker::OnObserverFinishedFrame( - BeginFrameObserver* obs, - const BeginFrameAck& ack) { - if (ack.source_id != current_source_id_) - return; - - DCHECK_LE(ack.sequence_number, current_sequence_number_); - if (ack.sequence_number != current_sequence_number_) - return; - - finished_observers_.insert(obs); - observers_had_damage_ |= ack.has_damage; - - // We max() with the current value in |latest_confirmed_sequence_numbers_| to - // handle situations where an observer just started observing (again) and may - // acknowledge with an ancient latest_confirmed_sequence_number. - latest_confirmed_sequence_numbers_[obs] = - std::max(ack.latest_confirmed_sequence_number, - latest_confirmed_sequence_numbers_[obs]); -} - -void BeginFrameObserverAckTracker::OnObserverAdded(BeginFrameObserver* obs) { - observers_.insert(obs); - - // Since the observer didn't want BeginFrames before, we consider it - // up-to-date up to the last BeginFrame, except if it already handled the - // current BeginFrame. In which case, we consider it up-to-date up to the - // current one. - DCHECK_LT(BeginFrameArgs::kInvalidFrameNumber, current_sequence_number_); - const BeginFrameArgs& last_args = obs->LastUsedBeginFrameArgs(); - if (last_args.IsValid() && - last_args.sequence_number == current_sequence_number_ && - last_args.source_id == current_source_id_) { - latest_confirmed_sequence_numbers_[obs] = current_sequence_number_; - finished_observers_.insert(obs); - } else { - latest_confirmed_sequence_numbers_[obs] = current_sequence_number_ - 1; - } -} - -void BeginFrameObserverAckTracker::OnObserverRemoved(BeginFrameObserver* obs) { - observers_.erase(obs); - finished_observers_.erase(obs); - latest_confirmed_sequence_numbers_.erase(obs); -} - -bool BeginFrameObserverAckTracker::AllObserversFinishedFrame() const { - if (finished_observers_.size() < observers_.size()) - return false; - return base::STLIncludes(finished_observers_, observers_); -} - -bool BeginFrameObserverAckTracker::AnyObserversHadDamage() const { - return observers_had_damage_; -} - -uint64_t BeginFrameObserverAckTracker::LatestConfirmedSequenceNumber() const { - uint64_t latest_confirmed_sequence_number = current_sequence_number_; - for (const auto& entry : latest_confirmed_sequence_numbers_) { - latest_confirmed_sequence_number = - std::min(latest_confirmed_sequence_number, entry.second); - } - return latest_confirmed_sequence_number; -} - // ExternalBeginFrameSource ----------------------------------------------- ExternalBeginFrameSource::ExternalBeginFrameSource( ExternalBeginFrameSourceClient* client) @@ -343,28 +261,41 @@ ExternalBeginFrameSource::ExternalBeginFrameSource( ExternalBeginFrameSource::~ExternalBeginFrameSource() = default; +void ExternalBeginFrameSource::AsValueInto( + base::trace_event::TracedValue* state) const { + BeginFrameSource::AsValueInto(state); + + state->SetBoolean("paused", paused_); + state->SetInteger("num_observers", observers_.size()); + + state->BeginDictionary("last_begin_frame_args"); + last_begin_frame_args_.AsValueInto(state); + state->EndDictionary(); +} + void ExternalBeginFrameSource::AddObserver(BeginFrameObserver* obs) { DCHECK(obs); DCHECK(observers_.find(obs) == observers_.end()); bool observers_was_empty = observers_.empty(); observers_.insert(obs); - ack_tracker_.OnObserverAdded(obs); obs->OnBeginFrameSourcePausedChanged(paused_); if (observers_was_empty) client_->OnNeedsBeginFrames(true); // Send a MISSED begin frame if necessary. - if (missed_begin_frame_args_.IsValid()) { + if (last_begin_frame_args_.IsValid()) { const BeginFrameArgs& last_args = obs->LastUsedBeginFrameArgs(); if (!last_args.IsValid() || - (missed_begin_frame_args_.frame_time > last_args.frame_time)) { - DCHECK((missed_begin_frame_args_.source_id != last_args.source_id) || - (missed_begin_frame_args_.sequence_number > - last_args.sequence_number)) - << "current " << missed_begin_frame_args_.AsValue()->ToString() + (last_begin_frame_args_.frame_time > last_args.frame_time)) { + DCHECK( + (last_begin_frame_args_.source_id != last_args.source_id) || + (last_begin_frame_args_.sequence_number > last_args.sequence_number)) + << "current " << last_begin_frame_args_.AsValue()->ToString() << ", last " << last_args.AsValue()->ToString(); - obs->OnBeginFrame(missed_begin_frame_args_); + BeginFrameArgs missed_args = last_begin_frame_args_; + missed_args.type = BeginFrameArgs::MISSED; + obs->OnBeginFrame(missed_args); } } } @@ -374,19 +305,14 @@ void ExternalBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) { DCHECK(observers_.find(obs) != observers_.end()); observers_.erase(obs); - ack_tracker_.OnObserverRemoved(obs); - MaybeFinishFrame(); if (observers_.empty()) { - missed_begin_frame_args_ = BeginFrameArgs(); + last_begin_frame_args_ = BeginFrameArgs(); client_->OnNeedsBeginFrames(false); } } void ExternalBeginFrameSource::DidFinishFrame(BeginFrameObserver* obs, - const BeginFrameAck& ack) { - ack_tracker_.OnObserverFinishedFrame(obs, ack); - MaybeFinishFrame(); -} + const BeginFrameAck& ack) {} bool ExternalBeginFrameSource::IsThrottled() const { return true; @@ -402,13 +328,7 @@ void ExternalBeginFrameSource::OnSetBeginFrameSourcePaused(bool paused) { } void ExternalBeginFrameSource::OnBeginFrame(const BeginFrameArgs& args) { - if (frame_active_) - FinishFrame(); - - frame_active_ = true; - missed_begin_frame_args_ = args; - missed_begin_frame_args_.type = BeginFrameArgs::MISSED; - ack_tracker_.OnBeginFrame(args); + last_begin_frame_args_ = args; std::unordered_set<BeginFrameObserver*> observers(observers_); for (auto* obs : observers) { // It is possible that the source in which |args| originate changes, or that @@ -423,23 +343,6 @@ void ExternalBeginFrameSource::OnBeginFrame(const BeginFrameArgs& args) { obs->OnBeginFrame(args); } } - MaybeFinishFrame(); -} - -void ExternalBeginFrameSource::MaybeFinishFrame() { - if (!frame_active_ || !ack_tracker_.AllObserversFinishedFrame()) - return; - FinishFrame(); -} - -void ExternalBeginFrameSource::FinishFrame() { - frame_active_ = false; - - BeginFrameAck ack(missed_begin_frame_args_.source_id, - missed_begin_frame_args_.sequence_number, - ack_tracker_.LatestConfirmedSequenceNumber(), - ack_tracker_.AnyObserversHadDamage()); - client_->OnDidFinishFrame(ack); } } // namespace cc diff --git a/chromium/cc/scheduler/begin_frame_source.h b/chromium/cc/scheduler/begin_frame_source.h index 9d5ce242b27..98ea3771b39 100644 --- a/chromium/cc/scheduler/begin_frame_source.h +++ b/chromium/cc/scheduler/begin_frame_source.h @@ -74,6 +74,7 @@ class CC_EXPORT BeginFrameObserver { class CC_EXPORT BeginFrameObserverBase : public BeginFrameObserver { public: BeginFrameObserverBase(); + ~BeginFrameObserverBase() override; // BeginFrameObserver @@ -84,12 +85,13 @@ class CC_EXPORT BeginFrameObserverBase : public BeginFrameObserver { const BeginFrameArgs& LastUsedBeginFrameArgs() const override; protected: - // Subclasses should override this method! // Return true if the given argument is (or will be) used. virtual bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) = 0; + void AsValueInto(base::trace_event::TracedValue* state) const; + BeginFrameArgs last_begin_frame_args_; - int64_t dropped_begin_frame_args_; + int64_t dropped_begin_frame_args_ = 0; private: DISALLOW_COPY_AND_ASSIGN(BeginFrameObserverBase); @@ -107,7 +109,13 @@ class CC_EXPORT BeginFrameObserverBase : public BeginFrameObserver { class CC_EXPORT BeginFrameSource { public: BeginFrameSource(); - virtual ~BeginFrameSource() {} + virtual ~BeginFrameSource(); + + // Returns an identifier for this BeginFrameSource. Guaranteed unique within a + // process, but not across processes. This is used to create BeginFrames that + // originate at this source. Note that BeginFrameSources may pass on + // BeginFrames created by other sources, with different IDs. + uint32_t source_id() const { return source_id_; } // BeginFrameObservers use DidFinishFrame to acknowledge that they have // completed handling a BeginFrame. @@ -138,14 +146,12 @@ class CC_EXPORT BeginFrameSource { // begin frames without waiting. virtual bool IsThrottled() const = 0; - // Returns an identifier for this BeginFrameSource. Guaranteed unique within a - // process, but not across processes. This is used to create BeginFrames that - // originate at this source. Note that BeginFrameSources may pass on - // BeginFrames created by other sources, with different IDs. - uint32_t source_id() const; + virtual void AsValueInto(base::trace_event::TracedValue* state) const; private: uint32_t source_id_; + + DISALLOW_COPY_AND_ASSIGN(BeginFrameSource); }; // A BeginFrameSource that does nothing. @@ -241,50 +247,10 @@ class CC_EXPORT DelayBasedBeginFrameSource : public SyntheticBeginFrameSource, DISALLOW_COPY_AND_ASSIGN(DelayBasedBeginFrameSource); }; -// Helper class that tracks outstanding acknowledgments from -// BeginFrameObservers. -class CC_EXPORT BeginFrameObserverAckTracker { - public: - BeginFrameObserverAckTracker(); - virtual ~BeginFrameObserverAckTracker(); - - // The BeginFrameSource uses these methods to notify us when a BeginFrame was - // started, an observer finished a frame, or an observer was added/removed. - void OnBeginFrame(const BeginFrameArgs& args); - void OnObserverFinishedFrame(BeginFrameObserver* obs, - const BeginFrameAck& ack); - void OnObserverAdded(BeginFrameObserver* obs); - void OnObserverRemoved(BeginFrameObserver* obs); - - // Returns |true| if all the source's observers completed the current frame. - bool AllObserversFinishedFrame() const; - - // Returns |true| if any observer had damage during the current frame. - bool AnyObserversHadDamage() const; - - // Return the sequence number of the latest frame that all active observers - // have confirmed. - uint64_t LatestConfirmedSequenceNumber() const; - - private: - void SourceChanged(const BeginFrameArgs& args); - - uint32_t current_source_id_; - uint64_t current_sequence_number_; - // Small sets, but order matters for intersection computation. - base::flat_set<BeginFrameObserver*> observers_; - base::flat_set<BeginFrameObserver*> finished_observers_; - bool observers_had_damage_; - base::SmallMap<std::map<BeginFrameObserver*, uint64_t>, 4> - latest_confirmed_sequence_numbers_; -}; - class CC_EXPORT ExternalBeginFrameSourceClient { public: // Only called when changed. Assumed false by default. virtual void OnNeedsBeginFrames(bool needs_begin_frames) = 0; - // Called when all observers have completed a frame. - virtual void OnDidFinishFrame(const BeginFrameAck& ack) = 0; }; // A BeginFrameSource that is only ticked manually. Usually the endpoint @@ -303,20 +269,16 @@ class CC_EXPORT ExternalBeginFrameSource : public BeginFrameSource { void DidFinishFrame(BeginFrameObserver* obs, const BeginFrameAck& ack) override; bool IsThrottled() const override; + void AsValueInto(base::trace_event::TracedValue* state) const override; void OnSetBeginFrameSourcePaused(bool paused); void OnBeginFrame(const BeginFrameArgs& args); protected: - void MaybeFinishFrame(); - void FinishFrame(); - - BeginFrameArgs missed_begin_frame_args_; + BeginFrameArgs last_begin_frame_args_; std::unordered_set<BeginFrameObserver*> observers_; ExternalBeginFrameSourceClient* client_; bool paused_ = false; - bool frame_active_ = false; - BeginFrameObserverAckTracker ack_tracker_; private: DISALLOW_COPY_AND_ASSIGN(ExternalBeginFrameSource); diff --git a/chromium/cc/scheduler/begin_frame_source_unittest.cc b/chromium/cc/scheduler/begin_frame_source_unittest.cc index a15c4be4ebb..a6884d35cf7 100644 --- a/chromium/cc/scheduler/begin_frame_source_unittest.cc +++ b/chromium/cc/scheduler/begin_frame_source_unittest.cc @@ -14,7 +14,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using testing::StrictMock; +using testing::NiceMock; namespace cc { namespace { @@ -44,7 +44,7 @@ class BackToBackBeginFrameSourceTest : public ::testing::Test { new TestDelayBasedTimeSource(now_src_.get(), task_runner_.get())); delay_based_time_source_ = time_source.get(); source_.reset(new BackToBackBeginFrameSource(std::move(time_source))); - obs_ = base::WrapUnique(new ::testing::StrictMock<MockBeginFrameObserver>); + obs_ = base::WrapUnique(new ::testing::NiceMock<MockBeginFrameObserver>); } void TearDown() override { obs_.reset(); } @@ -230,7 +230,7 @@ TEST_F(BackToBackBeginFrameSourceTest, DelayInPostedTaskProducesCorrectFrame) { } TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversSynchronized) { - StrictMock<MockBeginFrameObserver> obs1, obs2; + NiceMock<MockBeginFrameObserver> obs1, obs2; EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); source_->AddObserver(&obs1); @@ -266,7 +266,7 @@ TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversSynchronized) { } TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversInterleaved) { - StrictMock<MockBeginFrameObserver> obs1, obs2; + NiceMock<MockBeginFrameObserver> obs1, obs2; EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); source_->AddObserver(&obs1); @@ -310,7 +310,7 @@ TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversInterleaved) { } TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversAtOnce) { - StrictMock<MockBeginFrameObserver> obs1, obs2; + NiceMock<MockBeginFrameObserver> obs1, obs2; EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false); EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false); @@ -462,7 +462,7 @@ TEST_F(DelayBasedBeginFrameSourceTest, AuthoritativeVSyncChanges) { } TEST_F(DelayBasedBeginFrameSourceTest, MultipleObservers) { - StrictMock<MockBeginFrameObserver> obs1, obs2; + NiceMock<MockBeginFrameObserver> obs1, obs2; // now_src_ starts off at 1000. task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(9010)); @@ -497,7 +497,7 @@ TEST_F(DelayBasedBeginFrameSourceTest, MultipleObservers) { } TEST_F(DelayBasedBeginFrameSourceTest, DoubleTick) { - StrictMock<MockBeginFrameObserver> obs; + NiceMock<MockBeginFrameObserver> obs; EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000, 10000); @@ -519,7 +519,7 @@ TEST_F(DelayBasedBeginFrameSourceTest, DoubleTick) { } TEST_F(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) { - StrictMock<MockBeginFrameObserver> obs; + NiceMock<MockBeginFrameObserver> obs; EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false); EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000, 10000); @@ -550,224 +550,11 @@ TEST_F(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) { source_->RemoveObserver(&obs); } -// BeginFrameObserverAckTracker testing ---------------------------------------- -class TestBeginFrameConsumer : public BeginFrameObserverBase { - private: - bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) override { - // Consume the args. - return true; - } - void OnBeginFrameSourcePausedChanged(bool paused) override {} -}; - -// Use EXPECT_TRUE instead of EXPECT_EQ for |finished| and |damage| as gcc 4.7 -// issues the following warning on EXPECT_EQ(false, x), which is turned into an -// error with -Werror=conversion-null: -// -// converting 'false' to pointer type for argument 1 of -// 'char testing::internal::IsNullLiteralHelper(testing::internal::Secret*)' -#define EXPECT_ACK_TRACKER_STATE(finished, damage, latest_confirmed) \ - EXPECT_TRUE(finished == tracker_->AllObserversFinishedFrame()) \ - << "expected: " << finished; \ - EXPECT_TRUE(damage == tracker_->AnyObserversHadDamage()) << "expected: " \ - << damage; \ - EXPECT_EQ(latest_confirmed, tracker_->LatestConfirmedSequenceNumber()) - -class BeginFrameObserverAckTrackerTest : public ::testing::Test { - public: - BeginFrameArgs current_args_; - std::unique_ptr<BeginFrameObserverAckTracker> tracker_; - TestBeginFrameConsumer obs1_; - TestBeginFrameConsumer obs2_; - - void SetUp() override { - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - tracker_.reset(new BeginFrameObserverAckTracker()); - } -}; - -TEST_F(BeginFrameObserverAckTrackerTest, CorrectnessWithoutObservers) { - // Check initial state. - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // A new BeginFrame is immediately finished and confirmed. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(true, false, 2u); -} - -TEST_F(BeginFrameObserverAckTrackerTest, CorrectnessWith1Observer) { - // Check initial state. - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // After adding an observer, the BeginFrame is not finished or confirmed. - tracker_->OnObserverAdded(&obs1_); - EXPECT_ACK_TRACKER_STATE(false, false, 0u); // up to date to previous frame. - - // On removing it, the BeginFrame is back to original state. - tracker_->OnObserverRemoved(&obs1_); - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // After adding it back, the BeginFrame is again not finished or confirmed. - tracker_->OnObserverAdded(&obs1_); - EXPECT_ACK_TRACKER_STATE(false, false, 0u); // up to date to previous frame. - - // When the observer finishes and confirms, the BeginFrame is finished - // and confirmed. - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 1, 1, false)); - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // A new BeginFrame is initially not finished or confirmed. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(false, false, 1u); - - // Stray ACK for an old BeginFrame is ignored. - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 1, 1, false)); - EXPECT_ACK_TRACKER_STATE(false, false, 1u); - - // When the observer finishes but doesn't confirm, the BeginFrame is finished - // but not confirmed. - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 2, 1, false)); - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // Damage from ACK propagates. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(false, false, 1u); - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 3, 3, true)); - EXPECT_ACK_TRACKER_STATE(true, true, 3u); - - // Removing an out-of-date observer confirms the latest BeginFrame. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 4); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(false, false, 3u); - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 4, 3, false)); - EXPECT_ACK_TRACKER_STATE(true, false, 3u); - tracker_->OnObserverRemoved(&obs1_); - EXPECT_ACK_TRACKER_STATE(true, false, 4u); -} - -TEST_F(BeginFrameObserverAckTrackerTest, CorrectnessWith2Observers) { - // Check initial state. - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // After adding observers, the BeginFrame is not finished or confirmed. - tracker_->OnObserverAdded(&obs1_); - EXPECT_ACK_TRACKER_STATE(false, false, 0u); // up to date to previous frame. - tracker_->OnObserverAdded(&obs2_); - EXPECT_ACK_TRACKER_STATE(false, false, 0u); // up to date to previous frame. - - // Removing one of them changes nothing. Same for adding back. - tracker_->OnObserverRemoved(&obs1_); - EXPECT_ACK_TRACKER_STATE(false, false, 0u); - tracker_->OnObserverAdded(&obs1_); - EXPECT_ACK_TRACKER_STATE(false, false, 0u); - - // When one observer finishes and confirms, nothing changes. - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 1, 1, false)); - EXPECT_ACK_TRACKER_STATE(false, false, 0u); - // When both finish and confirm, the BeginFrame is finished and confirmed. - obs2_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs2_, BeginFrameAck(0, 1, 1, false)); - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // A new BeginFrame is not finished or confirmed. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(false, false, 1u); - - // When both observers finish but only one confirms, the BeginFrame is - // finished but not confirmed. - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 2, 2, false)); - EXPECT_ACK_TRACKER_STATE(false, false, 1u); - obs2_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs2_, BeginFrameAck(0, 2, 1, false)); - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // With reversed confirmations in the next ACKs, the latest confirmed frame - // increases but the latest BeginFrame remains unconfirmed. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(false, false, 1u); - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 3, 2, false)); - EXPECT_ACK_TRACKER_STATE(false, false, 1u); - obs2_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs2_, BeginFrameAck(0, 3, 3, false)); - EXPECT_ACK_TRACKER_STATE(true, false, 2u); - - // Only a single ACK with damage suffices. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 4); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(false, false, 2u); - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 4, 4, true)); - EXPECT_ACK_TRACKER_STATE(false, true, 3u); - obs2_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs2_, BeginFrameAck(0, 4, 4, false)); - EXPECT_ACK_TRACKER_STATE(true, true, 4u); - - // Removing the damaging observer makes no difference in this case. - tracker_->OnObserverRemoved(&obs1_); - EXPECT_ACK_TRACKER_STATE(true, true, 4u); - - // Adding the observer back considers it up to date up to the current - // BeginFrame, because it is the last used one. Thus, the current BeginFrame - // is still finished, too. - tracker_->OnObserverAdded(&obs1_); - EXPECT_ACK_TRACKER_STATE(true, true, 4u); - - // Adding the observer back after the next BeginFrame considers it up to date - // up to last BeginFrame only. - tracker_->OnObserverRemoved(&obs1_); - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 5); - tracker_->OnBeginFrame(current_args_); - tracker_->OnObserverAdded(&obs1_); - EXPECT_ACK_TRACKER_STATE(false, false, 4u); - // Both observers need to finish for the BeginFrame to be finished. - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(0, 5, 5, false)); - EXPECT_ACK_TRACKER_STATE(false, false, 4u); - obs2_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs2_, BeginFrameAck(0, 5, 5, false)); - EXPECT_ACK_TRACKER_STATE(true, false, 5u); -} - -TEST_F(BeginFrameObserverAckTrackerTest, ChangingSourceIdOnBeginFrame) { - // Check initial state. - EXPECT_ACK_TRACKER_STATE(true, false, 1u); - - // Changing source id without observer updates confirmed BeginFrame. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 1, 10); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(true, false, 10u); - - // Setup an observer for current BeginFrame. - tracker_->OnObserverAdded(&obs1_); - EXPECT_ACK_TRACKER_STATE(false, false, 9u); // up to date to previous frame. - obs1_.OnBeginFrame(current_args_); - tracker_->OnObserverFinishedFrame(&obs1_, BeginFrameAck(1, 10, 10, true)); - EXPECT_ACK_TRACKER_STATE(true, true, 10u); - - // Changing source id with an observer sets confirmed BeginFrame to invalid. - current_args_ = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 2, 20); - tracker_->OnBeginFrame(current_args_); - EXPECT_ACK_TRACKER_STATE(false, false, BeginFrameArgs::kInvalidFrameNumber); -} - // ExternalBeginFrameSource testing -------------------------------------------- class MockExternalBeginFrameSourceClient : public ExternalBeginFrameSourceClient { public: MOCK_METHOD1(OnNeedsBeginFrames, void(bool)); - MOCK_METHOD1(OnDidFinishFrame, void(const BeginFrameAck&)); }; class ExternalBeginFrameSourceTest : public ::testing::Test { @@ -788,69 +575,6 @@ class ExternalBeginFrameSourceTest : public ::testing::Test { } }; -TEST_F(ExternalBeginFrameSourceTest, CallsOnDidFinishFrameWithoutObservers) { - EXPECT_CALL((*client_), OnDidFinishFrame(BeginFrameAck(0, 2, 2, false))) - .Times(1); - source_->OnBeginFrame(CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 0, 2, base::TimeTicks::FromInternalValue(10000))); -} - -TEST_F(ExternalBeginFrameSourceTest, - CallsOnDidFinishFrameWhenObserverFinishes) { - EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); - EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1); - source_->AddObserver(obs_.get()); - - BeginFrameArgs args = CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 0, 2, base::TimeTicks::FromInternalValue(10000)); - EXPECT_BEGIN_FRAME_ARGS_USED(*obs_, args); - source_->OnBeginFrame(args); - - EXPECT_CALL((*client_), OnDidFinishFrame(BeginFrameAck(0, 2, 2, true))) - .Times(1); - source_->DidFinishFrame(obs_.get(), BeginFrameAck(0, 2, 2, true)); - - args = CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 0, 3, base::TimeTicks::FromInternalValue(20000)); - EXPECT_BEGIN_FRAME_ARGS_USED(*obs_, args); - source_->OnBeginFrame(args); - - EXPECT_CALL((*client_), OnDidFinishFrame(BeginFrameAck(0, 3, 2, false))) - .Times(1); - source_->DidFinishFrame(obs_.get(), BeginFrameAck(0, 3, 2, false)); -} - -TEST_F(ExternalBeginFrameSourceTest, - CallsOnDidFinishFrameWhenObserverDropsBeginFrame) { - EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); - EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1); - source_->AddObserver(obs_.get()); - - BeginFrameArgs args = CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 0, 2, base::TimeTicks::FromInternalValue(10000)); - EXPECT_BEGIN_FRAME_ARGS_DROP(*obs_, args); - source_->OnBeginFrame(args); - EXPECT_CALL((*client_), OnDidFinishFrame(BeginFrameAck(0, 2, 0, false))) - .Times(1); - source_->DidFinishFrame(obs_.get(), BeginFrameAck(0, 2, 0, false)); -} - -TEST_F(ExternalBeginFrameSourceTest, CallsOnDidFinishFrameWhenObserverRemoved) { - EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); - EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1); - source_->AddObserver(obs_.get()); - - BeginFrameArgs args = CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 0, 2, base::TimeTicks::FromInternalValue(10000)); - EXPECT_BEGIN_FRAME_ARGS_USED(*obs_, args); - source_->OnBeginFrame(args); - - EXPECT_CALL((*client_), OnDidFinishFrame(BeginFrameAck(0, 2, 2, false))) - .Times(1); - EXPECT_CALL((*client_), OnNeedsBeginFrames(false)).Times(1); - source_->RemoveObserver(obs_.get()); -} - // https://crbug.com/690127: Duplicate BeginFrame caused DCHECK crash. TEST_F(ExternalBeginFrameSourceTest, OnBeginFrameChecksBeginFrameContinuity) { EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false); @@ -863,8 +587,6 @@ TEST_F(ExternalBeginFrameSourceTest, OnBeginFrameChecksBeginFrameContinuity) { source_->OnBeginFrame(args); // Providing same args again to OnBeginFrame() should not notify observer. - EXPECT_CALL((*client_), OnDidFinishFrame(BeginFrameAck(0, 2, 0, false))) - .Times(1); source_->OnBeginFrame(args); // Providing same args through a different ExternalBeginFrameSource also does diff --git a/chromium/cc/scheduler/begin_frame_tracker.cc b/chromium/cc/scheduler/begin_frame_tracker.cc index dc18beaa4d3..fb39ee1eb1d 100644 --- a/chromium/cc/scheduler/begin_frame_tracker.cc +++ b/chromium/cc/scheduler/begin_frame_tracker.cc @@ -80,10 +80,11 @@ base::TimeDelta BeginFrameTracker::Interval() const { void BeginFrameTracker::AsValueInto( base::TimeTicks now, base::trace_event::TracedValue* state) const { - state->SetInteger("updated_at_us", - (current_updated_at_ - base::TimeTicks()).InMicroseconds()); - state->SetInteger("finished_at_us", (current_finished_at_ - base::TimeTicks()) - .InMicroseconds()); + state->SetDouble("updated_at_ms", + (current_updated_at_ - base::TimeTicks()).InMillisecondsF()); + state->SetDouble( + "finished_at_ms", + (current_finished_at_ - base::TimeTicks()).InMillisecondsF()); if (HasFinished()) { state->SetString("state", "FINISHED"); state->BeginDictionary("current_args_"); diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index 7212416461f..e3fdef521d8 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -37,16 +37,9 @@ Scheduler::Scheduler( client_(client), layer_tree_host_id_(layer_tree_host_id), task_runner_(task_runner), - begin_frame_source_(nullptr), - observing_begin_frame_source_(false), compositor_timing_history_(std::move(compositor_timing_history)), - begin_impl_frame_deadline_mode_( - SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE), begin_impl_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE), state_machine_(settings), - inside_process_scheduled_actions_(false), - inside_action_(SchedulerStateMachine::ACTION_NONE), - stopped_(false), weak_factory_(this) { TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue()); DCHECK(client_); @@ -215,6 +208,12 @@ base::TimeTicks Scheduler::LastBeginImplFrameTime() { return begin_impl_frame_tracker_.Current().frame_time; } +void Scheduler::BeginMainFrameNotExpectedUntil(base::TimeTicks time) { + TRACE_EVENT1("cc", "Scheduler::BeginMainFrameNotExpectedUntil", + "remaining_time", (time - Now()).InMillisecondsF()); + client_->ScheduledActionBeginMainFrameNotExpectedUntil(time); +} + void Scheduler::BeginImplFrameNotExpectedSoon() { compositor_timing_history_->BeginImplFrameNotExpectedSoon(); @@ -331,10 +330,13 @@ void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs& args) { // Discard missed begin frames if they are too late. if (adjusted_args.type == BeginFrameArgs::MISSED && now > adjusted_args.deadline) { + skipped_last_frame_missed_exceeded_deadline_ = true; SendBeginFrameAck(adjusted_args, kBeginFrameSkipped); return; } + skipped_last_frame_missed_exceeded_deadline_ = false; + // Run the previous deadline if any. if (state_machine_.begin_impl_frame_state() == SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME) { @@ -405,10 +407,13 @@ void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs& args) { can_activate_before_deadline)) { TRACE_EVENT_INSTANT0("cc", "SkipBeginImplFrameToReduceLatency", TRACE_EVENT_SCOPE_THREAD); + skipped_last_frame_to_reduce_latency_ = true; SendBeginFrameAck(begin_main_frame_args_, kBeginFrameSkipped); return; } + skipped_last_frame_to_reduce_latency_ = false; + BeginImplFrame(adjusted_args, now); } @@ -457,6 +462,8 @@ void Scheduler::SendBeginFrameAck(const BeginFrameArgs& args, BeginFrameAck ack(args.source_id, args.sequence_number, latest_confirmed_sequence_number, did_submit); begin_frame_source_->DidFinishFrame(this, ack); + if (!did_submit) + client_->DidNotProduceFrame(ack); } // BeginImplFrame starts a compositor frame that will wait up until a deadline @@ -488,7 +495,6 @@ void Scheduler::ScheduleBeginImplFrameDeadline() { begin_impl_frame_deadline_mode_ = state_machine_.CurrentBeginImplFrameDeadlineMode(); - base::TimeTicks deadline; switch (begin_impl_frame_deadline_mode_) { case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE: // No deadline. @@ -496,18 +502,18 @@ void Scheduler::ScheduleBeginImplFrameDeadline() { case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE: // We are ready to draw a new active tree immediately. // We don't use Now() here because it's somewhat expensive to call. - deadline = base::TimeTicks(); + deadline_ = base::TimeTicks(); break; case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR: // We are animating on the impl thread but we can wait for some time. - deadline = begin_impl_frame_tracker_.Current().deadline; + deadline_ = begin_impl_frame_tracker_.Current().deadline; break; case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE: // We are blocked for one reason or another and we should wait. // TODO(brianderson): Handle long deadlines (that are past the next // frame's frame time) properly instead of using this hack. - deadline = begin_impl_frame_tracker_.Current().frame_time + - begin_impl_frame_tracker_.Current().interval; + deadline_ = begin_impl_frame_tracker_.Current().frame_time + + begin_impl_frame_tracker_.Current().interval; break; case SchedulerStateMachine:: BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW: @@ -521,9 +527,11 @@ void Scheduler::ScheduleBeginImplFrameDeadline() { TRACE_EVENT2("cc", "Scheduler::ScheduleBeginImplFrameDeadline", "mode", SchedulerStateMachine::BeginImplFrameDeadlineModeToString( begin_impl_frame_deadline_mode_), - "deadline", deadline); + "deadline", deadline_); - base::TimeDelta delta = std::max(deadline - Now(), base::TimeDelta()); + deadline_scheduled_at_ = Now(); + base::TimeDelta delta = + std::max(deadline_ - deadline_scheduled_at_, base::TimeDelta()); task_runner_->PostDelayedTask( FROM_HERE, begin_impl_frame_deadline_task_.callback(), delta); } @@ -629,6 +637,11 @@ void Scheduler::ProcessScheduledActions() { // TODO(brianderson): Pass begin_main_frame_args_ directly to client. client_->ScheduledActionSendBeginMainFrame(begin_main_frame_args_); break; + case SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT: + state_machine_.WillNotifyBeginMainFrameNotSent(); + BeginMainFrameNotExpectedUntil(begin_main_frame_args_.frame_time + + begin_main_frame_args_.interval); + break; case SchedulerStateMachine::ACTION_COMMIT: { bool commit_has_no_updates = false; state_machine_.WillCommit(commit_has_no_updates); @@ -680,39 +693,62 @@ void Scheduler::ProcessScheduledActions() { std::unique_ptr<base::trace_event::ConvertableToTraceFormat> Scheduler::AsValue() const { - std::unique_ptr<base::trace_event::TracedValue> state( - new base::trace_event::TracedValue()); + auto state = base::MakeUnique<base::trace_event::TracedValue>(); + AsValueInto(state.get()); + return std::move(state); +} + +void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const { base::TimeTicks now = Now(); state->BeginDictionary("state_machine"); - state_machine_.AsValueInto(state.get()); + state_machine_.AsValueInto(state); state->EndDictionary(); - state->BeginDictionary("scheduler_state"); state->SetBoolean("observing_begin_frame_source", observing_begin_frame_source_); state->SetBoolean("begin_impl_frame_deadline_task", !begin_impl_frame_deadline_task_.IsCancelled()); state->SetBoolean("missed_begin_frame_task", !missed_begin_frame_task_.IsCancelled()); + state->SetBoolean("skipped_last_frame_missed_exceeded_deadline", + skipped_last_frame_missed_exceeded_deadline_); + state->SetBoolean("skipped_last_frame_to_reduce_latency", + skipped_last_frame_to_reduce_latency_); state->SetString("inside_action", SchedulerStateMachine::ActionToString(inside_action_)); - - state->BeginDictionary("begin_impl_frame_tracker"); - begin_impl_frame_tracker_.AsValueInto(now, state.get()); - state->EndDictionary(); - - state->SetString("begin_impl_frame_deadline_mode", SchedulerStateMachine::BeginImplFrameDeadlineModeToString( begin_impl_frame_deadline_mode_)); + + state->SetDouble("deadline_ms", + (deadline_ - base::TimeTicks()).InMillisecondsF()); + state->SetDouble( + "deadline_scheduled_at_ms", + (deadline_scheduled_at_ - base::TimeTicks()).InMillisecondsF()); + + state->SetDouble("now_ms", (Now() - base::TimeTicks()).InMillisecondsF()); + state->SetDouble("now_to_deadline_ms", (deadline_ - Now()).InMillisecondsF()); + state->SetDouble("now_to_deadline_scheduled_at_ms", + (deadline_scheduled_at_ - Now()).InMillisecondsF()); + + state->BeginDictionary("begin_impl_frame_args"); + begin_impl_frame_tracker_.AsValueInto(now, state); state->EndDictionary(); - state->BeginDictionary("compositor_timing_history"); - compositor_timing_history_->AsValueInto(state.get()); + state->BeginDictionary("begin_frame_observer_state"); + BeginFrameObserverBase::AsValueInto(state); state->EndDictionary(); - return std::move(state); + if (begin_frame_source_) { + state->BeginDictionary("begin_frame_source_state"); + begin_frame_source_->AsValueInto(state); + state->EndDictionary(); + } + + state->BeginDictionary("compositor_timing_history"); + compositor_timing_history_->AsValueInto(state); + state->EndDictionary(); } void Scheduler::UpdateCompositorTimingHistoryRecordingEnabled() { diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h index 9efd831d76a..6c9ccc4fd71 100644 --- a/chromium/cc/scheduler/scheduler.h +++ b/chromium/cc/scheduler/scheduler.h @@ -47,7 +47,10 @@ class SchedulerClient { virtual void ScheduledActionInvalidateCompositorFrameSink() = 0; virtual void ScheduledActionPerformImplSideInvalidation() = 0; virtual void DidFinishImplFrame() = 0; + virtual void DidNotProduceFrame(const BeginFrameAck& ack) = 0; virtual void SendBeginMainFrameNotExpectedSoon() = 0; + virtual void ScheduledActionBeginMainFrameNotExpectedUntil( + base::TimeTicks time) = 0; protected: virtual ~SchedulerClient() {} @@ -146,6 +149,8 @@ class CC_EXPORT Scheduler : public BeginFrameObserverBase { std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; + void AsValueInto(base::trace_event::TracedValue* state) const; + void SetVideoNeedsBeginFrames(bool video_needs_begin_frames); const BeginFrameSource* begin_frame_source() const { @@ -159,19 +164,24 @@ class CC_EXPORT Scheduler : public BeginFrameObserverBase { virtual base::TimeTicks Now() const; const SchedulerSettings settings_; - // Not owned. - SchedulerClient* client_; - int layer_tree_host_id_; + SchedulerClient* const client_; + const int layer_tree_host_id_; base::SingleThreadTaskRunner* task_runner_; - // Not owned. May be null. - BeginFrameSource* begin_frame_source_; - bool observing_begin_frame_source_; + BeginFrameSource* begin_frame_source_ = nullptr; + bool observing_begin_frame_source_ = false; + + bool skipped_last_frame_missed_exceeded_deadline_ = false; + bool skipped_last_frame_to_reduce_latency_ = false; std::unique_ptr<CompositorTimingHistory> compositor_timing_history_; SchedulerStateMachine::BeginImplFrameDeadlineMode - begin_impl_frame_deadline_mode_; + begin_impl_frame_deadline_mode_ = + SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE; + base::TimeTicks deadline_; + base::TimeTicks deadline_scheduled_at_; + BeginFrameTracker begin_impl_frame_tracker_; BeginFrameArgs begin_main_frame_args_; @@ -180,15 +190,17 @@ class CC_EXPORT Scheduler : public BeginFrameObserverBase { base::CancelableClosure missed_begin_frame_task_; SchedulerStateMachine state_machine_; - bool inside_process_scheduled_actions_; - SchedulerStateMachine::Action inside_action_; + bool inside_process_scheduled_actions_ = false; + SchedulerStateMachine::Action inside_action_ = + SchedulerStateMachine::ACTION_NONE; - bool stopped_; + bool stopped_ = false; private: void ScheduleBeginImplFrameDeadline(); void ScheduleBeginImplFrameDeadlineIfNeeded(); void BeginImplFrameNotExpectedSoon(); + void BeginMainFrameNotExpectedUntil(base::TimeTicks time); void SetupNextBeginFrameIfNeeded(); void DrawIfPossible(); void DrawForced(); diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc index e73508ce1ab..210a2bdc43b 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.cc +++ b/chromium/cc/scheduler/scheduler_state_machine.cc @@ -10,7 +10,6 @@ #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "base/values.h" -#include "cc/output/begin_frame_args.h" namespace cc { @@ -20,62 +19,9 @@ const int kMaxPendingSubmitFrames = 1; } // namespace SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) - : settings_(settings), - compositor_frame_sink_state_(COMPOSITOR_FRAME_SINK_NONE), - begin_impl_frame_state_(BEGIN_IMPL_FRAME_STATE_IDLE), - begin_main_frame_state_(BEGIN_MAIN_FRAME_STATE_IDLE), - forced_redraw_state_(FORCED_REDRAW_STATE_IDLE), - begin_frame_source_id_(0), - begin_frame_sequence_number_(BeginFrameArgs::kInvalidFrameNumber), - last_begin_frame_sequence_number_begin_main_frame_sent_( - BeginFrameArgs::kInvalidFrameNumber), - last_begin_frame_sequence_number_pending_tree_was_fresh_( - BeginFrameArgs::kInvalidFrameNumber), - last_begin_frame_sequence_number_active_tree_was_fresh_( - BeginFrameArgs::kInvalidFrameNumber), - last_begin_frame_sequence_number_compositor_frame_was_fresh_( - BeginFrameArgs::kInvalidFrameNumber), - commit_count_(0), - current_frame_number_(0), - last_frame_number_submit_performed_(-1), - last_frame_number_draw_performed_(-1), - last_frame_number_begin_main_frame_sent_(-1), - last_frame_number_invalidate_compositor_frame_sink_performed_(-1), - draw_funnel_(false), - send_begin_main_frame_funnel_(true), - invalidate_compositor_frame_sink_funnel_(false), - impl_side_invalidation_funnel_(false), - prepare_tiles_funnel_(0), - consecutive_checkerboard_animations_(0), - pending_submit_frames_(0), - submit_frames_with_current_compositor_frame_sink_(0), - needs_redraw_(false), - needs_prepare_tiles_(false), - needs_begin_main_frame_(false), - needs_one_begin_impl_frame_(false), - visible_(false), - begin_frame_source_paused_(false), - resourceless_draw_(false), - can_draw_(false), - has_pending_tree_(false), - pending_tree_is_ready_for_activation_(false), - active_tree_needs_first_draw_(false), - did_create_and_initialize_first_compositor_frame_sink_(false), - tree_priority_(NEW_CONTENT_TAKES_PRIORITY), - scroll_handler_state_( - ScrollHandlerState::SCROLL_DOES_NOT_AFFECT_SCROLL_HANDLER), - critical_begin_main_frame_to_activate_is_fast_(true), - main_thread_missed_last_deadline_(false), - skip_next_begin_main_frame_to_reduce_latency_(false), - defer_commits_(false), - video_needs_begin_frames_(false), - last_commit_had_no_updates_(false), - wait_for_ready_to_draw_(false), - did_draw_in_last_frame_(false), - did_submit_in_last_frame_(false), - needs_impl_side_invalidation_(false), - previous_pending_tree_was_impl_side_(false), - current_pending_tree_is_impl_side_(false) {} + : settings_(settings) {} + +SchedulerStateMachine::~SchedulerStateMachine() = default; const char* SchedulerStateMachine::CompositorFrameSinkStateToString( CompositorFrameSinkState state) { @@ -194,6 +140,8 @@ const char* SchedulerStateMachine::ActionToString(Action action) { return "ACTION_INVALIDATE_COMPOSITOR_FRAME_SINK"; case ACTION_PERFORM_IMPL_SIDE_INVALIDATION: return "ACTION_PERFORM_IMPL_SIDE_INVALIDATION"; + case ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT: + return "ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT"; } NOTREACHED(); return "???"; @@ -216,7 +164,7 @@ void SchedulerStateMachine::AsValueInto( state->SetString("begin_main_frame_state", BeginMainFrameStateToString(begin_main_frame_state_)); state->SetString( - "compositor_frame_sink_state_", + "compositor_frame_sink_state", CompositorFrameSinkStateToString(compositor_frame_sink_state_)); state->SetString("forced_redraw_state", ForcedRedrawOnTimeoutStateToString(forced_redraw_state_)); @@ -243,17 +191,20 @@ void SchedulerStateMachine::AsValueInto( state->SetInteger( "last_begin_frame_sequence_number_compositor_frame_was_fresh", last_begin_frame_sequence_number_compositor_frame_was_fresh_); - state->SetBoolean("funnel: draw_funnel", draw_funnel_); - state->SetBoolean("funnel: send_begin_main_frame_funnel", - send_begin_main_frame_funnel_); - state->SetInteger("funnel: prepare_tiles_funnel", prepare_tiles_funnel_); - state->SetBoolean("funnel: invalidate_compositor_frame_sink_funnel", - invalidate_compositor_frame_sink_funnel_); - state->SetBoolean("funnel: impl_side_invalidation_funnel", - impl_side_invalidation_funnel_); + state->SetBoolean("did_draw", did_draw_); + state->SetBoolean("did_send_begin_main_frame_for_current_frame", + did_send_begin_main_frame_for_current_frame_); + state->SetBoolean("did_notify_begin_main_frame_not_sent", + did_notify_begin_main_frame_not_sent_); + state->SetBoolean("did_commit_during_frame", did_commit_during_frame_); + state->SetBoolean("did_invalidate_compositor_frame_sink", + did_invalidate_compositor_frame_sink_); + state->SetBoolean("did_perform_impl_side_invalidaion", + did_perform_impl_side_invalidation_); + state->SetBoolean("did_prepare_tiles", did_prepare_tiles_); state->SetInteger("consecutive_checkerboard_animations", consecutive_checkerboard_animations_); - state->SetInteger("pending_submit_frames_", pending_submit_frames_); + state->SetInteger("pending_submit_frames", pending_submit_frames_); state->SetInteger("submit_frames_with_current_compositor_frame_sink", submit_frames_with_current_compositor_frame_sink_); state->SetBoolean("needs_redraw", needs_redraw_); @@ -275,7 +226,7 @@ void SchedulerStateMachine::AsValueInto( state->SetString("tree_priority", TreePriorityToString(tree_priority_)); state->SetString("scroll_handler_state", ScrollHandlerStateToString(scroll_handler_state_)); - state->SetBoolean("critical_begin_main_frame_to_activate_is_fast_", + state->SetBoolean("critical_begin_main_frame_to_activate_is_fast", critical_begin_main_frame_to_activate_is_fast_); state->SetBoolean("main_thread_missed_last_deadline", main_thread_missed_last_deadline_); @@ -288,6 +239,10 @@ void SchedulerStateMachine::AsValueInto( state->SetBoolean("did_submit_in_last_frame", did_submit_in_last_frame_); state->SetBoolean("needs_impl_side_invalidation", needs_impl_side_invalidation_); + state->SetBoolean("current_pending_tree_is_impl_side", + current_pending_tree_is_impl_side_); + state->SetBoolean("previous_pending_tree_was_impl_side", + previous_pending_tree_was_impl_side_); state->EndDictionary(); } @@ -372,10 +327,9 @@ bool SchedulerStateMachine::ShouldDraw() const { if (PendingDrawsShouldBeAborted()) return active_tree_needs_first_draw_; - // Do not draw too many times in a single frame. It's okay that we don't check - // this before checking for aborted draws because aborted draws do not request - // a swap. - if (draw_funnel_) + // Do not draw more than once in the deadline. Aborted draws are ok because + // those are effectively nops. + if (did_draw_) return false; // Don't draw if we are waiting on the first commit after a surface. @@ -428,6 +382,42 @@ bool SchedulerStateMachine::ShouldActivatePendingTree() const { return pending_tree_is_ready_for_activation_; } +bool SchedulerStateMachine::ShouldNotifyBeginMainFrameNotSent() const { + // This method returns true if most of the conditions for sending a + // BeginMainFrame are met, but one is not actually requested. This gives the + // main thread the chance to do something else. + + // Don't notify if a BeginMainFrame has already been requested or is in + // progress. + if (needs_begin_main_frame_ || + begin_main_frame_state_ != BEGIN_MAIN_FRAME_STATE_IDLE) + return false; + + // Only notify when we're visible. + if (!visible_) + return false; + + // There are no BeginImplFrames while BeginFrameSource is paused, meaning + // the scheduler should send SendBeginMainFrameNotExpectedSoon instead, + // indicating a longer period of inactivity. + if (begin_frame_source_paused_) + return false; + + // Do not notify that no BeginMainFrame was sent too many times in a single + // frame. + if (did_notify_begin_main_frame_not_sent_) + return false; + + // Do not notify if a commit happened during this frame as the main thread + // will already be active and does not need to be woken up to make further + // actions. (This occurs if the main frame was scheduled but didn't complete + // before the vsync deadline). + if (did_commit_during_frame_) + return false; + + return true; +} + bool SchedulerStateMachine::CouldSendBeginMainFrame() const { if (!needs_begin_main_frame_) return false; @@ -452,9 +442,8 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { if (!CouldSendBeginMainFrame()) return false; - // Do not send begin main frame too many times in a single frame or before - // the first BeginFrame. - if (send_begin_main_frame_funnel_) + // Do not send more than one begin main frame in a begin frame. + if (did_send_begin_main_frame_for_current_frame_) return false; // Only send BeginMainFrame when there isn't another commit pending already. @@ -542,14 +531,13 @@ bool SchedulerStateMachine::ShouldCommit() const { } bool SchedulerStateMachine::ShouldPrepareTiles() const { - // PrepareTiles only really needs to be called immediately after commit - // and then periodically after that. Use a funnel to make sure we average - // one PrepareTiles per BeginImplFrame in the long run. - if (prepare_tiles_funnel_ > 0) + // Do not prepare tiles if we've already done so in commit or impl side + // invalidation. + if (did_prepare_tiles_) return false; - // Limiting to once per-frame is not enough, since we only want to - // prepare tiles _after_ draws. + // Limiting to once per-frame is not enough, since we only want to prepare + // tiles _after_ draws. if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) return false; @@ -557,8 +545,8 @@ bool SchedulerStateMachine::ShouldPrepareTiles() const { } bool SchedulerStateMachine::ShouldInvalidateCompositorFrameSink() const { - // Do not invalidate too many times in a frame. - if (invalidate_compositor_frame_sink_funnel_) + // Do not invalidate more than once per begin frame. + if (did_invalidate_compositor_frame_sink_) return false; // Only the synchronous compositor requires invalidations. @@ -598,6 +586,8 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { return ACTION_INVALIDATE_COMPOSITOR_FRAME_SINK; if (ShouldBeginCompositorFrameSinkCreation()) return ACTION_BEGIN_COMPOSITOR_FRAME_SINK_CREATION; + if (ShouldNotifyBeginMainFrameNotSent()) + return ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT; return ACTION_NONE; } @@ -605,6 +595,11 @@ bool SchedulerStateMachine::ShouldPerformImplSideInvalidation() const { if (!needs_impl_side_invalidation_) return false; + // Only perform impl side invalidation after the frame ends so that we wait + // for any commit to happen before invalidating. + if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) + return false; + if (!CouldCreatePendingTree()) return false; @@ -613,8 +608,9 @@ bool SchedulerStateMachine::ShouldPerformImplSideInvalidation() const { if (begin_main_frame_state_ == BEGIN_MAIN_FRAME_STATE_READY_TO_COMMIT) return false; - // Don't invalidate too many times in the same frame. - if (impl_side_invalidation_funnel_) + // Don't invalidate if we've already done so either from the scheduler or as + // part of commit. + if (did_perform_impl_side_invalidation_) return false; // If invalidations go to the active tree and we are waiting for the previous @@ -624,22 +620,7 @@ bool SchedulerStateMachine::ShouldPerformImplSideInvalidation() const { return false; } - // If we are inside the deadline and an impl-side invalidation request is - // still pending, do it now. We restrict performing impl-side invalidations - // until the deadline to give the main thread a chance to respond to a sent - // BeginMainFrame. If the main thread responds with a commit, we know the - // invalidations will have been merged with the main frame. - // If the commit was aborted, or the main thread fails to respond within the - // deadline, then we create a pending tree for impl-side invalidations now. - // This also checks to make sure that the |prepare_tiles_funnel_| is not full, - // since impl-side invalidations will cause a PrepareTiles. - if (begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE && - prepare_tiles_funnel_ == 0) { - return true; - } - - // Wait till the deadline to perform impl-side invalidations. - return false; + return true; } void SchedulerStateMachine::WillPerformImplSideInvalidation() { @@ -653,7 +634,7 @@ void SchedulerStateMachine::WillPerformImplSideInvalidationInternal() { needs_impl_side_invalidation_ = false; has_pending_tree_ = true; - impl_side_invalidation_funnel_ = true; + did_perform_impl_side_invalidation_ = true; // TODO(eseckler): Track impl-side invalidations for pending/active tree and // CompositorFrame freshness computation. } @@ -683,15 +664,22 @@ void SchedulerStateMachine::WillSendBeginMainFrame() { DCHECK(!has_pending_tree_ || settings_.main_frame_before_activation_enabled); DCHECK(visible_); DCHECK(!begin_frame_source_paused_); - DCHECK(!send_begin_main_frame_funnel_); + DCHECK(!did_send_begin_main_frame_for_current_frame_); begin_main_frame_state_ = BEGIN_MAIN_FRAME_STATE_SENT; needs_begin_main_frame_ = false; - send_begin_main_frame_funnel_ = true; + did_send_begin_main_frame_for_current_frame_ = true; last_frame_number_begin_main_frame_sent_ = current_frame_number_; last_begin_frame_sequence_number_begin_main_frame_sent_ = begin_frame_sequence_number_; } +void SchedulerStateMachine::WillNotifyBeginMainFrameNotSent() { + DCHECK(visible_); + DCHECK(!begin_frame_source_paused_); + DCHECK(!did_notify_begin_main_frame_not_sent_); + did_notify_begin_main_frame_not_sent_ = true; +} + void SchedulerStateMachine::WillCommit(bool commit_has_no_updates) { bool can_have_pending_tree = commit_has_no_updates && @@ -701,6 +689,7 @@ void SchedulerStateMachine::WillCommit(bool commit_has_no_updates) { commit_count_++; last_commit_had_no_updates_ = commit_has_no_updates; begin_main_frame_state_ = BEGIN_MAIN_FRAME_STATE_IDLE; + did_commit_during_frame_ = true; if (commit_has_no_updates) { // Pending tree might still exist from prior commit. @@ -727,7 +716,7 @@ void SchedulerStateMachine::WillCommit(bool commit_has_no_updates) { // request is received after the commit. if (needs_impl_side_invalidation_) WillPerformImplSideInvalidationInternal(); - impl_side_invalidation_funnel_ = true; + did_perform_impl_side_invalidation_ = true; // We have a new pending tree. has_pending_tree_ = true; @@ -784,7 +773,7 @@ void SchedulerStateMachine::WillDrawInternal() { // draw itself might request another draw. needs_redraw_ = false; - draw_funnel_ = true; + did_draw_ = true; active_tree_needs_first_draw_ = false; did_draw_in_last_frame_ = true; last_frame_number_draw_performed_ = current_frame_number_; @@ -840,7 +829,7 @@ void SchedulerStateMachine::DidDrawInternal(DrawResult draw_result) { } void SchedulerStateMachine::WillDraw() { - DCHECK(!draw_funnel_); + DCHECK(!did_draw_); WillDrawInternal(); } @@ -877,8 +866,8 @@ void SchedulerStateMachine::WillBeginCompositorFrameSinkCreation() { } void SchedulerStateMachine::WillInvalidateCompositorFrameSink() { - DCHECK(!invalidate_compositor_frame_sink_funnel_); - invalidate_compositor_frame_sink_funnel_ = true; + DCHECK(!did_invalidate_compositor_frame_sink_); + did_invalidate_compositor_frame_sink_ = true; last_frame_number_invalidate_compositor_frame_sink_performed_ = current_frame_number_; @@ -1046,27 +1035,18 @@ void SchedulerStateMachine::OnBeginImplFrame(uint32_t source_id, did_submit_in_last_frame_ = false; needs_one_begin_impl_frame_ = false; - // Clear funnels for any actions we perform during the frame. - send_begin_main_frame_funnel_ = false; - invalidate_compositor_frame_sink_funnel_ = false; - impl_side_invalidation_funnel_ = false; - - // "Drain" the PrepareTiles funnel. - if (prepare_tiles_funnel_ > 0) - prepare_tiles_funnel_--; + did_notify_begin_main_frame_not_sent_ = false; + did_send_begin_main_frame_for_current_frame_ = false; + did_commit_during_frame_ = false; + did_invalidate_compositor_frame_sink_ = false; + did_perform_impl_side_invalidation_ = false; } void SchedulerStateMachine::OnBeginImplFrameDeadline() { begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE; // Clear funnels for any actions we perform during the deadline. - draw_funnel_ = false; - - // Allow one PrepareTiles per draw for synchronous compositor. - if (settings_.using_synchronous_renderer_compositor) { - if (prepare_tiles_funnel_ > 0) - prepare_tiles_funnel_--; - } + did_draw_ = false; if (!settings_.using_synchronous_renderer_compositor) UpdateBeginFrameSequenceNumbersForBeginFrameDeadline(); @@ -1075,6 +1055,12 @@ void SchedulerStateMachine::OnBeginImplFrameDeadline() { void SchedulerStateMachine::OnBeginImplFrameIdle() { begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_IDLE; + // Count any prepare tiles that happens in commits in between frames. We want + // to prevent a prepare tiles during the next frame's deadline in that case. + // This also allows synchronous compositor to do one PrepareTiles per draw. + // This is same as the old prepare tiles funnel behavior. + did_prepare_tiles_ = false; + skip_next_begin_main_frame_to_reduce_latency_ = false; // If a new or undrawn active tree is pending after the deadline, @@ -1085,7 +1071,7 @@ void SchedulerStateMachine::OnBeginImplFrameIdle() { // If we're entering a state where we won't get BeginFrames set all the // funnels so that we don't perform any actions that we shouldn't. if (!BeginFrameNeeded()) - send_begin_main_frame_funnel_ = true; + did_send_begin_main_frame_for_current_frame_ = true; // Synchronous compositor finishes BeginFrames before triggering their // deadline. Therefore, we update sequence numbers when becoming idle, before @@ -1161,8 +1147,7 @@ void SchedulerStateMachine::SetVisible(bool visible) { if (visible) main_thread_missed_last_deadline_ = false; - // TODO(sunnyps): Change the funnel to a bool to avoid hacks like this. - prepare_tiles_funnel_ = 0; + did_prepare_tiles_ = false; wait_for_ready_to_draw_ = false; } @@ -1284,8 +1269,7 @@ void SchedulerStateMachine::BeginMainFrameAborted(CommitEarlyOutReason reason) { void SchedulerStateMachine::DidPrepareTiles() { needs_prepare_tiles_ = false; - // "Fill" the PrepareTiles funnel. - prepare_tiles_funnel_++; + did_prepare_tiles_ = true; } void SchedulerStateMachine::DidLoseCompositorFrameSink() { diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h index cd4a7f2ee66..6656188de03 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.h +++ b/chromium/cc/scheduler/scheduler_state_machine.h @@ -12,6 +12,7 @@ #include "base/macros.h" #include "cc/cc_export.h" +#include "cc/output/begin_frame_args.h" #include "cc/scheduler/commit_earlyout_reason.h" #include "cc/scheduler/draw_result.h" #include "cc/scheduler/scheduler_settings.h" @@ -47,6 +48,7 @@ class CC_EXPORT SchedulerStateMachine { public: // settings must be valid for the lifetime of this class. explicit SchedulerStateMachine(const SchedulerSettings& settings); + ~SchedulerStateMachine(); enum CompositorFrameSinkState { COMPOSITOR_FRAME_SINK_NONE, @@ -123,6 +125,7 @@ class CC_EXPORT SchedulerStateMachine { ACTION_BEGIN_COMPOSITOR_FRAME_SINK_CREATION, ACTION_PREPARE_TILES, ACTION_INVALIDATE_COMPOSITOR_FRAME_SINK, + ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT, }; static const char* ActionToString(Action action); @@ -131,6 +134,7 @@ class CC_EXPORT SchedulerStateMachine { Action NextAction() const; void WillSendBeginMainFrame(); + void WillNotifyBeginMainFrameNotSent(); void WillCommit(bool commit_had_no_updates); void WillActivate(); void WillDraw(); @@ -318,6 +322,7 @@ class CC_EXPORT SchedulerStateMachine { bool ShouldCommit() const; bool ShouldPrepareTiles() const; bool ShouldInvalidateCompositorFrameSink() const; + bool ShouldNotifyBeginMainFrameNotSent() const; void WillDrawInternal(); void WillPerformImplSideInvalidationInternal(); @@ -329,72 +334,78 @@ class CC_EXPORT SchedulerStateMachine { const SchedulerSettings settings_; - CompositorFrameSinkState compositor_frame_sink_state_; - BeginImplFrameState begin_impl_frame_state_; - BeginMainFrameState begin_main_frame_state_; - ForcedRedrawOnTimeoutState forced_redraw_state_; + CompositorFrameSinkState compositor_frame_sink_state_ = + COMPOSITOR_FRAME_SINK_NONE; + BeginImplFrameState begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_IDLE; + BeginMainFrameState begin_main_frame_state_ = BEGIN_MAIN_FRAME_STATE_IDLE; + ForcedRedrawOnTimeoutState forced_redraw_state_ = FORCED_REDRAW_STATE_IDLE; // These fields are used to track the freshness of pending updates in the // commit/activate/draw pipeline. The Scheduler uses the CompositorFrame's // freshness to fill the |latest_confirmed_sequence_number| field in // BeginFrameAcks. - uint32_t begin_frame_source_id_; - uint64_t begin_frame_sequence_number_; - uint64_t last_begin_frame_sequence_number_begin_main_frame_sent_; - uint64_t last_begin_frame_sequence_number_pending_tree_was_fresh_; - uint64_t last_begin_frame_sequence_number_active_tree_was_fresh_; - uint64_t last_begin_frame_sequence_number_compositor_frame_was_fresh_; + uint32_t begin_frame_source_id_ = 0; + uint64_t begin_frame_sequence_number_ = BeginFrameArgs::kInvalidFrameNumber; + uint64_t last_begin_frame_sequence_number_begin_main_frame_sent_ = + BeginFrameArgs::kInvalidFrameNumber; + uint64_t last_begin_frame_sequence_number_pending_tree_was_fresh_ = + BeginFrameArgs::kInvalidFrameNumber; + uint64_t last_begin_frame_sequence_number_active_tree_was_fresh_ = + BeginFrameArgs::kInvalidFrameNumber; + uint64_t last_begin_frame_sequence_number_compositor_frame_was_fresh_ = + BeginFrameArgs::kInvalidFrameNumber; // These are used for tracing only. - int commit_count_; - int current_frame_number_; - int last_frame_number_submit_performed_; - int last_frame_number_draw_performed_; - int last_frame_number_begin_main_frame_sent_; - int last_frame_number_invalidate_compositor_frame_sink_performed_; + int commit_count_ = 0; + int current_frame_number_ = 0; + int last_frame_number_submit_performed_ = -1; + int last_frame_number_draw_performed_ = -1; + int last_frame_number_begin_main_frame_sent_ = -1; + int last_frame_number_invalidate_compositor_frame_sink_performed_ = -1; // These are used to ensure that an action only happens once per frame, // deadline, etc. - bool draw_funnel_; - bool send_begin_main_frame_funnel_; - bool invalidate_compositor_frame_sink_funnel_; - bool impl_side_invalidation_funnel_; - // prepare_tiles_funnel_ is "filled" each time PrepareTiles is called - // and "drained" on each BeginImplFrame. If the funnel gets too full, - // we start throttling ACTION_PREPARE_TILES such that we average one - // PrepareTiles per BeginImplFrame. - int prepare_tiles_funnel_; - - int consecutive_checkerboard_animations_; - int pending_submit_frames_; - int submit_frames_with_current_compositor_frame_sink_; - bool needs_redraw_; - bool needs_prepare_tiles_; - bool needs_begin_main_frame_; - bool needs_one_begin_impl_frame_; - bool visible_; - bool begin_frame_source_paused_; - bool resourceless_draw_; - bool can_draw_; - bool has_pending_tree_; - bool pending_tree_is_ready_for_activation_; - bool active_tree_needs_first_draw_; - bool did_create_and_initialize_first_compositor_frame_sink_; - TreePriority tree_priority_; - ScrollHandlerState scroll_handler_state_; - bool critical_begin_main_frame_to_activate_is_fast_; - bool main_thread_missed_last_deadline_; - bool skip_next_begin_main_frame_to_reduce_latency_; - bool defer_commits_; - bool video_needs_begin_frames_; - bool last_commit_had_no_updates_; - bool wait_for_ready_to_draw_; - bool did_draw_in_last_frame_; - bool did_submit_in_last_frame_; - bool needs_impl_side_invalidation_; - - bool previous_pending_tree_was_impl_side_; - bool current_pending_tree_is_impl_side_; + bool did_draw_ = false; + bool did_send_begin_main_frame_for_current_frame_ = true; + // Initialized to true to prevent begin main frame before begin frames have + // started. Reset to true when we stop asking for begin frames. + bool did_notify_begin_main_frame_not_sent_ = true; + bool did_commit_during_frame_ = false; + bool did_invalidate_compositor_frame_sink_ = false; + bool did_perform_impl_side_invalidation_ = false; + bool did_prepare_tiles_ = false; + + int consecutive_checkerboard_animations_ = 0; + int pending_submit_frames_ = 0; + int submit_frames_with_current_compositor_frame_sink_ = 0; + bool needs_redraw_ = false; + bool needs_prepare_tiles_ = false; + bool needs_begin_main_frame_ = false; + bool needs_one_begin_impl_frame_ = false; + bool visible_ = false; + bool begin_frame_source_paused_ = false; + bool resourceless_draw_ = false; + bool can_draw_ = false; + bool has_pending_tree_ = false; + bool pending_tree_is_ready_for_activation_ = false; + bool active_tree_needs_first_draw_ = false; + bool did_create_and_initialize_first_compositor_frame_sink_ = false; + TreePriority tree_priority_ = NEW_CONTENT_TAKES_PRIORITY; + ScrollHandlerState scroll_handler_state_ = + ScrollHandlerState::SCROLL_DOES_NOT_AFFECT_SCROLL_HANDLER; + bool critical_begin_main_frame_to_activate_is_fast_ = true; + bool main_thread_missed_last_deadline_ = false; + bool skip_next_begin_main_frame_to_reduce_latency_ = false; + bool defer_commits_ = false; + bool video_needs_begin_frames_ = false; + bool last_commit_had_no_updates_ = false; + bool wait_for_ready_to_draw_ = false; + bool did_draw_in_last_frame_ = false; + bool did_submit_in_last_frame_ = false; + bool needs_impl_side_invalidation_ = false; + + bool previous_pending_tree_was_impl_side_ = false; + bool current_pending_tree_is_impl_side_ = false; private: DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine); diff --git a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc index 0a99a0969a0..da53e5dea99 100644 --- a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc @@ -204,6 +204,10 @@ void PerformAction(StateMachine* sm, SchedulerStateMachine::Action action) { sm->WillSendBeginMainFrame(); return; + case SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT: + sm->WillNotifyBeginMainFrameNotSent(); + return; + case SchedulerStateMachine::ACTION_COMMIT: { bool commit_has_no_updates = false; sm->WillCommit(commit_has_no_updates); @@ -290,6 +294,27 @@ TEST(SchedulerStateMachineTest, BeginFrameNeeded) { EXPECT_FALSE(state.BeginFrameNeeded()); } +TEST(SchedulerStateMachineTest, TestNextActionNotifyBeginMainFrameNotSent) { + SchedulerSettings default_scheduler_settings; + StateMachine state(default_scheduler_settings); + state.SetVisible(true); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_BEGIN_COMPOSITOR_FRAME_SINK_CREATION); + state.IssueNextBeginImplFrame(); + state.CreateAndInitializeCompositorFrameSinkWithActivatedCommit(); + // A main frame was not requested so short idle work is scheduled instead. + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + state.SetNeedsRedraw(true); + state.SetNeedsBeginMainFrame(); + // Now a main frame is requested no short idle work is scheduled. + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); +} + TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { SchedulerSettings default_scheduler_settings; @@ -309,6 +334,8 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { EXPECT_FALSE(state.NeedsCommit()); state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -458,6 +485,8 @@ TEST(SchedulerStateMachineTest, // Start a frame. state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_FALSE(state.CommitPending()); @@ -495,6 +524,8 @@ TEST(SchedulerStateMachineTest, FailedDrawForMissingHighResNeedsCommit) { // Start a frame. state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_FALSE(state.CommitPending()); @@ -531,6 +562,8 @@ TEST(SchedulerStateMachineTest, FailedDrawForMissingHighResNeedsCommit) { // Verify we draw with the new frame. state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); state.SetDrawResultForTest(DRAW_SUCCESS); @@ -664,6 +697,8 @@ TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginImplFrame) { state.SetNeedsRedraw(true); EXPECT_TRUE(state.BeginFrameNeeded()); state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_TRUE(state.RedrawPending()); @@ -700,6 +735,8 @@ TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { // Draw the first frame. EXPECT_TRUE(state.BeginFrameNeeded()); state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -716,6 +753,8 @@ TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { // Move to another frame. This should now draw. EXPECT_TRUE(state.BeginFrameNeeded()); state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); @@ -1400,6 +1439,8 @@ TEST(SchedulerStateMachineTest, TestAbortBeginMainFrameBecauseCommitNotNeeded) { EXPECT_MAIN_FRAME_STATE(SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_IDLE); state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -1424,6 +1465,8 @@ TEST(SchedulerStateMachineTest, TestFirstContextCreation) { // Check that the first init does not SetNeedsBeginMainFrame. state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -1478,6 +1521,8 @@ TEST(SchedulerStateMachineTest, // Once context recreation begins, nothing should happen. state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -1528,6 +1573,8 @@ TEST(SchedulerStateMachineTest, // automatically cause a redraw. EXPECT_TRUE(state.RedrawPending()); state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); @@ -1536,6 +1583,8 @@ TEST(SchedulerStateMachineTest, // Next frame as no work to do. state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -1544,6 +1593,8 @@ TEST(SchedulerStateMachineTest, // SetCanDraw if waiting on first draw after activate. state.SetNeedsRedraw(true); state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); @@ -1636,6 +1687,8 @@ TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { state.OnBeginImplFrame(0, 11); EXPECT_IMPL_FRAME_STATE( SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE); EXPECT_SEQUENCE_NUMBERS(11u, 10u, 10u, 10u, BeginFrameArgs::kInvalidFrameNumber); @@ -1993,7 +2046,8 @@ TEST(SchedulerStateMachineTest, } void FinishPreviousCommitAndDrawWithoutExitingDeadline( - StateMachine* state_ptr) { + StateMachine* state_ptr, + bool expect_begin_main_frame_not_sent_before_impl_frame_deadline) { // Gross, but allows us to use macros below. StateMachine& state = *state_ptr; @@ -2006,6 +2060,10 @@ void FinishPreviousCommitAndDrawWithoutExitingDeadline( EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.IssueNextBeginImplFrame(); + if (expect_begin_main_frame_not_sent_before_impl_frame_deadline) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); + } EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately()); @@ -2045,7 +2103,7 @@ TEST(SchedulerStateMachineTest, TestImplLatencyTakesPriority) { // Request a new commit and finish the previous one. state.SetNeedsBeginMainFrame(); - FinishPreviousCommitAndDrawWithoutExitingDeadline(&state); + FinishPreviousCommitAndDrawWithoutExitingDeadline(&state, false); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -2053,7 +2111,7 @@ TEST(SchedulerStateMachineTest, TestImplLatencyTakesPriority) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Finish the previous commit and draw it. - FinishPreviousCommitAndDrawWithoutExitingDeadline(&state); + FinishPreviousCommitAndDrawWithoutExitingDeadline(&state, true); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Verify we do not send another BeginMainFrame if was are submit-frame @@ -2234,6 +2292,8 @@ TEST(SchedulerStateMachineTest, ImplSideInvalidationOnlyInsideDeadline) { state.SetNeedsImplSideInvalidation(); state.IssueNextBeginImplFrame(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE( @@ -2257,6 +2317,8 @@ TEST(SchedulerStateMachineTest, state.SetNeedsImplSideInvalidation(); state.IssueNextBeginImplFrame(); state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Initializing the CompositorFrameSink puts us in a state waiting for the @@ -2351,6 +2413,8 @@ TEST(SchedulerStateMachineTest, state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_PERFORM_IMPL_SIDE_INVALIDATION); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Request another invalidation, which should wait until the pending tree is @@ -2399,6 +2463,8 @@ TEST(SchedulerStateMachineTest, ImplSideInvalidationsThrottledOnDraw) { state.SetNeedsImplSideInvalidation(); state.IssueNextBeginImplFrame(); state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Ack the previous frame and begin impl frame, which should perform the @@ -2447,6 +2513,8 @@ TEST(SchedulerStateMachineTest, PrepareTilesWaitForImplSideInvalidation) { EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_PERFORM_IMPL_SIDE_INVALIDATION); state.DidPrepareTiles(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } @@ -2473,6 +2541,8 @@ TEST(SchedulerStateMachineTest, TestBeginFrameFreshnessWithoutUpdates) { // OnBeginImplFrame() updates the sequence number. state.OnBeginImplFrame(0, 10); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_SEQUENCE_NUMBERS(10u, BeginFrameArgs::kInvalidFrameNumber, BeginFrameArgs::kInvalidFrameNumber, @@ -2500,6 +2570,8 @@ TEST(SchedulerStateMachineTest, TestBeginFrameFreshnessWithImplFrameUpdates) { // OnBeginImplFrame() updates the sequence number. state.OnBeginImplFrame(0, 10); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_SEQUENCE_NUMBERS(10u, BeginFrameArgs::kInvalidFrameNumber, BeginFrameArgs::kInvalidFrameNumber, @@ -2583,6 +2655,8 @@ TEST(SchedulerStateMachineTest, TestBeginFrameFreshnessWithMainFrameUpdates) { // If no further BeginMainFrame is needed, OnBeginFrameImplDeadline() // updates the pending tree's frame number. state.OnBeginImplFrame(0, 12); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_SEQUENCE_NUMBERS(12u, 10u, 10u, BeginFrameArgs::kInvalidFrameNumber, BeginFrameArgs::kInvalidFrameNumber); @@ -2644,6 +2718,8 @@ TEST(SchedulerStateMachineTest, TestBeginFrameFreshnessWithMainFrameUpdates) { // When no updates are required, OnBeginImplFrameDeadline() updates active // tree and compositor frame freshness. state.OnBeginImplFrame(0, 15); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -2673,6 +2749,8 @@ TEST(SchedulerStateMachineTest, TestBeginFrameFreshnessWithMainFrameUpdates) { // When the source changes, the current frame number is updated and frame // numbers for freshness are reset to invalid numbers. state.OnBeginImplFrame(1, 5); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_SENT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_SEQUENCE_NUMBERS(5u, BeginFrameArgs::kInvalidFrameNumber, BeginFrameArgs::kInvalidFrameNumber, diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index 72e31f53735..1049445fa7f 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -123,6 +123,7 @@ class FakeSchedulerClient : public SchedulerClient, EXPECT_TRUE(inside_begin_impl_frame_); inside_begin_impl_frame_ = false; } + void DidNotProduceFrame(const BeginFrameAck& ack) override {} void ScheduledActionSendBeginMainFrame(const BeginFrameArgs& args) override { PushAction("ScheduledActionSendBeginMainFrame"); @@ -179,6 +180,11 @@ class FakeSchedulerClient : public SchedulerClient, PushAction("SendBeginMainFrameNotExpectedSoon"); } + void ScheduledActionBeginMainFrameNotExpectedUntil( + base::TimeTicks time) override { + PushAction("ScheduledActionBeginMainFrameNotExpectedUntil"); + } + bool IsInsideBeginImplFrame() const { return inside_begin_impl_frame_; } base::Callback<bool(void)> InsideBeginImplFrame(bool state) { @@ -481,12 +487,14 @@ TEST_F(SchedulerTest, VideoNeedsBeginFrames) { EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); // WillBeginImplFrame is responsible for sending BeginFrames to video. - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); client_->Reset(); scheduler_->SetVideoNeedsBeginFrames(false); @@ -537,7 +545,8 @@ TEST_F(SchedulerTest, RequestCommit) { // BeginImplFrame should prepare the draw. EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); @@ -552,7 +561,8 @@ TEST_F(SchedulerTest, RequestCommit) { // The following BeginImplFrame deadline should SetNeedsBeginFrame(false) // to avoid excessive toggles. EXPECT_SCOPED(AdvanceFrame()); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -972,7 +982,8 @@ TEST_F(SchedulerTest, PrepareTiles) { // the deadline task. client->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); // On the deadline, the actions should have occured in the right order. @@ -999,7 +1010,8 @@ TEST_F(SchedulerTest, PrepareTiles) { // the deadline task. client->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); // Draw. The draw will trigger SetNeedsPrepareTiles, and @@ -1019,7 +1031,8 @@ TEST_F(SchedulerTest, PrepareTiles) { // We need a BeginImplFrame where we don't swap to go idle. client->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client); + EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client->Reset(); task_runner().RunPendingTasks(); // Run posted deadline. @@ -1040,7 +1053,8 @@ TEST_F(SchedulerTest, PrepareTiles) { // BeginImplFrame. There will be no draw, only PrepareTiles. client->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client); + EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client->Reset(); task_runner().RunPendingTasks(); // Run posted deadline. @@ -1061,7 +1075,8 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { scheduler_->SetNeedsRedraw(); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->PrepareTilesPending()); @@ -1083,7 +1098,8 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { scheduler_->SetNeedsRedraw(); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -1105,7 +1121,8 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { scheduler_->SetNeedsRedraw(); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->PrepareTilesPending()); @@ -1129,7 +1146,8 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { scheduler_->SetNeedsRedraw(); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->PrepareTilesPending()); @@ -1147,7 +1165,8 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { scheduler_->SetNeedsRedraw(); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -1162,33 +1181,58 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { EXPECT_FALSE(client_->IsInsideBeginImplFrame()); } -TEST_F(SchedulerTest, PrepareTilesFunnelResetOnVisibilityChange) { +TEST_F(SchedulerTest, DidPrepareTilesPreventsPrepareTilesForOneFrame) { std::unique_ptr<SchedulerClientNeedsPrepareTilesInDraw> client = base::WrapUnique(new SchedulerClientNeedsPrepareTilesInDraw); SetUpScheduler(EXTERNAL_BFS, std::move(client)); - // Simulate a few visibility changes and associated PrepareTiles. - for (int i = 0; i < 10; i++) { - scheduler_->SetVisible(false); - scheduler_->WillPrepareTiles(); - scheduler_->DidPrepareTiles(); + client_->Reset(); + scheduler_->SetNeedsRedraw(); + EXPECT_SINGLE_ACTION("AddObserver(this)", client_); - scheduler_->SetVisible(true); + client_->Reset(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); + EXPECT_TRUE(client_->IsInsideBeginImplFrame()); + + client_->Reset(); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_FALSE(client_->IsInsideBeginImplFrame()); + EXPECT_ACTION("ScheduledActionDrawIfPossible", client_, 0, 2); + EXPECT_ACTION("ScheduledActionPrepareTiles", client_, 1, 2); + + // We don't want to hinder scheduled prepare tiles for more than one frame + // even if we call unscheduled prepare tiles many times. + for (int i = 0; i < 10; i++) { scheduler_->WillPrepareTiles(); scheduler_->DidPrepareTiles(); } client_->Reset(); scheduler_->SetNeedsRedraw(); - EXPECT_SINGLE_ACTION("AddObserver(this)", client_); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); + EXPECT_TRUE(client_->IsInsideBeginImplFrame()); + // No scheduled prepare tiles because we've already counted a prepare tiles in + // between frames. client_->Reset(); - AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + task_runner().RunPendingTasks(); // Run posted deadline. + EXPECT_FALSE(client_->IsInsideBeginImplFrame()); + EXPECT_SINGLE_ACTION("ScheduledActionDrawIfPossible", client_); + + client_->Reset(); + scheduler_->SetNeedsRedraw(); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); + // Resume scheduled prepare tiles. client_->Reset(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner().RunPendingTasks(); // Run posted deadline. EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_ACTION("ScheduledActionDrawIfPossible", client_, 0, 2); EXPECT_ACTION("ScheduledActionPrepareTiles", client_, 1, 2); @@ -1671,8 +1715,9 @@ TEST_F(SchedulerTest, scheduler_->SetNeedsRedraw(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); SendNextBeginFrame(); - EXPECT_ACTION("AddObserver(this)", client_, 0, 2); - EXPECT_ACTION("WillBeginImplFrame", client_, 1, 2); + EXPECT_ACTION("AddObserver(this)", client_, 0, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 2, 3); client_->Reset(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); @@ -1704,7 +1749,9 @@ TEST_F(SchedulerTest, client_->Reset(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); SendNextBeginFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, + 2); client_->Reset(); // Deadline should be immediate. @@ -2082,7 +2129,7 @@ void SchedulerTest::BeginFramesNotFromClient(BeginFrameSourceType bfs_type) { // NotifyReadyToCommit should trigger the commit. scheduler_->NotifyBeginMainFrameStarted(base::TimeTicks()); scheduler_->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 1); client_->Reset(); // NotifyReadyToActivate should trigger the activation. @@ -2093,8 +2140,9 @@ void SchedulerTest::BeginFramesNotFromClient(BeginFrameSourceType bfs_type) { // BeginImplFrame deadline should draw. The following BeginImplFrame deadline // should SetNeedsBeginFrame(false) to avoid excessive toggles. EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("ScheduledActionDrawIfPossible", client_, 0, 2); - EXPECT_ACTION("WillBeginImplFrame", client_, 1, 2); + EXPECT_ACTION("ScheduledActionDrawIfPossible", client_, 0, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 2, 3); client_->Reset(); // Make sure SetNeedsBeginFrame isn't called on the client @@ -2345,7 +2393,8 @@ TEST_F(SchedulerTest, DidLoseCompositorFrameSinkAfterSetNeedsPrepareTiles) { client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2506,7 +2555,8 @@ TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottled) { client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); @@ -2520,7 +2570,8 @@ TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottled) { // Unthrottled frame source will immediately begin a new frame. task_runner().RunPendingTasks(); // Run posted BeginFrame. - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2542,7 +2593,8 @@ TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottledBeforeDeadline) { client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); // Switch to an unthrottled frame source before the frame deadline is hit. scheduler_->SetBeginFrameSource(unthrottled_frame_source_.get()); @@ -2553,9 +2605,10 @@ TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottledBeforeDeadline) { client_->Reset(); task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_ACTION("ScheduledActionDrawIfPossible", client_, 0, 2); + EXPECT_ACTION("ScheduledActionDrawIfPossible", client_, 0, 3); // Unthrottled frame source will immediately begin a new frame. - EXPECT_ACTION("WillBeginImplFrame", client_, 1, 2); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 2, 3); scheduler_->SetNeedsRedraw(); client_->Reset(); @@ -2575,7 +2628,8 @@ TEST_F(SchedulerTest, SwitchFrameSourceToThrottled) { client_->Reset(); task_runner().RunPendingTasks(); // Run posted BeginFrame. - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2595,7 +2649,8 @@ TEST_F(SchedulerTest, SwitchFrameSourceToThrottled) { client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); @@ -2611,7 +2666,8 @@ TEST_F(SchedulerTest, SwitchFrameSourceToNullInsideDeadline) { client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); client_->Reset(); // Switch to a null frame source. @@ -2708,6 +2764,23 @@ TEST_F(SchedulerTest, SwitchFrameSourceWhenNotObserving) { EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2); } +// Tests to ensure that we send a ScheduledActionBeginMainFrameNotExpectedUntil +// when expected. +TEST_F(SchedulerTest, ScheduledActionBeginMainFrameNotExpectedUntil) { + SetUpScheduler(EXTERNAL_BFS); + + scheduler_->SetNeedsRedraw(); + EXPECT_ACTION("AddObserver(this)", client_, 0, 1); + client_->Reset(); + + EXPECT_SCOPED(AdvanceFrame()); + task_runner().RunPendingTasks(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 3); + EXPECT_ACTION("ScheduledActionDrawIfPossible", client_, 2, 3); + client_->Reset(); +} + // Tests to ensure that we send a BeginMainFrameNotExpectedSoon when expected. TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon) { SetUpScheduler(EXTERNAL_BFS); @@ -2733,7 +2806,8 @@ TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon) { // The following BeginImplFrame deadline should SetNeedsBeginFrame(false) // and send a SendBeginMainFrameNotExpectedSoon. EXPECT_SCOPED(AdvanceFrame()); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2758,8 +2832,9 @@ TEST_F(SchedulerTest, SynchronousCompositorAnimation) { // Next vsync. AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); - EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 2); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 2, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 3); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2777,8 +2852,9 @@ TEST_F(SchedulerTest, SynchronousCompositorAnimation) { // Next vsync. AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); - EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 2); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 2, 3); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2791,9 +2867,10 @@ TEST_F(SchedulerTest, SynchronousCompositorAnimation) { // Idle on next vsync, as the animation has completed. AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); - EXPECT_ACTION("RemoveObserver(this)", client_, 1, 3); - EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 4); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 4); + EXPECT_ACTION("RemoveObserver(this)", client_, 2, 4); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 3, 4); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); } @@ -2812,9 +2889,10 @@ TEST_F(SchedulerTest, SynchronousCompositorOnDrawDuringIdle) { // Idle on next vsync. AdvanceFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); - EXPECT_ACTION("RemoveObserver(this)", client_, 1, 3); - EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 4); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 4); + EXPECT_ACTION("RemoveObserver(this)", client_, 2, 4); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 3, 4); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); } @@ -2834,7 +2912,8 @@ TEST_F(SchedulerTest, SetNeedsOneBeginImplFrame) { // Next vsync, the first requested frame happens. EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2843,7 +2922,8 @@ TEST_F(SchedulerTest, SetNeedsOneBeginImplFrame) { // Next vsync, the second requested frame happens (the one requested inside // the previous frame's begin impl frame step). EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2904,7 +2984,7 @@ TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { fake_external_begin_frame_source_->LastAckForObserver(scheduler_.get())); scheduler_->NotifyReadyToCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 1); client_->Reset(); scheduler_->NotifyReadyToActivate(); @@ -2913,8 +2993,9 @@ TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { // Next vsync. args = SendNextBeginFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); - EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 2); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 2, 3); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2938,9 +3019,10 @@ TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { // Idle on next vsync. args = SendNextBeginFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); - EXPECT_ACTION("RemoveObserver(this)", client_, 1, 3); - EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 4); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 4); + EXPECT_ACTION("RemoveObserver(this)", client_, 2, 4); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 3, 4); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -3021,8 +3103,9 @@ TEST_F(SchedulerTest, SynchronousCompositorPrepareTilesOnDraw) { // Next vsync. EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); - EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 2); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 2, 3); client_->Reset(); // Android onDraw. @@ -3047,9 +3130,10 @@ TEST_F(SchedulerTest, SynchronousCompositorPrepareTilesOnDraw) { // Next vsync. EXPECT_SCOPED(AdvanceFrame()); EXPECT_FALSE(scheduler_->PrepareTilesPending()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); - EXPECT_ACTION("RemoveObserver(this)", client_, 1, 3); - EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 4); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 4); + EXPECT_ACTION("RemoveObserver(this)", client_, 2, 4); + EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 3, 4); EXPECT_FALSE(scheduler_->begin_frames_expected()); client_->Reset(); } @@ -3064,8 +3148,9 @@ TEST_F(SchedulerTest, SynchronousCompositorSendBeginMainFrameWhileIdle) { // Next vsync. EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); - EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 2); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 2, 3); client_->Reset(); // Android onDraw. @@ -3093,8 +3178,9 @@ TEST_F(SchedulerTest, SynchronousCompositorSendBeginMainFrameWhileIdle) { // Next vsync. EXPECT_SCOPED(AdvanceFrame()); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); - EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 2); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionInvalidateCompositorFrameSink", client_, 1, 3); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 2, 3); client_->Reset(); // Android onDraw. @@ -3229,7 +3315,8 @@ TEST_F(SchedulerTest, ImplSideInvalidationsInDeadline) { scheduler_->SetNeedsImplSideInvalidation(); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); - EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); // Deadline. client_->Reset(); @@ -3395,7 +3482,8 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) { BeginFrameArgs args = SendNextBeginFrame(); EXPECT_LT(latest_confirmed_sequence_number, args.sequence_number); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); @@ -3421,7 +3509,8 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) { args = SendNextBeginFrame(); EXPECT_LT(latest_confirmed_sequence_number, args.sequence_number); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); @@ -3456,7 +3545,8 @@ TEST_F(SchedulerTest, BeginFrameAckForSkippedImplFrame) { client_->Reset(); BeginFrameArgs args = SendNextBeginFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); @@ -3503,7 +3593,8 @@ TEST_F(SchedulerTest, BeginFrameAckForBeginFrameBeforeLastDeadline) { client_->Reset(); SendNextBeginFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); // Until tiles were prepared, further proactive BeginFrames are expected. EXPECT_TRUE(scheduler_->begin_frames_expected()); @@ -3546,7 +3637,8 @@ TEST_F(SchedulerTest, BeginFrameAckForDroppedBeginFrame) { // First BeginFrame is handled by StateMachine. BeginFrameArgs first_args = SendNextBeginFrame(); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); // State machine is no longer interested in BeginFrames, but scheduler is // still observing the source. @@ -3627,7 +3719,8 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedBeginFrameWithNewSourceId) { source_id, 1, now_src()); fake_external_begin_frame_source_->TestOnBeginFrame(args); - EXPECT_ACTION("WillBeginImplFrame", client_, 0, 1); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionBeginMainFrameNotExpectedUntil", client_, 1, 2); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); diff --git a/chromium/cc/surfaces/BUILD.gn b/chromium/cc/surfaces/BUILD.gn index 3ba5480608b..352d636b6b3 100644 --- a/chromium/cc/surfaces/BUILD.gn +++ b/chromium/cc/surfaces/BUILD.gn @@ -45,11 +45,13 @@ cc_component("surfaces") { "display_client.h", "display_scheduler.cc", "display_scheduler.h", - "framesink_manager.cc", - "framesink_manager.h", + "frame_sink_manager.cc", + "frame_sink_manager.h", + "frame_sink_manager_client.h", "local_surface_id_allocator.cc", "local_surface_id_allocator.h", - "pending_frame_observer.h", + "primary_begin_frame_source.cc", + "primary_begin_frame_source.h", "referenced_surface_tracker.cc", "referenced_surface_tracker.h", "sequence_surface_reference_factory.cc", @@ -58,11 +60,10 @@ cc_component("surfaces") { "surface.h", "surface_aggregator.cc", "surface_aggregator.h", + "surface_dependency_deadline.cc", + "surface_dependency_deadline.h", "surface_dependency_tracker.cc", "surface_dependency_tracker.h", - "surface_factory.cc", - "surface_factory.h", - "surface_factory_client.h", "surface_hittest.cc", "surface_hittest.h", "surface_hittest_delegate.h", @@ -70,6 +71,7 @@ cc_component("surfaces") { "surface_manager.h", "surface_resource_holder.cc", "surface_resource_holder.h", + "surface_resource_holder_client.h", "surfaces_export.h", ] diff --git a/chromium/cc/surfaces/compositor_frame_sink_support.cc b/chromium/cc/surfaces/compositor_frame_sink_support.cc index 84b0a41d9cc..fa42842b86d 100644 --- a/chromium/cc/surfaces/compositor_frame_sink_support.cc +++ b/chromium/cc/surfaces/compositor_frame_sink_support.cc @@ -12,6 +12,7 @@ #include "cc/surfaces/compositor_frame_sink_support_client.h" #include "cc/surfaces/display.h" #include "cc/surfaces/surface.h" +#include "cc/surfaces/surface_info.h" #include "cc/surfaces/surface_manager.h" #include "cc/surfaces/surface_reference.h" @@ -27,8 +28,9 @@ std::unique_ptr<CompositorFrameSinkSupport> CompositorFrameSinkSupport::Create( bool needs_sync_points) { std::unique_ptr<CompositorFrameSinkSupport> support = base::WrapUnique(new CompositorFrameSinkSupport( - client, frame_sink_id, is_root, handles_frame_sink_id_invalidation)); - support->Init(surface_manager, needs_sync_points); + client, frame_sink_id, is_root, handles_frame_sink_id_invalidation, + needs_sync_points)); + support->Init(surface_manager); return support; } @@ -43,33 +45,12 @@ CompositorFrameSinkSupport::~CompositorFrameSinkSupport() { reference_tracker_.current_surface_id().is_valid()) RemoveTopLevelRootReference(reference_tracker_.current_surface_id()); - // SurfaceFactory's destructor will attempt to return resources which will - // call back into here and access |client_| so we should destroy - // |surface_factory_|'s resources early on. - surface_factory_->EvictSurface(); - surface_manager_->UnregisterSurfaceFactoryClient(frame_sink_id_); + EvictCurrentSurface(); + surface_manager_->UnregisterFrameSinkManagerClient(frame_sink_id_); if (handles_frame_sink_id_invalidation_) surface_manager_->InvalidateFrameSinkId(frame_sink_id_); } -void CompositorFrameSinkSupport::ReferencedSurfacesChanged( - const LocalSurfaceId& local_surface_id, - const std::vector<SurfaceId>* active_referenced_surfaces) { - if (!surface_manager_->using_surface_references()) - return; - - SurfaceId last_surface_id = reference_tracker_.current_surface_id(); - - // Populate list of surface references to add and remove based on reference - // surfaces in current frame compared with the last frame. The list of - // surface references includes references from both the pending and active - // frame if any. - reference_tracker_.UpdateReferences(local_surface_id, - active_referenced_surfaces); - - UpdateSurfaceReferences(last_surface_id, local_surface_id); -} - void CompositorFrameSinkSupport::ReturnResources( const ReturnedResourceArray& resources) { if (resources.empty()) @@ -93,16 +74,10 @@ void CompositorFrameSinkSupport::SetBeginFrameSource( UpdateNeedsBeginFramesInternal(); } -void CompositorFrameSinkSupport::WillDrawSurface( - const LocalSurfaceId& local_surface_id, - const gfx::Rect& damage_rect) { - if (client_) - client_->WillDrawSurface(local_surface_id, damage_rect); -} - -void CompositorFrameSinkSupport::EvictFrame() { - DCHECK(surface_factory_); - surface_factory_->EvictSurface(); +void CompositorFrameSinkSupport::EvictCurrentSurface() { + if (!current_surface_) + return; + DestroyCurrentSurface(); } void CompositorFrameSinkSupport::SetNeedsBeginFrame(bool needs_begin_frame) { @@ -110,15 +85,11 @@ void CompositorFrameSinkSupport::SetNeedsBeginFrame(bool needs_begin_frame) { UpdateNeedsBeginFramesInternal(); } -void CompositorFrameSinkSupport::BeginFrameDidNotSwap( - const BeginFrameAck& ack) { +void CompositorFrameSinkSupport::DidNotProduceFrame(const BeginFrameAck& ack) { // TODO(eseckler): While a pending CompositorFrame exists (see TODO below), we // should not acknowledge immediately. Instead, we should update the ack that // will be sent to DisplayScheduler when the pending frame is activated. - if (ack.sequence_number < BeginFrameArgs::kStartingFrameNumber) { - DLOG(ERROR) << "Received BeginFrameDidNotSwap with invalid BeginFrameAck."; - return; - } + DCHECK_GE(ack.sequence_number, BeginFrameArgs::kStartingFrameNumber); // |has_damage| is not transmitted, but false by default. DCHECK(!ack.has_damage); @@ -126,27 +97,77 @@ void CompositorFrameSinkSupport::BeginFrameDidNotSwap( begin_frame_source_->DidFinishFrame(this, ack); } -void CompositorFrameSinkSupport::SubmitCompositorFrame( +bool CompositorFrameSinkSupport::SubmitCompositorFrame( const LocalSurfaceId& local_surface_id, CompositorFrame frame) { - DCHECK(surface_factory_); + TRACE_EVENT0("cc", "CompositorFrameSinkSupport::SubmitCompositorFrame"); + DCHECK(local_surface_id.is_valid()); + DCHECK_GE(frame.metadata.begin_frame_ack.sequence_number, + BeginFrameArgs::kStartingFrameNumber); + DCHECK(!frame.render_pass_list.empty()); + ++ack_pending_count_; - if (frame.metadata.begin_frame_ack.sequence_number < - BeginFrameArgs::kStartingFrameNumber) { - DLOG(ERROR) << "Received CompositorFrame with invalid BeginFrameAck."; - frame.metadata.begin_frame_ack.source_id = BeginFrameArgs::kManualSourceId; - frame.metadata.begin_frame_ack.sequence_number = - BeginFrameArgs::kStartingFrameNumber; - } // |has_damage| is not transmitted. frame.metadata.begin_frame_ack.has_damage = true; BeginFrameAck ack = frame.metadata.begin_frame_ack; - surface_factory_->SubmitCompositorFrame( - local_surface_id, std::move(frame), + + if (!ui::LatencyInfo::Verify(frame.metadata.latency_info, + "RenderWidgetHostImpl::OnSwapCompositorFrame")) { + std::vector<ui::LatencyInfo>().swap(frame.metadata.latency_info); + } + for (ui::LatencyInfo& latency : frame.metadata.latency_info) { + if (latency.latency_components().size() > 0) { + latency.AddLatencyNumber(ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, + 0, 0); + } + } + + std::unique_ptr<Surface> surface; + bool create_new_surface = + (!current_surface_ || + local_surface_id != current_surface_->surface_id().local_surface_id()); + if (!create_new_surface) { + surface = std::move(current_surface_); + } else { + SurfaceId surface_id(frame_sink_id_, local_surface_id); + gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size(); + float device_scale_factor = frame.metadata.device_scale_factor; + SurfaceInfo surface_info(surface_id, device_scale_factor, frame_size); + + if (!surface_info.is_valid()) { + TRACE_EVENT_INSTANT0("cc", "Invalid SurfaceInfo", + TRACE_EVENT_SCOPE_THREAD); + if (current_surface_) + DestroyCurrentSurface(); + ReturnedResourceArray resources; + TransferableResource::ReturnResources(frame.resource_list, &resources); + ReturnResources(resources); + DidReceiveCompositorFrameAck(); + return true; + } + + surface = CreateSurface(surface_info); + } + + bool result = surface->QueueFrame( + std::move(frame), base::Bind(&CompositorFrameSinkSupport::DidReceiveCompositorFrameAck, - weak_factory_.GetWeakPtr())); + weak_factory_.GetWeakPtr()), + base::BindRepeating(&CompositorFrameSinkSupport::WillDrawSurface, + weak_factory_.GetWeakPtr())); + + if (!result) { + surface_manager_->DestroySurface(std::move(surface)); + return false; + } + + if (current_surface_) { + surface->SetPreviousFrameSurface(current_surface_.get()); + DestroyCurrentSurface(); + } + current_surface_ = std::move(surface); // TODO(eseckler): The CompositorFrame submitted below might not be activated // right away b/c of surface synchronization. We should only send the @@ -155,6 +176,8 @@ void CompositorFrameSinkSupport::SubmitCompositorFrame( // See https://crbug.com/703079. if (begin_frame_source_) begin_frame_source_->DidFinishFrame(this, ack); + + return true; } void CompositorFrameSinkSupport::UpdateSurfaceReferences( @@ -202,18 +225,39 @@ void CompositorFrameSinkSupport::RemoveTopLevelRootReference( surface_manager_->RemoveSurfaceReferences({reference}); } +void CompositorFrameSinkSupport::ReferencedSurfacesChanged( + const LocalSurfaceId& local_surface_id, + const std::vector<SurfaceId>* active_referenced_surfaces) { + if (!surface_manager_->using_surface_references()) + return; + + SurfaceId last_surface_id = reference_tracker_.current_surface_id(); + + // Populate list of surface references to add and remove based on reference + // surfaces in current frame compared with the last frame. The list of + // surface references includes references from both the pending and active + // frame if any. + reference_tracker_.UpdateReferences(local_surface_id, + active_referenced_surfaces); + + UpdateSurfaceReferences(last_surface_id, local_surface_id); +} + void CompositorFrameSinkSupport::DidReceiveCompositorFrameAck() { DCHECK_GT(ack_pending_count_, 0); ack_pending_count_--; if (!client_) return; + client_->DidReceiveCompositorFrameAck(surface_returned_resources_); surface_returned_resources_.clear(); } -void CompositorFrameSinkSupport::ForceReclaimResources() { - DCHECK(surface_factory_); - surface_factory_->ClearSurface(); +void CompositorFrameSinkSupport::WillDrawSurface( + const LocalSurfaceId& local_surface_id, + const gfx::Rect& damage_rect) { + if (client_) + client_->WillDrawSurface(local_surface_id, damage_rect); } void CompositorFrameSinkSupport::ClaimTemporaryReference( @@ -221,27 +265,41 @@ void CompositorFrameSinkSupport::ClaimTemporaryReference( surface_manager_->AssignTemporaryReference(surface_id, frame_sink_id_); } +void CompositorFrameSinkSupport::ReceiveFromChild( + const TransferableResourceArray& resources) { + surface_resource_holder_.ReceiveFromChild(resources); +} + +void CompositorFrameSinkSupport::RefResources( + const TransferableResourceArray& resources) { + surface_resource_holder_.RefResources(resources); +} + +void CompositorFrameSinkSupport::UnrefResources( + const ReturnedResourceArray& resources) { + surface_resource_holder_.UnrefResources(resources); +} + CompositorFrameSinkSupport::CompositorFrameSinkSupport( CompositorFrameSinkSupportClient* client, const FrameSinkId& frame_sink_id, bool is_root, - bool handles_frame_sink_id_invalidation) + bool handles_frame_sink_id_invalidation, + bool needs_sync_points) : client_(client), frame_sink_id_(frame_sink_id), + surface_resource_holder_(this), reference_tracker_(frame_sink_id), is_root_(is_root), + needs_sync_points_(needs_sync_points), handles_frame_sink_id_invalidation_(handles_frame_sink_id_invalidation), weak_factory_(this) {} -void CompositorFrameSinkSupport::Init(SurfaceManager* surface_manager, - bool needs_sync_points) { +void CompositorFrameSinkSupport::Init(SurfaceManager* surface_manager) { surface_manager_ = surface_manager; - surface_factory_ = - base::MakeUnique<SurfaceFactory>(frame_sink_id_, surface_manager_, this); if (handles_frame_sink_id_invalidation_) surface_manager_->RegisterFrameSinkId(frame_sink_id_); - surface_manager_->RegisterSurfaceFactoryClient(frame_sink_id_, this); - surface_factory_->set_needs_sync_points(needs_sync_points); + surface_manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this); } void CompositorFrameSinkSupport::OnBeginFrame(const BeginFrameArgs& args) { @@ -258,6 +316,30 @@ const BeginFrameArgs& CompositorFrameSinkSupport::LastUsedBeginFrameArgs() void CompositorFrameSinkSupport::OnBeginFrameSourcePausedChanged(bool paused) {} +void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) { + DCHECK(surface->HasActiveFrame()); + if (!seen_first_frame_activation_) { + seen_first_frame_activation_ = true; + + const CompositorFrame& frame = surface->GetActiveFrame(); + gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size(); + + // SurfaceCreated only applies for the first Surface activation. Thus, + // SurfaceFactory stops observing new activations after the first one. + surface_manager_->SurfaceCreated(SurfaceInfo( + surface->surface_id(), frame.metadata.device_scale_factor, frame_size)); + } + // Fire SurfaceCreated first so that a temporary reference is added before it + // is potentially transformed into a real reference by the client. + ReferencedSurfacesChanged(surface->surface_id().local_surface_id(), + surface->active_referenced_surfaces()); + if (!surface_manager_->SurfaceModified(surface->surface_id())) { + TRACE_EVENT_INSTANT0("cc", "Damage not visible.", TRACE_EVENT_SCOPE_THREAD); + surface->RunDrawCallback(); + } + surface_manager_->SurfaceActivated(surface); +} + void CompositorFrameSinkSupport::UpdateNeedsBeginFramesInternal() { if (!begin_frame_source_) return; @@ -272,10 +354,25 @@ void CompositorFrameSinkSupport::UpdateNeedsBeginFramesInternal() { begin_frame_source_->RemoveObserver(this); } +std::unique_ptr<Surface> CompositorFrameSinkSupport::CreateSurface( + const SurfaceInfo& surface_info) { + seen_first_frame_activation_ = false; + return surface_manager_->CreateSurface(weak_factory_.GetWeakPtr(), + surface_info); +} + +void CompositorFrameSinkSupport::DestroyCurrentSurface() { + surface_manager_->DestroySurface(std::move(current_surface_)); +} + void CompositorFrameSinkSupport::RequestCopyOfSurface( - std::unique_ptr<CopyOutputRequest> request) { - DCHECK(surface_factory_); - surface_factory_->RequestCopyOfSurface(std::move(request)); + std::unique_ptr<CopyOutputRequest> copy_request) { + if (!current_surface_) + return; + + DCHECK(current_surface_->compositor_frame_sink_support().get() == this); + current_surface_->RequestCopyOfOutput(std::move(copy_request)); + surface_manager_->SurfaceModified(current_surface_->surface_id()); } } // namespace cc diff --git a/chromium/cc/surfaces/compositor_frame_sink_support.h b/chromium/cc/surfaces/compositor_frame_sink_support.h index 977acdf9788..ed2c154a7f8 100644 --- a/chromium/cc/surfaces/compositor_frame_sink_support.h +++ b/chromium/cc/surfaces/compositor_frame_sink_support.h @@ -13,20 +13,23 @@ #include "base/memory/weak_ptr.h" #include "cc/output/compositor_frame.h" #include "cc/scheduler/begin_frame_source.h" +#include "cc/surfaces/frame_sink_manager_client.h" #include "cc/surfaces/referenced_surface_tracker.h" -#include "cc/surfaces/surface_factory.h" -#include "cc/surfaces/surface_factory_client.h" -#include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_info.h" +#include "cc/surfaces/surface_resource_holder.h" +#include "cc/surfaces/surface_resource_holder_client.h" #include "cc/surfaces/surfaces_export.h" namespace cc { class CompositorFrameSinkSupportClient; +class Surface; class SurfaceManager; class CC_SURFACES_EXPORT CompositorFrameSinkSupport - : public SurfaceFactoryClient, - public BeginFrameObserver { + : public BeginFrameObserver, + public SurfaceResourceHolderClient, + public FrameSinkManagerClient { public: static std::unique_ptr<CompositorFrameSinkSupport> Create( CompositorFrameSinkSupportClient* client, @@ -40,39 +43,43 @@ class CC_SURFACES_EXPORT CompositorFrameSinkSupport const FrameSinkId& frame_sink_id() const { return frame_sink_id_; } - Surface* current_surface_for_testing() { - return surface_factory_->current_surface_for_testing(); - } + Surface* current_surface_for_testing() { return current_surface_.get(); } + SurfaceManager* surface_manager() { return surface_manager_; } + bool needs_sync_points() { return needs_sync_points_; } const ReferencedSurfaceTracker& ReferenceTrackerForTesting() const { return reference_tracker_; } - // SurfaceFactoryClient implementation. - void ReferencedSurfacesChanged( - const LocalSurfaceId& local_surface_id, - const std::vector<SurfaceId>* active_referenced_surfaces) override; + // SurfaceResourceHolderClient implementation. void ReturnResources(const ReturnedResourceArray& resources) override; + + // FrameSinkManagerClient implementation. void SetBeginFrameSource(BeginFrameSource* begin_frame_source) override; - void WillDrawSurface(const LocalSurfaceId& local_surface_id, - const gfx::Rect& damage_rect) override; - void EvictFrame(); + void EvictCurrentSurface(); void SetNeedsBeginFrame(bool needs_begin_frame); - void BeginFrameDidNotSwap(const BeginFrameAck& ack); - void SubmitCompositorFrame(const LocalSurfaceId& local_surface_id, + void DidNotProduceFrame(const BeginFrameAck& ack); + bool SubmitCompositorFrame(const LocalSurfaceId& local_surface_id, CompositorFrame frame); void RequestCopyOfSurface(std::unique_ptr<CopyOutputRequest> request); - void ForceReclaimResources(); void ClaimTemporaryReference(const SurfaceId& surface_id); + // TODO(staraz): Move the following 3 methods to private. + void ReceiveFromChild(const TransferableResourceArray& resources); + void RefResources(const TransferableResourceArray& resources); + void UnrefResources(const ReturnedResourceArray& resources); + + void OnSurfaceActivated(Surface* surface); + protected: CompositorFrameSinkSupport(CompositorFrameSinkSupportClient* client, const FrameSinkId& frame_sink_id, bool is_root, - bool handles_frame_sink_id_invalidation); + bool handles_frame_sink_id_invalidation, + bool needs_sync_points); - void Init(SurfaceManager* surface_manager, bool needs_sync_points); + void Init(SurfaceManager* surface_manager); private: // Update surface references with SurfaceManager for current CompositorFrame @@ -84,8 +91,13 @@ class CC_SURFACES_EXPORT CompositorFrameSinkSupport void AddTopLevelRootReference(const SurfaceId& surface_id); void RemoveTopLevelRootReference(const SurfaceId& surface_id); + void ReferencedSurfacesChanged( + const LocalSurfaceId& local_surface_id, + const std::vector<SurfaceId>* active_referenced_surfaces); void DidReceiveCompositorFrameAck(); + void WillDrawSurface(const LocalSurfaceId& local_surface_id, + const gfx::Rect& damage_rect); // BeginFrameObserver implementation. void OnBeginFrame(const BeginFrameArgs& args) override; @@ -93,6 +105,8 @@ class CC_SURFACES_EXPORT CompositorFrameSinkSupport void OnBeginFrameSourcePausedChanged(bool paused) override; void UpdateNeedsBeginFramesInternal(); + std::unique_ptr<Surface> CreateSurface(const SurfaceInfo& surface_info); + void DestroyCurrentSurface(); CompositorFrameSinkSupportClient* const client_; @@ -100,7 +114,9 @@ class CC_SURFACES_EXPORT CompositorFrameSinkSupport const FrameSinkId frame_sink_id_; - std::unique_ptr<SurfaceFactory> surface_factory_; + SurfaceResourceHolder surface_resource_holder_; + + std::unique_ptr<Surface> current_surface_; // Counts the number of CompositorFrames that have been submitted and have not // yet received an ACK. int ack_pending_count_ = 0; @@ -123,6 +139,8 @@ class CC_SURFACES_EXPORT CompositorFrameSinkSupport ReferencedSurfaceTracker reference_tracker_; const bool is_root_; + const bool needs_sync_points_; + bool seen_first_frame_activation_ = false; // TODO(staraz): Remove this flag once ui::Compositor no longer needs to call // RegisterFrameSinkId(). diff --git a/chromium/cc/surfaces/compositor_frame_sink_support_unittest.cc b/chromium/cc/surfaces/compositor_frame_sink_support_unittest.cc index 6a7a5089ae5..62e2cbe6532 100644 --- a/chromium/cc/surfaces/compositor_frame_sink_support_unittest.cc +++ b/chromium/cc/surfaces/compositor_frame_sink_support_unittest.cc @@ -4,15 +4,20 @@ #include "cc/surfaces/compositor_frame_sink_support.h" -#include "base/debug/stack_trace.h" #include "base/macros.h" #include "cc/output/compositor_frame.h" +#include "cc/output/copy_output_request.h" +#include "cc/output/copy_output_result.h" +#include "cc/resources/resource_provider.h" #include "cc/surfaces/compositor_frame_sink_support_client.h" #include "cc/surfaces/frame_sink_id.h" #include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_info.h" #include "cc/surfaces/surface_manager.h" #include "cc/test/begin_frame_args_test.h" +#include "cc/test/compositor_frame_helpers.h" #include "cc/test/fake_external_begin_frame_source.h" +#include "cc/test/mock_compositor_frame_sink_support_client.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -27,1229 +32,824 @@ namespace cc { namespace test { namespace { -constexpr FrameSinkId kDisplayFrameSink(2, 0); -constexpr FrameSinkId kParentFrameSink(3, 0); -constexpr FrameSinkId kChildFrameSink1(65563, 0); -constexpr FrameSinkId kChildFrameSink2(65564, 0); -constexpr FrameSinkId kArbitraryFrameSink(1337, 7331); +constexpr bool kIsRoot = true; +constexpr bool kIsChildRoot = false; +constexpr bool kHandlesFrameSinkIdInvalidation = true; +constexpr bool kNeedsSyncPoints = true; + +constexpr FrameSinkId kArbitraryFrameSinkId(1, 1); +constexpr FrameSinkId kAnotherArbitraryFrameSinkId(2, 2); +constexpr FrameSinkId kYetAnotherArbitraryFrameSinkId(3, 3); + +const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create(); +const base::UnguessableToken kArbitrarySourceId1 = + base::UnguessableToken::Deserialize(0xdead, 0xbeef); +const base::UnguessableToken kArbitrarySourceId2 = + base::UnguessableToken::Deserialize(0xdead, 0xbee0); + +gpu::SyncToken GenTestSyncToken(int id) { + gpu::SyncToken token; + token.Set(gpu::CommandBufferNamespace::GPU_IO, 0, + gpu::CommandBufferId::FromUnsafeValue(id), 1); + return token; +} -class MockCompositorFrameSinkSupportClient +class FakeCompositorFrameSinkSupportClient : public CompositorFrameSinkSupportClient { public: - MockCompositorFrameSinkSupportClient() { - ON_CALL(*this, ReclaimResources(_)) - .WillByDefault(Invoke( - this, - &MockCompositorFrameSinkSupportClient::ReclaimResourcesInternal)); - ON_CALL(*this, DidReceiveCompositorFrameAck(_)) - .WillByDefault(Invoke( - this, - &MockCompositorFrameSinkSupportClient::ReclaimResourcesInternal)); - } + FakeCompositorFrameSinkSupportClient() = default; + ~FakeCompositorFrameSinkSupportClient() override = default; - ReturnedResourceArray& last_returned_resources() { - return last_returned_resources_; + void DidReceiveCompositorFrameAck( + const ReturnedResourceArray& resources) override { + InsertResources(resources); } - // CompositorFrameSinkSupportClient implementation. - MOCK_METHOD1(DidReceiveCompositorFrameAck, - void(const ReturnedResourceArray&)); - MOCK_METHOD1(OnBeginFrame, void(const BeginFrameArgs&)); - MOCK_METHOD1(ReclaimResources, void(const ReturnedResourceArray&)); - MOCK_METHOD2(WillDrawSurface, void(const LocalSurfaceId&, const gfx::Rect&)); + void OnBeginFrame(const BeginFrameArgs& args) override {} - private: - void ReclaimResourcesInternal(const ReturnedResourceArray& resources) { - last_returned_resources_ = resources; + void ReclaimResources(const ReturnedResourceArray& resources) override { + InsertResources(resources); } - ReturnedResourceArray last_returned_resources_; -}; - -std::vector<SurfaceId> empty_surface_ids() { - return std::vector<SurfaceId>(); -} - -SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) { - return SurfaceId( - frame_sink_id, - LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u))); -} - -CompositorFrame MakeCompositorFrame(std::vector<SurfaceId> embedded_surfaces, - std::vector<SurfaceId> referenced_surfaces, - TransferableResourceArray resource_list) { - CompositorFrame compositor_frame; - compositor_frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, 1, true); - compositor_frame.metadata.embedded_surfaces = std::move(embedded_surfaces); - compositor_frame.metadata.referenced_surfaces = - std::move(referenced_surfaces); - compositor_frame.resource_list = std::move(resource_list); - return compositor_frame; -} - -CompositorFrame MakeCompositorFrame() { - return MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), - TransferableResourceArray()); -} - -CompositorFrame MakeCompositorFrame(std::vector<SurfaceId> embedded_surfaces) { - return MakeCompositorFrame(embedded_surfaces, embedded_surfaces, - TransferableResourceArray()); -} + void WillDrawSurface(const LocalSurfaceId& local_surface_id, + const gfx::Rect& damage_rect) override {} -CompositorFrame MakeCompositorFrame( - std::vector<SurfaceId> embedded_surfaces, - std::vector<SurfaceId> referenced_surfaces) { - return MakeCompositorFrame(std::move(embedded_surfaces), - std::move(referenced_surfaces), - TransferableResourceArray()); -} + void clear_returned_resources() { returned_resources_.clear(); } + const ReturnedResourceArray& returned_resources() { + return returned_resources_; + } -CompositorFrame MakeCompositorFrameWithResources( - std::vector<SurfaceId> embedded_surfaces, - TransferableResourceArray resource_list) { - return MakeCompositorFrame(embedded_surfaces, embedded_surfaces, - std::move(resource_list)); -} + private: + void InsertResources(const ReturnedResourceArray& resources) { + returned_resources_.insert(returned_resources_.end(), resources.begin(), + resources.end()); + } -TransferableResource MakeResource(ResourceId id, - ResourceFormat format, - uint32_t filter, - const gfx::Size& size) { - TransferableResource resource; - resource.id = id; - resource.format = format; - resource.filter = filter; - resource.size = size; - return resource; -} + ReturnedResourceArray returned_resources_; -} // namespace + DISALLOW_COPY_AND_ASSIGN(FakeCompositorFrameSinkSupportClient); +}; -class CompositorFrameSinkSupportTest : public testing::Test { +class CompositorFrameSinkSupportTest : public testing::Test, + public SurfaceObserver { public: CompositorFrameSinkSupportTest() - : surface_manager_(SurfaceManager::LifetimeType::REFERENCES) {} - ~CompositorFrameSinkSupportTest() override {} - - CompositorFrameSinkSupport& display_support() { return *supports_[0]; } - Surface* display_surface() { - return display_support().current_surface_for_testing(); + : support_( + CompositorFrameSinkSupport::Create(&fake_support_client_, + &manager_, + kArbitraryFrameSinkId, + kIsRoot, + kHandlesFrameSinkIdInvalidation, + kNeedsSyncPoints)), + local_surface_id_(3, kArbitraryToken), + frame_sync_token_(GenTestSyncToken(4)), + consumer_sync_token_(GenTestSyncToken(5)) { + manager_.AddObserver(this); } - - CompositorFrameSinkSupport& parent_support() { return *supports_[1]; } - Surface* parent_surface() { - return parent_support().current_surface_for_testing(); - } - const ReferencedSurfaceTracker& parent_reference_tracker() { - return parent_support().ReferenceTrackerForTesting(); + ~CompositorFrameSinkSupportTest() override { + manager_.RemoveObserver(this); + support_->EvictCurrentSurface(); } - CompositorFrameSinkSupport& child_support1() { return *supports_[2]; } - Surface* child_surface1() { - return child_support1().current_surface_for_testing(); + const SurfaceId& last_created_surface_id() const { + return last_created_surface_id_; } - CompositorFrameSinkSupport& child_support2() { return *supports_[3]; } - Surface* child_surface2() { - return child_support2().current_surface_for_testing(); + // SurfaceObserver implementation. + void OnSurfaceCreated(const SurfaceInfo& surface_info) override { + last_created_surface_id_ = surface_info.id(); + last_surface_info_ = surface_info; } - - CompositorFrameSinkSupport& support(int index) { return *supports_[index]; } - Surface* surface(int index) { - return support(index).current_surface_for_testing(); + void OnSurfaceDamaged(const SurfaceId& id, bool* changed) override { + *changed = true; } - - SurfaceManager& surface_manager() { return surface_manager_; } - - // Returns all the references where |surface_id| is the parent. - const SurfaceManager::SurfaceIdSet& GetChildReferences( - const SurfaceId& surface_id) { - return surface_manager().parent_to_child_refs_[surface_id]; - } - - // Returns true if there is a temporary reference for |surface_id|. - bool HasTemporaryReference(const SurfaceId& surface_id) { - return surface_manager().HasTemporaryReference(surface_id); + void OnSurfaceDiscarded(const SurfaceId& surface_id) override {} + + void SubmitCompositorFrameWithResources(ResourceId* resource_ids, + size_t num_resource_ids) { + CompositorFrame frame = MakeCompositorFrame(); + for (size_t i = 0u; i < num_resource_ids; ++i) { + TransferableResource resource; + resource.id = resource_ids[i]; + resource.mailbox_holder.texture_target = GL_TEXTURE_2D; + resource.mailbox_holder.sync_token = frame_sync_token_; + frame.resource_list.push_back(resource); + } + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + EXPECT_EQ(last_created_surface_id_.local_surface_id(), local_surface_id_); } - SurfaceDependencyTracker& dependency_tracker() { - return *surface_manager_.dependency_tracker(); + void UnrefResources(ResourceId* ids_to_unref, + int* counts_to_unref, + size_t num_ids_to_unref) { + ReturnedResourceArray unref_array; + for (size_t i = 0; i < num_ids_to_unref; ++i) { + ReturnedResource resource; + resource.sync_token = consumer_sync_token_; + resource.id = ids_to_unref[i]; + resource.count = counts_to_unref[i]; + unref_array.push_back(resource); + } + support_->UnrefResources(unref_array); } - FakeExternalBeginFrameSource* begin_frame_source() { - return begin_frame_source_.get(); + void CheckReturnedResourcesMatchExpected(ResourceId* expected_returned_ids, + int* expected_returned_counts, + size_t expected_resources, + gpu::SyncToken expected_sync_token) { + const ReturnedResourceArray& actual_resources = + fake_support_client_.returned_resources(); + ASSERT_EQ(expected_resources, actual_resources.size()); + for (size_t i = 0; i < expected_resources; ++i) { + ReturnedResource resource = actual_resources[i]; + EXPECT_EQ(expected_sync_token, resource.sync_token); + EXPECT_EQ(expected_returned_ids[i], resource.id); + EXPECT_EQ(expected_returned_counts[i], resource.count); + } + fake_support_client_.clear_returned_resources(); } - // testing::Test: - void SetUp() override { - testing::Test::SetUp(); - constexpr bool is_root = true; - constexpr bool is_child_root = false; - constexpr bool handles_frame_sink_id_invalidation = true; - constexpr bool needs_sync_points = true; - begin_frame_source_ = - base::MakeUnique<FakeExternalBeginFrameSource>(0.f, false); - surface_manager_.SetDependencyTracker( - base::MakeUnique<SurfaceDependencyTracker>(&surface_manager_, - begin_frame_source_.get())); - supports_.push_back(CompositorFrameSinkSupport::Create( - &support_client_, &surface_manager_, kDisplayFrameSink, is_root, - handles_frame_sink_id_invalidation, needs_sync_points)); - supports_.push_back(CompositorFrameSinkSupport::Create( - &support_client_, &surface_manager_, kParentFrameSink, is_child_root, - handles_frame_sink_id_invalidation, needs_sync_points)); - supports_.push_back(CompositorFrameSinkSupport::Create( - &support_client_, &surface_manager_, kChildFrameSink1, is_child_root, - handles_frame_sink_id_invalidation, needs_sync_points)); - supports_.push_back(CompositorFrameSinkSupport::Create( - &support_client_, &surface_manager_, kChildFrameSink2, is_child_root, - handles_frame_sink_id_invalidation, needs_sync_points)); - - // Normally, the BeginFrameSource would be registered by the Display. We - // register it here so that BeginFrames are received by the display support, - // for use in the PassesOnBeginFrameAcks test. Other supports do not receive - // BeginFrames, since the frame sink hierarchy is not set up in this test. - surface_manager_.RegisterBeginFrameSource(begin_frame_source_.get(), - kDisplayFrameSink); - } - - void TearDown() override { - surface_manager_.SetDependencyTracker(nullptr); - surface_manager_.UnregisterBeginFrameSource(begin_frame_source_.get()); - - // SurfaceDependencyTracker depends on this BeginFrameSource and so it must - // be destroyed AFTER the dependency tracker is destroyed. - begin_frame_source_.reset(); - - supports_.clear(); + void RefCurrentFrameResources() { + Surface* surface = manager_.GetSurfaceForId( + SurfaceId(support_->frame_sink_id(), local_surface_id_)); + support_->RefResources(surface->GetActiveFrame().resource_list); } protected: - testing::NiceMock<MockCompositorFrameSinkSupportClient> support_client_; - - private: - SurfaceManager surface_manager_; - std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_; - std::vector<std::unique_ptr<CompositorFrameSinkSupport>> supports_; - - DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkSupportTest); + SurfaceManager manager_; + FakeCompositorFrameSinkSupportClient fake_support_client_; + std::unique_ptr<CompositorFrameSinkSupport> support_; + LocalSurfaceId local_surface_id_; + SurfaceId last_created_surface_id_; + SurfaceInfo last_surface_info_; + + // This is the sync token submitted with the frame. It should never be + // returned to the client. + const gpu::SyncToken frame_sync_token_; + + // This is the sync token returned by the consumer. It should always be + // returned to the client. + const gpu::SyncToken consumer_sync_token_; }; -// The display root surface should have a surface reference from the top-level -// root added/removed when a CompositorFrame is submitted with a new SurfaceId. -TEST_F(CompositorFrameSinkSupportTest, RootSurfaceReceivesReferences) { - const SurfaceId display_id_first = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId display_id_second = MakeSurfaceId(kDisplayFrameSink, 2); - - // Submit a CompositorFrame for the first display root surface. - display_support().SubmitCompositorFrame(display_id_first.local_surface_id(), - MakeCompositorFrame()); - - // A surface reference from the top-level root is added and there shouldn't be - // a temporary reference. - EXPECT_FALSE(HasTemporaryReference(display_id_first)); - EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), - UnorderedElementsAre(display_id_first)); - - // Submit a CompositorFrame for the second display root surface. - display_support().SubmitCompositorFrame(display_id_second.local_surface_id(), - MakeCompositorFrame()); - - // A surface reference from the top-level root to |display_id_second| should - // be added and the reference to |display_root_first| removed. - EXPECT_FALSE(HasTemporaryReference(display_id_second)); - EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), - UnorderedElementsAre(display_id_second)); - - // Surface |display_id_first| is unreachable and should get deleted. - EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(display_id_first)); +// Tests submitting a frame with resources followed by one with no resources +// with no resource provider action in between. +TEST_F(CompositorFrameSinkSupportTest, ResourceLifetimeSimple) { + ResourceId first_frame_ids[] = {1, 2, 3}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); + + // All of the resources submitted in the first frame are still in use at this + // time by virtue of being in the pending frame, so none can be returned to + // the client yet. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + // The second frame references no resources of first frame and thus should + // make all resources of first frame available to be returned. + SubmitCompositorFrameWithResources(NULL, 0); + + ResourceId expected_returned_ids[] = {1, 2, 3}; + int expected_returned_counts[] = {1, 1, 1}; + // Resources were never consumed so no sync token should be set. + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), gpu::SyncToken()); + + ResourceId third_frame_ids[] = {4, 5, 6}; + SubmitCompositorFrameWithResources(third_frame_ids, + arraysize(third_frame_ids)); + + // All of the resources submitted in the third frame are still in use at this + // time by virtue of being in the pending frame, so none can be returned to + // the client yet. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + // The forth frame references no resources of third frame and thus should + // make all resources of third frame available to be returned. + ResourceId forth_frame_ids[] = {7, 8, 9}; + SubmitCompositorFrameWithResources(forth_frame_ids, + arraysize(forth_frame_ids)); + + ResourceId forth_expected_returned_ids[] = {4, 5, 6}; + int forth_expected_returned_counts[] = {1, 1, 1}; + // Resources were never consumed so no sync token should be set. + CheckReturnedResourcesMatchExpected( + forth_expected_returned_ids, forth_expected_returned_counts, + arraysize(forth_expected_returned_counts), gpu::SyncToken()); } -// The parent Surface is blocked on |child_id1| and |child_id2|. -TEST_F(CompositorFrameSinkSupportTest, DisplayCompositorLockingBlockedOnTwo) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id1, child_id2})); - - // parent_support is blocked on |child_id1| and |child_id2|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1, child_id2)); - - // Submit a CompositorFrame without any dependencies to |child_id1|. - // parent_support should now only be blocked on |child_id2|. - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // Submit a CompositorFrame without any dependencies to |child_id2|. - // parent_support should be activated. - child_support2().SubmitCompositorFrame( - child_id2.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +// Tests submitting a frame with resources followed by one with no resources +// with the resource provider holding everything alive. +TEST_F(CompositorFrameSinkSupportTest, + ResourceLifetimeSimpleWithProviderHoldingAlive) { + ResourceId first_frame_ids[] = {1, 2, 3}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); + + // All of the resources submitted in the first frame are still in use at this + // time by virtue of being in the pending frame, so none can be returned to + // the client yet. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + // Hold on to everything. + RefCurrentFrameResources(); + + // The second frame references no resources and thus should make all resources + // available to be returned as soon as the resource provider releases them. + SubmitCompositorFrameWithResources(NULL, 0); + + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + int release_counts[] = {1, 1, 1}; + UnrefResources(first_frame_ids, release_counts, arraysize(first_frame_ids)); + + // None is returned to the client since DidReceiveCompositorAck is not + // invoked. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + + // Submitting an empty frame causes previous resources referenced by the + // previous frame to be returned to client. + SubmitCompositorFrameWithResources(nullptr, 0); + ResourceId expected_returned_ids[] = {1, 2, 3}; + int expected_returned_counts[] = {1, 1, 1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); } -// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. -TEST_F(CompositorFrameSinkSupportTest, DisplayCompositorLockingBlockedChain) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id1})); - - // parent_support is blocked on |child_id1|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeCompositorFrame({child_id2})); - - // child_support1 should now be blocked on |child_id2|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // The parent should still be blocked on |child_id1| because it's pending. - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - // Submit a CompositorFrame without any dependencies to |child_id2|. - // parent_support should be activated. - child_support2().SubmitCompositorFrame( - child_id2.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - EXPECT_FALSE(dependency_tracker().has_deadline()); - - // child_surface1 should now be active. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // parent_surface should now be active. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +// Tests referencing a resource, unref'ing it to zero, then using it again +// before returning it to the client. +TEST_F(CompositorFrameSinkSupportTest, ResourceReusedBeforeReturn) { + ResourceId first_frame_ids[] = {7}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); + + // This removes all references to resource id 7. + SubmitCompositorFrameWithResources(NULL, 0); + + // This references id 7 again. + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); + + // This removes it again. + SubmitCompositorFrameWithResources(NULL, 0); + + // Now it should be returned. + // We don't care how many entries are in the returned array for 7, so long as + // the total returned count matches the submitted count. + const ReturnedResourceArray& returned = + fake_support_client_.returned_resources(); + size_t return_count = 0; + for (size_t i = 0; i < returned.size(); ++i) { + EXPECT_EQ(7u, returned[i].id); + return_count += returned[i].count; + } + EXPECT_EQ(2u, return_count); } -// parent_surface and child_surface1 are blocked on |child_id2|. -TEST_F(CompositorFrameSinkSupportTest, - DisplayCompositorLockingTwoBlockedOnOne) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id2})); - - // parent_support is blocked on |child_id2|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // child_support1 should now be blocked on |child_id2|. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeCompositorFrame({child_id2})); - - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // The parent should still be blocked on |child_id2|. - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // Submit a CompositorFrame without any dependencies to |child_id2|. - // parent_support should be activated. - child_support2().SubmitCompositorFrame( - child_id2.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - EXPECT_FALSE(dependency_tracker().has_deadline()); - - // child_surface1 should now be active. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // parent_surface should now be active. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +// Tests having resources referenced multiple times, as if referenced by +// multiple providers. +TEST_F(CompositorFrameSinkSupportTest, ResourceRefMultipleTimes) { + ResourceId first_frame_ids[] = {3, 4}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); + + // Ref resources from the first frame twice. + RefCurrentFrameResources(); + RefCurrentFrameResources(); + + ResourceId second_frame_ids[] = {4, 5}; + SubmitCompositorFrameWithResources(second_frame_ids, + arraysize(second_frame_ids)); + + // Ref resources from the second frame 3 times. + RefCurrentFrameResources(); + RefCurrentFrameResources(); + RefCurrentFrameResources(); + + // Submit a frame with no resources to remove all current frame refs from + // submitted resources. + SubmitCompositorFrameWithResources(NULL, 0); + + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + // Expected current refs: + // 3 -> 2 + // 4 -> 2 + 3 = 5 + // 5 -> 3 + { + SCOPED_TRACE("unref all 3"); + ResourceId ids_to_unref[] = {3, 4, 5}; + int counts[] = {1, 1, 1}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + SubmitCompositorFrameWithResources(nullptr, 0); + ResourceId expected_returned_ids[] = {3}; + int expected_returned_counts[] = {1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); + } + + // Expected refs remaining: + // 4 -> 3 + // 5 -> 1 + { + SCOPED_TRACE("unref 4 and 5"); + ResourceId ids_to_unref[] = {4, 5}; + int counts[] = {1, 1}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + SubmitCompositorFrameWithResources(nullptr, 0); + + ResourceId expected_returned_ids[] = {5}; + int expected_returned_counts[] = {1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); + } + + // Now, just 2 refs remaining on resource 4. Unref both at once and make sure + // the returned count is correct. + { + SCOPED_TRACE("unref only 4"); + ResourceId ids_to_unref[] = {4}; + int counts[] = {2}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + SubmitCompositorFrameWithResources(nullptr, 0); + + ResourceId expected_returned_ids[] = {4}; + int expected_returned_counts[] = {2}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); + } } -// parent_surface is blocked on |child_id1|, and child_surface2 is blocked on -// |child_id2| until the deadline hits. -TEST_F(CompositorFrameSinkSupportTest, DisplayCompositorLockingDeadlineHits) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id1})); - - // parent_support is blocked on |child_id1|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeCompositorFrame({child_id2})); - - // child_support1 should now be blocked on |child_id2|. - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // The parent should still be blocked on |child_id1| because it's pending. - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - // There is still a looming deadline! Eeek! - EXPECT_TRUE(dependency_tracker().has_deadline()); - - // parent_support is still blocked on |child_id1|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - // child_support1 is still blocked on |child_id2|. - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); +TEST_F(CompositorFrameSinkSupportTest, ResourceLifetime) { + ResourceId first_frame_ids[] = {1, 2, 3}; + SubmitCompositorFrameWithResources(first_frame_ids, + arraysize(first_frame_ids)); + + // All of the resources submitted in the first frame are still in use at this + // time by virtue of being in the pending frame, so none can be returned to + // the client yet. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + fake_support_client_.clear_returned_resources(); + + // The second frame references some of the same resources, but some different + // ones. We expect to receive back resource 1 with a count of 1 since it was + // only referenced by the first frame. + ResourceId second_frame_ids[] = {2, 3, 4}; + SubmitCompositorFrameWithResources(second_frame_ids, + arraysize(second_frame_ids)); + { + SCOPED_TRACE("second frame"); + ResourceId expected_returned_ids[] = {1}; + int expected_returned_counts[] = {1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), gpu::SyncToken()); } - begin_frame_source()->TestOnBeginFrame(args); + // The third frame references a disjoint set of resources, so we expect to + // receive back all resources from the first and second frames. Resource IDs 2 + // and 3 will have counts of 2, since they were used in both frames, and + // resource ID 4 will have a count of 1. + ResourceId third_frame_ids[] = {10, 11, 12, 13}; + SubmitCompositorFrameWithResources(third_frame_ids, + arraysize(third_frame_ids)); + + { + SCOPED_TRACE("third frame"); + ResourceId expected_returned_ids[] = {2, 3, 4}; + int expected_returned_counts[] = {2, 2, 1}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), gpu::SyncToken()); + } - // The deadline has passed. - EXPECT_FALSE(dependency_tracker().has_deadline()); + // Simulate a ResourceProvider taking a ref on all of the resources. + RefCurrentFrameResources(); - // parent_surface has been activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + ResourceId fourth_frame_ids[] = {12, 13}; + SubmitCompositorFrameWithResources(fourth_frame_ids, + arraysize(fourth_frame_ids)); - // child_surface1 has been activated. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); -} + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); -// Verifies that the deadline does not reset if we submit CompositorFrames -// to new Surfaces with unresolved dependencies. -TEST_F(CompositorFrameSinkSupportTest, - DisplayCompositorLockingFramesSubmittedAfterDeadlineSet) { - const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - for (int i = 0; i < 3; ++i) { - LocalSurfaceId local_surface_id(1, base::UnguessableToken::Create()); - support(i).SubmitCompositorFrame(local_surface_id, - MakeCompositorFrame({arbitrary_id})); - // The deadline has been set. - EXPECT_TRUE(dependency_tracker().has_deadline()); - - // support(i) should be blocked on arbitrary_id. - EXPECT_FALSE(surface(i)->HasActiveFrame()); - EXPECT_TRUE(surface(i)->HasPendingFrame()); - EXPECT_THAT(surface(i)->blocking_surfaces(), - UnorderedElementsAre(arbitrary_id)); - - // Issue a BeginFrame to get closer to the deadline. - begin_frame_source()->TestOnBeginFrame(args); + RefCurrentFrameResources(); + + // All resources are still being used by the external reference, so none can + // be returned to the client. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + + // Release resources associated with the first RefCurrentFrameResources() call + // first. + { + ResourceId ids_to_unref[] = {10, 11, 12, 13}; + int counts[] = {1, 1, 1, 1}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); } - // The deadline hits and all the Surfaces should activate. - begin_frame_source()->TestOnBeginFrame(args); - for (int i = 0; i < 3; ++i) { - EXPECT_TRUE(surface(i)->HasActiveFrame()); - EXPECT_FALSE(surface(i)->HasPendingFrame()); - EXPECT_THAT(surface(i)->blocking_surfaces(), IsEmpty()); + // Nothing is returned to the client yet since DidReceiveCompositorFrameAck + // is not invoked. + { + SCOPED_TRACE("fourth frame, first unref"); + CheckReturnedResourcesMatchExpected(nullptr, nullptr, 0, + consumer_sync_token_); } -} -// This test verifies at the Surface activates once a CompositorFrame is -// submitted that has no unresolved dependencies. -TEST_F(CompositorFrameSinkSupportTest, - DisplayCompositorLockingNewFrameOverridesOldDependencies) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); - - // Submit a CompositorFrame that depends on |arbitrary_id|. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({arbitrary_id})); - - // Verify that the CompositorFrame is blocked on |arbitrary_id|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(arbitrary_id)); - - // Submit a CompositorFrame that has no dependencies. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - // Verify that the CompositorFrame has been activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); -} + { + ResourceId ids_to_unref[] = {12, 13}; + int counts[] = {1, 1}; + UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); + } -// This test verifies that a pending CompositorFrame does not affect surface -// references. A new surface from a child will continue to exist as a temporary -// reference until the parent's frame activates. -TEST_F(CompositorFrameSinkSupportTest, - OnlyActiveFramesAffectSurfaceReferences) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - // child_support1 submits a CompositorFrame without any dependencies. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeCompositorFrame()); - - // Verify that the child surface is not blocked. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // Verify that there's a temporary reference for |child_id1|. - EXPECT_TRUE(HasTemporaryReference(child_id1)); - - // parent_support submits a CompositorFrame that depends on |child_id1| - // (which is already active) and |child_id2|. Thus, the parent should not - // activate immediately. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id1, child_id2})); - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); - - // Verify that there's a temporary reference for |child_id1| that still - // exists. - EXPECT_TRUE(HasTemporaryReference(child_id1)); - - // child_support2 submits a CompositorFrame without any dependencies. - child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), - MakeCompositorFrame()); - - // Verify that the child surface is not blocked. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // Verify that the parent surface's CompositorFrame has activated and that the - // temporary reference has been replaced by a permanent one. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - EXPECT_FALSE(HasTemporaryReference(child_id1)); - EXPECT_THAT(GetChildReferences(parent_id), - UnorderedElementsAre(child_id1, child_id2)); + // Resources 12 and 13 are still in use by the current frame, so they + // shouldn't be available to be returned. + EXPECT_EQ(0u, fake_support_client_.returned_resources().size()); + + // If we submit an empty frame, however, they should become available. + // Resources that were previously unref'd also return at this point. + SubmitCompositorFrameWithResources(NULL, 0u); + + { + SCOPED_TRACE("fourth frame, second unref"); + ResourceId expected_returned_ids[] = {10, 11, 12, 13}; + int expected_returned_counts[] = {1, 1, 2, 2}; + CheckReturnedResourcesMatchExpected( + expected_returned_ids, expected_returned_counts, + arraysize(expected_returned_counts), consumer_sync_token_); + } } -// This test verifies that we do not double count returned resources when a -// CompositorFrame starts out as pending, then becomes active, and then is -// replaced with another active CompositorFrame. -TEST_F(CompositorFrameSinkSupportTest, - DisplayCompositorLockingResourcesOnlyReturnedOnce) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - // The parent submits a CompositorFrame that depends on |child_id| before the - // child submits a CompositorFrame. The CompositorFrame also has resources in - // its resource list. - TransferableResource resource = - MakeResource(1337 /* id */, ALPHA_8 /* format */, 1234 /* filter */, - gfx::Size(1234, 5678)); - TransferableResourceArray resource_list = {resource}; - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrameWithResources({child_id}, resource_list)); - - // Verify that the CompositorFrame is blocked on |child_id|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id)); - - child_support1().SubmitCompositorFrame( - child_id.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - // Verify that the child CompositorFrame activates immediately. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // Verify that the parent has activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - - // The parent submits a CompositorFrame without any dependencies. That frame - // should activate immediately, replacing the earlier frame. The resource from - // the earlier frame should be returned to the client. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), MakeCompositorFrame({empty_surface_ids()})); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - ReturnedResource returned_resource = resource.ToReturnedResource(); - EXPECT_THAT(support_client_.last_returned_resources(), - UnorderedElementsAre(returned_resource)); +TEST_F(CompositorFrameSinkSupportTest, AddDuringEviction) { + MockCompositorFrameSinkSupportClient mock_client; + std::unique_ptr<CompositorFrameSinkSupport> support = + CompositorFrameSinkSupport::Create( + &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + LocalSurfaceId local_surface_id(6, kArbitraryToken); + support->SubmitCompositorFrame(local_surface_id, MakeCompositorFrame()); + + EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(_)) + .WillOnce(testing::InvokeWithoutArgs([&support, &mock_client]() { + LocalSurfaceId new_id(7, base::UnguessableToken::Create()); + support->SubmitCompositorFrame(new_id, MakeCompositorFrame()); + })) + .WillRepeatedly(testing::Return()); + support->EvictCurrentSurface(); } -// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. -// child_support1 evicts its blocked Surface. The parent surface should -// activate. -TEST_F(CompositorFrameSinkSupportTest, EvictSurfaceWithPendingFrame) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - // Submit a CompositorFrame that depends on |child_id1|. - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - MakeCompositorFrame({child_id1})); - - // Verify that the CompositorFrame is blocked on |child_id1|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - // Submit a CompositorFrame that depends on |child_id2|. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeCompositorFrame({child_id2})); - - // Verify that the CompositorFrame is blocked on |child_id2|. - EXPECT_FALSE(child_surface1()->HasActiveFrame()); - EXPECT_TRUE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - - // Evict child_support1's current Surface. - // TODO(fsamuel): EvictFrame => EvictCurrentSurface. - child_support1().EvictFrame(); - - // The parent Surface should immediately activate. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - EXPECT_FALSE(dependency_tracker().has_deadline()); -} +// Tests doing an EvictCurrentSurface before shutting down the factory. +TEST_F(CompositorFrameSinkSupportTest, EvictCurrentSurface) { + MockCompositorFrameSinkSupportClient mock_client; + std::unique_ptr<CompositorFrameSinkSupport> support = + CompositorFrameSinkSupport::Create( + &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + LocalSurfaceId local_surface_id(7, kArbitraryToken); + SurfaceId id(kAnotherArbitraryFrameSinkId, local_surface_id); -// This test verifies that if a surface has both a pending and active -// CompositorFrame and the pending CompositorFrame activates, replacing the -// existing active CompositorFrame, then the surface reference hierarchy will be -// updated allowing garbage collection of surfaces that are no longer -// referenced. -TEST_F(CompositorFrameSinkSupportTest, DropStaleReferencesAfterActivation) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); - - // The parent submits a CompositorFrame that depends on |child_id1| before the - // child submits a CompositorFrame. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id1})); - - // Verify that the CompositorFrame is blocked on |child_id|. - EXPECT_FALSE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id1)); - - // Verify that no references are added while the CompositorFrame is pending. - EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); - - child_support1().SubmitCompositorFrame( - child_id1.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - // Verify that the child CompositorFrame activates immediately. - EXPECT_TRUE(child_surface1()->HasActiveFrame()); - EXPECT_FALSE(child_surface1()->HasPendingFrame()); - EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); - - // Verify that the parent Surface has activated. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - - // Verify that there is no temporary reference for the child and that - // the reference from the parent to the child still exists. - EXPECT_FALSE(HasTemporaryReference(child_id1)); - EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); - - // The parent submits another CompositorFrame that depends on |child_id2|. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id2})); - - // The parent surface should now have both a pending and activate - // CompositorFrame. Verify that the set of child references from - // |parent_id| are only from the active CompositorFrame. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_TRUE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), - UnorderedElementsAre(child_id2)); - EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); - - child_support2().SubmitCompositorFrame( - child_id2.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); - - // Verify that the parent Surface has activated and no longer has a pending - // CompositorFrame. Also verify that |child_id1| is no longer a child - // reference of |parent_id|. - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); - EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id2)); + TransferableResource resource; + resource.id = 1; + resource.mailbox_holder.texture_target = GL_TEXTURE_2D; + CompositorFrame frame = MakeCompositorFrame(); + frame.resource_list.push_back(resource); + support->SubmitCompositorFrame(local_surface_id, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); + local_surface_id_ = LocalSurfaceId(); + + ReturnedResourceArray returned_resources = {resource.ToReturnedResource()}; + EXPECT_TRUE(manager_.GetSurfaceForId(id)); + EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources)) + .Times(1); + support->EvictCurrentSurface(); + EXPECT_FALSE(manager_.GetSurfaceForId(id)); } -// Checks whether the latency info are moved to the new surface from the old -// one when LocalSurfaceId changes. No frame has unresolved dependencies. +// Tests doing an EvictCurrentSurface which has unregistered dependency. TEST_F(CompositorFrameSinkSupportTest, - LatencyInfoCarriedOverOnResize_NoUnresolvedDependencies) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const ui::LatencyComponentType latency_type1 = - ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; - const int64_t latency_id1 = 234; - const int64_t latency_sequence_number1 = 5645432; - const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; - const int64_t latency_id2 = 31434351; - const int64_t latency_sequence_number2 = 663788; - - // Submit a frame with latency info - ui::LatencyInfo info; - info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); + EvictCurrentSurfaceDependencyUnRegistered) { + MockCompositorFrameSinkSupportClient mock_client; + std::unique_ptr<CompositorFrameSinkSupport> support = + CompositorFrameSinkSupport::Create( + &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + LocalSurfaceId local_surface_id(7, kArbitraryToken); + TransferableResource resource; + resource.id = 1; + resource.mailbox_holder.texture_target = GL_TEXTURE_2D; CompositorFrame frame = MakeCompositorFrame(); - frame.metadata.latency_info.push_back(info); - - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - std::move(frame)); - - // Verify that the old surface has an active frame and no pending frame. - Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); - ASSERT_NE(nullptr, old_surface); - EXPECT_TRUE(old_surface->HasActiveFrame()); - EXPECT_FALSE(old_surface->HasPendingFrame()); - - // Submit another frame with some other latency info and a different - // LocalSurfaceId. - ui::LatencyInfo info2; - info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); - - CompositorFrame frame2 = MakeCompositorFrame(); - frame2.metadata.latency_info.push_back(info2); - - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - std::move(frame2)); - - // Verify that the new surface has an active frame and no pending frames. - Surface* surface = surface_manager().GetSurfaceForId(parent_id2); - ASSERT_NE(nullptr, surface); - EXPECT_TRUE(surface->HasActiveFrame()); - EXPECT_FALSE(surface->HasPendingFrame()); - - // Verify that the new surface has both latency info elements. - std::vector<ui::LatencyInfo> info_list; - surface->TakeLatencyInfo(&info_list); - EXPECT_EQ(2u, info_list.size()); - - ui::LatencyInfo aggregated_latency_info = info_list[0]; - aggregated_latency_info.AddNewLatencyFrom(info_list[1]); - EXPECT_EQ(2u, aggregated_latency_info.latency_components().size()); - - ui::LatencyInfo::LatencyComponent comp1; - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); - EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); + frame.resource_list.push_back(resource); + support->SubmitCompositorFrame(local_surface_id, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); + local_surface_id_ = LocalSurfaceId(); + + SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id); + Surface* surface = manager_.GetSurfaceForId(surface_id); + surface->AddDestructionDependency( + SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + + ReturnedResourceArray returned_resource = {resource.ToReturnedResource()}; + + EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); + EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resource)) + .Times(1); + support->EvictCurrentSurface(); + EXPECT_FALSE(manager_.GetSurfaceForId(surface_id)); } -// Checks whether the latency info are moved to the new surface from the old -// one when LocalSurfaceId changes. Old surface has unresolved dependencies. +// Tests doing an EvictCurrentSurface which has registered dependency. TEST_F(CompositorFrameSinkSupportTest, - LatencyInfoCarriedOverOnResize_OldSurfaceHasPendingAndActiveFrame) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - const ui::LatencyComponentType latency_type1 = - ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; - const int64_t latency_id1 = 234; - const int64_t latency_sequence_number1 = 5645432; - const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; - const int64_t latency_id2 = 31434351; - const int64_t latency_sequence_number2 = 663788; - - // Submit a frame with no unresolved dependecy. - ui::LatencyInfo info; - info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); + EvictCurrentSurfaceDependencyRegistered) { + MockCompositorFrameSinkSupportClient mock_client; + std::unique_ptr<CompositorFrameSinkSupport> support = + CompositorFrameSinkSupport::Create( + &mock_client, &manager_, kAnotherArbitraryFrameSinkId, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + LocalSurfaceId local_surface_id(7, kArbitraryToken); + TransferableResource resource; + resource.id = 1; + resource.mailbox_holder.texture_target = GL_TEXTURE_2D; CompositorFrame frame = MakeCompositorFrame(); - frame.metadata.latency_info.push_back(info); + frame.resource_list.push_back(resource); + uint32_t execute_count = 0; + support->SubmitCompositorFrame(local_surface_id, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); + local_surface_id_ = LocalSurfaceId(); - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - std::move(frame)); + manager_.RegisterFrameSinkId(kYetAnotherArbitraryFrameSinkId); - // Submit a frame with unresolved dependencies. - ui::LatencyInfo info2; - info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); + SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id); + Surface* surface = manager_.GetSurfaceForId(surface_id); + surface->AddDestructionDependency( + SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); - CompositorFrame frame2 = MakeCompositorFrame({child_id}); - frame2.metadata.latency_info.push_back(info2); + ReturnedResourceArray returned_resources; + EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); + support->EvictCurrentSurface(); + EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); + EXPECT_EQ(0u, execute_count); + + returned_resources.push_back(resource.ToReturnedResource()); + EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources)) + .Times(1); + manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + EXPECT_FALSE(manager_.GetSurfaceForId(surface_id)); +} - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - std::move(frame2)); +TEST_F(CompositorFrameSinkSupportTest, DestroySequence) { + LocalSurfaceId local_surface_id2(5, kArbitraryToken); + std::unique_ptr<CompositorFrameSinkSupport> support2 = + CompositorFrameSinkSupport::Create( + &fake_support_client_, &manager_, kYetAnotherArbitraryFrameSinkId, + kIsChildRoot, kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + SurfaceId id2(kYetAnotherArbitraryFrameSinkId, local_surface_id2); + support2->SubmitCompositorFrame(local_surface_id2, MakeCompositorFrame()); + + // Check that waiting before the sequence is satisfied works. + manager_.GetSurfaceForId(id2)->AddDestructionDependency( + SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + support2->EvictCurrentSurface(); + + DCHECK(manager_.GetSurfaceForId(id2)); + manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4)); + manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 6)); + DCHECK(!manager_.GetSurfaceForId(id2)); + + // Check that waiting after the sequence is satisfied works. + support2->SubmitCompositorFrame(local_surface_id2, MakeCompositorFrame()); + DCHECK(manager_.GetSurfaceForId(id2)); + manager_.GetSurfaceForId(id2)->AddDestructionDependency( + SurfaceSequence(kAnotherArbitraryFrameSinkId, 6)); + support2->EvictCurrentSurface(); + DCHECK(!manager_.GetSurfaceForId(id2)); +} - // Verify that the old surface has both an active and a pending frame. - Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); - ASSERT_NE(nullptr, old_surface); - EXPECT_TRUE(old_surface->HasActiveFrame()); - EXPECT_TRUE(old_surface->HasPendingFrame()); +// Tests that Surface ID namespace invalidation correctly allows +// Sequences to be ignored. +TEST_F(CompositorFrameSinkSupportTest, InvalidFrameSinkId) { + FrameSinkId frame_sink_id(1234, 5678); - // Submit a frame with a new local surface id. - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - MakeCompositorFrame()); + LocalSurfaceId local_surface_id(5, kArbitraryToken); + SurfaceId id(support_->frame_sink_id(), local_surface_id); + support_->SubmitCompositorFrame(local_surface_id, MakeCompositorFrame()); - // Verify that the new surface has an active frame only. - Surface* surface = surface_manager().GetSurfaceForId(parent_id2); - ASSERT_NE(nullptr, surface); - EXPECT_TRUE(surface->HasActiveFrame()); - EXPECT_FALSE(surface->HasPendingFrame()); + manager_.RegisterFrameSinkId(frame_sink_id); + manager_.GetSurfaceForId(id)->AddDestructionDependency( + SurfaceSequence(frame_sink_id, 4)); - // Verify that the new surface has latency info from both active and pending - // frame of the old surface. - std::vector<ui::LatencyInfo> info_list; - surface->TakeLatencyInfo(&info_list); - EXPECT_EQ(2u, info_list.size()); + support_->EvictCurrentSurface(); - ui::LatencyInfo aggregated_latency_info = info_list[0]; - aggregated_latency_info.AddNewLatencyFrom(info_list[1]); - EXPECT_EQ(2u, aggregated_latency_info.latency_components().size()); + // Verify the dependency has prevented the surface from getting destroyed. + EXPECT_TRUE(manager_.GetSurfaceForId(id)); - ui::LatencyInfo::LatencyComponent comp1; - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); - EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); + manager_.InvalidateFrameSinkId(frame_sink_id); + + // Verify that the invalidated namespace caused the unsatisfied sequence + // to be ignored. + EXPECT_FALSE(manager_.GetSurfaceForId(id)); } -// Checks whether the latency info are moved to the new surface from the old -// one when LocalSurfaceId changes. The new surface has unresolved dependencies. -TEST_F(CompositorFrameSinkSupportTest, - LatencyInfoCarriedOverOnResize_NewSurfaceHasPendingFrame) { - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - const ui::LatencyComponentType latency_type1 = - ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; - const int64_t latency_id1 = 234; - const int64_t latency_sequence_number1 = 5645432; - const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; - const int64_t latency_id2 = 31434351; - const int64_t latency_sequence_number2 = 663788; - - // Submit a frame with no unresolved dependencies. - ui::LatencyInfo info; - info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); +TEST_F(CompositorFrameSinkSupportTest, DestroyCycle) { + LocalSurfaceId local_surface_id2(5, kArbitraryToken); + SurfaceId id2(kYetAnotherArbitraryFrameSinkId, local_surface_id2); + std::unique_ptr<CompositorFrameSinkSupport> support2 = + CompositorFrameSinkSupport::Create( + &fake_support_client_, &manager_, kYetAnotherArbitraryFrameSinkId, + kIsChildRoot, kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + manager_.RegisterFrameSinkId(kAnotherArbitraryFrameSinkId); + // Give id2 a frame that references local_surface_id_. + { + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.referenced_surfaces.push_back( + SurfaceId(support_->frame_sink_id(), local_surface_id_)); + support2->SubmitCompositorFrame(local_surface_id2, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id2); + } + manager_.GetSurfaceForId(id2)->AddDestructionDependency( + SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); + support2->EvictCurrentSurface(); + // Give local_surface_id_ a frame that references id2. + { + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.referenced_surfaces.push_back(id2); + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + } + support_->EvictCurrentSurface(); + EXPECT_TRUE(manager_.GetSurfaceForId(id2)); + // local_surface_id_ should be retained by reference from id2. + EXPECT_TRUE(manager_.GetSurfaceForId( + SurfaceId(support_->frame_sink_id(), local_surface_id_))); + + // Satisfy last destruction dependency for id2. + manager_.SatisfySequence(SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); + + // id2 and local_surface_id_ are in a reference cycle that has no surface + // sequences holding on to it, so they should be destroyed. + EXPECT_TRUE(!manager_.GetSurfaceForId(id2)); + EXPECT_TRUE(!manager_.GetSurfaceForId( + SurfaceId(support_->frame_sink_id(), local_surface_id_))); + + local_surface_id_ = LocalSurfaceId(); +} - CompositorFrame frame = MakeCompositorFrame(); - frame.metadata.latency_info.push_back(info); - - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - std::move(frame)); - - // Verify that the old surface has an active frame only. - Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); - ASSERT_NE(nullptr, old_surface); - EXPECT_TRUE(old_surface->HasActiveFrame()); - EXPECT_FALSE(old_surface->HasPendingFrame()); - - // Submit a frame with a new local surface id and with unresolved - // dependencies. - ui::LatencyInfo info2; - info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); - - CompositorFrame frame2 = MakeCompositorFrame({child_id}); - frame2.metadata.latency_info.push_back(info2); - - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - std::move(frame2)); - - // Verify that the new surface has a pending frame and no active frame. - Surface* surface = surface_manager().GetSurfaceForId(parent_id2); - ASSERT_NE(nullptr, surface); - EXPECT_TRUE(surface->HasPendingFrame()); - EXPECT_FALSE(surface->HasActiveFrame()); - - // Resolve the dependencies. The frame in parent's surface must become active. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - EXPECT_FALSE(surface->HasPendingFrame()); - EXPECT_TRUE(surface->HasActiveFrame()); - - // Both latency info elements must exist in the now-activated frame of the - // new surface. - std::vector<ui::LatencyInfo> info_list; - surface->TakeLatencyInfo(&info_list); - EXPECT_EQ(2u, info_list.size()); - - ui::LatencyInfo aggregated_latency_info = info_list[0]; - aggregated_latency_info.AddNewLatencyFrom(info_list[1]); - EXPECT_EQ(2u, aggregated_latency_info.latency_components().size()); - - ui::LatencyInfo::LatencyComponent comp1; - EXPECT_TRUE( - aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); - EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); +void CopyRequestTestCallback(bool* called, + std::unique_ptr<CopyOutputResult> result) { + *called = true; } -TEST_F(CompositorFrameSinkSupportTest, PassesOnBeginFrameAcks) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); +TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) { + { + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.referenced_surfaces.push_back( + SurfaceId(support_->frame_sink_id(), local_surface_id_)); + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id_); + } - // Request BeginFrames. - display_support().SetNeedsBeginFrame(true); + bool called1 = false; + std::unique_ptr<CopyOutputRequest> request; + request = CopyOutputRequest::CreateRequest( + base::Bind(&CopyRequestTestCallback, &called1)); + request->set_source(kArbitrarySourceId1); + + support_->RequestCopyOfSurface(std::move(request)); + EXPECT_FALSE(called1); + + bool called2 = false; + request = CopyOutputRequest::CreateRequest( + base::Bind(&CopyRequestTestCallback, &called2)); + request->set_source(kArbitrarySourceId2); + + support_->RequestCopyOfSurface(std::move(request)); + // Callbacks have different sources so neither should be called. + EXPECT_FALSE(called1); + EXPECT_FALSE(called2); + + bool called3 = false; + request = CopyOutputRequest::CreateRequest( + base::Bind(&CopyRequestTestCallback, &called3)); + request->set_source(kArbitrarySourceId1); + + support_->RequestCopyOfSurface(std::move(request)); + // Two callbacks are from source1, so the first should be called. + EXPECT_TRUE(called1); + EXPECT_FALSE(called2); + EXPECT_FALSE(called3); + + support_->EvictCurrentSurface(); + local_surface_id_ = LocalSurfaceId(); + EXPECT_TRUE(called1); + EXPECT_TRUE(called2); + EXPECT_TRUE(called3); +} - // Issue a BeginFrame. - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - begin_frame_source()->TestOnBeginFrame(args); +// Check whether the SurfaceInfo object is created and populated correctly +// after the frame submission. +TEST_F(CompositorFrameSinkSupportTest, SurfaceInfo) { + CompositorFrame frame = MakeCompositorFrame(); - // Check that the support forwards a BeginFrameDidNotSwap ack to the - // BeginFrameSource. - BeginFrameAck ack(0, 1, 1, false); - display_support().BeginFrameDidNotSwap(ack); - EXPECT_EQ(ack, begin_frame_source()->LastAckForObserver(&display_support())); + auto render_pass = RenderPass::Create(); + render_pass->SetNew(1, gfx::Rect(5, 6), gfx::Rect(), gfx::Transform()); + frame.render_pass_list.push_back(std::move(render_pass)); - // Issue another BeginFrame. - args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); - begin_frame_source()->TestOnBeginFrame(args); + render_pass = RenderPass::Create(); + render_pass->SetNew(2, gfx::Rect(7, 8), gfx::Rect(), gfx::Transform()); + frame.render_pass_list.push_back(std::move(render_pass)); - // Check that the support forwards the BeginFrameAck attached - // to a CompositorFrame to the BeginFrameSource. - BeginFrameAck ack2(0, 2, 2, true); - CompositorFrame frame = MakeCompositorFrame(); - frame.metadata.begin_frame_ack = ack2; - display_support().SubmitCompositorFrame(display_id.local_surface_id(), - std::move(frame)); - EXPECT_EQ(ack2, begin_frame_source()->LastAckForObserver(&display_support())); -} + frame.metadata.device_scale_factor = 2.5f; -// Checks that resources and ack are sent together if possible. -TEST_F(CompositorFrameSinkSupportTest, ReturnResourcesWithAck) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - TransferableResource resource; - resource.id = 1234; - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrameWithResources(empty_surface_ids(), {resource})); - ReturnedResourceArray returned_resources; - TransferableResource::ReturnResources({resource}, &returned_resources); - EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); - EXPECT_CALL(support_client_, - DidReceiveCompositorFrameAck(Eq(returned_resources))); - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame()); + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + SurfaceId expected_surface_id(support_->frame_sink_id(), local_surface_id_); + EXPECT_EQ(expected_surface_id, last_surface_info_.id()); + EXPECT_EQ(2.5f, last_surface_info_.device_scale_factor()); + EXPECT_EQ(gfx::Size(7, 8), last_surface_info_.size_in_pixels()); } -// Verifies that if a surface is marked destroyed and a new frame arrives for -// it, it will be recovered. -TEST_F(CompositorFrameSinkSupportTest, SurfaceResurrection) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); - - // Add a reference from the parent to the child. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id})); - - // Create the child surface by submitting a frame to it. - EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - - // Verify that the child surface is created. - Surface* surface = surface_manager().GetSurfaceForId(child_id); - EXPECT_NE(nullptr, surface); - - // Attempt to destroy the child surface. The surface must still exist since - // the parent needs it but it will be marked as destroyed. - child_support1().EvictFrame(); - surface = surface_manager().GetSurfaceForId(child_id); - EXPECT_NE(nullptr, surface); - EXPECT_TRUE(surface->destroyed()); - - // Child submits another frame to the same local surface id that is marked - // destroyed. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - - // Verify that the surface that was marked destroyed is recovered and is being - // used again. - Surface* surface2 = surface_manager().GetSurfaceForId(child_id); - EXPECT_EQ(surface, surface2); - EXPECT_FALSE(surface2->destroyed()); +// Check that if a CompositorFrame is received with size zero, we don't create +// a Surface for it. +TEST_F(CompositorFrameSinkSupportTest, ZeroFrameSize) { + SurfaceId id(support_->frame_sink_id(), local_surface_id_); + CompositorFrame frame = MakeEmptyCompositorFrame(); + frame.render_pass_list.push_back(RenderPass::Create()); + EXPECT_TRUE( + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame))); + EXPECT_FALSE(manager_.GetSurfaceForId(id)); } -// Verifies that if a LocalSurfaceId belonged to a surface that doesn't exist -// anymore, it can still be reused for new surfaces. -TEST_F(CompositorFrameSinkSupportTest, LocalSurfaceIdIsReusable) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); - - // Add a reference from parent. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame({child_id})); - - // Submit the first frame. Creates the surface. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); - - // Remove the reference from parant. This allows us to destroy the surface. - parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), - MakeCompositorFrame()); - - // Destroy the surface. - child_support1().EvictFrame(); - EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); - - // Submit another frame with the same local surface id. This should work fine - // and a new surface must be created. - child_support1().SubmitCompositorFrame(child_id.local_surface_id(), - MakeCompositorFrame()); - EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); +// Check that if a CompositorFrame is received with device scale factor of 0, we +// don't create a Surface for it. +TEST_F(CompositorFrameSinkSupportTest, ZeroDeviceScaleFactor) { + SurfaceId id(support_->frame_sink_id(), local_surface_id_); + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.device_scale_factor = 0.f; + EXPECT_TRUE( + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame))); + EXPECT_FALSE(manager_.GetSurfaceForId(id)); } -// This test verifies that a crash does not occur if garbage collection is -// triggered during surface dependency resolution. This test triggers garbage -// collection during surface resolution, by causing an activation to remove -// a surface subtree from the root. Both the old subtree and the new -// activated subtree refer to the same dependency. The old subtree was activated -// by deadline, and the new subtree was activated by a dependency finally -// resolving. -TEST_F(CompositorFrameSinkSupportTest, DependencyTrackingGarbageCollection) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - MakeCompositorFrame({child_id})); - display_support().SubmitCompositorFrame(display_id.local_surface_id(), - MakeCompositorFrame({parent_id1})); - - EXPECT_TRUE(dependency_tracker().has_deadline()); - - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - - // Advance BeginFrames to trigger a deadline. - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); - } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); - - EXPECT_TRUE(display_surface()->HasActiveFrame()); - EXPECT_FALSE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - MakeCompositorFrame({child_id})); - display_support().SubmitCompositorFrame(display_id.local_surface_id(), - MakeCompositorFrame({parent_id2})); - - // The display surface now has two CompositorFrames. One that is pending, - // indirectly blocked on child_id and one that is active, also indirectly - // referring to child_id, but activated due to the deadline above. - EXPECT_TRUE(display_surface()->HasActiveFrame()); - EXPECT_TRUE(display_surface()->HasPendingFrame()); - - // Submitting a CompositorFrame will trigger garbage collection of the - // |parent_id1| subtree. This should not crash. - child_support1().SubmitCompositorFrame( - child_id.local_surface_id(), MakeCompositorFrame(empty_surface_ids())); -} +// Check that if the size of a CompositorFrame doesn't match the size of the +// Surface it's being submitted to, we skip the frame. +TEST_F(CompositorFrameSinkSupportTest, FrameSizeMismatch) { + SurfaceId id(support_->frame_sink_id(), local_surface_id_); -// This test verifies that a crash does not occur if garbage collection is -// triggered when a deadline forces frame activation. This test triggers garbage -// collection during deadline activation by causing the activation of a display -// frame to replace a previously activated display frame that was referring to -// a now-unreachable surface subtree. That subtree gets garbage collected during -// deadline activation. SurfaceDependencyTracker is also tracking a surface -// from that subtree due to an unresolved dependency. This test verifies that -// this dependency resolution does not crash. -TEST_F(CompositorFrameSinkSupportTest, GarbageCollectionOnDeadline) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); - - display_support().SubmitCompositorFrame(display_id.local_surface_id(), - MakeCompositorFrame({parent_id1})); - - EXPECT_TRUE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(dependency_tracker().has_deadline()); - EXPECT_FALSE(display_surface()->HasActiveFrame()); - - // Advance BeginFrames to trigger a deadline. This activates the - // CompositorFrame submitted above. - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); - } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(display_surface()->HasActiveFrame()); - - // |parent_id1| is blocked on |child_id|, but |display_id|'s CompositorFrame - // has activated due to a deadline. |parent_id1| will be tracked by the - // SurfaceDependencyTracker. - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - MakeCompositorFrame({child_id})); - - // By submitting a display CompositorFrame, and replacing the parent's - // CompositorFrame with another surface ID, parent_id1 becomes unreachable and - // a candidate for garbage collection. - display_support().SubmitCompositorFrame(display_id.local_surface_id(), - MakeCompositorFrame({parent_id2})); - - // Now |parent_id1| is only kept alive by the active |display_id| frame. - parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), - MakeCompositorFrame({child_id})); - - // SurfaceDependencyTracker should now be tracking |display_id|, |parent_id1| - // and |parent_id2|. By activating the pending |display_id| frame by deadline, - // |parent_id1| becomes unreachable and is garbage collected while - // SurfaceDependencyTracker is in the process of activating surfaces. This - // should not cause a crash or use-after-free. - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); - } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); + // Submit a frame with size (5,5). + CompositorFrame frame = MakeEmptyCompositorFrame(); + auto pass = RenderPass::Create(); + pass->SetNew(1, gfx::Rect(5, 5), gfx::Rect(), gfx::Transform()); + frame.render_pass_list.push_back(std::move(pass)); + EXPECT_TRUE( + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame))); + EXPECT_TRUE(manager_.GetSurfaceForId(id)); + + // Submit a frame with size (5,4). This frame should be rejected and the + // surface should be destroyed. + frame = MakeEmptyCompositorFrame(); + pass = RenderPass::Create(); + pass->SetNew(1, gfx::Rect(5, 4), gfx::Rect(), gfx::Transform()); + frame.render_pass_list.push_back(std::move(pass)); + EXPECT_FALSE( + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame))); + EXPECT_FALSE(manager_.GetSurfaceForId(id)); } -// This test verifies that a CompositorFrame will only blocked on embedded -// surfaces but not on other retained surface IDs in the CompositorFrame. -TEST_F(CompositorFrameSinkSupportTest, OnlyBlockOnEmbeddedSurfaces) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); - - display_support().SubmitCompositorFrame( - display_id.local_surface_id(), - MakeCompositorFrame({parent_id1}, {parent_id1, parent_id2})); - - EXPECT_TRUE(display_surface()->HasPendingFrame()); - EXPECT_FALSE(display_surface()->HasActiveFrame()); - EXPECT_TRUE(dependency_tracker().has_deadline()); - - // Verify that the display CompositorFrame will only block on |parent_id1| but - // not |parent_id2|. - EXPECT_THAT(display_surface()->blocking_surfaces(), - UnorderedElementsAre(parent_id1)); - // Verify that the display surface holds no references while its - // CompositorFrame is pending. - EXPECT_THAT(GetChildReferences(display_id), IsEmpty()); - - // Submitting a CompositorFrame with |parent_id1| should unblock the display - // CompositorFrame. - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - MakeCompositorFrame()); - - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(display_surface()->HasActiveFrame()); - EXPECT_THAT(display_surface()->blocking_surfaces(), IsEmpty()); - - // Only a reference to |parent_id1| is added because |parent_id2| does not - // exist. - EXPECT_THAT(GetChildReferences(display_id), UnorderedElementsAre(parent_id1)); -} +// Check that if the device scale factor of a CompositorFrame doesn't match the +// device scale factor of the Surface it's being submitted to, the frame is +// rejected and the surface is destroyed. +TEST_F(CompositorFrameSinkSupportTest, DeviceScaleFactorMismatch) { + SurfaceId id(support_->frame_sink_id(), local_surface_id_); -// This test verifies that a late arriving CompositorFrame activates immediately -// and does not trigger a new deadline. -TEST_F(CompositorFrameSinkSupportTest, LateArrivingDependency) { - const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); - const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); - - display_support().SubmitCompositorFrame(display_id.local_surface_id(), - MakeCompositorFrame({parent_id1})); - - EXPECT_TRUE(display_surface()->HasPendingFrame()); - EXPECT_FALSE(display_surface()->HasActiveFrame()); - EXPECT_TRUE(dependency_tracker().has_deadline()); - - // Advance BeginFrames to trigger a deadline. This activates the - // CompositorFrame submitted above. - BeginFrameArgs args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - for (int i = 0; i < 3; ++i) { - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_TRUE(dependency_tracker().has_deadline()); - } - begin_frame_source()->TestOnBeginFrame(args); - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(display_surface()->HasPendingFrame()); - EXPECT_TRUE(display_surface()->HasActiveFrame()); - - // A late arriving CompositorFrame should activate immediately without - // scheduling a deadline and without waiting for dependencies to resolve. - parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), - MakeCompositorFrame({child_id1})); - EXPECT_FALSE(dependency_tracker().has_deadline()); - EXPECT_FALSE(parent_surface()->HasPendingFrame()); - EXPECT_TRUE(parent_surface()->HasActiveFrame()); + // Submit a frame with device scale factor of 0.5. + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.device_scale_factor = 0.5f; + EXPECT_TRUE( + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame))); + EXPECT_TRUE(manager_.GetSurfaceForId(id)); + + // Submit a frame with device scale factor of 0.4. This frame should be + // rejected and the surface should be destroyed. + frame = MakeCompositorFrame(); + frame.metadata.device_scale_factor = 0.4f; + EXPECT_FALSE( + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame))); + EXPECT_FALSE(manager_.GetSurfaceForId(id)); } +} // namespace + } // namespace test + } // namespace cc diff --git a/chromium/cc/surfaces/direct_compositor_frame_sink.cc b/chromium/cc/surfaces/direct_compositor_frame_sink.cc index 13b6c6299b0..da704c512f9 100644 --- a/chromium/cc/surfaces/direct_compositor_frame_sink.cc +++ b/chromium/cc/surfaces/direct_compositor_frame_sink.cc @@ -31,7 +31,7 @@ DirectCompositorFrameSink::DirectCompositorFrameSink( surface_manager_(surface_manager), display_(display) { DCHECK(thread_checker_.CalledOnValidThread()); - capabilities_.can_force_reclaim_resources = true; + capabilities_.must_always_swap = true; // Display and DirectCompositorFrameSink share a GL context, so sync // points aren't needed when passing resources between them. capabilities_.delegated_sync_points_required = false; @@ -47,7 +47,7 @@ DirectCompositorFrameSink::DirectCompositorFrameSink( surface_manager_(surface_manager), display_(display) { DCHECK(thread_checker_.CalledOnValidThread()); - capabilities_.can_force_reclaim_resources = true; + capabilities_.must_always_swap = true; } DirectCompositorFrameSink::~DirectCompositorFrameSink() { @@ -99,19 +99,23 @@ void DirectCompositorFrameSink::SubmitCompositorFrame(CompositorFrame frame) { frame.metadata.begin_frame_ack.sequence_number); gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size(); - if (frame_size.IsEmpty() || frame_size != last_swap_frame_size_) { - delegated_local_surface_id_ = local_surface_id_allocator_.GenerateId(); + if (!local_surface_id_.is_valid() || frame_size != last_swap_frame_size_ || + frame.metadata.device_scale_factor != device_scale_factor_) { + local_surface_id_ = local_surface_id_allocator_.GenerateId(); last_swap_frame_size_ = frame_size; + device_scale_factor_ = frame.metadata.device_scale_factor; + display_->SetLocalSurfaceId(local_surface_id_, device_scale_factor_); } - display_->SetLocalSurfaceId(delegated_local_surface_id_, - frame.metadata.device_scale_factor); - support_->SubmitCompositorFrame(delegated_local_surface_id_, - std::move(frame)); + bool result = + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + DCHECK(result); } -void DirectCompositorFrameSink::ForceReclaimResources() { - support_->ForceReclaimResources(); +void DirectCompositorFrameSink::DidNotProduceFrame(const BeginFrameAck& ack) { + DCHECK(!ack.has_damage); + DCHECK_LE(BeginFrameArgs::kStartingFrameNumber, ack.sequence_number); + support_->DidNotProduceFrame(ack); } void DirectCompositorFrameSink::DisplayOutputSurfaceLost() { @@ -156,10 +160,4 @@ void DirectCompositorFrameSink::OnNeedsBeginFrames(bool needs_begin_frame) { support_->SetNeedsBeginFrame(needs_begin_frame); } -void DirectCompositorFrameSink::OnDidFinishFrame(const BeginFrameAck& ack) { - // If there was damage, SubmitCompositorFrame includes the ack. - if (!ack.has_damage) - support_->BeginFrameDidNotSwap(ack); -} - } // namespace cc diff --git a/chromium/cc/surfaces/direct_compositor_frame_sink.h b/chromium/cc/surfaces/direct_compositor_frame_sink.h index 5aa021b448d..24480cb27ef 100644 --- a/chromium/cc/surfaces/direct_compositor_frame_sink.h +++ b/chromium/cc/surfaces/direct_compositor_frame_sink.h @@ -13,8 +13,6 @@ #include "cc/surfaces/compositor_frame_sink_support_client.h" #include "cc/surfaces/display_client.h" #include "cc/surfaces/local_surface_id_allocator.h" -#include "cc/surfaces/surface_factory.h" -#include "cc/surfaces/surface_factory_client.h" #include "cc/surfaces/surfaces_export.h" namespace cc { @@ -51,7 +49,7 @@ class CC_SURFACES_EXPORT DirectCompositorFrameSink bool BindToClient(CompositorFrameSinkClient* client) override; void DetachFromClient() override; void SubmitCompositorFrame(CompositorFrame frame) override; - void ForceReclaimResources() override; + void DidNotProduceFrame(const BeginFrameAck& ack) override; // DisplayClient implementation. void DisplayOutputSurfaceLost() override; @@ -73,17 +71,17 @@ class CC_SURFACES_EXPORT DirectCompositorFrameSink // ExternalBeginFrameSourceClient implementation: void OnNeedsBeginFrames(bool needs_begin_frame) override; - void OnDidFinishFrame(const BeginFrameAck& ack) override; // This class is only meant to be used on a single thread. base::ThreadChecker thread_checker_; const FrameSinkId frame_sink_id_; - LocalSurfaceId delegated_local_surface_id_; + LocalSurfaceId local_surface_id_; SurfaceManager* surface_manager_; LocalSurfaceIdAllocator local_surface_id_allocator_; Display* display_; gfx::Size last_swap_frame_size_; + float device_scale_factor_ = 1.f; bool is_lost_ = false; std::unique_ptr<ExternalBeginFrameSource> begin_frame_source_; diff --git a/chromium/cc/surfaces/direct_compositor_frame_sink_unittest.cc b/chromium/cc/surfaces/direct_compositor_frame_sink_unittest.cc index 5f1b8b885a0..9fefc2967b6 100644 --- a/chromium/cc/surfaces/direct_compositor_frame_sink_unittest.cc +++ b/chromium/cc/surfaces/direct_compositor_frame_sink_unittest.cc @@ -17,6 +17,7 @@ #include "cc/surfaces/local_surface_id_allocator.h" #include "cc/surfaces/surface_manager.h" #include "cc/test/begin_frame_args_test.h" +#include "cc/test/compositor_frame_helpers.h" #include "cc/test/fake_compositor_frame_sink_client.h" #include "cc/test/fake_output_surface.h" #include "cc/test/ordered_simple_task_runner.h" @@ -108,7 +109,7 @@ class DirectCompositorFrameSinkTest : public testing::Test { std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); render_pass->SetNew(1, display_rect_, damage_rect, gfx::Transform()); - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, 1, true); frame.render_pass_list.push_back(std::move(render_pass)); @@ -171,19 +172,6 @@ TEST_F(DirectCompositorFrameSinkTest, SuspendedDoesNotTriggerSwapBuffers) { EXPECT_EQ(2u, display_output_surface_->num_sent_frames()); } -TEST_F(DirectCompositorFrameSinkTest, - LockingResourcesDoesNotIndirectlyCauseDamage) { - compositor_frame_sink_->ForceReclaimResources(); - EXPECT_EQ(1u, display_output_surface_->num_sent_frames()); - task_runner_->RunPendingTasks(); - EXPECT_EQ(1u, display_output_surface_->num_sent_frames()); - - SwapBuffersWithDamage(gfx::Rect()); - EXPECT_EQ(1u, display_output_surface_->num_sent_frames()); - task_runner_->RunUntilIdle(); - EXPECT_EQ(1u, display_output_surface_->num_sent_frames()); -} - class TestBeginFrameObserver : public BeginFrameObserverBase { public: const BeginFrameAck& ack() const { return ack_; } @@ -215,6 +203,7 @@ TEST_F(DirectCompositorFrameSinkTest, AcknowledgesBeginFramesWithoutDamage) { observer.ack().sequence_number); compositor_frame_sink_client_.begin_frame_source()->DidFinishFrame( &observer, observer.ack()); + compositor_frame_sink_->DidNotProduceFrame(observer.ack()); compositor_frame_sink_client_.begin_frame_source()->RemoveObserver(&observer); // Verify that the frame sink acknowledged the last BeginFrame. diff --git a/chromium/cc/surfaces/display.cc b/chromium/cc/surfaces/display.cc index 2b982d4694b..846897b2180 100644 --- a/chromium/cc/surfaces/display.cc +++ b/chromium/cc/surfaces/display.cc @@ -71,7 +71,7 @@ Display::~Display() { for (const auto& id_entry : aggregator_->previous_contained_surfaces()) { Surface* surface = surface_manager_->GetSurfaceForId(id_entry.first); if (surface) - surface->RunDrawCallbacks(); + surface->RunDrawCallback(); } } } @@ -194,9 +194,9 @@ void Display::InitializeRenderer() { if (output_surface_->context_provider()) { DCHECK(texture_mailbox_deleter_); - renderer_ = base::MakeUnique<GLRenderer>( - &settings_, output_surface_.get(), resource_provider_.get(), - texture_mailbox_deleter_.get(), settings_.highp_threshold_min); + renderer_ = base::MakeUnique<GLRenderer>(&settings_, output_surface_.get(), + resource_provider_.get(), + texture_mailbox_deleter_.get()); } else if (output_surface_->vulkan_context_provider()) { #if defined(ENABLE_VULKAN) DCHECK(texture_mailbox_deleter_); @@ -269,7 +269,7 @@ bool Display::DrawAndSwap() { for (const auto& id_entry : aggregator_->previous_contained_surfaces()) { Surface* surface = surface_manager_->GetSurfaceForId(id_entry.first); if (surface) - surface->RunDrawCallbacks(); + surface->RunDrawCallback(); } frame.metadata.latency_info.insert(frame.metadata.latency_info.end(), @@ -283,19 +283,17 @@ bool Display::DrawAndSwap() { gfx::Size surface_size; bool have_damage = false; - if (!frame.render_pass_list.empty()) { - RenderPass& last_render_pass = *frame.render_pass_list.back(); - if (last_render_pass.output_rect.size() != current_surface_size_ && - last_render_pass.damage_rect == last_render_pass.output_rect && - !current_surface_size_.IsEmpty()) { - // Resize the output rect to the current surface size so that we won't - // skip the draw and so that the GL swap won't stretch the output. - last_render_pass.output_rect.set_size(current_surface_size_); - last_render_pass.damage_rect = last_render_pass.output_rect; - } - surface_size = last_render_pass.output_rect.size(); - have_damage = !last_render_pass.damage_rect.size().IsEmpty(); + RenderPass& last_render_pass = *frame.render_pass_list.back(); + if (last_render_pass.output_rect.size() != current_surface_size_ && + last_render_pass.damage_rect == last_render_pass.output_rect && + !current_surface_size_.IsEmpty()) { + // Resize the output rect to the current surface size so that we won't + // skip the draw and so that the GL swap won't stretch the output. + last_render_pass.output_rect.set_size(current_surface_size_); + last_render_pass.damage_rect = last_render_pass.output_rect; } + surface_size = last_render_pass.output_rect.size(); + have_damage = !last_render_pass.damage_rect.size().IsEmpty(); bool size_matches = surface_size == current_surface_size_; if (!size_matches) @@ -414,6 +412,11 @@ void Display::OnSurfaceDamaged(const SurfaceId& surface_id, bool* changed) { void Display::OnSurfaceCreated(const SurfaceInfo& surface_info) {} +void Display::OnSurfaceDiscarded(const SurfaceId& surface_id) { + if (aggregator_) + aggregator_->ReleaseResources(surface_id); +} + const SurfaceId& Display::CurrentSurfaceId() { return current_surface_id_; } diff --git a/chromium/cc/surfaces/display.h b/chromium/cc/surfaces/display.h index 7718ffe32ea..df0f717061c 100644 --- a/chromium/cc/surfaces/display.h +++ b/chromium/cc/surfaces/display.h @@ -88,6 +88,7 @@ class CC_SURFACES_EXPORT Display : public DisplaySchedulerClient, // SurfaceObserver implementation. void OnSurfaceDamaged(const SurfaceId& surface, bool* changed) override; void OnSurfaceCreated(const SurfaceInfo& surface_info) override; + void OnSurfaceDiscarded(const SurfaceId& surface_id) override; bool has_scheduler() const { return !!scheduler_; } DirectRenderer* renderer_for_testing() const { return renderer_.get(); } @@ -109,8 +110,8 @@ class CC_SURFACES_EXPORT Display : public DisplaySchedulerClient, SurfaceId current_surface_id_; gfx::Size current_surface_size_; float device_scale_factor_ = 1.f; - gfx::ColorSpace blending_color_space_; - gfx::ColorSpace device_color_space_; + gfx::ColorSpace blending_color_space_ = gfx::ColorSpace::CreateSRGB(); + gfx::ColorSpace device_color_space_ = gfx::ColorSpace::CreateSRGB(); bool visible_ = false; bool swapped_since_resize_ = false; bool output_is_secure_ = false; diff --git a/chromium/cc/surfaces/display_unittest.cc b/chromium/cc/surfaces/display_unittest.cc index 603d0682001..2966adddb06 100644 --- a/chromium/cc/surfaces/display_unittest.cc +++ b/chromium/cc/surfaces/display_unittest.cc @@ -21,6 +21,7 @@ #include "cc/surfaces/local_surface_id_allocator.h" #include "cc/surfaces/surface.h" #include "cc/surfaces/surface_manager.h" +#include "cc/test/compositor_frame_helpers.h" #include "cc/test/fake_output_surface.h" #include "cc/test/scheduler_test_common.h" #include "cc/test/test_shared_bitmap_manager.h" @@ -91,7 +92,7 @@ class DisplayTest : public testing::Test { true /* needs_sync_points */)), task_runner_(new base::NullTaskRunner) {} - ~DisplayTest() override { support_->EvictFrame(); } + ~DisplayTest() override { support_->EvictCurrentSurface(); } void SetUpDisplay(const RendererSettings& settings, std::unique_ptr<TestWebGraphicsContext3D> context) { @@ -125,7 +126,7 @@ class DisplayTest : public testing::Test { protected: void SubmitCompositorFrame(RenderPassList* pass_list, const LocalSurfaceId& local_surface_id) { - CompositorFrame frame; + CompositorFrame frame = test::MakeCompositorFrame(); pass_list->swap(frame.render_pass_list); support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); @@ -206,7 +207,6 @@ TEST_F(DisplayTest, DisplayDamaged) { EXPECT_EQ(gfx::Size(100, 100), software_output_device_->viewport_pixel_size()); EXPECT_EQ(gfx::Rect(0, 0, 100, 100), software_output_device_->damage_rect()); - { // Only damaged portion should be swapped. pass = RenderPass::Create(); @@ -260,6 +260,9 @@ TEST_F(DisplayTest, DisplayDamaged) { pass->damage_rect = gfx::Rect(10, 10, 10, 10); pass->id = 1; + local_surface_id = id_allocator_.GenerateId(); + display_->SetLocalSurfaceId(local_surface_id, 1.f); + pass_list.push_back(std::move(pass)); scheduler_->ResetDamageForTest(); SubmitCompositorFrame(&pass_list, local_surface_id); @@ -280,6 +283,9 @@ TEST_F(DisplayTest, DisplayDamaged) { pass->damage_rect = gfx::Rect(10, 10, 0, 0); pass->id = 1; + local_surface_id = id_allocator_.GenerateId(); + display_->SetLocalSurfaceId(local_surface_id, 1.f); + pass_list.push_back(std::move(pass)); scheduler_->ResetDamageForTest(); SubmitCompositorFrame(&pass_list, local_surface_id); @@ -330,7 +336,7 @@ TEST_F(DisplayTest, DisplayDamaged) { pass_list.push_back(std::move(pass)); scheduler_->ResetDamageForTest(); - CompositorFrame frame; + CompositorFrame frame = test::MakeCompositorFrame(); pass_list.swap(frame.render_pass_list); frame.metadata.latency_info.push_back(ui::LatencyInfo()); @@ -347,6 +353,8 @@ TEST_F(DisplayTest, DisplayDamaged) { // Resize should cause a swap if no frame was swapped at the previous size. { + local_surface_id = id_allocator_.GenerateId(); + display_->SetLocalSurfaceId(local_surface_id, 1.f); scheduler_->swapped = false; display_->Resize(gfx::Size(200, 200)); EXPECT_FALSE(scheduler_->swapped); @@ -360,7 +368,7 @@ TEST_F(DisplayTest, DisplayDamaged) { pass_list.push_back(std::move(pass)); scheduler_->ResetDamageForTest(); - CompositorFrame frame; + CompositorFrame frame = test::MakeCompositorFrame(); pass_list.swap(frame.render_pass_list); support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); @@ -378,6 +386,8 @@ TEST_F(DisplayTest, DisplayDamaged) { } { + local_surface_id = id_allocator_.GenerateId(); + display_->SetLocalSurfaceId(local_surface_id, 1.0f); // Surface that's damaged completely should be resized and swapped. pass = RenderPass::Create(); pass->output_rect = gfx::Rect(0, 0, 99, 99); @@ -409,7 +419,8 @@ class MockedContext : public TestWebGraphicsContext3D { }; TEST_F(DisplayTest, Finish) { - LocalSurfaceId local_surface_id(id_allocator_.GenerateId()); + LocalSurfaceId local_surface_id1(id_allocator_.GenerateId()); + LocalSurfaceId local_surface_id2(id_allocator_.GenerateId()); RendererSettings settings; settings.partial_swap_enabled = true; @@ -424,7 +435,7 @@ TEST_F(DisplayTest, Finish) { StubDisplayClient client; display_->Initialize(&client, &manager_); - display_->SetLocalSurfaceId(local_surface_id, 1.f); + display_->SetLocalSurfaceId(local_surface_id1, 1.f); display_->Resize(gfx::Size(100, 100)); @@ -436,7 +447,7 @@ TEST_F(DisplayTest, Finish) { pass->id = 1; pass_list.push_back(std::move(pass)); - SubmitCompositorFrame(&pass_list, local_surface_id); + SubmitCompositorFrame(&pass_list, local_surface_id1); } display_->DrawAndSwap(); @@ -450,6 +461,7 @@ TEST_F(DisplayTest, Finish) { // Another resize without a swap doesn't need to finish. EXPECT_CALL(*context_ptr, shallowFinishCHROMIUM()).Times(0); + display_->SetLocalSurfaceId(local_surface_id2, 1.f); display_->Resize(gfx::Size(200, 200)); testing::Mock::VerifyAndClearExpectations(context_ptr); @@ -462,7 +474,7 @@ TEST_F(DisplayTest, Finish) { pass->id = 1; pass_list.push_back(std::move(pass)); - SubmitCompositorFrame(&pass_list, local_surface_id); + SubmitCompositorFrame(&pass_list, local_surface_id2); } display_->DrawAndSwap(); diff --git a/chromium/cc/surfaces/framesink_manager.cc b/chromium/cc/surfaces/frame_sink_manager.cc index d8fad7e3bbe..22f1d172dfc 100644 --- a/chromium/cc/surfaces/framesink_manager.cc +++ b/chromium/cc/surfaces/frame_sink_manager.cc @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "cc/surfaces/framesink_manager.h" +#include "cc/surfaces/frame_sink_manager.h" #include <stddef.h> #include <stdint.h> #include "base/logging.h" -#include "cc/surfaces/surface_factory_client.h" +#include "cc/surfaces/frame_sink_manager_client.h" +#include "cc/surfaces/primary_begin_frame_source.h" #if DCHECK_IS_ON() #include <sstream> @@ -16,20 +17,18 @@ namespace cc { -FrameSinkManager::FrameSinkSourceMapping::FrameSinkSourceMapping() - : source(nullptr) {} +FrameSinkManager::FrameSinkSourceMapping::FrameSinkSourceMapping() = default; FrameSinkManager::FrameSinkSourceMapping::FrameSinkSourceMapping( const FrameSinkSourceMapping& other) = default; -FrameSinkManager::FrameSinkSourceMapping::~FrameSinkSourceMapping() { -} +FrameSinkManager::FrameSinkSourceMapping::~FrameSinkSourceMapping() = default; -FrameSinkManager::FrameSinkManager() {} +FrameSinkManager::FrameSinkManager() = default; FrameSinkManager::~FrameSinkManager() { - // All surface factory clients should be unregistered prior to SurfaceManager - // destruction. + // All CompositorFrameSinks should be unregistered prior to + // SurfaceManager destruction. DCHECK_EQ(clients_.size(), 0u); DCHECK_EQ(registered_sources_.size(), 0u); } @@ -43,9 +42,9 @@ void FrameSinkManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) { valid_frame_sink_ids_.erase(frame_sink_id); } -void FrameSinkManager::RegisterSurfaceFactoryClient( +void FrameSinkManager::RegisterFrameSinkManagerClient( const FrameSinkId& frame_sink_id, - SurfaceFactoryClient* client) { + FrameSinkManagerClient* client) { DCHECK(client); DCHECK_EQ(valid_frame_sink_ids_.count(frame_sink_id), 1u); @@ -58,7 +57,7 @@ void FrameSinkManager::RegisterSurfaceFactoryClient( } } -void FrameSinkManager::UnregisterSurfaceFactoryClient( +void FrameSinkManager::UnregisterFrameSinkManagerClient( const FrameSinkId& frame_sink_id) { DCHECK_EQ(valid_frame_sink_ids_.count(frame_sink_id), 1u); auto client_iter = clients_.find(frame_sink_id); @@ -83,6 +82,8 @@ void FrameSinkManager::RegisterBeginFrameSource( registered_sources_[source] = frame_sink_id; RecursivelyAttachBeginFrameSource(frame_sink_id, source); + + primary_source_.OnBeginFrameSourceAdded(source); } void FrameSinkManager::UnregisterBeginFrameSource(BeginFrameSource* source) { @@ -92,6 +93,8 @@ void FrameSinkManager::UnregisterBeginFrameSource(BeginFrameSource* source) { FrameSinkId frame_sink_id = registered_sources_[source]; registered_sources_.erase(source); + primary_source_.OnBeginFrameSourceRemoved(source); + if (frame_sink_source_map_.count(frame_sink_id) == 0u) return; @@ -104,6 +107,10 @@ void FrameSinkManager::UnregisterBeginFrameSource(BeginFrameSource* source) { RecursivelyAttachBeginFrameSource(source_iter.second, source_iter.first); } +BeginFrameSource* FrameSinkManager::GetPrimaryBeginFrameSource() { + return &primary_source_; +} + void FrameSinkManager::RecursivelyAttachBeginFrameSource( const FrameSinkId& frame_sink_id, BeginFrameSource* source) { @@ -207,7 +214,7 @@ void FrameSinkManager::UnregisterFrameSinkHierarchy( } DCHECK(found_child); - // The SurfaceFactoryClient and hierarchy can be registered/unregistered + // The CompositorFrameSinkSupport and hierarchy can be registered/unregistered // in either order, so empty frame_sink_source_map entries need to be // checked when removing either clients or relationships. if (!iter->second.has_children() && !clients_.count(parent_frame_sink_id) && diff --git a/chromium/cc/surfaces/framesink_manager.h b/chromium/cc/surfaces/frame_sink_manager.h index 087126c1fb2..11c0f97668d 100644 --- a/chromium/cc/surfaces/framesink_manager.h +++ b/chromium/cc/surfaces/frame_sink_manager.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CC_SURFACES_FRAMESINK_MANAGER_H_ -#define CC_SURFACES_FRAMESINK_MANAGER_H_ +#ifndef CC_SURFACES_FRAME_SINK_MANAGER_H_ +#define CC_SURFACES_FRAME_SINK_MANAGER_H_ #include <stdint.h> @@ -14,14 +14,15 @@ #include "base/logging.h" #include "base/macros.h" #include "cc/surfaces/frame_sink_id.h" +#include "cc/surfaces/primary_begin_frame_source.h" #include "cc/surfaces/surfaces_export.h" namespace cc { class BeginFrameSource; -class SurfaceFactoryClient; +class FrameSinkManagerClient; namespace test { -class CompositorFrameSinkSupportTest; +class SurfaceSynchronizationTest; } class CC_SURFACES_EXPORT FrameSinkManager { @@ -35,20 +36,20 @@ class CC_SURFACES_EXPORT FrameSinkManager { // possibly because a renderer process has crashed. void InvalidateFrameSinkId(const FrameSinkId& frame_sink_id); - // SurfaceFactoryClient, hierarchy, and BeginFrameSource can be registered - // and unregistered in any order with respect to each other. + // CompositorFrameSinkSupport, hierarchy, and BeginFrameSource can be + // registered and unregistered in any order with respect to each other. // // This happens in practice, e.g. the relationship to between ui::Compositor / // DelegatedFrameHost is known before ui::Compositor has a surface/client). // However, DelegatedFrameHost can register itself as a client before its // relationship with the ui::Compositor is known. - // Associates a SurfaceFactoryClient with the frame_sink_id it uses. - // SurfaceFactoryClient and framesink allocators have a 1:1 mapping. + // Associates a FrameSinkManagerClient with the frame_sink_id it uses. + // FrameSinkManagerClient and framesink allocators have a 1:1 mapping. // Caller guarantees the client is alive between register/unregister. - void RegisterSurfaceFactoryClient(const FrameSinkId& frame_sink_id, - SurfaceFactoryClient* client); - void UnregisterSurfaceFactoryClient(const FrameSinkId& frame_sink_id); + void RegisterFrameSinkManagerClient(const FrameSinkId& frame_sink_id, + FrameSinkManagerClient* client); + void UnregisterFrameSinkManagerClient(const FrameSinkId& frame_sink_id); // Associates a |source| with a particular framesink. That framesink and // any children of that framesink with valid clients can potentially use @@ -57,6 +58,10 @@ class CC_SURFACES_EXPORT FrameSinkManager { const FrameSinkId& frame_sink_id); void UnregisterBeginFrameSource(BeginFrameSource* source); + // Returns a stable BeginFrameSource that forwards BeginFrames from the first + // available BeginFrameSource. + BeginFrameSource* GetPrimaryBeginFrameSource(); + // Register a relationship between two framesinks. This relationship means // that surfaces from the child framesik will be displayed in the parent. // Children are allowed to use any begin frame source that their parent can @@ -68,12 +73,12 @@ class CC_SURFACES_EXPORT FrameSinkManager { // Export list of valid frame_sink_ids for SatisfyDestructionDeps in surface // may be removed later when References replace Sequences - std::unordered_set<FrameSinkId, FrameSinkIdHash>* GetValidFrameSinkIds(){ + std::unordered_set<FrameSinkId, FrameSinkIdHash>* GetValidFrameSinkIds() { return &valid_frame_sink_ids_; } private: - friend class test::CompositorFrameSinkSupportTest; + friend class test::SurfaceSynchronizationTest; void RecursivelyAttachBeginFrameSource(const FrameSinkId& frame_sink_id, BeginFrameSource* source); @@ -90,33 +95,36 @@ class CC_SURFACES_EXPORT FrameSinkManager { // considered satisfied. std::unordered_set<FrameSinkId, FrameSinkIdHash> valid_frame_sink_ids_; - // Begin frame source routing. Both BeginFrameSource and SurfaceFactoryClient - // pointers guaranteed alive by callers until unregistered. + // Begin frame source routing. Both BeginFrameSource and + // CompositorFrameSinkSupport pointers guaranteed alive by callers until + // unregistered. struct FrameSinkSourceMapping { FrameSinkSourceMapping(); FrameSinkSourceMapping(const FrameSinkSourceMapping& other); ~FrameSinkSourceMapping(); bool has_children() const { return !children.empty(); } // The currently assigned begin frame source for this client. - BeginFrameSource* source; + BeginFrameSource* source = nullptr; // This represents a dag of parent -> children mapping. std::vector<FrameSinkId> children; }; - std::unordered_map<FrameSinkId, SurfaceFactoryClient*, FrameSinkIdHash> + std::unordered_map<FrameSinkId, FrameSinkManagerClient*, FrameSinkIdHash> clients_; std::unordered_map<FrameSinkId, FrameSinkSourceMapping, FrameSinkIdHash> frame_sink_source_map_; - // Set of which sources are registered to which frmesinks. Any child + // Set of BeginFrameSource along with associated FrameSinkIds. Any child // that is implicitly using this framesink must be reachable by the // parent in the dag. std::unordered_map<BeginFrameSource*, FrameSinkId> registered_sources_; + PrimaryBeginFrameSource primary_source_; + DISALLOW_COPY_AND_ASSIGN(FrameSinkManager); }; } // namespace cc -#endif // CC_SURFACES_FRAMESINK_MANAGER_H_ +#endif // CC_SURFACES_FRAME_SINK_MANAGER_H_ diff --git a/chromium/cc/surfaces/frame_sink_manager_client.h b/chromium/cc/surfaces/frame_sink_manager_client.h new file mode 100644 index 00000000000..83784b9b9ec --- /dev/null +++ b/chromium/cc/surfaces/frame_sink_manager_client.h @@ -0,0 +1,22 @@ +// Copyright 2017 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. +#ifndef CC_SURFACES_FRAME_SINK_MANAGER_CLIENT_H_ +#define CC_SURFACES_FRAME_SINK_MANAGER_CLIENT_H_ + +#include "cc/scheduler/begin_frame_source.h" +#include "cc/surfaces/surfaces_export.h" + +namespace cc { + +class CC_SURFACES_EXPORT FrameSinkManagerClient { + public: + virtual ~FrameSinkManagerClient() = default; + + // This allows the FrameSinkManager to pass a BeginFrameSource to use. + virtual void SetBeginFrameSource(BeginFrameSource* begin_frame_source) = 0; +}; + +} // namespace cc + +#endif // CC_SURFACES_FRAME_SINK_MANAGER_CLIENT_H_ diff --git a/chromium/cc/surfaces/pending_frame_observer.h b/chromium/cc/surfaces/pending_frame_observer.h deleted file mode 100644 index ec7acab2e2a..00000000000 --- a/chromium/cc/surfaces/pending_frame_observer.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2017 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. - -#ifndef CC_SURFACES_PENDING_FRAME_OBSERVER_H_ -#define CC_SURFACES_PENDING_FRAME_OBSERVER_H_ - -#include "base/containers/flat_set.h" -#include "cc/surfaces/surface_id.h" -#include "cc/surfaces/surfaces_export.h" - -namespace cc { - -using SurfaceDependencies = base::flat_set<SurfaceId>; - -class Surface; - -// Clients that wish to observe when the state of a pending CompostiorFrame -// changes should implement this class. -class CC_SURFACES_EXPORT PendingFrameObserver { - public: - // Called when a CompositorFrame within |surface| has activated. - virtual void OnSurfaceActivated(Surface* surface) = 0; - - // Called when the dependencies of a pending CompositorFrame within |surface| - // have changed. - virtual void OnSurfaceDependenciesChanged( - Surface* surface, - const SurfaceDependencies& added_dependencies, - const SurfaceDependencies& removed_dependencies) = 0; - - // Called when |surface| is being destroyed. - virtual void OnSurfaceDiscarded(Surface* surface) = 0; - - protected: - virtual ~PendingFrameObserver() = default; -}; - -} // namespace cc - -#endif // CC_SURFACES_PENDING_FRAME_OBSERVER_H_ diff --git a/chromium/cc/surfaces/primary_begin_frame_source.cc b/chromium/cc/surfaces/primary_begin_frame_source.cc new file mode 100644 index 00000000000..588dbfae67e --- /dev/null +++ b/chromium/cc/surfaces/primary_begin_frame_source.cc @@ -0,0 +1,87 @@ +// Copyright 2017 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/surfaces/primary_begin_frame_source.h" + +namespace cc { + +PrimaryBeginFrameSource::PrimaryBeginFrameSource() + : begin_frame_source_(this) {} + +PrimaryBeginFrameSource::~PrimaryBeginFrameSource() = default; + +void PrimaryBeginFrameSource::OnBeginFrameSourceAdded( + BeginFrameSource* begin_frame_source) { + sources_.insert(begin_frame_source); + + if (current_begin_frame_source_) + return; + + current_begin_frame_source_ = begin_frame_source; + if (needs_begin_frames_ && current_begin_frame_source_) + current_begin_frame_source_->AddObserver(this); +} + +void PrimaryBeginFrameSource::OnBeginFrameSourceRemoved( + BeginFrameSource* begin_frame_source) { + sources_.erase(begin_frame_source); + if (current_begin_frame_source_ != begin_frame_source) + return; + + if (needs_begin_frames_) + current_begin_frame_source_->RemoveObserver(this); + + if (!sources_.empty()) + current_begin_frame_source_ = *sources_.begin(); + else + current_begin_frame_source_ = nullptr; + + if (needs_begin_frames_ && current_begin_frame_source_) + current_begin_frame_source_->AddObserver(this); +} + +void PrimaryBeginFrameSource::OnBeginFrame(const BeginFrameArgs& args) { + begin_frame_source_.OnBeginFrame(args); + last_begin_frame_args_ = args; +} + +const BeginFrameArgs& PrimaryBeginFrameSource::LastUsedBeginFrameArgs() const { + return last_begin_frame_args_; +} + +void PrimaryBeginFrameSource::OnBeginFrameSourcePausedChanged(bool paused) {} + +void PrimaryBeginFrameSource::DidFinishFrame(BeginFrameObserver* obs, + const BeginFrameAck& ack) { + begin_frame_source_.DidFinishFrame(obs, ack); +} + +void PrimaryBeginFrameSource::AddObserver(BeginFrameObserver* obs) { + begin_frame_source_.AddObserver(obs); +} + +void PrimaryBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) { + begin_frame_source_.RemoveObserver(obs); +} + +bool PrimaryBeginFrameSource::IsThrottled() const { + return begin_frame_source_.IsThrottled(); +} + +void PrimaryBeginFrameSource::OnNeedsBeginFrames(bool needs_begin_frames) { + if (needs_begin_frames_ == needs_begin_frames) + return; + + needs_begin_frames_ = needs_begin_frames; + + if (!current_begin_frame_source_) + return; + + if (needs_begin_frames_) + current_begin_frame_source_->AddObserver(this); + else + current_begin_frame_source_->RemoveObserver(this); +} + +} // namespace cc diff --git a/chromium/cc/surfaces/primary_begin_frame_source.h b/chromium/cc/surfaces/primary_begin_frame_source.h new file mode 100644 index 00000000000..e881a297271 --- /dev/null +++ b/chromium/cc/surfaces/primary_begin_frame_source.h @@ -0,0 +1,56 @@ +// Copyright 2017 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. + +#ifndef CC_SURFACES_PRIMARY_BEGIN_FRAME_SOURCE_H_ +#define CC_SURFACES_PRIMARY_BEGIN_FRAME_SOURCE_H_ + +#include "cc/scheduler/begin_frame_source.h" + +namespace cc { + +// PrimaryBeginFrameSource echos the first BeginFrameSource in the system. +// If the first source goes away then it will echo the new first +// BeginFrameSource. +class PrimaryBeginFrameSource : public BeginFrameSource, + public BeginFrameObserver, + public ExternalBeginFrameSourceClient { + public: + PrimaryBeginFrameSource(); + ~PrimaryBeginFrameSource() override; + + void OnBeginFrameSourceAdded(BeginFrameSource* begin_frame_source); + void OnBeginFrameSourceRemoved(BeginFrameSource* begin_frame_source); + + // BeginFrameObserver implementation. + void OnBeginFrame(const BeginFrameArgs& args) override; + const BeginFrameArgs& LastUsedBeginFrameArgs() const override; + void OnBeginFrameSourcePausedChanged(bool paused) override; + + // BeginFrameSource implementation. + void DidFinishFrame(BeginFrameObserver* obs, + const BeginFrameAck& ack) override; + void AddObserver(BeginFrameObserver* obs) override; + void RemoveObserver(BeginFrameObserver* obs) override; + bool IsThrottled() const override; + + // ExternalBeginFrameSourceClient implementation. + void OnNeedsBeginFrames(bool needs_begin_frames) override; + + private: + ExternalBeginFrameSource begin_frame_source_; + BeginFrameSource* current_begin_frame_source_ = nullptr; + + // The last begin frame args generated by the begin frame source. + BeginFrameArgs last_begin_frame_args_; + + bool needs_begin_frames_ = false; + + base::flat_set<BeginFrameSource*> sources_; + + DISALLOW_COPY_AND_ASSIGN(PrimaryBeginFrameSource); +}; + +} // namespace cc + +#endif // CC_SURFACES_PRIMARY_BEGIN_FRAME_SOURCE_H_ diff --git a/chromium/cc/surfaces/referenced_surface_tracker.cc b/chromium/cc/surfaces/referenced_surface_tracker.cc index 5e440d88286..1922a77d937 100644 --- a/chromium/cc/surfaces/referenced_surface_tracker.cc +++ b/chromium/cc/surfaces/referenced_surface_tracker.cc @@ -36,49 +36,36 @@ void ReferencedSurfaceTracker::UpdateReferences( referenced_surfaces_.clear(); } - std::unordered_set<SurfaceId, SurfaceIdHash> referenced_surface_set; + base::flat_set<SurfaceId> new_referenced_surfaces; if (active_referenced_surfaces) { - referenced_surface_set.insert(active_referenced_surfaces->begin(), - active_referenced_surfaces->end()); + new_referenced_surfaces = base::flat_set<SurfaceId>( + active_referenced_surfaces->begin(), active_referenced_surfaces->end(), + base::KEEP_FIRST_OF_DUPES); } + FindReferenceDiff(new_referenced_surfaces); - ProcessNewReferences(referenced_surface_set); + if (!references_to_add_.empty() || !references_to_remove_.empty()) + swap(referenced_surfaces_, new_referenced_surfaces); } -void ReferencedSurfaceTracker::ProcessNewReferences( - const std::unordered_set<SurfaceId, SurfaceIdHash>& - new_referenced_surfaces) { - // Removed references for each SurfaceId in |referenced_surfaces_| if they - // aren't referenced anymore. Removing from a set invalidates iterators, so - // create a new vector then remove everything in it. - std::vector<SurfaceId> not_referenced; +void ReferencedSurfaceTracker::FindReferenceDiff( + const base::flat_set<SurfaceId>& new_referenced_surfaces) { + // Find SurfaceIds in |referenced_surfaces_| that aren't referenced anymore. for (const SurfaceId& surface_id : referenced_surfaces_) { - if (new_referenced_surfaces.count(surface_id) == 0) - not_referenced.push_back(surface_id); + if (new_referenced_surfaces.count(surface_id) == 0) { + references_to_remove_.push_back( + SurfaceReference(current_surface_id_, surface_id)); + } } - for (const SurfaceId& surface_id : not_referenced) - RemoveSurfaceReference(surface_id); - // Add references for each SurfaceId in |new_referenced_surfaces| if they - // aren't already referenced. + // Find SurfaceIds in |new_referenced_surfaces| that aren't already + // referenced. for (const SurfaceId& surface_id : new_referenced_surfaces) { - if (referenced_surfaces_.count(surface_id) == 0) - AddSurfaceReference(surface_id); + if (referenced_surfaces_.count(surface_id) == 0) { + references_to_add_.push_back( + SurfaceReference(current_surface_id_, surface_id)); + } } } -void ReferencedSurfaceTracker::AddSurfaceReference( - const SurfaceId& surface_id) { - references_to_add_.push_back( - SurfaceReference(current_surface_id_, surface_id)); - referenced_surfaces_.insert(surface_id); -} - -void ReferencedSurfaceTracker::RemoveSurfaceReference( - const SurfaceId& surface_id) { - references_to_remove_.push_back( - SurfaceReference(current_surface_id_, surface_id)); - referenced_surfaces_.erase(surface_id); -} - } // namespace cc diff --git a/chromium/cc/surfaces/referenced_surface_tracker.h b/chromium/cc/surfaces/referenced_surface_tracker.h index bfb3f3c7f99..ffe989845d3 100644 --- a/chromium/cc/surfaces/referenced_surface_tracker.h +++ b/chromium/cc/surfaces/referenced_surface_tracker.h @@ -5,9 +5,9 @@ #ifndef CC_SURFACES_REFERENCED_SURFACE_TRACKER_H_ #define CC_SURFACES_REFERENCED_SURFACE_TRACKER_H_ -#include <unordered_set> #include <vector> +#include "base/containers/flat_set.h" #include "base/macros.h" #include "cc/surfaces/frame_sink_id.h" #include "cc/surfaces/surface_id.h" @@ -44,24 +44,18 @@ class CC_SURFACES_EXPORT ReferencedSurfaceTracker { const std::vector<SurfaceId>* active_referenced_surfaces); private: - // Updates |referenced_surfaces_| based on a |new_referenced_surfaces| from a - // CompositorFrame. Populates |references_to_add_| and |references_to_remove_| - // based on the difference between the sets. - void ProcessNewReferences(const std::unordered_set<SurfaceId, SurfaceIdHash>& - new_referenced_surfaces); - - // Adds reference from |current_surface_id_| to |surface_id|. - void AddSurfaceReference(const SurfaceId& surface_id); - - // Removes reference from |current_surface_id_| to |surface_id|. - void RemoveSurfaceReference(const SurfaceId& surface_id); + // Finds the difference between original |referenced_surfaces_| and new + // |new_referenced_surfaces|. Populates |references_to_add_| and + // |references_to_remove_| based on the difference between the sets. + void FindReferenceDiff( + const base::flat_set<SurfaceId>& new_referenced_surfaces); // The id of the client surface that is embedding other surfaces. SurfaceId current_surface_id_; - // TODO(samans): Use the same SurfaceId set that SurfaceManager holds. + // TODO(kylechar): Use the same SurfaceId set that SurfaceManager holds. // Set of surfaces referenced by the last submitted CompositorFrame. - std::unordered_set<SurfaceId, SurfaceIdHash> referenced_surfaces_; + base::flat_set<SurfaceId> referenced_surfaces_; // References to surfaces that should be added for the next CompositorFrame. std::vector<SurfaceReference> references_to_add_; diff --git a/chromium/cc/surfaces/surface.cc b/chromium/cc/surfaces/surface.cc index a161eee5a0c..9653f6005dc 100644 --- a/chromium/cc/surfaces/surface.cc +++ b/chromium/cc/surfaces/surface.cc @@ -12,10 +12,10 @@ #include "base/stl_util.h" #include "cc/output/compositor_frame.h" #include "cc/output/copy_output_request.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/local_surface_id_allocator.h" -#include "cc/surfaces/pending_frame_observer.h" -#include "cc/surfaces/surface_factory.h" #include "cc/surfaces/surface_manager.h" +#include "cc/surfaces/surface_resource_holder_client.h" namespace cc { @@ -23,84 +23,123 @@ namespace cc { // completely damaged the first time they're drawn from. static const int kFrameIndexStart = 2; -Surface::Surface(const SurfaceId& id, base::WeakPtr<SurfaceFactory> factory) - : surface_id_(id), - previous_frame_surface_id_(id), - factory_(factory), +Surface::Surface( + const SurfaceInfo& surface_info, + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support) + : surface_info_(surface_info), + previous_frame_surface_id_(surface_info.id()), + compositor_frame_sink_support_(std::move(compositor_frame_sink_support)), + surface_manager_(compositor_frame_sink_support_->surface_manager()), frame_index_(kFrameIndexStart), destroyed_(false) {} Surface::~Surface() { ClearCopyRequests(); - if (factory_) { - if (pending_frame_) - UnrefFrameResources(*pending_frame_); - if (active_frame_) - UnrefFrameResources(*active_frame_); - } - if (!draw_callback_.is_null()) - draw_callback_.Run(); + surface_manager_->SurfaceDiscarded(this); - for (auto& observer : observers_) - observer.OnSurfaceDiscarded(this); - observers_.Clear(); + UnrefFrameResourcesAndRunDrawCallback(std::move(pending_frame_data_)); + UnrefFrameResourcesAndRunDrawCallback(std::move(active_frame_data_)); } void Surface::SetPreviousFrameSurface(Surface* surface) { - DCHECK(surface); + DCHECK(surface && (HasActiveFrame() || HasPendingFrame())); frame_index_ = surface->frame_index() + 1; previous_frame_surface_id_ = surface->surface_id(); - CompositorFrame& frame = active_frame_ ? *active_frame_ : *pending_frame_; + CompositorFrame& frame = active_frame_data_ ? active_frame_data_->frame + : pending_frame_data_->frame; surface->TakeLatencyInfo(&frame.metadata.latency_info); surface->TakeLatencyInfoFromPendingFrame(&frame.metadata.latency_info); } -void Surface::QueueFrame(CompositorFrame frame, const DrawCallback& callback) { +void Surface::Close() { + closed_ = true; +} + +bool Surface::QueueFrame(CompositorFrame frame, + const base::Closure& callback, + const WillDrawCallback& will_draw_callback) { + gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size(); + float device_scale_factor = frame.metadata.device_scale_factor; + + if (frame_size != surface_info_.size_in_pixels() || + device_scale_factor != surface_info_.device_scale_factor()) { + TRACE_EVENT_INSTANT0("cc", "Surface invariants violation", + TRACE_EVENT_SCOPE_THREAD); + return false; + } + + if (closed_) { + if (compositor_frame_sink_support_) { + ReturnedResourceArray resources; + TransferableResource::ReturnResources(frame.resource_list, &resources); + compositor_frame_sink_support_->ReturnResources(resources); + } + callback.Run(); + return true; + } + TakeLatencyInfoFromPendingFrame(&frame.metadata.latency_info); - base::Optional<CompositorFrame> previous_pending_frame = - std::move(pending_frame_); - pending_frame_.reset(); + base::Optional<FrameData> previous_pending_frame_data = + std::move(pending_frame_data_); + pending_frame_data_.reset(); - UpdateBlockingSurfaces(previous_pending_frame, frame); + UpdateBlockingSurfaces(previous_pending_frame_data.has_value(), frame); // Receive and track the resources referenced from the CompositorFrame // regardless of whether it's pending or active. - factory_->ReceiveFromChild(frame.resource_list); - - if (!blocking_surfaces_.empty()) { - pending_frame_ = std::move(frame); + compositor_frame_sink_support_->ReceiveFromChild(frame.resource_list); + + bool is_pending_frame = !blocking_surfaces_.empty(); + + if (is_pending_frame) { + // Reject CompositorFrames submitted to surfaces referenced from this + // CompositorFrame as fallbacks. This saves some CPU cycles to allow + // children to catch up to the parent. + base::flat_set<FrameSinkId> frame_sink_ids_for_dependencies; + for (const SurfaceId& surface_id : frame.metadata.activation_dependencies) + frame_sink_ids_for_dependencies.insert(surface_id.frame_sink_id()); + for (const SurfaceId& surface_id : frame.metadata.referenced_surfaces) { + // A surface ID in |referenced_surfaces| that has a corresponding surface + // ID in |activation_dependencies| with the same frame sink ID is said to + // be a fallback surface that can be used in place of the primary surface + // if the deadline passes before the dependency becomes available. + bool is_fallback_surface = + frame_sink_ids_for_dependencies.count(surface_id.frame_sink_id()) > 0; + if (is_fallback_surface) { + Surface* surface = surface_manager_->GetSurfaceForId(surface_id); + // A misbehaving client may report a non-existent surface ID as a + // |referenced_surface|. In that case, |surface| would be nullptr, and + // there is nothing to do here. + if (surface) + surface->Close(); + } + } + pending_frame_data_ = + FrameData(std::move(frame), callback, will_draw_callback); // Ask the surface manager to inform |this| when its dependencies are // resolved. - factory_->manager()->RequestSurfaceResolution(this); + surface_manager_->RequestSurfaceResolution(this); } else { // If there are no blockers, then immediately activate the frame. - ActivateFrame(std::move(frame)); + ActivateFrame(FrameData(std::move(frame), callback, will_draw_callback)); } // Returns resources for the previous pending frame. - if (previous_pending_frame) - UnrefFrameResources(*previous_pending_frame); + UnrefFrameResourcesAndRunDrawCallback(std::move(previous_pending_frame_data)); - if (!draw_callback_.is_null()) - draw_callback_.Run(); - draw_callback_ = callback; -} - -void Surface::EvictFrame() { - QueueFrame(CompositorFrame(), DrawCallback()); - active_frame_.reset(); + return true; } void Surface::RequestCopyOfOutput( std::unique_ptr<CopyOutputRequest> copy_request) { - if (!active_frame_ || active_frame_->render_pass_list.empty()) { + if (!active_frame_data_) { copy_request->SendEmptyResult(); return; } std::vector<std::unique_ptr<CopyOutputRequest>>& copy_requests = - active_frame_->render_pass_list.back()->copy_requests; + active_frame_data_->frame.render_pass_list.back()->copy_requests; if (copy_request->has_source()) { const base::UnguessableToken& source = copy_request->source(); @@ -129,17 +168,9 @@ void Surface::NotifySurfaceIdAvailable(const SurfaceId& surface_id) { ActivatePendingFrame(); } -void Surface::AddObserver(PendingFrameObserver* observer) { - observers_.AddObserver(observer); -} - -void Surface::RemoveObserver(PendingFrameObserver* observer) { - observers_.RemoveObserver(observer); -} - void Surface::ActivatePendingFrameForDeadline() { - if (!pending_frame_ || - !pending_frame_->metadata.can_activate_before_dependencies) { + if (!pending_frame_data_ || + !pending_frame_data_->frame.metadata.can_activate_before_dependencies) { return; } @@ -149,63 +180,73 @@ void Surface::ActivatePendingFrameForDeadline() { ActivatePendingFrame(); } +Surface::FrameData::FrameData(CompositorFrame&& frame, + const base::Closure& draw_callback, + const WillDrawCallback& will_draw_callback) + : frame(std::move(frame)), + draw_callback(draw_callback), + will_draw_callback(will_draw_callback) {} + +Surface::FrameData::FrameData(FrameData&& other) = default; + +Surface::FrameData& Surface::FrameData::operator=(FrameData&& other) = default; + +Surface::FrameData::~FrameData() = default; + void Surface::ActivatePendingFrame() { - DCHECK(pending_frame_); - ActivateFrame(std::move(pending_frame_.value())); - pending_frame_.reset(); + DCHECK(pending_frame_data_); + FrameData frame_data = std::move(*pending_frame_data_); + pending_frame_data_.reset(); + ActivateFrame(std::move(frame_data)); } // A frame is activated if all its Surface ID dependences are active or a // deadline has hit and the frame was forcibly activated by the display // compositor. -void Surface::ActivateFrame(CompositorFrame frame) { - DCHECK(factory_); +void Surface::ActivateFrame(FrameData frame_data) { + DCHECK(compositor_frame_sink_support_); // Save root pass copy requests. std::vector<std::unique_ptr<CopyOutputRequest>> old_copy_requests; - if (active_frame_ && !active_frame_->render_pass_list.empty()) { + if (active_frame_data_) { std::swap(old_copy_requests, - active_frame_->render_pass_list.back()->copy_requests); + active_frame_data_->frame.render_pass_list.back()->copy_requests); } ClearCopyRequests(); - TakeLatencyInfo(&frame.metadata.latency_info); + TakeLatencyInfo(&frame_data.frame.metadata.latency_info); - base::Optional<CompositorFrame> previous_frame = std::move(active_frame_); - active_frame_ = std::move(frame); + base::Optional<FrameData> previous_frame_data = std::move(active_frame_data_); + + active_frame_data_ = std::move(frame_data); for (auto& copy_request : old_copy_requests) RequestCopyOfOutput(std::move(copy_request)); - // Empty frames shouldn't be drawn and shouldn't contribute damage, so don't - // increment frame index for them. - if (!active_frame_->render_pass_list.empty()) - ++frame_index_; + ++frame_index_; previous_frame_surface_id_ = surface_id(); - if (previous_frame) - UnrefFrameResources(*previous_frame); + UnrefFrameResourcesAndRunDrawCallback(std::move(previous_frame_data)); - for (auto& observer : observers_) - observer.OnSurfaceActivated(this); + compositor_frame_sink_support_->OnSurfaceActivated(this); } -void Surface::UpdateBlockingSurfaces( - const base::Optional<CompositorFrame>& previous_pending_frame, - const CompositorFrame& current_frame) { +void Surface::UpdateBlockingSurfaces(bool has_previous_pending_frame, + const CompositorFrame& current_frame) { // If there is no SurfaceDependencyTracker installed then the |current_frame| // does not block on anything. - if (!factory_->manager()->dependency_tracker()) { + if (!surface_manager_->dependency_tracker()) { blocking_surfaces_.clear(); return; } base::flat_set<SurfaceId> new_blocking_surfaces; - for (const SurfaceId& surface_id : current_frame.metadata.embedded_surfaces) { - Surface* surface = factory_->manager()->GetSurfaceForId(surface_id); + for (const SurfaceId& surface_id : + current_frame.metadata.activation_dependencies) { + Surface* surface = surface_manager_->GetSurfaceForId(surface_id); // If a referenced surface does not have a corresponding active frame in the // display compositor, then it blocks this frame. if (!surface || !surface->HasActiveFrame()) @@ -215,14 +256,14 @@ void Surface::UpdateBlockingSurfaces( // If this Surface has a previous pending frame, then we must determine the // changes in dependencies so that we can update the SurfaceDependencyTracker // map. - if (previous_pending_frame.has_value()) { - SurfaceDependencies removed_dependencies; + if (has_previous_pending_frame) { + base::flat_set<SurfaceId> removed_dependencies; for (const SurfaceId& surface_id : blocking_surfaces_) { if (!new_blocking_surfaces.count(surface_id)) removed_dependencies.insert(surface_id); } - SurfaceDependencies added_dependencies; + base::flat_set<SurfaceId> added_dependencies; for (const SurfaceId& surface_id : new_blocking_surfaces) { if (!blocking_surfaces_.count(surface_id)) added_dependencies.insert(surface_id); @@ -230,10 +271,8 @@ void Surface::UpdateBlockingSurfaces( // If there is a change in the dependency set, then inform observers. if (!added_dependencies.empty() || !removed_dependencies.empty()) { - for (auto& observer : observers_) { - observer.OnSurfaceDependenciesChanged(this, added_dependencies, - removed_dependencies); - } + surface_manager_->SurfaceDependenciesChanged(this, added_dependencies, + removed_dependencies); } } @@ -243,10 +282,10 @@ void Surface::UpdateBlockingSurfaces( void Surface::TakeCopyOutputRequests( std::multimap<int, std::unique_ptr<CopyOutputRequest>>* copy_requests) { DCHECK(copy_requests->empty()); - if (!active_frame_) + if (!active_frame_data_) return; - for (const auto& render_pass : active_frame_->render_pass_list) { + for (const auto& render_pass : active_frame_data_->frame.render_pass_list) { for (auto& request : render_pass->copy_requests) { copy_requests->insert( std::make_pair(render_pass->id, std::move(request))); @@ -256,29 +295,37 @@ void Surface::TakeCopyOutputRequests( } const CompositorFrame& Surface::GetActiveFrame() const { - DCHECK(active_frame_); - return active_frame_.value(); + DCHECK(active_frame_data_); + return active_frame_data_->frame; } const CompositorFrame& Surface::GetPendingFrame() { - DCHECK(pending_frame_); - return pending_frame_.value(); + DCHECK(pending_frame_data_); + return pending_frame_data_->frame; } void Surface::TakeLatencyInfo(std::vector<ui::LatencyInfo>* latency_info) { - if (!active_frame_) + if (!active_frame_data_) return; - TakeLatencyInfoFromFrame(&active_frame_.value(), latency_info); + TakeLatencyInfoFromFrame(&active_frame_data_->frame, latency_info); } -void Surface::RunDrawCallbacks() { - if (!draw_callback_.is_null()) { - DrawCallback callback = draw_callback_; - draw_callback_ = DrawCallback(); +void Surface::RunDrawCallback() { + if (active_frame_data_ && !active_frame_data_->draw_callback.is_null()) { + base::Closure callback = active_frame_data_->draw_callback; + active_frame_data_->draw_callback = base::Closure(); callback.Run(); } } +void Surface::RunWillDrawCallback(const gfx::Rect& damage_rect) { + if (!active_frame_data_ || active_frame_data_->will_draw_callback.is_null()) + return; + + active_frame_data_->will_draw_callback.Run(surface_id().local_surface_id(), + damage_rect); +} + void Surface::AddDestructionDependency(SurfaceSequence sequence) { destruction_dependencies_.push_back(sequence); } @@ -293,18 +340,26 @@ void Surface::SatisfyDestructionDependencies( }); } -void Surface::UnrefFrameResources(const CompositorFrame& frame) { +void Surface::UnrefFrameResourcesAndRunDrawCallback( + base::Optional<FrameData> frame_data) { + if (!frame_data || !compositor_frame_sink_support_) + return; + ReturnedResourceArray resources; - TransferableResource::ReturnResources(frame.resource_list, &resources); + TransferableResource::ReturnResources(frame_data->frame.resource_list, + &resources); // No point in returning same sync token to sender. for (auto& resource : resources) resource.sync_token.Clear(); - factory_->UnrefResources(resources); + compositor_frame_sink_support_->UnrefResources(resources); + + if (!frame_data->draw_callback.is_null()) + frame_data->draw_callback.Run(); } void Surface::ClearCopyRequests() { - if (active_frame_) { - for (const auto& render_pass : active_frame_->render_pass_list) { + if (active_frame_data_) { + for (const auto& render_pass : active_frame_data_->frame.render_pass_list) { for (const auto& copy_request : render_pass->copy_requests) copy_request->SendEmptyResult(); } @@ -313,9 +368,9 @@ void Surface::ClearCopyRequests() { void Surface::TakeLatencyInfoFromPendingFrame( std::vector<ui::LatencyInfo>* latency_info) { - if (!pending_frame_) + if (!pending_frame_data_) return; - TakeLatencyInfoFromFrame(&pending_frame_.value(), latency_info); + TakeLatencyInfoFromFrame(&pending_frame_data_->frame, latency_info); } // static diff --git a/chromium/cc/surfaces/surface.h b/chromium/cc/surfaces/surface.h index 6cd9bbf70db..56e1403b6b3 100644 --- a/chromium/cc/surfaces/surface.h +++ b/chromium/cc/surfaces/surface.h @@ -19,10 +19,9 @@ #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "cc/output/copy_output_request.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/frame_sink_id.h" -#include "cc/surfaces/pending_frame_observer.h" -#include "cc/surfaces/surface_factory.h" -#include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_info.h" #include "cc/surfaces/surface_sequence.h" #include "cc/surfaces/surfaces_export.h" #include "ui/gfx/geometry/size.h" @@ -33,35 +32,40 @@ class LatencyInfo; namespace cc { -class BeginFrameSource; class CompositorFrame; class CopyOutputRequest; -class SurfaceFactory; class CC_SURFACES_EXPORT Surface { public: - using DrawCallback = SurfaceFactory::DrawCallback; + using WillDrawCallback = + base::RepeatingCallback<void(const LocalSurfaceId&, const gfx::Rect&)>; - Surface(const SurfaceId& id, base::WeakPtr<SurfaceFactory> factory); + Surface( + const SurfaceInfo& surface_info, + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support); ~Surface(); - const SurfaceId& surface_id() const { return surface_id_; } + const SurfaceId& surface_id() const { return surface_info_.id(); } const SurfaceId& previous_frame_surface_id() const { return previous_frame_surface_id_; } void SetPreviousFrameSurface(Surface* surface); - void QueueFrame(CompositorFrame frame, const DrawCallback& draw_callback); - void EvictFrame(); + // Returns false if |frame| is invalid. + // |draw_callback| is called once to notify the client that the previously + // submitted CompositorFrame is processed and that another frame can be + // submitted. + // |will_draw_callback| is called when |surface| is scheduled for a draw and + // there is visible damage. + bool QueueFrame(CompositorFrame frame, + const base::Closure& draw_callback, + const WillDrawCallback& will_draw_callback); void RequestCopyOfOutput(std::unique_ptr<CopyOutputRequest> copy_request); // Notifies the Surface that a blocking SurfaceId now has an active frame. void NotifySurfaceIdAvailable(const SurfaceId& surface_id); - void AddObserver(PendingFrameObserver* observer); - void RemoveObserver(PendingFrameObserver* observer); - // Called if a deadline has been hit and this surface is not yet active but // it's marked as respecting deadlines. void ActivatePendingFrameForDeadline(); @@ -84,9 +88,12 @@ class CC_SURFACES_EXPORT Surface { int frame_index() const { return frame_index_; } void TakeLatencyInfo(std::vector<ui::LatencyInfo>* latency_info); - void RunDrawCallbacks(); + void RunDrawCallback(); + void RunWillDrawCallback(const gfx::Rect& damage_rect); - base::WeakPtr<SurfaceFactory> factory() { return factory_; } + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support() { + return compositor_frame_sink_support_; + } // Add a SurfaceSequence that must be satisfied before the Surface is // destroyed. @@ -102,29 +109,46 @@ class CC_SURFACES_EXPORT Surface { } const std::vector<SurfaceId>* active_referenced_surfaces() const { - return active_frame_ ? &active_frame_->metadata.referenced_surfaces - : nullptr; + return active_frame_data_ + ? &active_frame_data_->frame.metadata.referenced_surfaces + : nullptr; } - const SurfaceDependencies& blocking_surfaces() const { + const base::flat_set<SurfaceId>& blocking_surfaces() const { return blocking_surfaces_; } - bool HasActiveFrame() const { return active_frame_.has_value(); } - bool HasPendingFrame() const { return pending_frame_.has_value(); } + bool HasActiveFrame() const { return active_frame_data_.has_value(); } + bool HasPendingFrame() const { return pending_frame_data_.has_value(); } bool destroyed() const { return destroyed_; } void set_destroyed(bool destroyed) { destroyed_ = destroyed; } private: + struct FrameData { + FrameData(CompositorFrame&& frame, + const base::Closure& draw_callback, + const WillDrawCallback& will_draw_callback); + FrameData(FrameData&& other); + ~FrameData(); + FrameData& operator=(FrameData&& other); + CompositorFrame frame; + base::Closure draw_callback; + WillDrawCallback will_draw_callback; + }; + + // Called to prevent additional CompositorFrames from being accepted into this + // surface. Once a Surface is closed, it cannot accept CompositorFrames again. + void Close(); + void ActivatePendingFrame(); // Called when all of the surface's dependencies have been resolved. - void ActivateFrame(CompositorFrame frame); - void UpdateBlockingSurfaces( - const base::Optional<CompositorFrame>& previous_pending_frame, - const CompositorFrame& current_frame); + void ActivateFrame(FrameData frame_data); + void UpdateBlockingSurfaces(bool has_previous_pending_frame, + const CompositorFrame& current_frame); - void UnrefFrameResources(const CompositorFrame& frame_data); + void UnrefFrameResourcesAndRunDrawCallback( + base::Optional<FrameData> frame_data); void ClearCopyRequests(); void TakeLatencyInfoFromPendingFrame( @@ -133,24 +157,19 @@ class CC_SURFACES_EXPORT Surface { CompositorFrame* frame, std::vector<ui::LatencyInfo>* latency_info); - SurfaceId surface_id_; + SurfaceInfo surface_info_; SurfaceId previous_frame_surface_id_; - base::WeakPtr<SurfaceFactory> factory_; - // TODO(jamesr): Support multiple frames in flight. - base::Optional<CompositorFrame> pending_frame_; - base::Optional<CompositorFrame> active_frame_; + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support_; + SurfaceManager* const surface_manager_; + + base::Optional<FrameData> pending_frame_data_; + base::Optional<FrameData> active_frame_data_; int frame_index_; + bool closed_ = false; bool destroyed_; std::vector<SurfaceSequence> destruction_dependencies_; - // This surface may have multiple BeginFrameSources if it is - // on multiple Displays. - std::set<BeginFrameSource*> begin_frame_sources_; - - SurfaceDependencies blocking_surfaces_; - base::ObserverList<PendingFrameObserver, true> observers_; - - DrawCallback draw_callback_; + base::flat_set<SurfaceId> blocking_surfaces_; DISALLOW_COPY_AND_ASSIGN(Surface); }; diff --git a/chromium/cc/surfaces/surface_aggregator.cc b/chromium/cc/surfaces/surface_aggregator.cc index db1366da53c..f0e73f8a06a 100644 --- a/chromium/cc/surfaces/surface_aggregator.cc +++ b/chromium/cc/surfaces/surface_aggregator.cc @@ -25,8 +25,8 @@ #include "cc/quads/surface_draw_quad.h" #include "cc/quads/texture_draw_quad.h" #include "cc/resources/resource_provider.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory.h" #include "cc/surfaces/surface_manager.h" #include "cc/trees/blocking_task_runner.h" @@ -121,11 +121,12 @@ SurfaceAggregator::ClipData SurfaceAggregator::CalculateClipRect( return out_clip; } -static void UnrefHelper(base::WeakPtr<SurfaceFactory> surface_factory, - const ReturnedResourceArray& resources, - BlockingTaskRunner* main_thread_task_runner) { - if (surface_factory) - surface_factory->UnrefResources(resources); +static void UnrefHelper( + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support, + const ReturnedResourceArray& resources, + BlockingTaskRunner* main_thread_task_runner) { + if (compositor_frame_sink_support) + compositor_frame_sink_support->UnrefResources(resources); } int SurfaceAggregator::RemapPassId(int surface_local_pass_id, @@ -144,14 +145,14 @@ int SurfaceAggregator::RemapPassId(int surface_local_pass_id, } int SurfaceAggregator::ChildIdForSurface(Surface* surface) { - SurfaceToResourceChildIdMap::iterator it = - surface_id_to_resource_child_id_.find(surface->surface_id()); + auto it = surface_id_to_resource_child_id_.find(surface->surface_id()); if (it == surface_id_to_resource_child_id_.end()) { - int child_id = - provider_->CreateChild(base::Bind(&UnrefHelper, surface->factory())); - if (surface->factory()) { + int child_id = provider_->CreateChild( + base::Bind(&UnrefHelper, surface->compositor_frame_sink_support())); + if (surface->compositor_frame_sink_support()) { provider_->SetChildNeedsSyncTokens( - child_id, surface->factory()->needs_sync_points()); + child_id, + surface->compositor_frame_sink_support()->needs_sync_points()); } surface_id_to_resource_child_id_[surface->surface_id()] = child_id; return child_id; @@ -239,7 +240,7 @@ void SurfaceAggregator::HandleSurfaceQuad( return; } - SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first; + referenced_surfaces_.insert(surface_id); // TODO(vmpstr): provider check is a hack for unittests that don't set up a // resource provider. ResourceProvider::ResourceIdMap empty_map; @@ -337,7 +338,8 @@ void SurfaceAggregator::HandleSurfaceQuad( gfx::RectF(surface_quad->rect)); } - referenced_surfaces_.erase(it); + // Need to re-query since referenced_surfaces_ iterators are not stable. + referenced_surfaces_.erase(referenced_surfaces_.find(surface_id)); } void SurfaceAggregator::AddColorConversionPass() { @@ -362,7 +364,7 @@ void SurfaceAggregator::AddColorConversionPass() { SharedQuadState* shared_quad_state = color_conversion_pass->CreateAndAppendSharedQuadState(); - shared_quad_state->quad_layer_bounds = output_rect.size(); + shared_quad_state->quad_layer_rect = output_rect; shared_quad_state->visible_quad_layer_rect = output_rect; shared_quad_state->opacity = 1.f; @@ -568,8 +570,7 @@ void SurfaceAggregator::ProcessAddedAndRemovedSurfaces() { for (const auto& surface : previous_contained_surfaces_) { if (!contained_surfaces_.count(surface.first)) { // Release resources of removed surface. - SurfaceToResourceChildIdMap::iterator it = - surface_id_to_resource_child_id_.find(surface.first); + auto it = surface_id_to_resource_child_id_.find(surface.first); if (it != surface_id_to_resource_child_id_.end()) { provider_->DestroyChild(it->second); surface_id_to_resource_child_id_.erase(it); @@ -578,7 +579,7 @@ void SurfaceAggregator::ProcessAddedAndRemovedSurfaces() { // Notify client of removed surface. Surface* surface_ptr = manager_->GetSurfaceForId(surface.first); if (surface_ptr) { - surface_ptr->RunDrawCallbacks(); + surface_ptr->RunDrawCallback(); } } } @@ -611,13 +612,14 @@ gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id, // TODO(jbauman): hack for unit tests that don't set up rp if (provider_) { child_id = ChildIdForSurface(surface); - if (surface->factory()) - surface->factory()->RefResources(frame.resource_list); + if (surface->compositor_frame_sink_support()) + surface->compositor_frame_sink_support()->RefResources( + frame.resource_list); provider_->ReceiveFromChild(child_id, frame.resource_list); } CHECK(debug_weak_this.get()); - ResourceIdSet referenced_resources; + std::vector<ResourceId> referenced_resources; size_t reserve_size = frame.resource_list.size(); referenced_resources.reserve(reserve_size); @@ -627,14 +629,12 @@ gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id, provider_ ? provider_->GetChildToParentMap(child_id) : empty_map; CHECK(debug_weak_this.get()); - if (!frame.render_pass_list.empty()) { - int remapped_pass_id = - RemapPassId(frame.render_pass_list.back()->id, surface_id); - if (in_moved_pixel_surface) - moved_pixel_passes_.insert(remapped_pass_id); - if (parent_pass_id) - render_pass_dependencies_[parent_pass_id].insert(remapped_pass_id); - } + int remapped_pass_id = + RemapPassId(frame.render_pass_list.back()->id, surface_id); + if (in_moved_pixel_surface) + moved_pixel_passes_.insert(remapped_pass_id); + if (parent_pass_id) + render_pass_dependencies_[parent_pass_id].insert(remapped_pass_id); struct SurfaceInfo { SurfaceInfo(const SurfaceId& id, @@ -653,13 +653,18 @@ gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id, }; std::vector<SurfaceInfo> child_surfaces; - std::unordered_set<int> pixel_moving_background_filter_passes; + // This data is created once and typically small or empty. Collect all items + // and pass to a flat_vector to sort once. + std::vector<int> pixel_moving_background_filter_passes_data; for (const auto& render_pass : frame.render_pass_list) { if (render_pass->background_filters.HasFilterThatMovesPixels()) { - pixel_moving_background_filter_passes.insert( + pixel_moving_background_filter_passes_data.push_back( RemapPassId(render_pass->id, surface_id)); } } + base::flat_set<int> pixel_moving_background_filter_passes( + std::move(pixel_moving_background_filter_passes_data), + base::KEEP_FIRST_OF_DUPES); for (const auto& render_pass : base::Reversed(frame.render_pass_list)) { int remapped_pass_id = RemapPassId(render_pass->id, surface_id); @@ -701,7 +706,7 @@ gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id, invalid_frame = true; break; } - referenced_resources.insert(resource_id); + referenced_resources.push_back(resource_id); } } } @@ -711,23 +716,22 @@ gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id, CHECK(debug_weak_this.get()); valid_surfaces_.insert(surface->surface_id()); + ResourceIdSet resource_set(std::move(referenced_resources), + base::KEEP_FIRST_OF_DUPES); if (provider_) - provider_->DeclareUsedResourcesFromChild(child_id, referenced_resources); + provider_->DeclareUsedResourcesFromChild(child_id, resource_set); CHECK(debug_weak_this.get()); gfx::Rect damage_rect; gfx::Rect full_damage; - if (!frame.render_pass_list.empty()) { - RenderPass* last_pass = frame.render_pass_list.back().get(); - full_damage = last_pass->output_rect; - damage_rect = - DamageRectForSurface(surface, *last_pass, last_pass->output_rect); - } + RenderPass* last_pass = frame.render_pass_list.back().get(); + full_damage = last_pass->output_rect; + damage_rect = + DamageRectForSurface(surface, *last_pass, last_pass->output_rect); // Avoid infinite recursion by adding current surface to // referenced_surfaces_. - SurfaceSet::iterator it = - referenced_surfaces_.insert(surface->surface_id()).first; + referenced_surfaces_.insert(surface->surface_id()); for (const auto& surface_info : child_surfaces) { gfx::Rect surface_damage = PrewalkTree(surface_info.id, surface_info.has_moved_pixels, @@ -754,10 +758,10 @@ gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id, } CHECK(debug_weak_this.get()); - if (surface->factory()) { - surface->factory()->WillDrawSurface( - surface->surface_id().local_surface_id(), damage_rect); - } + // TODO(staraz): It shouldn't need to call the callback when the damage is + // from |surface| and not from |child_surfaces|. + if (!damage_rect.IsEmpty()) + surface->RunWillDrawCallback(damage_rect); CHECK(debug_weak_this.get()); for (const auto& render_pass : frame.render_pass_list) { @@ -767,7 +771,7 @@ gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id, } } - referenced_surfaces_.erase(it); + referenced_surfaces_.erase(referenced_surfaces_.find(surface->surface_id())); if (!damage_rect.IsEmpty() && frame.metadata.may_contain_video) result->may_contain_video = true; return damage_rect; @@ -808,7 +812,7 @@ void SurfaceAggregator::CopyUndrawnSurfaces(PrewalkResult* prewalk_result) { } } } else { - SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first; + auto it = referenced_surfaces_.insert(surface_id).first; CopyPasses(frame, surface); referenced_surfaces_.erase(it); } @@ -847,7 +851,6 @@ CompositorFrame SurfaceAggregator::Aggregate(const SurfaceId& surface_id) { CompositorFrame frame; - dest_resource_list_ = &frame.resource_list; dest_pass_list_ = &frame.render_pass_list; valid_surfaces_.clear(); @@ -858,9 +861,10 @@ CompositorFrame SurfaceAggregator::Aggregate(const SurfaceId& surface_id) { frame.metadata.may_contain_video = prewalk_result.may_contain_video; CopyUndrawnSurfaces(&prewalk_result); - SurfaceSet::iterator it = referenced_surfaces_.insert(surface_id).first; + referenced_surfaces_.insert(surface_id); CopyPasses(root_surface_frame, surface); - referenced_surfaces_.erase(it); + // CopyPasses may have mutated container, need to re-query to erase. + referenced_surfaces_.erase(referenced_surfaces_.find(surface_id)); AddColorConversionPass(); moved_pixel_passes_.clear(); @@ -888,10 +892,8 @@ CompositorFrame SurfaceAggregator::Aggregate(const SurfaceId& surface_id) { contained_surfaces_.swap(previous_contained_surfaces_); contained_surfaces_.clear(); - for (SurfaceIndexMap::iterator it = previous_contained_surfaces_.begin(); - it != previous_contained_surfaces_.end(); - ++it) { - Surface* surface = manager_->GetSurfaceForId(it->first); + for (auto it : previous_contained_surfaces_) { + Surface* surface = manager_->GetSurfaceForId(it.first); if (surface) surface->TakeLatencyInfo(&frame.metadata.latency_info); } @@ -912,8 +914,7 @@ CompositorFrame SurfaceAggregator::Aggregate(const SurfaceId& surface_id) { } void SurfaceAggregator::ReleaseResources(const SurfaceId& surface_id) { - SurfaceToResourceChildIdMap::iterator it = - surface_id_to_resource_child_id_.find(surface_id); + auto it = surface_id_to_resource_child_id_.find(surface_id); if (it != surface_id_to_resource_child_id_.end()) { provider_->DestroyChild(it->second); surface_id_to_resource_child_id_.erase(it); diff --git a/chromium/cc/surfaces/surface_aggregator.h b/chromium/cc/surfaces/surface_aggregator.h index ec69f5d3319..28aeb493bcc 100644 --- a/chromium/cc/surfaces/surface_aggregator.h +++ b/chromium/cc/surfaces/surface_aggregator.h @@ -5,12 +5,11 @@ #ifndef CC_SURFACES_SURFACE_AGGREGATOR_H_ #define CC_SURFACES_SURFACE_AGGREGATOR_H_ -#include <map> #include <memory> -#include <set> #include <unordered_map> -#include <unordered_set> +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "cc/quads/draw_quad.h" @@ -30,7 +29,7 @@ class SurfaceManager; class CC_SURFACES_EXPORT SurfaceAggregator { public: - using SurfaceIndexMap = std::unordered_map<SurfaceId, int, SurfaceIdHash>; + using SurfaceIndexMap = base::flat_map<SurfaceId, int>; SurfaceAggregator(SurfaceManager* manager, ResourceProvider* provider, @@ -65,7 +64,7 @@ class CC_SURFACES_EXPORT SurfaceAggregator { ~PrewalkResult(); // This is the set of Surfaces that were referenced by another Surface, but // not included in a SurfaceDrawQuad. - std::set<SurfaceId> undrawn_surfaces; + base::flat_set<SurfaceId> undrawn_surfaces; bool may_contain_video = false; }; @@ -145,9 +144,8 @@ class CC_SURFACES_EXPORT SurfaceAggregator { // each source (SurfaceId, RenderPass id) to a unified ID namespace that's // used in the aggregated frame. An entry is removed from the map if it's not // used for one output frame. - using RenderPassIdAllocatorMap = - std::map<std::pair<SurfaceId, int>, RenderPassInfo>; - RenderPassIdAllocatorMap render_pass_allocator_map_; + base::flat_map<std::pair<SurfaceId, int>, RenderPassInfo> + render_pass_allocator_map_; int next_render_pass_id_; const bool aggregate_only_damaged_; bool output_is_secure_; @@ -162,9 +160,7 @@ class CC_SURFACES_EXPORT SurfaceAggregator { // The id for the final color conversion render pass. int color_conversion_render_pass_id_ = 0; - using SurfaceToResourceChildIdMap = - std::unordered_map<SurfaceId, int, SurfaceIdHash>; - SurfaceToResourceChildIdMap surface_id_to_resource_child_id_; + base::flat_map<SurfaceId, int> surface_id_to_resource_child_id_; // The following state is only valid for the duration of one Aggregate call // and is only stored on the class to avoid having to pass through every @@ -172,8 +168,7 @@ class CC_SURFACES_EXPORT SurfaceAggregator { // This is the set of surfaces referenced in the aggregation so far, used to // detect cycles. - using SurfaceSet = std::set<SurfaceId>; - SurfaceSet referenced_surfaces_; + base::flat_set<SurfaceId> referenced_surfaces_; // For each Surface used in the last aggregation, gives the frame_index at // that time. @@ -181,22 +176,22 @@ class CC_SURFACES_EXPORT SurfaceAggregator { SurfaceIndexMap contained_surfaces_; // After surface validation, every Surface in this set is valid. - std::unordered_set<SurfaceId, SurfaceIdHash> valid_surfaces_; + base::flat_set<SurfaceId> valid_surfaces_; // This is the pass list for the aggregated frame. RenderPassList* dest_pass_list_; // This is the set of aggregated pass ids that are affected by filters that // move pixels. - std::unordered_set<int> moved_pixel_passes_; + base::flat_set<int> moved_pixel_passes_; // This is the set of aggregated pass ids that are drawn by copy requests, so // should not have their damage rects clipped to the root damage rect. - std::unordered_set<int> copy_request_passes_; + base::flat_set<int> copy_request_passes_; // This maps each aggregated pass id to the set of (aggregated) pass ids // that its RenderPassDrawQuads depend on - std::unordered_map<int, std::unordered_set<int>> render_pass_dependencies_; + base::flat_map<int, base::flat_set<int>> render_pass_dependencies_; // The root damage rect of the currently-aggregating frame. gfx::Rect root_damage_rect_; @@ -205,9 +200,6 @@ class CC_SURFACES_EXPORT SurfaceAggregator { // This is valid during Aggregate after PrewalkTree is called. bool has_copy_requests_; - // Resource list for the aggregated frame. - TransferableResourceArray* dest_resource_list_; - // Tracks UMA stats for SurfaceDrawQuads during a call to Aggregate(). SurfaceDrawQuadUmaStats uma_stats_; diff --git a/chromium/cc/surfaces/surface_aggregator_perftest.cc b/chromium/cc/surfaces/surface_aggregator_perftest.cc index 691acfd9908..ac7fb4b5b45 100644 --- a/chromium/cc/surfaces/surface_aggregator_perftest.cc +++ b/chromium/cc/surfaces/surface_aggregator_perftest.cc @@ -7,10 +7,10 @@ #include "cc/output/compositor_frame.h" #include "cc/quads/surface_draw_quad.h" #include "cc/quads/texture_draw_quad.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/surface_aggregator.h" -#include "cc/surfaces/surface_factory.h" -#include "cc/surfaces/surface_factory_client.h" #include "cc/surfaces/surface_manager.h" +#include "cc/test/compositor_frame_helpers.h" #include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_resource_provider.h" #include "cc/test/test_context_provider.h" @@ -21,14 +21,12 @@ namespace cc { namespace { -static const base::UnguessableToken kArbitraryToken = - base::UnguessableToken::Create(); +constexpr bool kIsRoot = true; +constexpr bool kIsChildRoot = false; +constexpr bool kHandlesFrameSinkIdInvalidation = true; +constexpr bool kNeedsSyncPoints = true; -class EmptySurfaceFactoryClient : public SurfaceFactoryClient { - public: - void ReturnResources(const ReturnedResourceArray& resources) override {} - void SetBeginFrameSource(BeginFrameSource* begin_frame_source) override {} -}; +const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create(); class SurfaceAggregatorPerfTest : public testing::Test { public: @@ -47,16 +45,22 @@ class SurfaceAggregatorPerfTest : public testing::Test { bool optimize_damage, bool full_damage, const std::string& name) { - std::vector<std::unique_ptr<SurfaceFactory>> child_factories(num_surfaces); - for (int i = 0; i < num_surfaces; i++) - child_factories[i].reset( - new SurfaceFactory(FrameSinkId(1, i + 1), &manager_, &empty_client_)); + std::vector<std::unique_ptr<CompositorFrameSinkSupport>> child_supports( + num_surfaces); + for (int i = 0; i < num_surfaces; i++) { + child_supports[i] = CompositorFrameSinkSupport::Create( + nullptr, &manager_, FrameSinkId(1, i + 1), kIsChildRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); + } aggregator_.reset(new SurfaceAggregator(&manager_, resource_provider_.get(), optimize_damage)); for (int i = 0; i < num_surfaces; i++) { LocalSurfaceId local_surface_id(i + 1, kArbitraryToken); + std::unique_ptr<RenderPass> pass(RenderPass::Create()); - CompositorFrame frame; + pass->output_rect = gfx::Rect(0, 0, 1, 2); + + CompositorFrame frame = test::MakeEmptyCompositorFrame(); SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState(); for (int j = 0; j < num_textures; j++) { @@ -67,11 +71,11 @@ class SurfaceAggregatorPerfTest : public testing::Test { TextureDrawQuad* quad = pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); - const gfx::Rect rect(0, 0, 1, 1); + const gfx::Rect rect(0, 0, 1, 2); const gfx::Rect opaque_rect; // Half of rects should be visible with partial damage. gfx::Rect visible_rect = - j % 2 == 0 ? gfx::Rect(0, 0, 1, 1) : gfx::Rect(1, 1, 1, 1); + j % 2 == 0 ? gfx::Rect(0, 0, 1, 2) : gfx::Rect(0, 1, 1, 1); bool needs_blending = false; bool premultiplied_alpha = false; const gfx::PointF uv_top_left; @@ -97,16 +101,18 @@ class SurfaceAggregatorPerfTest : public testing::Test { } frame.render_pass_list.push_back(std::move(pass)); - child_factories[i]->SubmitCompositorFrame( - local_surface_id, std::move(frame), SurfaceFactory::DrawCallback()); + child_supports[i]->SubmitCompositorFrame(local_surface_id, + std::move(frame)); } - SurfaceFactory root_factory(FrameSinkId(1, num_surfaces + 1), &manager_, - &empty_client_); + std::unique_ptr<CompositorFrameSinkSupport> root_support = + CompositorFrameSinkSupport::Create( + nullptr, &manager_, FrameSinkId(1, num_surfaces + 1), kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints); timer_.Reset(); do { std::unique_ptr<RenderPass> pass(RenderPass::Create()); - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState(); SurfaceDrawQuad* surface_quad = @@ -117,6 +123,8 @@ class SurfaceAggregatorPerfTest : public testing::Test { LocalSurfaceId(num_surfaces, kArbitraryToken)), SurfaceDrawQuadType::PRIMARY, nullptr); + pass->output_rect = gfx::Rect(0, 0, 100, 100); + if (full_damage) pass->damage_rect = gfx::Rect(0, 0, 100, 100); else @@ -124,9 +132,8 @@ class SurfaceAggregatorPerfTest : public testing::Test { frame.render_pass_list.push_back(std::move(pass)); - root_factory.SubmitCompositorFrame( - LocalSurfaceId(num_surfaces + 1, kArbitraryToken), std::move(frame), - SurfaceFactory::DrawCallback()); + root_support->SubmitCompositorFrame( + LocalSurfaceId(num_surfaces + 1, kArbitraryToken), std::move(frame)); CompositorFrame aggregated = aggregator_->Aggregate( SurfaceId(FrameSinkId(1, num_surfaces + 1), @@ -137,13 +144,12 @@ class SurfaceAggregatorPerfTest : public testing::Test { perf_test::PrintResult("aggregator_speed", "", name, timer_.LapsPerSecond(), "runs/s", true); for (int i = 0; i < num_surfaces; i++) - child_factories[i]->EvictSurface(); - root_factory.EvictSurface(); + child_supports[i]->EvictCurrentSurface(); + root_support->EvictCurrentSurface(); } protected: SurfaceManager manager_; - EmptySurfaceFactoryClient empty_client_; scoped_refptr<TestContextProvider> context_provider_; std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_; std::unique_ptr<ResourceProvider> resource_provider_; diff --git a/chromium/cc/surfaces/surface_aggregator_unittest.cc b/chromium/cc/surfaces/surface_aggregator_unittest.cc index 04cca7989e7..0ba3a146963 100644 --- a/chromium/cc/surfaces/surface_aggregator_unittest.cc +++ b/chromium/cc/surfaces/surface_aggregator_unittest.cc @@ -22,6 +22,7 @@ #include "cc/surfaces/local_surface_id_allocator.h" #include "cc/surfaces/surface.h" #include "cc/surfaces/surface_manager.h" +#include "cc/test/compositor_frame_helpers.h" #include "cc/test/fake_compositor_frame_sink_support_client.h" #include "cc/test/fake_resource_provider.h" #include "cc/test/render_pass_test_utils.h" @@ -72,7 +73,7 @@ class SurfaceAggregatorTest : public testing::Test { SurfaceAggregatorTest() : SurfaceAggregatorTest(false) {} void TearDown() override { - support_->EvictFrame(); + support_->EvictCurrentSurface(); testing::Test::TearDown(); } @@ -83,15 +84,6 @@ class SurfaceAggregatorTest : public testing::Test { SurfaceAggregator aggregator_; }; -TEST_F(SurfaceAggregatorTest, ValidSurfaceNoFrame) { - LocalSurfaceId local_surface_id(7, base::UnguessableToken::Create()); - SurfaceId one_id(kArbitraryRootFrameSinkId, local_surface_id); - support_->SubmitCompositorFrame(local_surface_id, CompositorFrame()); - - CompositorFrame frame = aggregator_.Aggregate(one_id); - EXPECT_TRUE(frame.render_pass_list.empty()); -} - class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest { public: explicit SurfaceAggregatorValidSurfaceTest(bool use_damage_rect) @@ -114,7 +106,7 @@ class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest { } void TearDown() override { - child_support_->EvictFrame(); + child_support_->EvictCurrentSurface(); SurfaceAggregatorTest::TearDown(); } @@ -146,7 +138,7 @@ class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest { void SubmitPassListAsFrame(CompositorFrameSinkSupport* support, const LocalSurfaceId& local_surface_id, RenderPassList* pass_list) { - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); pass_list->swap(frame.render_pass_list); support->SubmitCompositorFrame(local_surface_id, std::move(frame)); @@ -164,7 +156,7 @@ class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest { void QueuePassAsFrame(std::unique_ptr<RenderPass> pass, const LocalSurfaceId& local_surface_id, CompositorFrameSinkSupport* support) { - CompositorFrame child_frame; + CompositorFrame child_frame = test::MakeEmptyCompositorFrame(); child_frame.render_pass_list.push_back(std::move(pass)); support->SubmitCompositorFrame(local_surface_id, std::move(child_frame)); @@ -238,7 +230,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, OpacityCopied) { ASSERT_EQ(1u, shared_quad_state_list2.size()); EXPECT_EQ(.5f, shared_quad_state_list2.ElementAt(0)->opacity); - embedded_support->EvictFrame(); + embedded_support->EvictCurrentSurface(); } TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSimpleFrame) { @@ -348,7 +340,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleSurfaceReference) { AggregateAndVerify( expected_passes, arraysize(expected_passes), ids, arraysize(ids)); - embedded_support->EvictFrame(); + embedded_support->EvictCurrentSurface(); } // This test verifies that in the absence of a primary Surface, @@ -423,8 +415,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) { AggregateAndVerify(expected_passes2, arraysize(expected_passes2), ids, arraysize(ids)); - primary_child_support->EvictFrame(); - fallback_child_support->EvictFrame(); + primary_child_support->EvictCurrentSurface(); + fallback_child_support->EvictCurrentSurface(); } // This test verifies that in the presence of both primary Surface and fallback @@ -488,8 +480,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) { AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids, arraysize(ids)); - primary_child_support->EvictFrame(); - fallback_child_support->EvictFrame(); + primary_child_support->EvictCurrentSurface(); + fallback_child_support->EvictCurrentSurface(); } TEST_F(SurfaceAggregatorValidSurfaceTest, CopyRequest) { @@ -547,7 +539,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, CopyRequest) { aggregator_.previous_contained_surfaces().end()); } - embedded_support->EvictFrame(); + embedded_support->EvictCurrentSurface(); } // Root surface may contain copy requests. @@ -582,7 +574,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) { test::Pass(root_quads, arraysize(root_quads), 1), test::Pass(root_quads2, arraysize(root_quads2), 2)}; { - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); AddPasses(&frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes, arraysize(root_passes)); frame.render_pass_list[0]->copy_requests.push_back(std::move(copy_request)); @@ -628,7 +620,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) { DCHECK(original_pass_list[0]->copy_requests.empty()); DCHECK(original_pass_list[1]->copy_requests.empty()); - embedded_support->EvictFrame(); + embedded_support->EvictCurrentSurface(); } TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) { @@ -669,7 +661,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) { test::Pass(parent_quads, arraysize(parent_quads))}; { - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); AddPasses(&frame.render_pass_list, gfx::Rect(SurfaceSize()), parent_passes, arraysize(parent_passes)); @@ -685,7 +677,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) { test::Pass root_passes[] = {test::Pass(root_quads, arraysize(root_quads))}; { - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); AddPasses(&frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes, arraysize(root_passes)); @@ -725,8 +717,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) { aggregator_.previous_contained_surfaces().end()); } - embedded_support->EvictFrame(); - parent_support->EvictFrame(); + embedded_support->EvictCurrentSurface(); + parent_support->EvictCurrentSurface(); } // This tests referencing a surface that has multiple render passes. @@ -1042,7 +1034,7 @@ void AddSolidColorQuadWithBlendMode(const gfx::Size& size, RenderPass* pass, const SkBlendMode blend_mode) { const gfx::Transform layer_to_target_transform; - const gfx::Size layer_bounds(size); + const gfx::Rect layer_rect(size); const gfx::Rect visible_layer_rect(size); const gfx::Rect clip_rect(size); @@ -1051,7 +1043,7 @@ void AddSolidColorQuadWithBlendMode(const gfx::Size& size, bool force_anti_aliasing_off = false; SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState(); - sqs->SetAll(layer_to_target_transform, layer_bounds, visible_layer_rect, + sqs->SetAll(layer_to_target_transform, layer_rect, visible_layer_rect, clip_rect, is_clipped, opacity, blend_mode, 0); SolidColorDrawQuad* color_quad = @@ -1114,8 +1106,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateSharedQuadStateProperties) { LocalSurfaceId grandchild_local_surface_id = allocator_.GenerateId(); SurfaceId grandchild_surface_id(grandchild_support->frame_sink_id(), grandchild_local_surface_id); - grandchild_support->SubmitCompositorFrame(grandchild_local_surface_id, - CompositorFrame()); + std::unique_ptr<RenderPass> grandchild_pass = RenderPass::Create(); gfx::Rect output_rect(SurfaceSize()); gfx::Rect damage_rect(SurfaceSize()); @@ -1130,8 +1121,6 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateSharedQuadStateProperties) { LocalSurfaceId child_one_local_surface_id = allocator_.GenerateId(); SurfaceId child_one_surface_id(child_one_support->frame_sink_id(), child_one_local_surface_id); - child_one_support->SubmitCompositorFrame(child_one_local_surface_id, - CompositorFrame()); std::unique_ptr<RenderPass> child_one_pass = RenderPass::Create(); child_one_pass->SetNew(pass_id, output_rect, damage_rect, @@ -1152,8 +1141,6 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateSharedQuadStateProperties) { LocalSurfaceId child_two_local_surface_id = allocator_.GenerateId(); SurfaceId child_two_surface_id(child_two_support->frame_sink_id(), child_two_local_surface_id); - child_two_support->SubmitCompositorFrame(child_two_local_surface_id, - CompositorFrame()); std::unique_ptr<RenderPass> child_two_pass = RenderPass::Create(); child_two_pass->SetNew(pass_id, output_rect, damage_rect, @@ -1208,9 +1195,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateSharedQuadStateProperties) { << iter.index(); } - grandchild_support->EvictFrame(); - child_one_support->EvictFrame(); - child_two_support->EvictFrame(); + grandchild_support->EvictCurrentSurface(); + child_one_support->EvictCurrentSurface(); + child_two_support->EvictCurrentSurface(); } // This tests that when aggregating a frame with multiple render passes that we @@ -1251,7 +1238,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) { test::Pass(child_quads[1], arraysize(child_quads[1]), child_pass_id[1])}; - CompositorFrame child_frame; + CompositorFrame child_frame = test::MakeEmptyCompositorFrame(); AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()), child_passes, arraysize(child_passes)); @@ -1283,7 +1270,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) { test::Pass(middle_quads, arraysize(middle_quads)), }; - CompositorFrame middle_frame; + CompositorFrame middle_frame = test::MakeEmptyCompositorFrame(); AddPasses(&middle_frame.render_pass_list, gfx::Rect(SurfaceSize()), middle_passes, arraysize(middle_passes)); @@ -1307,7 +1294,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) { test::Pass(secondary_quads, arraysize(secondary_quads)), test::Pass(root_quads, arraysize(root_quads))}; - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeEmptyCompositorFrame(); AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes, arraysize(root_passes)); @@ -1398,7 +1385,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) { ->shared_quad_state_list.ElementAt(1) ->clip_rect.ToString()); - middle_support->EvictFrame(); + middle_support->EvictCurrentSurface(); } // Tests that damage rects are aggregated correctly when surfaces change. @@ -1411,7 +1398,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) { test::Pass child_passes[] = { test::Pass(child_quads, arraysize(child_quads), 1)}; - CompositorFrame child_frame; + CompositorFrame child_frame = test::MakeEmptyCompositorFrame(); AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()), child_passes, arraysize(child_passes)); @@ -1433,7 +1420,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) { // Parent surface is only used to test if the transform is applied correctly // to the child surface's damage. - CompositorFrame parent_surface_frame; + CompositorFrame parent_surface_frame = test::MakeEmptyCompositorFrame(); AddPasses(&parent_surface_frame.render_pass_list, gfx::Rect(SurfaceSize()), parent_surface_passes, arraysize(parent_surface_passes)); @@ -1451,7 +1438,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) { test::Pass(root_surface_quads, arraysize(root_surface_quads), 1), test::Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2)}; - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeEmptyCompositorFrame(); AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes, arraysize(root_passes)); @@ -1477,7 +1464,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) { aggregated_pass_list[1]->damage_rect.Contains(gfx::Rect(SurfaceSize()))); { - CompositorFrame child_frame; + CompositorFrame child_frame = test::MakeEmptyCompositorFrame(); AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()), child_passes, arraysize(child_passes)); @@ -1506,7 +1493,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) { } { - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeEmptyCompositorFrame(); AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes, arraysize(root_passes)); @@ -1520,7 +1507,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) { } { - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeEmptyCompositorFrame(); AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes, arraysize(root_passes)); @@ -1576,7 +1563,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) { gfx::Rect(SurfaceSize()))); } - parent_support->EvictFrame(); + parent_support->EvictCurrentSurface(); } // Check that damage is correctly calculated for surfaces. @@ -1586,7 +1573,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) { test::Pass root_passes[] = { test::Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2)}; - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeEmptyCompositorFrame(); AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes, arraysize(root_passes)); @@ -1619,7 +1606,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) { test::Pass root_passes[] = {test::Pass( root_render_pass_quads, arraysize(root_render_pass_quads), 2)}; - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeEmptyCompositorFrame(); AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes, arraysize(root_passes)); @@ -1964,9 +1951,9 @@ void SubmitCompositorFrameWithResources(ResourceId* resource_ids, SurfaceId child_id, CompositorFrameSinkSupport* support, SurfaceId surface_id) { - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); std::unique_ptr<RenderPass> pass = RenderPass::Create(); - pass->id = 1; + pass->SetNew(1, gfx::Rect(0, 0, 20, 20), gfx::Rect(), gfx::Transform()); SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState(); sqs->opacity = 1.f; if (child_id.is_valid()) { @@ -2036,7 +2023,7 @@ TEST_F(SurfaceAggregatorWithResourcesTest, TakeResourcesOneSurface) { EXPECT_THAT(returned_ids, testing::WhenSorted(testing::ElementsAreArray(ids))); - support->EvictFrame(); + support->EvictCurrentSurface(); } TEST_F(SurfaceAggregatorWithResourcesTest, TakeInvalidResources) { @@ -2048,16 +2035,13 @@ TEST_F(SurfaceAggregatorWithResourcesTest, TakeInvalidResources) { LocalSurfaceId local_surface_id(7u, base::UnguessableToken::Create()); SurfaceId surface_id(support->frame_sink_id(), local_surface_id); - CompositorFrame frame; - std::unique_ptr<RenderPass> pass = RenderPass::Create(); - pass->id = 1; + CompositorFrame frame = test::MakeCompositorFrame(); TransferableResource resource; resource.id = 11; // ResourceProvider is software but resource is not, so it should be // ignored. resource.is_software = false; frame.resource_list.push_back(resource); - frame.render_pass_list.push_back(std::move(pass)); support->SubmitCompositorFrame(local_surface_id, std::move(frame)); CompositorFrame returned_frame = aggregator_->Aggregate(surface_id); @@ -2070,7 +2054,7 @@ TEST_F(SurfaceAggregatorWithResourcesTest, TakeInvalidResources) { ASSERT_EQ(1u, client.returned_resources().size()); EXPECT_EQ(11u, client.returned_resources()[0].id); - support->EvictFrame(); + support->EvictCurrentSurface(); } TEST_F(SurfaceAggregatorWithResourcesTest, TwoSurfaces) { @@ -2116,8 +2100,8 @@ TEST_F(SurfaceAggregatorWithResourcesTest, TwoSurfaces) { testing::WhenSorted(testing::ElementsAreArray(ids))); EXPECT_EQ(3u, resource_provider_->num_resources()); - support1->EvictFrame(); - support2->EvictFrame(); + support1->EvictCurrentSurface(); + support2->EvictCurrentSurface(); } // Ensure that aggregator completely ignores Surfaces that reference invalid @@ -2177,9 +2161,9 @@ TEST_F(SurfaceAggregatorWithResourcesTest, InvalidChildSurface) { EXPECT_EQ(3u, pass_list->back()->shared_quad_state_list.size()); EXPECT_EQ(9u, pass_list->back()->quad_list.size()); - root_support->EvictFrame(); - middle_support->EvictFrame(); - child_support->EvictFrame(); + root_support->EvictCurrentSurface(); + middle_support->EvictCurrentSurface(); + child_support->EvictCurrentSurface(); } TEST_F(SurfaceAggregatorWithResourcesTest, SecureOutputTexture) { @@ -2209,7 +2193,7 @@ TEST_F(SurfaceAggregatorWithResourcesTest, SecureOutputTexture) { { std::unique_ptr<RenderPass> pass = RenderPass::Create(); - pass->id = 1; + pass->SetNew(1, gfx::Rect(0, 0, 20, 20), gfx::Rect(), gfx::Transform()); SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState(); sqs->opacity = 1.f; SurfaceDrawQuad* surface_quad = @@ -2219,7 +2203,7 @@ TEST_F(SurfaceAggregatorWithResourcesTest, SecureOutputTexture) { surface1_id, SurfaceDrawQuadType::PRIMARY, nullptr); pass->copy_requests.push_back(CopyOutputRequest::CreateEmptyRequest()); - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); frame.render_pass_list.push_back(std::move(pass)); support2->SubmitCompositorFrame(local_frame2_id, std::move(frame)); @@ -2248,8 +2232,8 @@ TEST_F(SurfaceAggregatorWithResourcesTest, SecureOutputTexture) { // Output is insecure, so texture should be drawn. EXPECT_EQ(DrawQuad::SOLID_COLOR, render_pass->quad_list.back()->material); - support1->EvictFrame(); - support2->EvictFrame(); + support1->EvictCurrentSurface(); + support2->EvictCurrentSurface(); } // Ensure that the render passes have correct color spaces. diff --git a/chromium/cc/surfaces/surface_dependency_deadline.cc b/chromium/cc/surfaces/surface_dependency_deadline.cc new file mode 100644 index 00000000000..7bdbefcc546 --- /dev/null +++ b/chromium/cc/surfaces/surface_dependency_deadline.cc @@ -0,0 +1,55 @@ +// Copyright 2017 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/surfaces/surface_dependency_deadline.h" + +#include "cc/surfaces/surface_dependency_tracker.h" + +namespace cc { + +SurfaceDependencyDeadline::SurfaceDependencyDeadline( + SurfaceDependencyTracker* dependency_tracker, + BeginFrameSource* begin_frame_source) + : dependency_tracker_(dependency_tracker), + begin_frame_source_(begin_frame_source) { + DCHECK(begin_frame_source_); +} + +SurfaceDependencyDeadline::~SurfaceDependencyDeadline() { + // The deadline must be canceled before destruction. + DCHECK(!number_of_frames_to_deadline_); +} + +void SurfaceDependencyDeadline::Set(uint32_t number_of_frames_to_deadline) { + DCHECK_GT(number_of_frames_to_deadline, 0u); + DCHECK(!number_of_frames_to_deadline_); + number_of_frames_to_deadline_ = number_of_frames_to_deadline; + begin_frame_source_->AddObserver(this); +} + +void SurfaceDependencyDeadline::Cancel() { + if (!number_of_frames_to_deadline_) + return; + begin_frame_source_->RemoveObserver(this); + number_of_frames_to_deadline_.reset(); +} + +// BeginFrameObserver implementation. +void SurfaceDependencyDeadline::OnBeginFrame(const BeginFrameArgs& args) { + last_begin_frame_args_ = args; + if (--(*number_of_frames_to_deadline_) > 0) + return; + + Cancel(); + dependency_tracker_->OnDeadline(); +} + +const BeginFrameArgs& SurfaceDependencyDeadline::LastUsedBeginFrameArgs() + const { + return last_begin_frame_args_; +} + +void SurfaceDependencyDeadline::OnBeginFrameSourcePausedChanged(bool paused) {} + +} // namespace cc diff --git a/chromium/cc/surfaces/surface_dependency_deadline.h b/chromium/cc/surfaces/surface_dependency_deadline.h new file mode 100644 index 00000000000..f226bd0985f --- /dev/null +++ b/chromium/cc/surfaces/surface_dependency_deadline.h @@ -0,0 +1,44 @@ +// Copyright 2017 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. + +#ifndef CC_SURFACES_SURFACE_DEPENDENCY_DEADLINE_H_ +#define CC_SURFACES_SURFACE_DEPENDENCY_DEADLINE_H_ + +#include "cc/scheduler/begin_frame_source.h" + +namespace cc { + +class SurfaceDependencyTracker; + +class SurfaceDependencyDeadline : public BeginFrameObserver { + public: + SurfaceDependencyDeadline(SurfaceDependencyTracker* dependency_tracker, + BeginFrameSource* begin_frame_source); + ~SurfaceDependencyDeadline() override; + + void Set(uint32_t number_of_frames_to_deadline); + void Cancel(); + + bool has_deadline() const { + return number_of_frames_to_deadline_.has_value(); + } + + // BeginFrameObserver implementation. + void OnBeginFrame(const BeginFrameArgs& args) override; + const BeginFrameArgs& LastUsedBeginFrameArgs() const override; + void OnBeginFrameSourcePausedChanged(bool paused) override; + + private: + SurfaceDependencyTracker* const dependency_tracker_; + BeginFrameSource* begin_frame_source_ = nullptr; + base::Optional<uint32_t> number_of_frames_to_deadline_; + + BeginFrameArgs last_begin_frame_args_; + + DISALLOW_COPY_AND_ASSIGN(SurfaceDependencyDeadline); +}; + +} // namespace cc + +#endif // CC_SURFACES_SURFACE_DEPENDENCY_DEADLINE_H_ diff --git a/chromium/cc/surfaces/surface_dependency_tracker.cc b/chromium/cc/surfaces/surface_dependency_tracker.cc index 89eaf14fe0e..8540aab5236 100644 --- a/chromium/cc/surfaces/surface_dependency_tracker.cc +++ b/chromium/cc/surfaces/surface_dependency_tracker.cc @@ -17,21 +17,10 @@ constexpr uint32_t kMaxBeginFrameCount = 4; SurfaceDependencyTracker::SurfaceDependencyTracker( SurfaceManager* surface_manager, BeginFrameSource* begin_frame_source) - : surface_manager_(surface_manager), - begin_frame_source_(begin_frame_source) { - surface_manager_->AddObserver(this); - begin_frame_source_->AddObserver(this); -} + : surface_manager_(surface_manager), deadline_(this, begin_frame_source) {} SurfaceDependencyTracker::~SurfaceDependencyTracker() { - surface_manager_->RemoveObserver(this); - begin_frame_source_->RemoveObserver(this); - for (const SurfaceId& surface_id : observed_surfaces_by_id_) { - Surface* observed_surface = surface_manager_->GetSurfaceForId(surface_id); - DCHECK(observed_surface); - observed_surface->RemoveObserver(this); - } - observed_surfaces_by_id_.clear(); + deadline_.Cancel(); } void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) { @@ -49,72 +38,47 @@ void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) { // Referenced surface IDs that aren't currently known to the surface manager // or do not have an active CompsotiorFrame block this frame. - for (const SurfaceId& surface_id : pending_frame.metadata.embedded_surfaces) { + for (const SurfaceId& surface_id : + pending_frame.metadata.activation_dependencies) { Surface* surface_dependency = surface_manager_->GetSurfaceForId(surface_id); if (!surface_dependency || !surface_dependency->HasActiveFrame()) blocked_surfaces_from_dependency_[surface_id].insert( surface->surface_id()); } - if (!observed_surfaces_by_id_.count(surface->surface_id())) { - surface->AddObserver(this); - observed_surfaces_by_id_.insert(surface->surface_id()); - } + blocked_surfaces_by_id_.insert(surface->surface_id()); - if (needs_deadline && !frames_since_deadline_set_) - frames_since_deadline_set_ = 0; + if (needs_deadline && !deadline_.has_deadline()) + deadline_.Set(kMaxBeginFrameCount); } -void SurfaceDependencyTracker::OnBeginFrame(const BeginFrameArgs& args) { - // If no deadline is set then we have nothing to do. - if (!frames_since_deadline_set_) - return; - - // TODO(fsamuel, kylechar): We have a single global deadline here. We should - // scope deadlines to surface subtrees. We cannot do that until - // SurfaceReferences have been fully implemented - // (see https://crbug.com/689719). - last_begin_frame_args_ = args; - // Nothing to do if we haven't hit a deadline yet. - if (++(*frames_since_deadline_set_) != kMaxBeginFrameCount) - return; +void SurfaceDependencyTracker::OnSurfaceActivated(Surface* surface) { + blocked_surfaces_by_id_.erase(surface->surface_id()); + NotifySurfaceIdAvailable(surface->surface_id()); +} - late_surfaces_by_id_.clear(); +void SurfaceDependencyTracker::OnSurfaceDependenciesChanged( + Surface* surface, + const base::flat_set<SurfaceId>& added_dependencies, + const base::flat_set<SurfaceId>& removed_dependencies) { + // Update the |blocked_surfaces_from_dependency_| map with the changes in + // dependencies. + for (const SurfaceId& surface_id : added_dependencies) + blocked_surfaces_from_dependency_[surface_id].insert(surface->surface_id()); - // Activate all surfaces that respect the deadline. - // Copy the set of blocked surfaces here because that set can mutate as we - // activate CompositorFrames: an activation can trigger further activations - // which will remove elements from |observed_surfaces_by_id_|. This - // invalidates the iterator. - base::flat_set<SurfaceId> blocked_surfaces_by_id(observed_surfaces_by_id_); - for (const SurfaceId& surface_id : blocked_surfaces_by_id) { - Surface* blocked_surface = surface_manager_->GetSurfaceForId(surface_id); - if (!blocked_surface) { - // A blocked surface may have been garbage collected during dependency - // resolution. - DCHECK(!observed_surfaces_by_id_.count(surface_id)); - continue; - } - // Clear all tracked blockers for |blocked_surface|. - for (const SurfaceId& blocking_surface_id : - blocked_surface->blocking_surfaces()) { - // If we are not activating this blocker now, then it's late. - if (!blocked_surfaces_by_id.count(blocking_surface_id)) - late_surfaces_by_id_.insert(blocking_surface_id); - blocked_surfaces_from_dependency_[blocking_surface_id].erase(surface_id); - } - blocked_surface->ActivatePendingFrameForDeadline(); + for (const SurfaceId& surface_id : removed_dependencies) { + auto it = blocked_surfaces_from_dependency_.find(surface_id); + it->second.erase(surface->surface_id()); + if (it->second.empty()) + blocked_surfaces_from_dependency_.erase(it); } - frames_since_deadline_set_.reset(); -} - -const BeginFrameArgs& SurfaceDependencyTracker::LastUsedBeginFrameArgs() const { - return last_begin_frame_args_; + // If there are no more dependencies to resolve then we don't need to have a + // deadline. + if (blocked_surfaces_from_dependency_.empty()) + deadline_.Cancel(); } -void SurfaceDependencyTracker::OnBeginFrameSourcePausedChanged(bool paused) {} - void SurfaceDependencyTracker::OnSurfaceDiscarded(Surface* surface) { // If the surface being destroyed doesn't have a pending frame then we have // nothing to do here. @@ -123,9 +87,10 @@ void SurfaceDependencyTracker::OnSurfaceDiscarded(Surface* surface) { const CompositorFrame& pending_frame = surface->GetPendingFrame(); - DCHECK(!pending_frame.metadata.embedded_surfaces.empty()); + DCHECK(!pending_frame.metadata.activation_dependencies.empty()); - for (const SurfaceId& surface_id : pending_frame.metadata.embedded_surfaces) { + for (const SurfaceId& surface_id : + pending_frame.metadata.activation_dependencies) { auto it = blocked_surfaces_from_dependency_.find(surface_id); if (it == blocked_surfaces_from_dependency_.end()) continue; @@ -141,57 +106,44 @@ void SurfaceDependencyTracker::OnSurfaceDiscarded(Surface* surface) { } if (blocked_surfaces_from_dependency_.empty()) - frames_since_deadline_set_.reset(); + deadline_.Cancel(); - observed_surfaces_by_id_.erase(surface->surface_id()); - surface->RemoveObserver(this); + blocked_surfaces_by_id_.erase(surface->surface_id()); // Pretend that the discarded surface's SurfaceId is now available to unblock // dependencies because we now know the surface will never activate. NotifySurfaceIdAvailable(surface->surface_id()); } -void SurfaceDependencyTracker::OnSurfaceActivated(Surface* surface) { - surface->RemoveObserver(this); - observed_surfaces_by_id_.erase(surface->surface_id()); - NotifySurfaceIdAvailable(surface->surface_id()); -} - -void SurfaceDependencyTracker::OnSurfaceDependenciesChanged( - Surface* surface, - const SurfaceDependencies& added_dependencies, - const SurfaceDependencies& removed_dependencies) { - // Update the |blocked_surfaces_from_dependency_| map with the changes in - // dependencies. - for (const SurfaceId& surface_id : added_dependencies) - blocked_surfaces_from_dependency_[surface_id].insert(surface->surface_id()); +void SurfaceDependencyTracker::OnDeadline() { + late_surfaces_by_id_.clear(); - for (const SurfaceId& surface_id : removed_dependencies) { - auto it = blocked_surfaces_from_dependency_.find(surface_id); - it->second.erase(surface->surface_id()); - if (it->second.empty()) - blocked_surfaces_from_dependency_.erase(it); + // Activate all surfaces that respect the deadline. + // Copy the set of blocked surfaces here because that set can mutate as we + // activate CompositorFrames: an activation can trigger further activations + // which will remove elements from |blocked_surfaces_by_id_|. This + // invalidates the iterator. + base::flat_set<SurfaceId> blocked_surfaces_by_id(blocked_surfaces_by_id_); + for (const SurfaceId& surface_id : blocked_surfaces_by_id) { + Surface* blocked_surface = surface_manager_->GetSurfaceForId(surface_id); + if (!blocked_surface) { + // A blocked surface may have been garbage collected during dependency + // resolution. + DCHECK(!blocked_surfaces_by_id_.count(surface_id)); + continue; + } + // Clear all tracked blockers for |blocked_surface|. + for (const SurfaceId& blocking_surface_id : + blocked_surface->blocking_surfaces()) { + // If we are not activating this blocker now, then it's late. + if (!blocked_surfaces_by_id.count(blocking_surface_id)) + late_surfaces_by_id_.insert(blocking_surface_id); + blocked_surfaces_from_dependency_[blocking_surface_id].erase(surface_id); + } + blocked_surface->ActivatePendingFrameForDeadline(); } - - // If there are no more dependencies to resolve then we don't need to have a - // deadline. - if (blocked_surfaces_from_dependency_.empty()) - frames_since_deadline_set_.reset(); } -// SurfaceObserver implementation: -void SurfaceDependencyTracker::OnSurfaceCreated( - const SurfaceInfo& surface_info) { - // This is called when a Surface has an activated frame for the first time. - // SurfaceDependencyTracker only observes Surfaces that contain pending - // frames. SurfaceDependencyTracker becomes aware of CompositorFrames that - // activate immediately go through here. - NotifySurfaceIdAvailable(surface_info.id()); -} - -void SurfaceDependencyTracker::OnSurfaceDamaged(const SurfaceId& surface_id, - bool* changed) {} - void SurfaceDependencyTracker::NotifySurfaceIdAvailable( const SurfaceId& surface_id) { auto it = blocked_surfaces_from_dependency_.find(surface_id); @@ -204,7 +156,7 @@ void SurfaceDependencyTracker::NotifySurfaceIdAvailable( // If there are no more blockers in the system, then we no longer need to // have a deadline. if (blocked_surfaces_from_dependency_.empty()) - frames_since_deadline_set_.reset(); + deadline_.Cancel(); // Tell each surface about the availability of its blocker. for (const SurfaceId& blocked_surface_by_id : blocked_surfaces_by_id) { @@ -213,7 +165,7 @@ void SurfaceDependencyTracker::NotifySurfaceIdAvailable( if (!blocked_surface) { // A blocked surface may have been garbage collected during dependency // resolution. - DCHECK(!observed_surfaces_by_id_.count(blocked_surface_by_id)); + DCHECK(!blocked_surfaces_by_id_.count(blocked_surface_by_id)); continue; } blocked_surface->NotifySurfaceIdAvailable(surface_id); diff --git a/chromium/cc/surfaces/surface_dependency_tracker.h b/chromium/cc/surfaces/surface_dependency_tracker.h index bdb8717fd06..45b543cd6fe 100644 --- a/chromium/cc/surfaces/surface_dependency_tracker.h +++ b/chromium/cc/surfaces/surface_dependency_tracker.h @@ -5,14 +5,13 @@ #ifndef CC_SURFACES_SURFACE_DEPENDENCY_TRACKER_H_ #define CC_SURFACES_SURFACE_DEPENDENCY_TRACKER_H_ -#include "cc/scheduler/begin_frame_source.h" -#include "cc/surfaces/pending_frame_observer.h" #include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_observer.h" +#include "cc/surfaces/surface_dependency_deadline.h" #include "cc/surfaces/surfaces_export.h" namespace cc { +class BeginFrameSource; class SurfaceManager; // SurfaceDependencyTracker tracks unresolved dependencies blocking @@ -30,36 +29,26 @@ class SurfaceManager; // TODO(fsamuel): Deadlines should not be global. They should be scoped to a // surface subtree. However, that will not be possible until SurfaceReference // work is complete. -class CC_SURFACES_EXPORT SurfaceDependencyTracker : public BeginFrameObserver, - public PendingFrameObserver, - public SurfaceObserver { +class CC_SURFACES_EXPORT SurfaceDependencyTracker { public: SurfaceDependencyTracker(SurfaceManager* surface_manager, BeginFrameSource* begin_frame_source); - ~SurfaceDependencyTracker() override; + ~SurfaceDependencyTracker(); // Called when |surface| has a pending CompositorFrame and it wishes to be // informed when that surface's dependencies are resolved. void RequestSurfaceResolution(Surface* surface); - bool has_deadline() const { return frames_since_deadline_set_.has_value(); } + bool has_deadline() const { return deadline_.has_deadline(); } - // BeginFrameObserver implementation. - void OnBeginFrame(const BeginFrameArgs& args) override; - const BeginFrameArgs& LastUsedBeginFrameArgs() const override; - void OnBeginFrameSourcePausedChanged(bool paused) override; + void OnDeadline(); - // PendingFrameObserver implementation: - void OnSurfaceActivated(Surface* surface) override; + void OnSurfaceActivated(Surface* surface); void OnSurfaceDependenciesChanged( Surface* surface, - const SurfaceDependencies& added_dependencies, - const SurfaceDependencies& removed_dependencies) override; - void OnSurfaceDiscarded(Surface* surface) override; - - // SurfaceObserver implementation: - void OnSurfaceCreated(const SurfaceInfo& surface_info) override; - void OnSurfaceDamaged(const SurfaceId& surface_id, bool* changed) override; + const base::flat_set<SurfaceId>& added_dependencies, + const base::flat_set<SurfaceId>& removed_dependencies); + void OnSurfaceDiscarded(Surface* surface); private: // Informs all Surfaces with pending frames blocked on the provided @@ -69,23 +58,16 @@ class CC_SURFACES_EXPORT SurfaceDependencyTracker : public BeginFrameObserver, SurfaceManager* const surface_manager_; - // The last begin frame args generated by the begin frame source. - BeginFrameArgs last_begin_frame_args_; - - // The BeginFrameSource used to set deadlines. - BeginFrameSource* const begin_frame_source_; - - // The number of BeginFrames observed since a deadline was set. If - // base::nullopt_t then a deadline is not set. - base::Optional<uint32_t> frames_since_deadline_set_; + // This object tracks the deadline when all pending CompositorFrames in the + // system will be activated. + SurfaceDependencyDeadline deadline_; // A map from a SurfaceId to the set of Surfaces blocked on that SurfaceId. std::unordered_map<SurfaceId, base::flat_set<SurfaceId>, SurfaceIdHash> blocked_surfaces_from_dependency_; - // The set of SurfaceIds corresponding to observed Surfaces that have - // blockers. - base::flat_set<SurfaceId> observed_surfaces_by_id_; + // The set of SurfaceIds corresponding that are known to have blockers. + base::flat_set<SurfaceId> blocked_surfaces_by_id_; // The set of SurfaceIds to which corresponding CompositorFrames have not // arrived by the time their deadline fired. diff --git a/chromium/cc/surfaces/surface_factory.cc b/chromium/cc/surfaces/surface_factory.cc deleted file mode 100644 index f6a6e0ec0f2..00000000000 --- a/chromium/cc/surfaces/surface_factory.cc +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2014 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/surfaces/surface_factory.h" - -#include <utility> - -#include "base/memory/ptr_util.h" -#include "base/trace_event/trace_event.h" -#include "cc/output/compositor_frame.h" -#include "cc/output/copy_output_request.h" -#include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory_client.h" -#include "cc/surfaces/surface_info.h" -#include "cc/surfaces/surface_manager.h" -#include "ui/gfx/geometry/size.h" - -namespace cc { -SurfaceFactory::SurfaceFactory(const FrameSinkId& frame_sink_id, - SurfaceManager* manager, - SurfaceFactoryClient* client) - : frame_sink_id_(frame_sink_id), - manager_(manager), - client_(client), - holder_(client), - needs_sync_points_(true), - weak_factory_(this) {} - -SurfaceFactory::~SurfaceFactory() { - // This is to prevent troubles when a factory that resides in a client is - // being destroyed. In such cases, the factory might attempt to return - // resources to the client while it's in the middle of destruction and this - // could cause a crash or some unexpected behaviour. - DCHECK(!current_surface_) << "Please call EvictSurface before destruction"; -} - -void SurfaceFactory::EvictSurface() { - if (!current_surface_) - return; - Destroy(std::move(current_surface_)); -} - -void SurfaceFactory::SubmitCompositorFrame( - const LocalSurfaceId& local_surface_id, - CompositorFrame frame, - const DrawCallback& callback) { - TRACE_EVENT0("cc", "SurfaceFactory::SubmitCompositorFrame"); - DCHECK(local_surface_id.is_valid()); - std::unique_ptr<Surface> surface; - bool create_new_surface = - (!current_surface_ || - local_surface_id != current_surface_->surface_id().local_surface_id()); - if (!create_new_surface) { - surface = std::move(current_surface_); - } else { - surface = Create(local_surface_id); - } - surface->QueueFrame(std::move(frame), callback); - - if (!manager_->SurfaceModified(SurfaceId(frame_sink_id_, local_surface_id))) { - TRACE_EVENT_INSTANT0("cc", "Damage not visible.", TRACE_EVENT_SCOPE_THREAD); - surface->RunDrawCallbacks(); - } - if (current_surface_ && create_new_surface) { - surface->SetPreviousFrameSurface(current_surface_.get()); - Destroy(std::move(current_surface_)); - } - current_surface_ = std::move(surface); -} - -void SurfaceFactory::RequestCopyOfSurface( - std::unique_ptr<CopyOutputRequest> copy_request) { - if (!current_surface_) { - copy_request->SendEmptyResult(); - return; - } - DCHECK(current_surface_->factory().get() == this); - current_surface_->RequestCopyOfOutput(std::move(copy_request)); - manager_->SurfaceModified(current_surface_->surface_id()); -} - -void SurfaceFactory::ClearSurface() { - if (!current_surface_) - return; - current_surface_->EvictFrame(); - manager_->SurfaceModified(current_surface_->surface_id()); -} - -void SurfaceFactory::WillDrawSurface(const LocalSurfaceId& id, - const gfx::Rect& damage_rect) { - client_->WillDrawSurface(id, damage_rect); -} - -void SurfaceFactory::ReceiveFromChild( - const TransferableResourceArray& resources) { - holder_.ReceiveFromChild(resources); -} - -void SurfaceFactory::RefResources(const TransferableResourceArray& resources) { - holder_.RefResources(resources); -} - -void SurfaceFactory::UnrefResources(const ReturnedResourceArray& resources) { - holder_.UnrefResources(resources); -} - -void SurfaceFactory::OnSurfaceActivated(Surface* surface) { - DCHECK(surface->HasActiveFrame()); - if (!seen_first_frame_activation_) { - seen_first_frame_activation_ = true; - - const CompositorFrame& frame = surface->GetActiveFrame(); - // CompositorFrames might not be populated with a RenderPass in unit tests. - gfx::Size frame_size; - if (!frame.render_pass_list.empty()) - frame_size = frame.render_pass_list.back()->output_rect.size(); - - // SurfaceCreated only applies for the first Surface activation. Thus, - // SurfaceFactory stops observing new activations after the first one. - manager_->SurfaceCreated(SurfaceInfo( - surface->surface_id(), frame.metadata.device_scale_factor, frame_size)); - } - // Fire SurfaceCreated first so that a temporary reference is added before it - // is potentially transformed into a real reference by the client. - client_->ReferencedSurfacesChanged(surface->surface_id().local_surface_id(), - surface->active_referenced_surfaces()); -} - -void SurfaceFactory::OnSurfaceDependenciesChanged( - Surface* surface, - const SurfaceDependencies& added_dependencies, - const SurfaceDependencies& removed_dependencies) {} - -void SurfaceFactory::OnSurfaceDiscarded(Surface* surface) {} - -std::unique_ptr<Surface> SurfaceFactory::Create( - const LocalSurfaceId& local_surface_id) { - seen_first_frame_activation_ = false; - std::unique_ptr<Surface> surface = - manager_->CreateSurface(weak_factory_.GetWeakPtr(), local_surface_id); - surface->AddObserver(this); - return surface; -} - -void SurfaceFactory::Destroy(std::unique_ptr<Surface> surface) { - surface->RemoveObserver(this); - manager_->DestroySurface(std::move(surface)); -} - -} // namespace cc diff --git a/chromium/cc/surfaces/surface_factory.h b/chromium/cc/surfaces/surface_factory.h deleted file mode 100644 index 29b3e488ab4..00000000000 --- a/chromium/cc/surfaces/surface_factory.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2014 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. - -#ifndef CC_SURFACES_SURFACE_FACTORY_H_ -#define CC_SURFACES_SURFACE_FACTORY_H_ - -#include <memory> -#include <set> - -#include "base/callback_forward.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/observer_list.h" -#include "cc/output/compositor_frame.h" -#include "cc/surfaces/pending_frame_observer.h" -#include "cc/surfaces/surface_id.h" -#include "cc/surfaces/surface_resource_holder.h" -#include "cc/surfaces/surface_sequence.h" -#include "cc/surfaces/surfaces_export.h" - -namespace cc { -class CopyOutputRequest; -class Surface; -class SurfaceFactoryClient; -class SurfaceManager; - -// This class is used for creating surfaces and submitting compositor frames to -// them. Surfaces are created lazily each time SubmitCompositorFrame is -// called with a local frame id that is different from the last call. Only one -// surface is owned by this class at a time, and upon constructing a new surface -// the old one will be destructed. Resources submitted to surfaces created by a -// particular factory will be returned to that factory's client when they are no -// longer being used. This is the only class most users of surfaces will need to -// directly interact with. -class CC_SURFACES_EXPORT SurfaceFactory : public PendingFrameObserver { - public: - using DrawCallback = base::Callback<void()>; - - SurfaceFactory(const FrameSinkId& frame_sink_id, - SurfaceManager* manager, - SurfaceFactoryClient* client); - ~SurfaceFactory() override; - - const FrameSinkId& frame_sink_id() const { return frame_sink_id_; } - - // Destroys the current surface. You need to call this method before the - // factory is destroyed, or when you would like to get rid of the surface as - // soon as possible (otherwise, the next time you call SubmitCompositorFrame - // the old surface will be dealt with). - void EvictSurface(); - - // Submits the frame to the current surface being managed by the factory if - // the local frame ids match, or creates a new surface with the given local - // frame id, destroys the old one, and submits the frame to this new surface. - // The frame can contain references to any surface, regardless of which - // factory owns it. The callback is called the first time this frame is used - // to draw, or if the frame is discarded. - void SubmitCompositorFrame(const LocalSurfaceId& local_surface_id, - CompositorFrame frame, - const DrawCallback& callback); - void RequestCopyOfSurface(std::unique_ptr<CopyOutputRequest> copy_request); - - // Evicts the current frame on the surface. All the resources - // will be released and Surface::HasFrame will return false. - void ClearSurface(); - - void WillDrawSurface(const LocalSurfaceId& id, const gfx::Rect& damage_rect); - - SurfaceFactoryClient* client() { return client_; } - - void ReceiveFromChild(const TransferableResourceArray& resources); - void RefResources(const TransferableResourceArray& resources); - void UnrefResources(const ReturnedResourceArray& resources); - - SurfaceManager* manager() { return manager_; } - - Surface* current_surface_for_testing() { return current_surface_.get(); } - - // This can be set to false if resources from this SurfaceFactory don't need - // to have sync points set on them when returned from the Display, for - // example if the Display shares a context with the creator. - bool needs_sync_points() const { return needs_sync_points_; } - void set_needs_sync_points(bool needs) { needs_sync_points_ = needs; } - - private: - // PendingFrameObserver implementation. - void OnSurfaceActivated(Surface* surface) override; - void OnSurfaceDependenciesChanged( - Surface* surface, - const SurfaceDependencies& added_dependencies, - const SurfaceDependencies& removed_dependencies) override; - void OnSurfaceDiscarded(Surface* surface) override; - - std::unique_ptr<Surface> Create(const LocalSurfaceId& local_surface_id); - void Destroy(std::unique_ptr<Surface> surface); - - const FrameSinkId frame_sink_id_; - SurfaceManager* manager_; - SurfaceFactoryClient* client_; - SurfaceResourceHolder holder_; - bool needs_sync_points_; - bool seen_first_frame_activation_ = false; - std::unique_ptr<Surface> current_surface_; - base::WeakPtrFactory<SurfaceFactory> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(SurfaceFactory); -}; - -} // namespace cc - -#endif // CC_SURFACES_SURFACE_FACTORY_H_ diff --git a/chromium/cc/surfaces/surface_factory_client.h b/chromium/cc/surfaces/surface_factory_client.h deleted file mode 100644 index 57ecfed1104..00000000000 --- a/chromium/cc/surfaces/surface_factory_client.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2014 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. - -#ifndef CC_SURFACES_SURFACE_FACTORY_CLIENT_H_ -#define CC_SURFACES_SURFACE_FACTORY_CLIENT_H_ - -#include "cc/resources/returned_resource.h" -#include "cc/surfaces/local_surface_id.h" -#include "cc/surfaces/surfaces_export.h" -#include "ui/gfx/geometry/rect.h" - -namespace cc { - -class BeginFrameSource; -class SurfaceId; - -class CC_SURFACES_EXPORT SurfaceFactoryClient { - public: - virtual ~SurfaceFactoryClient() {} - - virtual void ReferencedSurfacesChanged( - const LocalSurfaceId& local_surface_id, - const std::vector<SurfaceId>* active_referenced_surfaces) {} - - virtual void ReturnResources(const ReturnedResourceArray& resources) = 0; - - virtual void WillDrawSurface(const LocalSurfaceId& local_surface_id, - const gfx::Rect& damage_rect) {} - - // This allows the SurfaceFactory to pass a BeginFrameSource to use. - virtual void SetBeginFrameSource(BeginFrameSource* begin_frame_source) = 0; -}; - -} // namespace cc - -#endif // CC_SURFACES_SURFACE_FACTORY_CLIENT_H_ diff --git a/chromium/cc/surfaces/surface_factory_unittest.cc b/chromium/cc/surfaces/surface_factory_unittest.cc deleted file mode 100644 index 0e445dd8caf..00000000000 --- a/chromium/cc/surfaces/surface_factory_unittest.cc +++ /dev/null @@ -1,768 +0,0 @@ -// Copyright 2014 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/surfaces/surface_factory.h" - -#include <stddef.h> -#include <stdint.h> - -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/macros.h" -#include "cc/output/compositor_frame.h" -#include "cc/output/copy_output_request.h" -#include "cc/output/copy_output_result.h" -#include "cc/resources/resource_provider.h" -#include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory_client.h" -#include "cc/surfaces/surface_info.h" -#include "cc/surfaces/surface_manager.h" -#include "cc/test/scheduler_test_common.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/geometry/size.h" - -namespace cc { -namespace { - -static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1); -static constexpr FrameSinkId kAnotherArbitraryFrameSinkId(2, 2); -static const base::UnguessableToken kArbitraryToken = - base::UnguessableToken::Create(); -static auto kArbitrarySourceId1 = - base::UnguessableToken::Deserialize(0xdead, 0xbeef); -static auto kArbitrarySourceId2 = - base::UnguessableToken::Deserialize(0xdead, 0xbee0); - -class TestSurfaceFactoryClient : public SurfaceFactoryClient { - public: - TestSurfaceFactoryClient() : begin_frame_source_(nullptr) {} - ~TestSurfaceFactoryClient() override {} - - void ReturnResources(const ReturnedResourceArray& resources) override { - returned_resources_.insert( - returned_resources_.end(), resources.begin(), resources.end()); - } - - void SetBeginFrameSource(BeginFrameSource* begin_frame_source) override { - begin_frame_source_ = begin_frame_source; - } - - const ReturnedResourceArray& returned_resources() const { - return returned_resources_; - } - - void clear_returned_resources() { returned_resources_.clear(); } - - BeginFrameSource* begin_frame_source() const { return begin_frame_source_; } - - private: - ReturnedResourceArray returned_resources_; - BeginFrameSource* begin_frame_source_; - - DISALLOW_COPY_AND_ASSIGN(TestSurfaceFactoryClient); -}; - -gpu::SyncToken GenTestSyncToken(int id) { - gpu::SyncToken token; - token.Set(gpu::CommandBufferNamespace::GPU_IO, 0, - gpu::CommandBufferId::FromUnsafeValue(id), 1); - return token; -} - -class SurfaceFactoryTest : public testing::Test, public SurfaceObserver { - public: - SurfaceFactoryTest() - : factory_( - new SurfaceFactory(kArbitraryFrameSinkId, &manager_, &client_)), - local_surface_id_(3, kArbitraryToken), - frame_sync_token_(GenTestSyncToken(4)), - consumer_sync_token_(GenTestSyncToken(5)) { - manager_.AddObserver(this); - } - - const SurfaceId& last_created_surface_id() const { - return last_created_surface_id_; - } - - // SurfaceObserver implementation. - void OnSurfaceCreated(const SurfaceInfo& surface_info) override { - EXPECT_EQ(kArbitraryFrameSinkId, surface_info.id().frame_sink_id()); - last_created_surface_id_ = surface_info.id(); - last_surface_info_ = surface_info; - } - - void OnSurfaceDamaged(const SurfaceId& id, bool* changed) override { - *changed = true; - } - - ~SurfaceFactoryTest() override { - manager_.RemoveObserver(this); - factory_->EvictSurface(); - } - - void SubmitCompositorFrameWithResources(ResourceId* resource_ids, - size_t num_resource_ids) { - CompositorFrame frame; - for (size_t i = 0u; i < num_resource_ids; ++i) { - TransferableResource resource; - resource.id = resource_ids[i]; - resource.mailbox_holder.texture_target = GL_TEXTURE_2D; - resource.mailbox_holder.sync_token = frame_sync_token_; - frame.resource_list.push_back(resource); - } - factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame), - SurfaceFactory::DrawCallback()); - EXPECT_EQ(last_created_surface_id_.local_surface_id(), local_surface_id_); - } - - void UnrefResources(ResourceId* ids_to_unref, - int* counts_to_unref, - size_t num_ids_to_unref) { - ReturnedResourceArray unref_array; - for (size_t i = 0; i < num_ids_to_unref; ++i) { - ReturnedResource resource; - resource.sync_token = consumer_sync_token_; - resource.id = ids_to_unref[i]; - resource.count = counts_to_unref[i]; - unref_array.push_back(resource); - } - factory_->UnrefResources(unref_array); - } - - void CheckReturnedResourcesMatchExpected(ResourceId* expected_returned_ids, - int* expected_returned_counts, - size_t expected_resources, - gpu::SyncToken expected_sync_token) { - const ReturnedResourceArray& actual_resources = - client_.returned_resources(); - ASSERT_EQ(expected_resources, actual_resources.size()); - for (size_t i = 0; i < expected_resources; ++i) { - ReturnedResource resource = actual_resources[i]; - EXPECT_EQ(expected_sync_token, resource.sync_token); - EXPECT_EQ(expected_returned_ids[i], resource.id); - EXPECT_EQ(expected_returned_counts[i], resource.count); - } - client_.clear_returned_resources(); - } - - void RefCurrentFrameResources() { - Surface* surface = manager_.GetSurfaceForId( - SurfaceId(factory_->frame_sink_id(), local_surface_id_)); - factory_->RefResources(surface->GetActiveFrame().resource_list); - } - - protected: - SurfaceManager manager_; - TestSurfaceFactoryClient client_; - std::unique_ptr<SurfaceFactory> factory_; - LocalSurfaceId local_surface_id_; - SurfaceId last_created_surface_id_; - SurfaceInfo last_surface_info_; - - // This is the sync token submitted with the frame. It should never be - // returned to the client. - const gpu::SyncToken frame_sync_token_; - - // This is the sync token returned by the consumer. It should always be - // returned to the client. - const gpu::SyncToken consumer_sync_token_; -}; - -// Tests submitting a frame with resources followed by one with no resources -// with no resource provider action in between. -TEST_F(SurfaceFactoryTest, ResourceLifetimeSimple) { - ResourceId first_frame_ids[] = {1, 2, 3}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // All of the resources submitted in the first frame are still in use at this - // time by virtue of being in the pending frame, so none can be returned to - // the client yet. - EXPECT_EQ(0u, client_.returned_resources().size()); - client_.clear_returned_resources(); - - // The second frame references no resources of first frame and thus should - // make all resources of first frame available to be returned. - SubmitCompositorFrameWithResources(NULL, 0); - - ResourceId expected_returned_ids[] = {1, 2, 3}; - int expected_returned_counts[] = {1, 1, 1}; - // Resources were never consumed so no sync token should be set. - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), gpu::SyncToken()); - - ResourceId third_frame_ids[] = {4, 5, 6}; - SubmitCompositorFrameWithResources(third_frame_ids, - arraysize(third_frame_ids)); - - // All of the resources submitted in the third frame are still in use at this - // time by virtue of being in the pending frame, so none can be returned to - // the client yet. - EXPECT_EQ(0u, client_.returned_resources().size()); - client_.clear_returned_resources(); - - // The forth frame references no resources of third frame and thus should - // make all resources of third frame available to be returned. - ResourceId forth_frame_ids[] = {7, 8, 9}; - SubmitCompositorFrameWithResources(forth_frame_ids, - arraysize(forth_frame_ids)); - - ResourceId forth_expected_returned_ids[] = {4, 5, 6}; - int forth_expected_returned_counts[] = {1, 1, 1}; - // Resources were never consumed so no sync token should be set. - CheckReturnedResourcesMatchExpected( - forth_expected_returned_ids, forth_expected_returned_counts, - arraysize(forth_expected_returned_counts), gpu::SyncToken()); -} - -// Tests submitting a frame with resources followed by one with no resources -// with the resource provider holding everything alive. -TEST_F(SurfaceFactoryTest, ResourceLifetimeSimpleWithProviderHoldingAlive) { - ResourceId first_frame_ids[] = {1, 2, 3}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // All of the resources submitted in the first frame are still in use at this - // time by virtue of being in the pending frame, so none can be returned to - // the client yet. - EXPECT_EQ(0u, client_.returned_resources().size()); - client_.clear_returned_resources(); - - // Hold on to everything. - RefCurrentFrameResources(); - - // The second frame references no resources and thus should make all resources - // available to be returned as soon as the resource provider releases them. - SubmitCompositorFrameWithResources(NULL, 0); - - EXPECT_EQ(0u, client_.returned_resources().size()); - client_.clear_returned_resources(); - - int release_counts[] = {1, 1, 1}; - UnrefResources(first_frame_ids, release_counts, arraysize(first_frame_ids)); - - ResourceId expected_returned_ids[] = {1, 2, 3}; - int expected_returned_counts[] = {1, 1, 1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); -} - -// Tests referencing a resource, unref'ing it to zero, then using it again -// before returning it to the client. -TEST_F(SurfaceFactoryTest, ResourceReusedBeforeReturn) { - ResourceId first_frame_ids[] = {7}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // This removes all references to resource id 7. - SubmitCompositorFrameWithResources(NULL, 0); - - // This references id 7 again. - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // This removes it again. - SubmitCompositorFrameWithResources(NULL, 0); - - // Now it should be returned. - // We don't care how many entries are in the returned array for 7, so long as - // the total returned count matches the submitted count. - const ReturnedResourceArray& returned = client_.returned_resources(); - size_t return_count = 0; - for (size_t i = 0; i < returned.size(); ++i) { - EXPECT_EQ(7u, returned[i].id); - return_count += returned[i].count; - } - EXPECT_EQ(2u, return_count); -} - -// Tests having resources referenced multiple times, as if referenced by -// multiple providers. -TEST_F(SurfaceFactoryTest, ResourceRefMultipleTimes) { - ResourceId first_frame_ids[] = {3, 4}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // Ref resources from the first frame twice. - RefCurrentFrameResources(); - RefCurrentFrameResources(); - - ResourceId second_frame_ids[] = {4, 5}; - SubmitCompositorFrameWithResources(second_frame_ids, - arraysize(second_frame_ids)); - - // Ref resources from the second frame 3 times. - RefCurrentFrameResources(); - RefCurrentFrameResources(); - RefCurrentFrameResources(); - - // Submit a frame with no resources to remove all current frame refs from - // submitted resources. - SubmitCompositorFrameWithResources(NULL, 0); - - EXPECT_EQ(0u, client_.returned_resources().size()); - client_.clear_returned_resources(); - - // Expected current refs: - // 3 -> 2 - // 4 -> 2 + 3 = 5 - // 5 -> 3 - { - SCOPED_TRACE("unref all 3"); - ResourceId ids_to_unref[] = {3, 4, 5}; - int counts[] = {1, 1, 1}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - - EXPECT_EQ(0u, client_.returned_resources().size()); - client_.clear_returned_resources(); - - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - - ResourceId expected_returned_ids[] = {3}; - int expected_returned_counts[] = {1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } - - // Expected refs remaining: - // 4 -> 3 - // 5 -> 1 - { - SCOPED_TRACE("unref 4 and 5"); - ResourceId ids_to_unref[] = {4, 5}; - int counts[] = {1, 1}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - - ResourceId expected_returned_ids[] = {5}; - int expected_returned_counts[] = {1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } - - // Now, just 2 refs remaining on resource 4. Unref both at once and make sure - // the returned count is correct. - { - SCOPED_TRACE("unref only 4"); - ResourceId ids_to_unref[] = {4}; - int counts[] = {2}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - - ResourceId expected_returned_ids[] = {4}; - int expected_returned_counts[] = {2}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } -} - -TEST_F(SurfaceFactoryTest, ResourceLifetime) { - ResourceId first_frame_ids[] = {1, 2, 3}; - SubmitCompositorFrameWithResources(first_frame_ids, - arraysize(first_frame_ids)); - - // All of the resources submitted in the first frame are still in use at this - // time by virtue of being in the pending frame, so none can be returned to - // the client yet. - EXPECT_EQ(0u, client_.returned_resources().size()); - client_.clear_returned_resources(); - - // The second frame references some of the same resources, but some different - // ones. We expect to receive back resource 1 with a count of 1 since it was - // only referenced by the first frame. - ResourceId second_frame_ids[] = {2, 3, 4}; - SubmitCompositorFrameWithResources(second_frame_ids, - arraysize(second_frame_ids)); - - { - SCOPED_TRACE("second frame"); - ResourceId expected_returned_ids[] = {1}; - int expected_returned_counts[] = {1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), gpu::SyncToken()); - } - - // The third frame references a disjoint set of resources, so we expect to - // receive back all resources from the first and second frames. Resource IDs 2 - // and 3 will have counts of 2, since they were used in both frames, and - // resource ID 4 will have a count of 1. - ResourceId third_frame_ids[] = {10, 11, 12, 13}; - SubmitCompositorFrameWithResources(third_frame_ids, - arraysize(third_frame_ids)); - - { - SCOPED_TRACE("third frame"); - ResourceId expected_returned_ids[] = {2, 3, 4}; - int expected_returned_counts[] = {2, 2, 1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), gpu::SyncToken()); - } - - // Simulate a ResourceProvider taking a ref on all of the resources. - RefCurrentFrameResources(); - - ResourceId fourth_frame_ids[] = {12, 13}; - SubmitCompositorFrameWithResources(fourth_frame_ids, - arraysize(fourth_frame_ids)); - - EXPECT_EQ(0u, client_.returned_resources().size()); - - RefCurrentFrameResources(); - - // All resources are still being used by the external reference, so none can - // be returned to the client. - EXPECT_EQ(0u, client_.returned_resources().size()); - - // Release resources associated with the first RefCurrentFrameResources() call - // first. - { - ResourceId ids_to_unref[] = {10, 11, 12, 13}; - int counts[] = {1, 1, 1, 1}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - } - - { - SCOPED_TRACE("fourth frame, first unref"); - ResourceId expected_returned_ids[] = {10, 11}; - int expected_returned_counts[] = {1, 1}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } - - { - ResourceId ids_to_unref[] = {12, 13}; - int counts[] = {1, 1}; - UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref)); - } - - // Resources 12 and 13 are still in use by the current frame, so they - // shouldn't be available to be returned. - EXPECT_EQ(0u, client_.returned_resources().size()); - - // If we submit an empty frame, however, they should become available. - SubmitCompositorFrameWithResources(NULL, 0u); - - { - SCOPED_TRACE("fourth frame, second unref"); - ResourceId expected_returned_ids[] = {12, 13}; - int expected_returned_counts[] = {2, 2}; - CheckReturnedResourcesMatchExpected( - expected_returned_ids, expected_returned_counts, - arraysize(expected_returned_counts), consumer_sync_token_); - } -} - -TEST_F(SurfaceFactoryTest, BlankNoIndexIncrement) { - LocalSurfaceId local_surface_id(6, kArbitraryToken); - SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id); - factory_->SubmitCompositorFrame(local_surface_id, CompositorFrame(), - SurfaceFactory::DrawCallback()); - Surface* surface = manager_.GetSurfaceForId(surface_id); - ASSERT_NE(nullptr, surface); - EXPECT_EQ(2, surface->frame_index()); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); -} - -void CreateSurfaceDrawCallback(SurfaceFactory* factory, - uint32_t* execute_count) { - LocalSurfaceId new_id(7, base::UnguessableToken::Create()); - factory->SubmitCompositorFrame(new_id, CompositorFrame(), - SurfaceFactory::DrawCallback()); - factory->EvictSurface(); - *execute_count += 1; -} - -TEST_F(SurfaceFactoryTest, AddDuringEviction) { - LocalSurfaceId local_surface_id(6, kArbitraryToken); - - uint32_t execute_count = 0; - factory_->SubmitCompositorFrame( - local_surface_id, CompositorFrame(), - base::Bind(&CreateSurfaceDrawCallback, base::Unretained(factory_.get()), - &execute_count)); - EXPECT_EQ(0u, execute_count); - factory_->EvictSurface(); - EXPECT_EQ(1u, execute_count); -} - -void DrawCallback(uint32_t* execute_count) { - *execute_count += 1; -} - -// Tests doing an EvictSurface before shutting down the factory. -TEST_F(SurfaceFactoryTest, EvictSurface) { - LocalSurfaceId local_surface_id(7, kArbitraryToken); - SurfaceId id(kArbitraryFrameSinkId, local_surface_id); - - TransferableResource resource; - resource.id = 1; - resource.mailbox_holder.texture_target = GL_TEXTURE_2D; - CompositorFrame frame; - frame.resource_list.push_back(resource); - uint32_t execute_count = 0; - factory_->SubmitCompositorFrame(local_surface_id, std::move(frame), - base::Bind(&DrawCallback, &execute_count)); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); - local_surface_id_ = LocalSurfaceId(); - - EXPECT_TRUE(manager_.GetSurfaceForId(id)); - EXPECT_TRUE(client_.returned_resources().empty()); - factory_->EvictSurface(); - EXPECT_FALSE(manager_.GetSurfaceForId(id)); - EXPECT_FALSE(client_.returned_resources().empty()); - EXPECT_EQ(1u, execute_count); -} - -// Tests doing an EvictSurface which has unregistered dependency. -TEST_F(SurfaceFactoryTest, EvictSurfaceDependencyUnRegistered) { - LocalSurfaceId local_surface_id(7, kArbitraryToken); - - TransferableResource resource; - resource.id = 1; - resource.mailbox_holder.texture_target = GL_TEXTURE_2D; - CompositorFrame frame; - frame.resource_list.push_back(resource); - uint32_t execute_count = 0; - factory_->SubmitCompositorFrame(local_surface_id, std::move(frame), - base::Bind(&DrawCallback, &execute_count)); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); - local_surface_id_ = LocalSurfaceId(); - - SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id); - Surface* surface = manager_.GetSurfaceForId(surface_id); - surface->AddDestructionDependency( - SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - - EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); - EXPECT_TRUE(client_.returned_resources().empty()); - factory_->EvictSurface(); - EXPECT_FALSE(manager_.GetSurfaceForId(surface_id)); - EXPECT_FALSE(client_.returned_resources().empty()); - EXPECT_EQ(1u, execute_count); -} - -// Tests doing an EvictSurface which has registered dependency. -TEST_F(SurfaceFactoryTest, EvictSurfaceDependencyRegistered) { - LocalSurfaceId local_surface_id(7, kArbitraryToken); - - TransferableResource resource; - resource.id = 1; - resource.mailbox_holder.texture_target = GL_TEXTURE_2D; - CompositorFrame frame; - frame.resource_list.push_back(resource); - uint32_t execute_count = 0; - factory_->SubmitCompositorFrame(local_surface_id, std::move(frame), - base::Bind(&DrawCallback, &execute_count)); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id); - local_surface_id_ = LocalSurfaceId(); - - manager_.RegisterFrameSinkId(kAnotherArbitraryFrameSinkId); - - SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id); - Surface* surface = manager_.GetSurfaceForId(surface_id); - surface->AddDestructionDependency( - SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - - EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); - EXPECT_TRUE(client_.returned_resources().empty()); - factory_->EvictSurface(); - EXPECT_TRUE(manager_.GetSurfaceForId(surface_id)); - EXPECT_TRUE(client_.returned_resources().empty()); - EXPECT_EQ(0u, execute_count); - - manager_.SatisfySequence(SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - EXPECT_FALSE(manager_.GetSurfaceForId(surface_id)); - EXPECT_FALSE(client_.returned_resources().empty()); -} - -TEST_F(SurfaceFactoryTest, DestroySequence) { - LocalSurfaceId local_surface_id2(5, kArbitraryToken); - std::unique_ptr<SurfaceFactory> factory2( - new SurfaceFactory(kArbitraryFrameSinkId, &manager_, &client_)); - SurfaceId id2(kArbitraryFrameSinkId, local_surface_id2); - factory2->SubmitCompositorFrame(local_surface_id2, CompositorFrame(), - SurfaceFactory::DrawCallback()); - - manager_.RegisterFrameSinkId(kArbitraryFrameSinkId); - - // Check that waiting before the sequence is satisfied works. - manager_.GetSurfaceForId(id2)->AddDestructionDependency( - SurfaceSequence(kArbitraryFrameSinkId, 4)); - factory2->EvictSurface(); - - DCHECK(manager_.GetSurfaceForId(id2)); - manager_.SatisfySequence(SurfaceSequence(kArbitraryFrameSinkId, 4)); - manager_.SatisfySequence(SurfaceSequence(kArbitraryFrameSinkId, 6)); - DCHECK(!manager_.GetSurfaceForId(id2)); - - // Check that waiting after the sequence is satisfied works. - factory2->SubmitCompositorFrame(local_surface_id2, CompositorFrame(), - SurfaceFactory::DrawCallback()); - DCHECK(manager_.GetSurfaceForId(id2)); - manager_.GetSurfaceForId(id2)->AddDestructionDependency( - SurfaceSequence(kAnotherArbitraryFrameSinkId, 6)); - factory2->EvictSurface(); - DCHECK(!manager_.GetSurfaceForId(id2)); -} - -// Tests that Surface ID namespace invalidation correctly allows -// Sequences to be ignored. -TEST_F(SurfaceFactoryTest, InvalidFrameSinkId) { - FrameSinkId frame_sink_id(1234, 5678); - - LocalSurfaceId local_surface_id(5, kArbitraryToken); - SurfaceId id(factory_->frame_sink_id(), local_surface_id); - factory_->SubmitCompositorFrame(local_surface_id, CompositorFrame(), - SurfaceFactory::DrawCallback()); - - manager_.RegisterFrameSinkId(frame_sink_id); - manager_.GetSurfaceForId(id)->AddDestructionDependency( - SurfaceSequence(frame_sink_id, 4)); - - factory_->EvictSurface(); - - // Verify the dependency has prevented the surface from getting destroyed. - EXPECT_TRUE(manager_.GetSurfaceForId(id)); - - manager_.InvalidateFrameSinkId(frame_sink_id); - - // Verify that the invalidated namespace caused the unsatisfied sequence - // to be ignored. - EXPECT_FALSE(manager_.GetSurfaceForId(id)); -} - -TEST_F(SurfaceFactoryTest, DestroyCycle) { - LocalSurfaceId local_surface_id2(5, kArbitraryToken); - SurfaceId id2(kArbitraryFrameSinkId, local_surface_id2); - std::unique_ptr<SurfaceFactory> factory2( - new SurfaceFactory(kArbitraryFrameSinkId, &manager_, &client_)); - manager_.RegisterFrameSinkId(kAnotherArbitraryFrameSinkId); - // Give id2 a frame that references local_surface_id_. - { - std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); - CompositorFrame frame; - frame.render_pass_list.push_back(std::move(render_pass)); - frame.metadata.referenced_surfaces.push_back( - SurfaceId(factory_->frame_sink_id(), local_surface_id_)); - factory2->SubmitCompositorFrame(local_surface_id2, std::move(frame), - SurfaceFactory::DrawCallback()); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id2); - } - manager_.GetSurfaceForId(id2)->AddDestructionDependency( - SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - factory2->EvictSurface(); - // Give local_surface_id_ a frame that references id2. - { - std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); - CompositorFrame frame; - frame.render_pass_list.push_back(std::move(render_pass)); - frame.metadata.referenced_surfaces.push_back(id2); - factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame), - SurfaceFactory::DrawCallback()); - } - factory_->EvictSurface(); - EXPECT_TRUE(manager_.GetSurfaceForId(id2)); - // local_surface_id_ should be retained by reference from id2. - EXPECT_TRUE(manager_.GetSurfaceForId( - SurfaceId(factory_->frame_sink_id(), local_surface_id_))); - - // Satisfy last destruction dependency for id2. - manager_.SatisfySequence(SurfaceSequence(kAnotherArbitraryFrameSinkId, 4)); - - // id2 and local_surface_id_ are in a reference cycle that has no surface - // sequences holding on to it, so they should be destroyed. - EXPECT_TRUE(!manager_.GetSurfaceForId(id2)); - EXPECT_TRUE(!manager_.GetSurfaceForId( - SurfaceId(factory_->frame_sink_id(), local_surface_id_))); - - local_surface_id_ = LocalSurfaceId(); -} - -void CopyRequestTestCallback(bool* called, - std::unique_ptr<CopyOutputResult> result) { - *called = true; -} - -TEST_F(SurfaceFactoryTest, DuplicateCopyRequest) { - { - std::unique_ptr<RenderPass> render_pass(RenderPass::Create()); - CompositorFrame frame; - frame.render_pass_list.push_back(std::move(render_pass)); - frame.metadata.referenced_surfaces.push_back( - SurfaceId(factory_->frame_sink_id(), local_surface_id_)); - factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame), - SurfaceFactory::DrawCallback()); - EXPECT_EQ(last_created_surface_id().local_surface_id(), local_surface_id_); - } - - bool called1 = false; - std::unique_ptr<CopyOutputRequest> request; - request = CopyOutputRequest::CreateRequest( - base::Bind(&CopyRequestTestCallback, &called1)); - request->set_source(kArbitrarySourceId1); - - factory_->RequestCopyOfSurface(std::move(request)); - EXPECT_FALSE(called1); - - bool called2 = false; - request = CopyOutputRequest::CreateRequest( - base::Bind(&CopyRequestTestCallback, &called2)); - request->set_source(kArbitrarySourceId2); - - factory_->RequestCopyOfSurface(std::move(request)); - // Callbacks have different sources so neither should be called. - EXPECT_FALSE(called1); - EXPECT_FALSE(called2); - - bool called3 = false; - request = CopyOutputRequest::CreateRequest( - base::Bind(&CopyRequestTestCallback, &called3)); - request->set_source(kArbitrarySourceId1); - - factory_->RequestCopyOfSurface(std::move(request)); - // Two callbacks are from source1, so the first should be called. - EXPECT_TRUE(called1); - EXPECT_FALSE(called2); - EXPECT_FALSE(called3); - - factory_->EvictSurface(); - local_surface_id_ = LocalSurfaceId(); - EXPECT_TRUE(called1); - EXPECT_TRUE(called2); - EXPECT_TRUE(called3); -} - -// Check whether the SurfaceInfo object is created and populated correctly -// after the frame submission. -TEST_F(SurfaceFactoryTest, SurfaceInfo) { - CompositorFrame frame; - - auto render_pass = RenderPass::Create(); - render_pass->SetNew(1, gfx::Rect(5, 6), gfx::Rect(), gfx::Transform()); - frame.render_pass_list.push_back(std::move(render_pass)); - - render_pass = RenderPass::Create(); - render_pass->SetNew(2, gfx::Rect(7, 8), gfx::Rect(), gfx::Transform()); - frame.render_pass_list.push_back(std::move(render_pass)); - - frame.metadata.device_scale_factor = 2.5f; - - factory_->SubmitCompositorFrame(local_surface_id_, std::move(frame), - SurfaceFactory::DrawCallback()); - SurfaceId expected_surface_id(factory_->frame_sink_id(), local_surface_id_); - EXPECT_EQ(expected_surface_id, last_surface_info_.id()); - EXPECT_EQ(2.5f, last_surface_info_.device_scale_factor()); - EXPECT_EQ(gfx::Size(7, 8), last_surface_info_.size_in_pixels()); -} - -} // namespace -} // namespace cc diff --git a/chromium/cc/surfaces/surface_hittest.cc b/chromium/cc/surfaces/surface_hittest.cc index 88de04fc8fe..01a0fe2e02e 100644 --- a/chromium/cc/surfaces/surface_hittest.cc +++ b/chromium/cc/surfaces/surface_hittest.cc @@ -260,9 +260,6 @@ const RenderPass* SurfaceHittest::GetRenderPassForSurfaceById( return nullptr; const CompositorFrame& surface_frame = surface->GetActiveFrame(); - if (surface_frame.render_pass_list.empty()) - return nullptr; - if (!render_pass_id) return surface_frame.render_pass_list.back().get(); diff --git a/chromium/cc/surfaces/surface_hittest_unittest.cc b/chromium/cc/surfaces/surface_hittest_unittest.cc index 312a7ae9b50..4e406e39ad6 100644 --- a/chromium/cc/surfaces/surface_hittest_unittest.cc +++ b/chromium/cc/surfaces/surface_hittest_unittest.cc @@ -10,6 +10,7 @@ #include "cc/surfaces/surface.h" #include "cc/surfaces/surface_hittest.h" #include "cc/surfaces/surface_manager.h" +#include "cc/test/compositor_frame_helpers.h" #include "cc/test/surface_hittest_test_helpers.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColor.h" @@ -103,7 +104,7 @@ TEST(SurfaceHittestTest, Hittest_BadCompositorFrameDoesNotCrash) { root_surface_id, gfx::Point(100, 100), &transform)); } - root_support->EvictFrame(); + root_support->EvictCurrentSurface(); } TEST(SurfaceHittestTest, Hittest_SingleSurface) { @@ -138,7 +139,7 @@ TEST(SurfaceHittestTest, Hittest_SingleSurface) { RunTests(nullptr, &manager, tests, arraysize(tests)); - root_support->EvictFrame(); + root_support->EvictCurrentSurface(); } TEST(SurfaceHittestTest, Hittest_ChildSurface) { @@ -278,8 +279,8 @@ TEST(SurfaceHittestTest, Hittest_ChildSurface) { EXPECT_EQ(gfx::Point(25, 25), point_in_target_space); } - root_support->EvictFrame(); - child_support->EvictFrame(); + root_support->EvictCurrentSurface(); + child_support->EvictCurrentSurface(); } // This test verifies that hit testing will progress to the next quad if it @@ -391,8 +392,8 @@ TEST(SurfaceHittestTest, Hittest_InvalidRenderPassDrawQuad) { RunTests(nullptr, &manager, tests, arraysize(tests)); - root_support->EvictFrame(); - child_support->EvictFrame(); + root_support->EvictCurrentSurface(); + child_support->EvictCurrentSurface(); } TEST(SurfaceHittestTest, Hittest_RenderPassDrawQuad) { @@ -405,7 +406,7 @@ TEST(SurfaceHittestTest, Hittest_RenderPassDrawQuad) { // Create a CompostiorFrame with two RenderPasses. gfx::Rect root_rect(300, 300); - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeCompositorFrame(); RenderPassList& render_pass_list = root_frame.render_pass_list; // Create a child RenderPass. @@ -495,7 +496,7 @@ TEST(SurfaceHittestTest, Hittest_RenderPassDrawQuad) { RunTests(nullptr, &manager, tests, arraysize(tests)); - support->EvictFrame(); + support->EvictCurrentSurface(); } TEST(SurfaceHittestTest, Hittest_SingleSurface_WithInsetsDelegate) { @@ -633,8 +634,8 @@ TEST(SurfaceHittestTest, Hittest_SingleSurface_WithInsetsDelegate) { EXPECT_EQ(0, accept_delegate.reject_target_overrides()); EXPECT_EQ(2, accept_delegate.accept_target_overrides()); - root_support->EvictFrame(); - child_support->EvictFrame(); + root_support->EvictCurrentSurface(); + child_support->EvictCurrentSurface(); } } // namespace cc diff --git a/chromium/cc/surfaces/surface_manager.cc b/chromium/cc/surfaces/surface_manager.cc index d4b0d01397f..07d23380094 100644 --- a/chromium/cc/surfaces/surface_manager.cc +++ b/chromium/cc/surfaces/surface_manager.cc @@ -11,10 +11,10 @@ #include <utility> #include "base/logging.h" +#include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/direct_surface_reference_factory.h" #include "cc/surfaces/local_surface_id_allocator.h" #include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory_client.h" #include "cc/surfaces/surface_info.h" #if DCHECK_IS_ON() @@ -65,8 +65,8 @@ std::string SurfaceManager::SurfaceReferencesToString() { #endif void SurfaceManager::SetDependencyTracker( - std::unique_ptr<SurfaceDependencyTracker> dependency_tracker) { - dependency_tracker_ = std::move(dependency_tracker); + SurfaceDependencyTracker* dependency_tracker) { + dependency_tracker_ = dependency_tracker; } void SurfaceManager::RequestSurfaceResolution(Surface* pending_surface) { @@ -75,18 +75,20 @@ void SurfaceManager::RequestSurfaceResolution(Surface* pending_surface) { } std::unique_ptr<Surface> SurfaceManager::CreateSurface( - base::WeakPtr<SurfaceFactory> surface_factory, - const LocalSurfaceId& local_surface_id) { + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support, + const SurfaceInfo& surface_info) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(local_surface_id.is_valid() && surface_factory); - - SurfaceId surface_id(surface_factory->frame_sink_id(), local_surface_id); + DCHECK(surface_info.is_valid()); + DCHECK(compositor_frame_sink_support); + DCHECK_EQ(surface_info.id().frame_sink_id(), + compositor_frame_sink_support->frame_sink_id()); // If no surface with this SurfaceId exists, simply create the surface and // return. - auto surface_iter = surface_map_.find(surface_id); + auto surface_iter = surface_map_.find(surface_info.id()); if (surface_iter == surface_map_.end()) { - auto surface = base::MakeUnique<Surface>(surface_id, surface_factory); + auto surface = + base::MakeUnique<Surface>(surface_info, compositor_frame_sink_support); surface_map_[surface->surface_id()] = surface.get(); return surface; } @@ -101,14 +103,15 @@ std::unique_ptr<Surface> SurfaceManager::CreateSurface( // the queue and reuse it. auto it = std::find_if(surfaces_to_destroy_.begin(), surfaces_to_destroy_.end(), - [&surface_id](const std::unique_ptr<Surface>& surface) { - return surface->surface_id() == surface_id; + [&surface_info](const std::unique_ptr<Surface>& surface) { + return surface->surface_id() == surface_info.id(); }); DCHECK(it != surfaces_to_destroy_.end()); std::unique_ptr<Surface> surface = std::move(*it); surfaces_to_destroy_.erase(it); surface->set_destroyed(false); - DCHECK_EQ(surface_factory.get(), surface->factory().get()); + DCHECK_EQ(compositor_frame_sink_support.get(), + surface->compositor_frame_sink_support().get()); return surface; } @@ -407,15 +410,15 @@ void SurfaceManager::RemoveTemporaryReference(const SurfaceId& surface_id, temporary_reference_ranges_.erase(frame_sink_id); } -void SurfaceManager::RegisterSurfaceFactoryClient( +void SurfaceManager::RegisterFrameSinkManagerClient( const FrameSinkId& frame_sink_id, - SurfaceFactoryClient* client) { - framesink_manager_.RegisterSurfaceFactoryClient(frame_sink_id, client); + FrameSinkManagerClient* client) { + framesink_manager_.RegisterFrameSinkManagerClient(frame_sink_id, client); } -void SurfaceManager::UnregisterSurfaceFactoryClient( +void SurfaceManager::UnregisterFrameSinkManagerClient( const FrameSinkId& frame_sink_id) { - framesink_manager_.UnregisterSurfaceFactoryClient(frame_sink_id); + framesink_manager_.UnregisterFrameSinkManagerClient(frame_sink_id); } void SurfaceManager::RegisterBeginFrameSource( @@ -428,6 +431,10 @@ void SurfaceManager::UnregisterBeginFrameSource(BeginFrameSource* source) { framesink_manager_.UnregisterBeginFrameSource(source); } +BeginFrameSource* SurfaceManager::GetPrimaryBeginFrameSource() { + return framesink_manager_.GetPrimaryBeginFrameSource(); +} + void SurfaceManager::RegisterFrameSinkHierarchy( const FrameSinkId& parent_frame_sink_id, const FrameSinkId& child_frame_sink_id) { @@ -475,6 +482,28 @@ void SurfaceManager::SurfaceCreated(const SurfaceInfo& surface_info) { observer.OnSurfaceCreated(surface_info); } +void SurfaceManager::SurfaceActivated(Surface* surface) { + if (dependency_tracker_) + dependency_tracker_->OnSurfaceActivated(surface); +} + +void SurfaceManager::SurfaceDependenciesChanged( + Surface* surface, + const base::flat_set<SurfaceId>& added_dependencies, + const base::flat_set<SurfaceId>& removed_dependencies) { + if (dependency_tracker_) { + dependency_tracker_->OnSurfaceDependenciesChanged( + surface, added_dependencies, removed_dependencies); + } +} + +void SurfaceManager::SurfaceDiscarded(Surface* surface) { + for (auto& observer : observer_list_) + observer.OnSurfaceDiscarded(surface->surface_id()); + if (dependency_tracker_) + dependency_tracker_->OnSurfaceDiscarded(surface); +} + void SurfaceManager::UnregisterSurface(const SurfaceId& surface_id) { DCHECK(thread_checker_.CalledOnValidThread()); SurfaceMap::iterator it = surface_map_.find(surface_id); @@ -498,19 +527,15 @@ void SurfaceManager::SurfaceReferencesToStringImpl(const SurfaceId& surface_id, if (surface->HasPendingFrame()) { // This provides the surface size from the root render pass. const CompositorFrame& frame = surface->GetPendingFrame(); - if (!frame.render_pass_list.empty()) { - *str << " pending " - << frame.render_pass_list.back()->output_rect.size().ToString(); - } + *str << " pending " + << frame.render_pass_list.back()->output_rect.size().ToString(); } if (surface->HasActiveFrame()) { // This provides the surface size from the root render pass. const CompositorFrame& frame = surface->GetActiveFrame(); - if (!frame.render_pass_list.empty()) { - *str << " active " - << frame.render_pass_list.back()->output_rect.size().ToString(); - } + *str << " active " + << frame.render_pass_list.back()->output_rect.size().ToString(); } } else { *str << surface_id; diff --git a/chromium/cc/surfaces/surface_manager.h b/chromium/cc/surfaces/surface_manager.h index 53998ec5cb9..4a45f2c34c2 100644 --- a/chromium/cc/surfaces/surface_manager.h +++ b/chromium/cc/surfaces/surface_manager.h @@ -13,13 +13,14 @@ #include <unordered_set> #include <vector> +#include "base/containers/flat_set.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/threading/thread_checker.h" #include "cc/surfaces/frame_sink_id.h" -#include "cc/surfaces/framesink_manager.h" +#include "cc/surfaces/frame_sink_manager.h" #include "cc/surfaces/surface_dependency_tracker.h" #include "cc/surfaces/surface_id.h" #include "cc/surfaces/surface_observer.h" @@ -36,12 +37,11 @@ namespace cc { class BeginFrameSource; class CompositorFrame; +class FrameSinkManagerClient; class Surface; -class SurfaceFactory; -class SurfaceFactoryClient; namespace test { -class CompositorFrameSinkSupportTest; +class SurfaceSynchronizationTest; } class CC_SURFACES_EXPORT SurfaceManager { @@ -59,17 +59,14 @@ class CC_SURFACES_EXPORT SurfaceManager { std::string SurfaceReferencesToString(); #endif - void SetDependencyTracker( - std::unique_ptr<SurfaceDependencyTracker> dependency_tracker); - SurfaceDependencyTracker* dependency_tracker() { - return dependency_tracker_.get(); - } + void SetDependencyTracker(SurfaceDependencyTracker* dependency_tracker); + SurfaceDependencyTracker* dependency_tracker() { return dependency_tracker_; } void RequestSurfaceResolution(Surface* pending_surface); std::unique_ptr<Surface> CreateSurface( - base::WeakPtr<SurfaceFactory> surface_factory, - const LocalSurfaceId& local_surface_id); + base::WeakPtr<CompositorFrameSinkSupport> compositor_frame_sink_support, + const SurfaceInfo& surface_info); // Destroy the Surface once a set of sequence numbers has been satisfied. void DestroySurface(std::unique_ptr<Surface> surface); @@ -84,10 +81,23 @@ class CC_SURFACES_EXPORT SurfaceManager { bool SurfaceModified(const SurfaceId& surface_id); - // Called when a CompositorFrame is submitted to a SurfaceFactory for a given - // |surface_id| for the first time. + // Called when a CompositorFrame is submitted to a CompositorFrameSinkSupport + // for a given |surface_id| for the first time. void SurfaceCreated(const SurfaceInfo& surface_info); + // Called when a CompositorFrame within |surface| has activated. + void SurfaceActivated(Surface* surface); + + // Called when the dependencies of a pending CompositorFrame within |surface| + // has changed. + void SurfaceDependenciesChanged( + Surface* surface, + const base::flat_set<SurfaceId>& added_dependencies, + const base::flat_set<SurfaceId>& removed_dependencies); + + // Called when |surface| is being destroyed. + void SurfaceDiscarded(Surface* surface); + // Require that the given sequence number must be satisfied (using // SatisfySequence) before the given surface can be destroyed. void RequireSequence(const SurfaceId& surface_id, @@ -103,23 +113,23 @@ class CC_SURFACES_EXPORT SurfaceManager { // possibly because a renderer process has crashed. void InvalidateFrameSinkId(const FrameSinkId& frame_sink_id); - // SurfaceFactoryClient, hierarchy, and BeginFrameSource can be registered - // and unregistered in any order with respect to each other. + // CompositorFrameSinkSupport, hierarchy, and BeginFrameSource can be + // registered and unregistered in any order with respect to each other. // // This happens in practice, e.g. the relationship to between ui::Compositor / // DelegatedFrameHost is known before ui::Compositor has a surface/client). // However, DelegatedFrameHost can register itself as a client before its // relationship with the ui::Compositor is known. - // Associates a SurfaceFactoryClient with the surface id frame_sink_id it + // Associates a FrameSinkManagerClient with the surface id frame_sink_id it // uses. - // SurfaceFactoryClient and surface namespaces/allocators have a 1:1 mapping. - // Caller guarantees the client is alive between register/unregister. + // FrameSinkManagerClient and surface namespaces/allocators have a 1:1 + // mapping. Caller guarantees the client is alive between register/unregister. // Reregistering the same namespace when a previous client is active is not // valid. - void RegisterSurfaceFactoryClient(const FrameSinkId& frame_sink_id, - SurfaceFactoryClient* client); - void UnregisterSurfaceFactoryClient(const FrameSinkId& frame_sink_id); + void RegisterFrameSinkManagerClient(const FrameSinkId& frame_sink_id, + FrameSinkManagerClient* client); + void UnregisterFrameSinkManagerClient(const FrameSinkId& frame_sink_id); // Associates a |source| with a particular namespace. That namespace and // any children of that namespace with valid clients can potentially use @@ -128,6 +138,10 @@ class CC_SURFACES_EXPORT SurfaceManager { const FrameSinkId& frame_sink_id); void UnregisterBeginFrameSource(BeginFrameSource* source); + // Returns a stable BeginFrameSource that forwards BeginFrames from the first + // available BeginFrameSource. + BeginFrameSource* GetPrimaryBeginFrameSource(); + // Register a relationship between two namespaces. This relationship means // that surfaces from the child namespace will be displayed in the parent. // Children are allowed to use any begin frame source that their parent can @@ -171,7 +185,7 @@ class CC_SURFACES_EXPORT SurfaceManager { } private: - friend class test::CompositorFrameSinkSupportTest; + friend class test::SurfaceSynchronizationTest; friend class SurfaceManagerRefTest; using SurfaceIdSet = std::unordered_set<SurfaceId, SurfaceIdHash>; @@ -242,11 +256,11 @@ class CC_SURFACES_EXPORT SurfaceManager { // Tracks references from the child surface to parent surface. If there are // zero entries in the set for a SurfaceId then nothing is referencing the // surface and it can be garbage collected. - std::unordered_map<SurfaceId, SurfaceIdSet, SurfaceIdHash> + std::unordered_map<SurfaceId, base::flat_set<SurfaceId>, SurfaceIdHash> child_to_parent_refs_; // Tracks references from the parent surface to child surface. Is the inverse // of |child_to_parent_refs_|. - std::unordered_map<SurfaceId, SurfaceIdSet, SurfaceIdHash> + std::unordered_map<SurfaceId, base::flat_set<SurfaceId>, SurfaceIdHash> parent_to_child_refs_; // Root SurfaceId that references display root surfaces. There is no Surface @@ -275,7 +289,7 @@ class CC_SURFACES_EXPORT SurfaceManager { std::unordered_map<FrameSinkId, std::vector<LocalSurfaceId>, FrameSinkIdHash> temporary_reference_ranges_; - std::unique_ptr<SurfaceDependencyTracker> dependency_tracker_; + SurfaceDependencyTracker* dependency_tracker_ = nullptr; base::WeakPtrFactory<SurfaceManager> weak_factory_; diff --git a/chromium/cc/surfaces/surface_manager_ref_unittest.cc b/chromium/cc/surfaces/surface_manager_ref_unittest.cc index eb678f8f0c9..e96b795e298 100644 --- a/chromium/cc/surfaces/surface_manager_ref_unittest.cc +++ b/chromium/cc/surfaces/surface_manager_ref_unittest.cc @@ -7,11 +7,13 @@ #include <unordered_map> #include <vector> +#include "base/containers/flat_set.h" #include "base/memory/ptr_util.h" #include "cc/surfaces/compositor_frame_sink_support.h" #include "cc/surfaces/surface.h" #include "cc/surfaces/surface_id.h" #include "cc/surfaces/surface_manager.h" +#include "cc/test/compositor_frame_helpers.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,7 +23,6 @@ using testing::SizeIs; using testing::UnorderedElementsAre; namespace cc { - namespace { constexpr FrameSinkId kFrameSink1(1, 0); @@ -41,13 +42,14 @@ class SurfaceManagerRefTest : public testing::Test { LocalSurfaceId local_surface_id(local_id, base::UnguessableToken::Deserialize(0, 1u)); GetCompositorFrameSinkSupport(frame_sink_id) - .SubmitCompositorFrame(local_surface_id, CompositorFrame()); + .SubmitCompositorFrame(local_surface_id, test::MakeCompositorFrame()); return SurfaceId(frame_sink_id, local_surface_id); } // Destroy Surface with |surface_id|. void DestroySurface(const SurfaceId& surface_id) { - GetCompositorFrameSinkSupport(surface_id.frame_sink_id()).EvictFrame(); + GetCompositorFrameSinkSupport(surface_id.frame_sink_id()) + .EvictCurrentSurface(); } CompositorFrameSinkSupport& GetCompositorFrameSinkSupport( @@ -81,13 +83,13 @@ class SurfaceManagerRefTest : public testing::Test { } // Returns all the references where |surface_id| is the parent. - const SurfaceManager::SurfaceIdSet& GetReferencesFrom( + const base::flat_set<SurfaceId>& GetReferencesFrom( const SurfaceId& surface_id) { return manager().parent_to_child_refs_[surface_id]; } // Returns all the references where |surface_id| is the child. - const SurfaceManager::SurfaceIdSet& GetReferencesFor( + const base::flat_set<SurfaceId>& GetReferencesFor( const SurfaceId& surface_id) { return manager().child_to_parent_refs_[surface_id]; } @@ -110,7 +112,7 @@ class SurfaceManagerRefTest : public testing::Test { } void TearDown() override { for (auto& support : supports_) - support.second->EvictFrame(); + support.second->EvictCurrentSurface(); supports_.clear(); manager_.reset(); } diff --git a/chromium/cc/surfaces/surface_manager_unittest.cc b/chromium/cc/surfaces/surface_manager_unittest.cc index 6142560acb4..4dbce5d094e 100644 --- a/chromium/cc/surfaces/surface_manager_unittest.cc +++ b/chromium/cc/surfaces/surface_manager_unittest.cc @@ -5,25 +5,27 @@ #include <stddef.h> #include "cc/scheduler/begin_frame_source.h" -#include "cc/surfaces/surface_factory_client.h" +#include "cc/surfaces/frame_sink_manager_client.h" #include "cc/surfaces/surface_manager.h" +#include "cc/test/begin_frame_source_test.h" +#include "cc/test/fake_external_begin_frame_source.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { -class FakeSurfaceFactoryClient : public SurfaceFactoryClient { +class FakeFrameSinkManagerClient : public FrameSinkManagerClient { public: - explicit FakeSurfaceFactoryClient(const FrameSinkId& frame_sink_id) + explicit FakeFrameSinkManagerClient(const FrameSinkId& frame_sink_id) : source_(nullptr), manager_(nullptr), frame_sink_id_(frame_sink_id) {} - FakeSurfaceFactoryClient(const FrameSinkId& frame_sink_id, - SurfaceManager* manager) + FakeFrameSinkManagerClient(const FrameSinkId& frame_sink_id, + SurfaceManager* manager) : source_(nullptr), manager_(nullptr), frame_sink_id_(frame_sink_id) { DCHECK(manager); Register(manager); } - ~FakeSurfaceFactoryClient() override { + ~FakeFrameSinkManagerClient() override { if (manager_) { Unregister(); } @@ -36,21 +38,20 @@ class FakeSurfaceFactoryClient : public SurfaceFactoryClient { void Register(SurfaceManager* manager) { EXPECT_EQ(nullptr, manager_); manager_ = manager; - manager_->RegisterSurfaceFactoryClient(frame_sink_id_, this); + manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this); } void Unregister() { EXPECT_NE(manager_, nullptr); - manager_->UnregisterSurfaceFactoryClient(frame_sink_id_); + manager_->UnregisterFrameSinkManagerClient(frame_sink_id_); manager_ = nullptr; } - // SurfaceFactoryClient implementation. - void ReturnResources(const ReturnedResourceArray& resources) override {} + // FrameSinkManagerClient implementation. void SetBeginFrameSource(BeginFrameSource* begin_frame_source) override { DCHECK(!source_ || !begin_frame_source); source_ = begin_frame_source; - }; + } private: BeginFrameSource* source_; @@ -80,8 +81,8 @@ class SurfaceManagerTest : public testing::Test { }; TEST_F(SurfaceManagerTest, SingleClients) { - FakeSurfaceFactoryClient client(FrameSinkId(1, 1)); - FakeSurfaceFactoryClient other_client(FrameSinkId(2, 2)); + FakeFrameSinkManagerClient client(FrameSinkId(1, 1)); + FakeFrameSinkManagerClient other_client(FrameSinkId(2, 2)); StubBeginFrameSource source; EXPECT_EQ(nullptr, client.source()); @@ -114,17 +115,76 @@ TEST_F(SurfaceManagerTest, SingleClients) { EXPECT_EQ(nullptr, client.source()); } +// This test verifies that a PrimaryBeginFrameSource will receive BeginFrames +// from the first BeginFrameSource registered. If that BeginFrameSource goes +// away then it will receive BeginFrames from the second BeginFrameSource. +TEST_F(SurfaceManagerTest, PrimaryBeginFrameSource) { + // This PrimaryBeginFrameSource should track the first BeginFrameSource + // registered with the SurfaceManager. + testing::NiceMock<MockBeginFrameObserver> obs; + BeginFrameSource* begin_frame_source = manager_.GetPrimaryBeginFrameSource(); + begin_frame_source->AddObserver(&obs); + + FakeFrameSinkManagerClient root1(FrameSinkId(1, 1), &manager_); + std::unique_ptr<FakeExternalBeginFrameSource> external_source1 = + base::MakeUnique<FakeExternalBeginFrameSource>(60.f, false); + manager_.RegisterBeginFrameSource(external_source1.get(), + root1.frame_sink_id()); + + FakeFrameSinkManagerClient root2(FrameSinkId(2, 2), &manager_); + std::unique_ptr<FakeExternalBeginFrameSource> external_source2 = + base::MakeUnique<FakeExternalBeginFrameSource>(60.f, false); + manager_.RegisterBeginFrameSource(external_source2.get(), + root2.frame_sink_id()); + + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + + // Ticking |external_source2| does not propagate to |begin_frame_source|. + { + EXPECT_CALL(obs, OnBeginFrame(args)).Times(0); + external_source2->TestOnBeginFrame(args); + testing::Mock::VerifyAndClearExpectations(&obs); + } + + // Ticking |external_source1| does propagate to |begin_frame_source| and + // |obs|. + { + EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(1); + external_source1->TestOnBeginFrame(args); + testing::Mock::VerifyAndClearExpectations(&obs); + } + + // Getting rid of |external_source1| means those BeginFrames will not + // propagate. Instead, |external_source2|'s BeginFrames will propagate + // to |begin_frame_source|. + { + manager_.UnregisterBeginFrameSource(external_source1.get()); + EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(0); + external_source1->TestOnBeginFrame(args); + testing::Mock::VerifyAndClearExpectations(&obs); + + EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(1); + external_source2->TestOnBeginFrame(args); + testing::Mock::VerifyAndClearExpectations(&obs); + } + + // Tear down + manager_.UnregisterBeginFrameSource(external_source2.get()); + begin_frame_source->RemoveObserver(&obs); +} + TEST_F(SurfaceManagerTest, MultipleDisplays) { StubBeginFrameSource root1_source; StubBeginFrameSource root2_source; // root1 -> A -> B // root2 -> C - FakeSurfaceFactoryClient root1(FrameSinkId(1, 1), &manager_); - FakeSurfaceFactoryClient root2(FrameSinkId(2, 2), &manager_); - FakeSurfaceFactoryClient client_a(FrameSinkId(3, 3), &manager_); - FakeSurfaceFactoryClient client_b(FrameSinkId(4, 4), &manager_); - FakeSurfaceFactoryClient client_c(FrameSinkId(5, 5), &manager_); + FakeFrameSinkManagerClient root1(FrameSinkId(1, 1), &manager_); + FakeFrameSinkManagerClient root2(FrameSinkId(2, 2), &manager_); + FakeFrameSinkManagerClient client_a(FrameSinkId(3, 3), &manager_); + FakeFrameSinkManagerClient client_b(FrameSinkId(4, 4), &manager_); + FakeFrameSinkManagerClient client_c(FrameSinkId(5, 5), &manager_); manager_.RegisterBeginFrameSource(&root1_source, root1.frame_sink_id()); manager_.RegisterBeginFrameSource(&root2_source, root2.frame_sink_id()); @@ -183,7 +243,7 @@ TEST_F(SurfaceManagerTest, MultipleDisplays) { // This test verifies that a BeginFrameSource path to the root from a // FrameSinkId is preserved even if that FrameSinkId has no children -// and does not have a corresponding SurfaceFactoryClient. +// and does not have a corresponding FrameSinkManagerClient. TEST_F(SurfaceManagerTest, ParentWithoutClientRetained) { StubBeginFrameSource root_source; @@ -192,15 +252,15 @@ TEST_F(SurfaceManagerTest, ParentWithoutClientRetained) { constexpr FrameSinkId kFrameSinkIdB(3, 3); constexpr FrameSinkId kFrameSinkIdC(4, 4); - FakeSurfaceFactoryClient root(kFrameSinkIdRoot, &manager_); - FakeSurfaceFactoryClient client_b(kFrameSinkIdB, &manager_); - FakeSurfaceFactoryClient client_c(kFrameSinkIdC, &manager_); + FakeFrameSinkManagerClient root(kFrameSinkIdRoot, &manager_); + FakeFrameSinkManagerClient client_b(kFrameSinkIdB, &manager_); + FakeFrameSinkManagerClient client_c(kFrameSinkIdC, &manager_); manager_.RegisterBeginFrameSource(&root_source, root.frame_sink_id()); EXPECT_EQ(&root_source, root.source()); // Set up initial hierarchy: root -> A -> B. - // Note that A does not have a SurfaceFactoryClient. + // Note that A does not have a FrameSinkManagerClient. manager_.RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA); manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB); // The root's BeginFrameSource should propagate to B. @@ -231,12 +291,12 @@ TEST_F(SurfaceManagerTest, constexpr FrameSinkId kFrameSinkIdB(3, 3); constexpr FrameSinkId kFrameSinkIdC(4, 4); - FakeSurfaceFactoryClient root(kFrameSinkIdRoot, &manager_); - FakeSurfaceFactoryClient client_b(kFrameSinkIdB, &manager_); - FakeSurfaceFactoryClient client_c(kFrameSinkIdC, &manager_); + FakeFrameSinkManagerClient root(kFrameSinkIdRoot, &manager_); + FakeFrameSinkManagerClient client_b(kFrameSinkIdB, &manager_); + FakeFrameSinkManagerClient client_c(kFrameSinkIdC, &manager_); // Set up initial hierarchy: root -> A -> B. - // Note that A does not have a SurfaceFactoryClient. + // Note that A does not have a FrameSinkManagerClient. manager_.RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA); manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB); // The root does not yet have a BeginFrameSource so client B should not have @@ -259,7 +319,7 @@ TEST_F(SurfaceManagerTest, } // In practice, registering and unregistering both parent/child relationships -// and SurfaceFactoryClients can happen in any ordering with respect to +// and FrameSinkManagerClients can happen in any ordering with respect to // each other. These following tests verify that all the data structures // are properly set up and cleaned up under the four permutations of orderings // of this nesting. @@ -364,9 +424,9 @@ class SurfaceManagerOrderingTest : public SurfaceManagerTest { StubBeginFrameSource source_; // A -> B -> C hierarchy, with A always having the BFS. - FakeSurfaceFactoryClient client_a_; - FakeSurfaceFactoryClient client_b_; - FakeSurfaceFactoryClient client_c_; + FakeFrameSinkManagerClient client_a_; + FakeFrameSinkManagerClient client_b_; + FakeFrameSinkManagerClient client_c_; bool hierarchy_registered_; bool clients_registered_; diff --git a/chromium/cc/surfaces/surface_observer.h b/chromium/cc/surfaces/surface_observer.h index e1c182a45c5..9b2fc1c57f6 100644 --- a/chromium/cc/surfaces/surface_observer.h +++ b/chromium/cc/surfaces/surface_observer.h @@ -18,6 +18,9 @@ class SurfaceObserver { // Runs when a Surface is damaged. *changed should be set to true if this // causes a Display to be damaged. virtual void OnSurfaceDamaged(const SurfaceId& surface_id, bool* changed) = 0; + + // Called when a surface is garbage-collected. + virtual void OnSurfaceDiscarded(const SurfaceId& surface_id) = 0; }; } // namespace cc diff --git a/chromium/cc/surfaces/surface_resource_holder.cc b/chromium/cc/surfaces/surface_resource_holder.cc index 6d1e60d0c6a..8d46b25fd03 100644 --- a/chromium/cc/surfaces/surface_resource_holder.cc +++ b/chromium/cc/surfaces/surface_resource_holder.cc @@ -4,20 +4,17 @@ #include "cc/surfaces/surface_resource_holder.h" -#include "cc/surfaces/surface_factory_client.h" - +#include "cc/surfaces/surface_resource_holder_client.h" namespace cc { -SurfaceResourceHolder::SurfaceResourceHolder(SurfaceFactoryClient* client) - : client_(client) { -} +SurfaceResourceHolder::SurfaceResourceHolder( + SurfaceResourceHolderClient* client) + : client_(client) {} -SurfaceResourceHolder::~SurfaceResourceHolder() { -} +SurfaceResourceHolder::~SurfaceResourceHolder() = default; SurfaceResourceHolder::ResourceRefs::ResourceRefs() - : refs_received_from_child(0), refs_holding_resource_alive(0) { -} + : refs_received_from_child(0), refs_holding_resource_alive(0) {} void SurfaceResourceHolder::Reset() { resource_id_info_map_.clear(); @@ -25,10 +22,8 @@ void SurfaceResourceHolder::Reset() { void SurfaceResourceHolder::ReceiveFromChild( const TransferableResourceArray& resources) { - for (TransferableResourceArray::const_iterator it = resources.begin(); - it != resources.end(); - ++it) { - ResourceRefs& ref = resource_id_info_map_[it->id]; + for (const auto& resource : resources) { + ResourceRefs& ref = resource_id_info_map_[resource.id]; ref.refs_holding_resource_alive++; ref.refs_received_from_child++; } diff --git a/chromium/cc/surfaces/surface_resource_holder.h b/chromium/cc/surfaces/surface_resource_holder.h index 5d222864e8b..e40f4945600 100644 --- a/chromium/cc/surfaces/surface_resource_holder.h +++ b/chromium/cc/surfaces/surface_resource_holder.h @@ -14,7 +14,7 @@ #include "cc/surfaces/surfaces_export.h" namespace cc { -class SurfaceFactoryClient; +class SurfaceResourceHolderClient; // A SurfaceResourceHolder manages the lifetime of resources submitted by a // particular SurfaceFactory. Each resource is held by the service until @@ -22,7 +22,7 @@ class SurfaceFactoryClient; // resource providers. class CC_SURFACES_EXPORT SurfaceResourceHolder { public: - explicit SurfaceResourceHolder(SurfaceFactoryClient* client); + explicit SurfaceResourceHolder(SurfaceResourceHolderClient* client); ~SurfaceResourceHolder(); void Reset(); @@ -31,7 +31,7 @@ class CC_SURFACES_EXPORT SurfaceResourceHolder { void UnrefResources(const ReturnedResourceArray& resources); private: - SurfaceFactoryClient* client_; + SurfaceResourceHolderClient* client_; struct ResourceRefs { ResourceRefs(); diff --git a/chromium/cc/surfaces/surface_resource_holder_client.h b/chromium/cc/surfaces/surface_resource_holder_client.h new file mode 100644 index 00000000000..2a9c78d86c2 --- /dev/null +++ b/chromium/cc/surfaces/surface_resource_holder_client.h @@ -0,0 +1,23 @@ +// Copyright 2017 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. +#ifndef CC_SURFACES_SURFACE_RESOURCE_HOLDER_CLIENT_H_ +#define CC_SURFACES_SURFACE_RESOURCE_HOLDER_CLIENT_H_ + +#include "cc/resources/returned_resource.h" +#include "cc/surfaces/surfaces_export.h" + +namespace cc { + +class CC_SURFACES_EXPORT SurfaceResourceHolderClient { + public: + virtual ~SurfaceResourceHolderClient() = default; + + // ReturnResources gets called when the display compositor is done using the + // resources so that the client can use them. + virtual void ReturnResources(const ReturnedResourceArray& resources) = 0; +}; + +} // namespace cc + +#endif // CC_SURFACES_SURFACE_RESOURCE_HOLDER_CLIENT_H_ diff --git a/chromium/cc/surfaces/surface_synchronization_unittest.cc b/chromium/cc/surfaces/surface_synchronization_unittest.cc new file mode 100644 index 00000000000..3117f69be63 --- /dev/null +++ b/chromium/cc/surfaces/surface_synchronization_unittest.cc @@ -0,0 +1,1394 @@ +// Copyright 2017 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 "base/containers/flat_set.h" +#include "cc/surfaces/compositor_frame_sink_support.h" +#include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_manager.h" +#include "cc/surfaces/surface_observer.h" +#include "cc/test/begin_frame_args_test.h" +#include "cc/test/compositor_frame_helpers.h" +#include "cc/test/fake_external_begin_frame_source.h" +#include "cc/test/mock_compositor_frame_sink_support_client.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Eq; +using testing::IsEmpty; +using testing::UnorderedElementsAre; + +namespace cc { +namespace test { +namespace { + +constexpr bool kIsRoot = true; +constexpr bool kIsChildRoot = false; +constexpr bool kHandlesFrameSinkIdInvalidation = true; +constexpr bool kNeedsSyncPoints = true; +constexpr FrameSinkId kDisplayFrameSink(2, 0); +constexpr FrameSinkId kParentFrameSink(3, 0); +constexpr FrameSinkId kChildFrameSink1(65563, 0); +constexpr FrameSinkId kChildFrameSink2(65564, 0); +constexpr FrameSinkId kArbitraryFrameSink(1337, 7331); + +std::vector<SurfaceId> empty_surface_ids() { + return std::vector<SurfaceId>(); +} + +SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) { + return SurfaceId( + frame_sink_id, + LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u))); +} + +} // namespace + +class SurfaceSynchronizationTest : public testing::Test, + public SurfaceObserver { + public: + SurfaceSynchronizationTest() + : surface_manager_(SurfaceManager::LifetimeType::REFERENCES) {} + ~SurfaceSynchronizationTest() override {} + + CompositorFrameSinkSupport& display_support() { return *supports_[0]; } + Surface* display_surface() { + return display_support().current_surface_for_testing(); + } + + CompositorFrameSinkSupport& parent_support() { return *supports_[1]; } + Surface* parent_surface() { + return parent_support().current_surface_for_testing(); + } + const ReferencedSurfaceTracker& parent_reference_tracker() { + return parent_support().ReferenceTrackerForTesting(); + } + + CompositorFrameSinkSupport& child_support1() { return *supports_[2]; } + Surface* child_surface1() { + return child_support1().current_surface_for_testing(); + } + + CompositorFrameSinkSupport& child_support2() { return *supports_[3]; } + Surface* child_surface2() { + return child_support2().current_surface_for_testing(); + } + + CompositorFrameSinkSupport& support(int index) { return *supports_[index]; } + Surface* surface(int index) { + return support(index).current_surface_for_testing(); + } + + SurfaceManager& surface_manager() { return surface_manager_; } + + // Returns all the references where |surface_id| is the parent. + const base::flat_set<SurfaceId>& GetChildReferences( + const SurfaceId& surface_id) { + return surface_manager().parent_to_child_refs_[surface_id]; + } + + // Returns true if there is a temporary reference for |surface_id|. + bool HasTemporaryReference(const SurfaceId& surface_id) { + return surface_manager().HasTemporaryReference(surface_id); + } + + SurfaceDependencyTracker& dependency_tracker() { + return *surface_manager_.dependency_tracker(); + } + + FakeExternalBeginFrameSource* begin_frame_source() { + return begin_frame_source_.get(); + } + + // testing::Test: + void SetUp() override { + testing::Test::SetUp(); + + begin_frame_source_ = + base::MakeUnique<FakeExternalBeginFrameSource>(0.f, false); + dependency_tracker_ = base::MakeUnique<SurfaceDependencyTracker>( + &surface_manager_, begin_frame_source_.get()); + surface_manager_.SetDependencyTracker(dependency_tracker_.get()); + surface_manager_.AddObserver(this); + supports_.push_back(CompositorFrameSinkSupport::Create( + &support_client_, &surface_manager_, kDisplayFrameSink, kIsRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); + supports_.push_back(CompositorFrameSinkSupport::Create( + &support_client_, &surface_manager_, kParentFrameSink, kIsChildRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); + supports_.push_back(CompositorFrameSinkSupport::Create( + &support_client_, &surface_manager_, kChildFrameSink1, kIsChildRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); + supports_.push_back(CompositorFrameSinkSupport::Create( + &support_client_, &surface_manager_, kChildFrameSink2, kIsChildRoot, + kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); + + // Normally, the BeginFrameSource would be registered by the Display. We + // register it here so that BeginFrames are received by the display support, + // for use in the PassesOnBeginFrameAcks test. Other supports do not receive + // BeginFrames, since the frame sink hierarchy is not set up in this test. + surface_manager_.RegisterBeginFrameSource(begin_frame_source_.get(), + kDisplayFrameSink); + } + + void TearDown() override { + surface_manager_.RemoveObserver(this); + surface_manager_.SetDependencyTracker(nullptr); + surface_manager_.UnregisterBeginFrameSource(begin_frame_source_.get()); + + dependency_tracker_.reset(); + + // SurfaceDependencyTracker depends on this BeginFrameSource and so it must + // be destroyed AFTER the dependency tracker is destroyed. + begin_frame_source_.reset(); + + supports_.clear(); + + damaged_surfaces_.clear(); + } + + bool IsSurfaceDamaged(const SurfaceId& surface_id) const { + return damaged_surfaces_.count(surface_id) > 0; + } + + // SurfaceObserver implementation: + void OnSurfaceCreated(const SurfaceInfo& surface_info) override {} + void OnSurfaceDamaged(const SurfaceId& surface_id, bool* changed) override { + damaged_surfaces_.insert(surface_id); + } + void OnSurfaceDiscarded(const SurfaceId& surface_id) override {} + + protected: + testing::NiceMock<MockCompositorFrameSinkSupportClient> support_client_; + + private: + base::flat_set<SurfaceId> damaged_surfaces_; + SurfaceManager surface_manager_; + std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_; + std::unique_ptr<SurfaceDependencyTracker> dependency_tracker_; + std::vector<std::unique_ptr<CompositorFrameSinkSupport>> supports_; + + DISALLOW_COPY_AND_ASSIGN(SurfaceSynchronizationTest); +}; + +// The display root surface should have a surface reference from the top-level +// root added/removed when a CompositorFrame is submitted with a new SurfaceId. +TEST_F(SurfaceSynchronizationTest, RootSurfaceReceivesReferences) { + const SurfaceId display_id_first = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId display_id_second = MakeSurfaceId(kDisplayFrameSink, 2); + + // Submit a CompositorFrame for the first display root surface. + display_support().SubmitCompositorFrame(display_id_first.local_surface_id(), + MakeCompositorFrame()); + + // A surface reference from the top-level root is added and there shouldn't be + // a temporary reference. + EXPECT_FALSE(HasTemporaryReference(display_id_first)); + EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), + UnorderedElementsAre(display_id_first)); + + // Submit a CompositorFrame for the second display root surface. + display_support().SubmitCompositorFrame(display_id_second.local_surface_id(), + MakeCompositorFrame()); + + // A surface reference from the top-level root to |display_id_second| should + // be added and the reference to |display_root_first| removed. + EXPECT_FALSE(HasTemporaryReference(display_id_second)); + EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), + UnorderedElementsAre(display_id_second)); + + // Surface |display_id_first| is unreachable and should get deleted. + EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(display_id_first)); +} + +// The parent Surface is blocked on |child_id1| and |child_id2|. +TEST_F(SurfaceSynchronizationTest, BlockedOnTwo) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // parent_support is blocked on |child_id1| and |child_id2|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1, child_id2)); + + // Submit a CompositorFrame without any dependencies to |child_id1|. + // parent_support should now only be blocked on |child_id2|. + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeCompositorFrame()); + + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // Submit a CompositorFrame without any dependencies to |child_id2|. + // parent_support should be activated. + child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeCompositorFrame()); + + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +} + +// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. +TEST_F(SurfaceSynchronizationTest, BlockedChain) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + + // parent_support is blocked on |child_id1|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + // The parent should not report damage until it activates. + EXPECT_FALSE(IsSurfaceDamaged(parent_id)); + + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // child_support1 should now be blocked on |child_id2|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + // The parent and child should not report damage until they activate. + EXPECT_FALSE(IsSurfaceDamaged(parent_id)); + EXPECT_FALSE(IsSurfaceDamaged(child_id1)); + + // The parent should still be blocked on |child_id1| because it's pending. + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + // Submit a CompositorFrame without any dependencies to |child_id2|. + // parent_support should be activated. + child_support2().SubmitCompositorFrame( + child_id2.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + TransferableResourceArray())); + + EXPECT_FALSE(dependency_tracker().has_deadline()); + + // child_surface1 should now be active. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // parent_surface should now be active. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + // All three surfaces |parent_id|, |child_id1|, and |child_id2| should + // now report damage. This would trigger a new display frame. + EXPECT_TRUE(IsSurfaceDamaged(parent_id)); + EXPECT_TRUE(IsSurfaceDamaged(child_id1)); + EXPECT_TRUE(IsSurfaceDamaged(child_id2)); +} + +// parent_surface and child_surface1 are blocked on |child_id2|. +TEST_F(SurfaceSynchronizationTest, TwoBlockedOnOne) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // parent_support is blocked on |child_id2|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // child_support1 should now be blocked on |child_id2|. + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // The parent should still be blocked on |child_id2|. + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // Submit a CompositorFrame without any dependencies to |child_id2|. + // parent_support should be activated. + child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeCompositorFrame()); + + EXPECT_FALSE(dependency_tracker().has_deadline()); + + // child_surface1 should now be active. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // parent_surface should now be active. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +} + +// parent_surface is blocked on |child_id1|, and child_surface2 is blocked on +// |child_id2| until the deadline hits. +TEST_F(SurfaceSynchronizationTest, DeadlineHits) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + + // parent_support is blocked on |child_id1|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // child_support1 should now be blocked on |child_id2|. + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // The parent should still be blocked on |child_id1| because it's pending. + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + // There is still a looming deadline! Eeek! + EXPECT_TRUE(dependency_tracker().has_deadline()); + + // parent_support is still blocked on |child_id1|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + // child_support1 is still blocked on |child_id2|. + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + } + + begin_frame_source()->TestOnBeginFrame(args); + + // The deadline has passed. + EXPECT_FALSE(dependency_tracker().has_deadline()); + + // parent_surface has been activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + // child_surface1 has been activated. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); +} + +// Verifies that the deadline does not reset if we submit CompositorFrames +// to new Surfaces with unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, FramesSubmittedAfterDeadlineSet) { + const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + for (int i = 0; i < 3; ++i) { + LocalSurfaceId local_surface_id(1, base::UnguessableToken::Create()); + support(i).SubmitCompositorFrame( + local_surface_id, + MakeCompositorFrame({arbitrary_id}, empty_surface_ids(), + TransferableResourceArray())); + // The deadline has been set. + EXPECT_TRUE(dependency_tracker().has_deadline()); + + // support(i) should be blocked on arbitrary_id. + EXPECT_FALSE(surface(i)->HasActiveFrame()); + EXPECT_TRUE(surface(i)->HasPendingFrame()); + EXPECT_THAT(surface(i)->blocking_surfaces(), + UnorderedElementsAre(arbitrary_id)); + + // Issue a BeginFrame to get closer to the deadline. + begin_frame_source()->TestOnBeginFrame(args); + } + + // The deadline hits and all the Surfaces should activate. + begin_frame_source()->TestOnBeginFrame(args); + for (int i = 0; i < 3; ++i) { + EXPECT_TRUE(surface(i)->HasActiveFrame()); + EXPECT_FALSE(surface(i)->HasPendingFrame()); + EXPECT_THAT(surface(i)->blocking_surfaces(), IsEmpty()); + } +} + +// This test verifies at the Surface activates once a CompositorFrame is +// submitted that has no unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, NewFrameOverridesOldDependencies) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); + + // Submit a CompositorFrame that depends on |arbitrary_id|. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({arbitrary_id}, empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the CompositorFrame is blocked on |arbitrary_id|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(arbitrary_id)); + + // Submit a CompositorFrame that has no dependencies. + parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the CompositorFrame has been activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +} + +// This test verifies that a pending CompositorFrame does not affect surface +// references. A new surface from a child will continue to exist as a temporary +// reference until the parent's frame activates. +TEST_F(SurfaceSynchronizationTest, OnlyActiveFramesAffectSurfaceReferences) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + // child_support1 submits a CompositorFrame without any dependencies. + // DidReceiveCompositorFrameAck should call on immediate activation. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(1); + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeCompositorFrame()); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that the child surface is not blocked. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // Verify that there's a temporary reference for |child_id1|. + EXPECT_TRUE(HasTemporaryReference(child_id1)); + + // parent_support submits a CompositorFrame that depends on |child_id1| + // (which is already active) and |child_id2|. Thus, the parent should not + // activate immediately. DidReceiveCompositorFrameAck should not be called + // immediately because the parent CompositorFrame is also blocked on + // |child_id2|. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id2}, {child_id1}, + TransferableResourceArray())); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that there's a temporary reference for |child_id1| that still + // exists. + EXPECT_TRUE(HasTemporaryReference(child_id1)); + + // child_support2 submits a CompositorFrame without any dependencies. + // Both the child and the parent should immediately ACK CompositorFrames + // on activation. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2); + child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeCompositorFrame()); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that the child surface is not blocked. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // Verify that the parent surface's CompositorFrame has activated and that the + // temporary reference has been replaced by a permanent one. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + EXPECT_FALSE(HasTemporaryReference(child_id1)); + EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); +} + +// This test verifies that we do not double count returned resources when a +// CompositorFrame starts out as pending, then becomes active, and then is +// replaced with another active CompositorFrame. +TEST_F(SurfaceSynchronizationTest, ResourcesOnlyReturnedOnce) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + // The parent submits a CompositorFrame that depends on |child_id| before the + // child submits a CompositorFrame. The CompositorFrame also has resources in + // its resource list. + TransferableResource resource; + resource.id = 1337; + resource.format = ALPHA_8; + resource.filter = 1234; + resource.size = gfx::Size(1234, 5678); + TransferableResourceArray resource_list = {resource}; + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), resource_list)); + + // Verify that the CompositorFrame is blocked on |child_id|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id)); + + child_support1().SubmitCompositorFrame( + child_id.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the child CompositorFrame activates immediately. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // Verify that the parent has activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + ReturnedResourceArray returned_resources = {resource.ToReturnedResource()}; + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(returned_resources)); + + // The parent submits a CompositorFrame without any dependencies. That frame + // should activate immediately, replacing the earlier frame. The resource from + // the earlier frame should be returned to the client. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({empty_surface_ids()}, {empty_surface_ids()}, + TransferableResourceArray())); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); +} + +// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. +// child_support1 evicts its blocked Surface. The parent surface should +// activate. +TEST_F(SurfaceSynchronizationTest, EvictSurfaceWithPendingFrame) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + // Submit a CompositorFrame that depends on |child_id1|. + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the CompositorFrame is blocked on |child_id1|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + + // Submit a CompositorFrame that depends on |child_id2|. + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the CompositorFrame is blocked on |child_id2|. + EXPECT_FALSE(child_surface1()->HasActiveFrame()); + EXPECT_TRUE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + + // Evict child_support1's current Surface. + // TODO(fsamuel): EvictCurrentSurface => EvictCurrentSurface. + child_support1().EvictCurrentSurface(); + + // The parent Surface should immediately activate. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + EXPECT_FALSE(dependency_tracker().has_deadline()); +} + +// This test verifies that if a surface has both a pending and active +// CompositorFrame and the pending CompositorFrame activates, replacing the +// existing active CompositorFrame, then the surface reference hierarchy will be +// updated allowing garbage collection of surfaces that are no longer +// referenced. +TEST_F(SurfaceSynchronizationTest, DropStaleReferencesAfterActivation) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); + + // The parent submits a CompositorFrame that depends on |child_id1| before the + // child submits a CompositorFrame. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + + // Verify that the CompositorFrame is blocked on |child_id|. + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id1)); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that no references are added while the CompositorFrame is pending. + EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); + + // DidReceiveCompositorFrameAck should get called twice: once for the child + // and once for the now active parent CompositorFrame. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2); + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeCompositorFrame()); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Verify that the child CompositorFrame activates immediately. + EXPECT_TRUE(child_surface1()->HasActiveFrame()); + EXPECT_FALSE(child_surface1()->HasPendingFrame()); + EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); + + // Verify that the parent Surface has activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + // Submit a new parent CompositorFrame to add a reference. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), {child_id1}, + TransferableResourceArray())); + + // Verify that the parent Surface has activated. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + + // Verify that there is no temporary reference for the child and that + // the reference from the parent to the child still exists. + EXPECT_FALSE(HasTemporaryReference(child_id1)); + EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); + + // The parent submits another CompositorFrame that depends on |child_id2|. + // Submitting a pending CompositorFrame will not trigger a CompositorFrameAck. + EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id2}, empty_surface_ids(), + TransferableResourceArray())); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // The parent surface should now have both a pending and activate + // CompositorFrame. Verify that the set of child references from + // |parent_id| are only from the active CompositorFrame. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), + UnorderedElementsAre(child_id2)); + EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); + + child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the parent Surface has activated and no longer has a pending + // CompositorFrame. Also verify that |child_id1| is no longer a child + // reference of |parent_id|. + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); + // The parent will not immediately refer to the child until it submits a new + // CompositorFrame with the reference. + EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); +} + +// Checks whether the latency info are moved to the new surface from the old +// one when LocalSurfaceId changes. No frame has unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, + LatencyInfoCarriedOverOnResize_NoUnresolvedDependencies) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const ui::LatencyComponentType latency_type1 = + ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; + const int64_t latency_id1 = 234; + const int64_t latency_sequence_number1 = 5645432; + const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; + const int64_t latency_id2 = 31434351; + const int64_t latency_sequence_number2 = 663788; + + // Submit a frame with latency info + ui::LatencyInfo info; + info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); + + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.latency_info.push_back(info); + + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + std::move(frame)); + + // Verify that the old surface has an active frame and no pending frame. + Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); + ASSERT_NE(nullptr, old_surface); + EXPECT_TRUE(old_surface->HasActiveFrame()); + EXPECT_FALSE(old_surface->HasPendingFrame()); + + // Submit another frame with some other latency info and a different + // LocalSurfaceId. + ui::LatencyInfo info2; + info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); + + CompositorFrame frame2 = MakeCompositorFrame(); + frame2.metadata.latency_info.push_back(info2); + + parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), + std::move(frame2)); + + // Verify that the new surface has an active frame and no pending frames. + Surface* surface = surface_manager().GetSurfaceForId(parent_id2); + ASSERT_NE(nullptr, surface); + EXPECT_TRUE(surface->HasActiveFrame()); + EXPECT_FALSE(surface->HasPendingFrame()); + + // Verify that the new surface has both latency info elements. + std::vector<ui::LatencyInfo> info_list; + surface->TakeLatencyInfo(&info_list); + EXPECT_EQ(2u, info_list.size()); + + ui::LatencyInfo aggregated_latency_info = info_list[0]; + aggregated_latency_info.AddNewLatencyFrom(info_list[1]); + + // Two components are the original ones, and the third one is + // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame + // submit. + EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); + + ui::LatencyInfo::LatencyComponent comp1; + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); + EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); + EXPECT_TRUE(aggregated_latency_info.FindLatency( + ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); +} + +// Checks whether the latency info are moved to the new surface from the old +// one when LocalSurfaceId changes. Old surface has unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, + LatencyInfoCarriedOverOnResize_OldSurfaceHasPendingAndActiveFrame) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + const ui::LatencyComponentType latency_type1 = + ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; + const int64_t latency_id1 = 234; + const int64_t latency_sequence_number1 = 5645432; + const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; + const int64_t latency_id2 = 31434351; + const int64_t latency_sequence_number2 = 663788; + + // Submit a frame with no unresolved dependecy. + ui::LatencyInfo info; + info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); + + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.latency_info.push_back(info); + + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + std::move(frame)); + + // Submit a frame with unresolved dependencies. + ui::LatencyInfo info2; + info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); + + CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray()); + frame2.metadata.latency_info.push_back(info2); + + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + std::move(frame2)); + + // Verify that the old surface has both an active and a pending frame. + Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); + ASSERT_NE(nullptr, old_surface); + EXPECT_TRUE(old_surface->HasActiveFrame()); + EXPECT_TRUE(old_surface->HasPendingFrame()); + + // Submit a frame with a new local surface id. + parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the new surface has an active frame only. + Surface* surface = surface_manager().GetSurfaceForId(parent_id2); + ASSERT_NE(nullptr, surface); + EXPECT_TRUE(surface->HasActiveFrame()); + EXPECT_FALSE(surface->HasPendingFrame()); + + // Verify that the new surface has latency info from both active and pending + // frame of the old surface. + std::vector<ui::LatencyInfo> info_list; + surface->TakeLatencyInfo(&info_list); + EXPECT_EQ(2u, info_list.size()); + + ui::LatencyInfo aggregated_latency_info = info_list[0]; + aggregated_latency_info.AddNewLatencyFrom(info_list[1]); + + // Two components are the original ones, and the third one is + // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame + // submit. + EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); + + ui::LatencyInfo::LatencyComponent comp1; + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); + EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); + EXPECT_TRUE(aggregated_latency_info.FindLatency( + ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); +} + +// Checks whether the latency info are moved to the new surface from the old +// one when LocalSurfaceId changes. The new surface has unresolved dependencies. +TEST_F(SurfaceSynchronizationTest, + LatencyInfoCarriedOverOnResize_NewSurfaceHasPendingFrame) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + const ui::LatencyComponentType latency_type1 = + ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; + const int64_t latency_id1 = 234; + const int64_t latency_sequence_number1 = 5645432; + const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; + const int64_t latency_id2 = 31434351; + const int64_t latency_sequence_number2 = 663788; + + // Submit a frame with no unresolved dependencies. + ui::LatencyInfo info; + info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); + + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.latency_info.push_back(info); + + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + std::move(frame)); + + // Verify that the old surface has an active frame only. + Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); + ASSERT_NE(nullptr, old_surface); + EXPECT_TRUE(old_surface->HasActiveFrame()); + EXPECT_FALSE(old_surface->HasPendingFrame()); + + // Submit a frame with a new local surface id and with unresolved + // dependencies. + ui::LatencyInfo info2; + info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); + + CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray()); + frame2.metadata.latency_info.push_back(info2); + + parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), + std::move(frame2)); + + // Verify that the new surface has a pending frame and no active frame. + Surface* surface = surface_manager().GetSurfaceForId(parent_id2); + ASSERT_NE(nullptr, surface); + EXPECT_TRUE(surface->HasPendingFrame()); + EXPECT_FALSE(surface->HasActiveFrame()); + + // Resolve the dependencies. The frame in parent's surface must become active. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + EXPECT_FALSE(surface->HasPendingFrame()); + EXPECT_TRUE(surface->HasActiveFrame()); + + // Both latency info elements must exist in the now-activated frame of the + // new surface. + std::vector<ui::LatencyInfo> info_list; + surface->TakeLatencyInfo(&info_list); + EXPECT_EQ(2u, info_list.size()); + + ui::LatencyInfo aggregated_latency_info = info_list[0]; + aggregated_latency_info.AddNewLatencyFrom(info_list[1]); + + // Two components are the original ones, and the third one is + // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame + // submit. + EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); + + ui::LatencyInfo::LatencyComponent comp1; + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); + EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); + EXPECT_TRUE( + aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); + EXPECT_TRUE(aggregated_latency_info.FindLatency( + ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); +} + +TEST_F(SurfaceSynchronizationTest, PassesOnBeginFrameAcks) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + + // Request BeginFrames. + display_support().SetNeedsBeginFrame(true); + + // Issue a BeginFrame. + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + begin_frame_source()->TestOnBeginFrame(args); + + // Check that the support forwards a DidNotProduceFrame ack to the + // BeginFrameSource. + BeginFrameAck ack(0, 1, 1, false); + display_support().DidNotProduceFrame(ack); + EXPECT_EQ(ack, begin_frame_source()->LastAckForObserver(&display_support())); + + // Issue another BeginFrame. + args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2); + begin_frame_source()->TestOnBeginFrame(args); + + // Check that the support forwards the BeginFrameAck attached + // to a CompositorFrame to the BeginFrameSource. + BeginFrameAck ack2(0, 2, 2, true); + CompositorFrame frame = MakeCompositorFrame(); + frame.metadata.begin_frame_ack = ack2; + display_support().SubmitCompositorFrame(display_id.local_surface_id(), + std::move(frame)); + EXPECT_EQ(ack2, begin_frame_source()->LastAckForObserver(&display_support())); +} + +// Checks that resources and ack are sent together if possible. +TEST_F(SurfaceSynchronizationTest, ReturnResourcesWithAck) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + TransferableResource resource; + resource.id = 1234; + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + {resource})); + ReturnedResourceArray returned_resources; + TransferableResource::ReturnResources({resource}, &returned_resources); + EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(Eq(returned_resources))); + parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), + MakeCompositorFrame()); +} + +// Verifies that if a surface is marked destroyed and a new frame arrives for +// it, it will be recovered. +TEST_F(SurfaceSynchronizationTest, SurfaceResurrection) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); + + // Create the child surface by submitting a frame to it. + EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the child surface is created. + Surface* surface = surface_manager().GetSurfaceForId(child_id); + EXPECT_NE(nullptr, surface); + + // Add a reference from the parent to the child. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id}, {child_id}, TransferableResourceArray())); + + // Attempt to destroy the child surface. The surface must still exist since + // the parent needs it but it will be marked as destroyed. + child_support1().EvictCurrentSurface(); + surface = surface_manager().GetSurfaceForId(child_id); + EXPECT_NE(nullptr, surface); + EXPECT_TRUE(surface->destroyed()); + + // Child submits another frame to the same local surface id that is marked + // destroyed. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + + // Verify that the surface that was marked destroyed is recovered and is being + // used again. + Surface* surface2 = surface_manager().GetSurfaceForId(child_id); + EXPECT_EQ(surface, surface2); + EXPECT_FALSE(surface2->destroyed()); +} + +// Verifies that if a LocalSurfaceId belonged to a surface that doesn't exist +// anymore, it can still be reused for new surfaces. +TEST_F(SurfaceSynchronizationTest, LocalSurfaceIdIsReusable) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); + + // Submit the first frame. Creates the surface. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); + + // Add a reference from parent. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id}, {child_id}, TransferableResourceArray())); + + // Remove the reference from parant. This allows us to destroy the surface. + parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), + MakeCompositorFrame()); + + // Destroy the surface. + child_support1().EvictCurrentSurface(); + EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); + + // Submit another frame with the same local surface id. This should work fine + // and a new surface must be created. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); + EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); +} + +// This test verifies that a crash does not occur if garbage collection is +// triggered during surface dependency resolution. This test triggers garbage +// collection during surface resolution, by causing an activation to remove +// a surface subtree from the root. Both the old subtree and the new +// activated subtree refer to the same dependency. The old subtree was activated +// by deadline, and the new subtree was activated by a dependency finally +// resolving. +TEST_F(SurfaceSynchronizationTest, DependencyTrackingGarbageCollection) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray())); + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id1}, empty_surface_ids(), + TransferableResourceArray())); + + EXPECT_TRUE(dependency_tracker().has_deadline()); + + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + + // Advance BeginFrames to trigger a deadline. + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); + + EXPECT_TRUE(display_surface()->HasActiveFrame()); + EXPECT_FALSE(display_surface()->HasPendingFrame()); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + + parent_support().SubmitCompositorFrame( + parent_id2.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray())); + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // The display surface now has two CompositorFrames. One that is pending, + // indirectly blocked on child_id and one that is active, also indirectly + // referring to child_id, but activated due to the deadline above. + EXPECT_TRUE(display_surface()->HasActiveFrame()); + EXPECT_TRUE(display_surface()->HasPendingFrame()); + + // Submitting a CompositorFrame will trigger garbage collection of the + // |parent_id1| subtree. This should not crash. + child_support1().SubmitCompositorFrame(child_id.local_surface_id(), + MakeCompositorFrame()); +} + +// This test verifies that a crash does not occur if garbage collection is +// triggered when a deadline forces frame activation. This test triggers garbage +// collection during deadline activation by causing the activation of a display +// frame to replace a previously activated display frame that was referring to +// a now-unreachable surface subtree. That subtree gets garbage collected during +// deadline activation. SurfaceDependencyTracker is also tracking a surface +// from that subtree due to an unresolved dependency. This test verifies that +// this dependency resolution does not crash. +TEST_F(SurfaceSynchronizationTest, GarbageCollectionOnDeadline) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); + + // |parent_id1| is blocked on |child_id|. + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray())); + + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id1}, {parent_id1}, + TransferableResourceArray())); + + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_TRUE(display_surface()->HasPendingFrame()); + EXPECT_FALSE(display_surface()->HasActiveFrame()); + + // Advance BeginFrames to trigger a deadline. This activates the + // CompositorFrame submitted above. + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(display_surface()->HasPendingFrame()); + EXPECT_TRUE(display_surface()->HasActiveFrame()); + + // By submitting a display CompositorFrame, and replacing the parent's + // CompositorFrame with another surface ID, parent_id1 becomes unreachable and + // a candidate for garbage collection. + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id2}, empty_surface_ids(), + TransferableResourceArray())); + + // Now |parent_id1| is only kept alive by the active |display_id| frame. + parent_support().SubmitCompositorFrame( + parent_id2.local_surface_id(), + MakeCompositorFrame({child_id}, empty_surface_ids(), + TransferableResourceArray())); + + // SurfaceDependencyTracker should now be tracking |display_id|, |parent_id1| + // and |parent_id2|. By activating the pending |display_id| frame by deadline, + // |parent_id1| becomes unreachable and is garbage collected while + // SurfaceDependencyTracker is in the process of activating surfaces. This + // should not cause a crash or use-after-free. + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); +} + +// This test verifies that a CompositorFrame will only blocked on embedded +// surfaces but not on other retained surface IDs in the CompositorFrame. +TEST_F(SurfaceSynchronizationTest, OnlyBlockOnEmbeddedSurfaces) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); + + // Submitting a CompositorFrame with |parent_id2| so that the display + // CompositorFrame can hold a reference to it. + parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), + MakeCompositorFrame()); + + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id1}, {parent_id2}, + TransferableResourceArray())); + + EXPECT_TRUE(display_surface()->HasPendingFrame()); + EXPECT_FALSE(display_surface()->HasActiveFrame()); + EXPECT_TRUE(dependency_tracker().has_deadline()); + + // Verify that the display CompositorFrame will only block on |parent_id1| but + // not |parent_id2|. + EXPECT_THAT(display_surface()->blocking_surfaces(), + UnorderedElementsAre(parent_id1)); + // Verify that the display surface holds no references while its + // CompositorFrame is pending. + EXPECT_THAT(GetChildReferences(display_id), IsEmpty()); + + // Submitting a CompositorFrame with |parent_id1| should unblock the display + // CompositorFrame. + parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), + MakeCompositorFrame()); + + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(display_surface()->HasPendingFrame()); + EXPECT_TRUE(display_surface()->HasActiveFrame()); + EXPECT_THAT(display_surface()->blocking_surfaces(), IsEmpty()); +} + +// This test verifies that a late arriving CompositorFrame activates immediately +// and does not trigger a new deadline. +TEST_F(SurfaceSynchronizationTest, LateArrivingDependency) { + const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + + display_support().SubmitCompositorFrame( + display_id.local_surface_id(), + MakeCompositorFrame({parent_id1}, empty_surface_ids(), + TransferableResourceArray())); + + EXPECT_TRUE(display_surface()->HasPendingFrame()); + EXPECT_FALSE(display_surface()->HasActiveFrame()); + EXPECT_TRUE(dependency_tracker().has_deadline()); + + // Advance BeginFrames to trigger a deadline. This activates the + // CompositorFrame submitted above. + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(display_surface()->HasPendingFrame()); + EXPECT_TRUE(display_surface()->HasActiveFrame()); + + // A late arriving CompositorFrame should activate immediately without + // scheduling a deadline and without waiting for dependencies to resolve. + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id1}, empty_surface_ids(), + TransferableResourceArray())); + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); +} + +// This test verifies that CompositorFrames submitted to a surface referenced +// by a parent CompositorFrame as a fallback will be rejected and ACK'ed +// immediately. +TEST_F(SurfaceSynchronizationTest, FallbackSurfacesClosed) { + const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); + // This is the fallback child surface that the parent holds a reference to. + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); + // This is the primary child surface that the parent wants to block on. + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); + + // child_support1 submits a CompositorFrame without any dependencies. + // DidReceiveCompositorFrameAck should call on immediate activation. + // However, resources will not be returned because this frame is a candidate + // for display. + TransferableResource resource; + resource.id = 1337; + resource.format = ALPHA_8; + resource.filter = 1234; + resource.size = gfx::Size(1234, 5678); + ReturnedResourceArray returned_resources; + TransferableResource::ReturnResources({resource}, &returned_resources); + + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(Eq(ReturnedResourceArray()))); + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + {resource})); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // The parent is blocked on |child_id2| and references |child_id1|. The + // surface corresponding to |child_id1| will not accept new CompositorFrames + // while the parent CompositorFrame is blocked. + parent_support().SubmitCompositorFrame( + parent_id1.local_surface_id(), + MakeCompositorFrame({child_id2}, {child_id1}, + TransferableResourceArray())); + EXPECT_TRUE(dependency_tracker().has_deadline()); + EXPECT_TRUE(parent_surface()->HasPendingFrame()); + EXPECT_FALSE(parent_surface()->HasActiveFrame()); + + // Resources will be returned immediately because |child_id1|'s surface is + // closed. + TransferableResource resource2; + resource2.id = 1246; + resource2.format = ALPHA_8; + resource2.filter = 1357; + resource2.size = gfx::Size(8765, 4321); + ReturnedResourceArray returned_resources2; + TransferableResource::ReturnResources({resource2}, &returned_resources2); + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(Eq(returned_resources2))); + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + {resource2})); + testing::Mock::VerifyAndClearExpectations(&support_client_); + + // Advance BeginFrames to trigger a deadline. This activates the + // CompositorFrame submitted to the parent. + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + for (int i = 0; i < 3; ++i) { + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_TRUE(dependency_tracker().has_deadline()); + } + begin_frame_source()->TestOnBeginFrame(args); + EXPECT_FALSE(dependency_tracker().has_deadline()); + EXPECT_FALSE(parent_surface()->HasPendingFrame()); + EXPECT_TRUE(parent_surface()->HasActiveFrame()); + + // Resources will be returned immediately because |child_id1|'s surface is + // closed forever. + EXPECT_CALL(support_client_, + DidReceiveCompositorFrameAck(Eq(returned_resources2))); + child_support1().SubmitCompositorFrame( + child_id1.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), + {resource2})); + testing::Mock::VerifyAndClearExpectations(&support_client_); +} + +} // namespace test +} // namespace cc diff --git a/chromium/cc/surfaces/surface_unittest.cc b/chromium/cc/surfaces/surface_unittest.cc index e40cc05f9b3..beeebfc9fb3 100644 --- a/chromium/cc/surfaces/surface_unittest.cc +++ b/chromium/cc/surfaces/surface_unittest.cc @@ -10,6 +10,7 @@ #include "cc/surfaces/surface_dependency_tracker.h" #include "cc/surfaces/surface_manager.h" #include "cc/test/begin_frame_args_test.h" +#include "cc/test/compositor_frame_helpers.h" #include "cc/test/fake_external_begin_frame_source.h" #include "cc/test/scheduler_test_common.h" #include "testing/gtest/include/gtest/gtest.h" @@ -32,9 +33,9 @@ TEST(SurfaceTest, SurfaceLifetime) { LocalSurfaceId local_surface_id(6, base::UnguessableToken::Create()); SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id); - support->SubmitCompositorFrame(local_surface_id, CompositorFrame()); + support->SubmitCompositorFrame(local_surface_id, test::MakeCompositorFrame()); EXPECT_TRUE(manager.GetSurfaceForId(surface_id)); - support->EvictFrame(); + support->EvictCurrentSurface(); EXPECT_EQ(NULL, manager.GetSurfaceForId(surface_id)); } @@ -64,8 +65,7 @@ TEST(SurfaceTest, CopyRequestLifetime) { LocalSurfaceId local_surface_id(6, base::UnguessableToken::Create()); SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id); - CompositorFrame frame; - frame.render_pass_list.push_back(RenderPass::Create()); + CompositorFrame frame = test::MakeCompositorFrame(); support->SubmitCompositorFrame(local_surface_id, std::move(frame)); Surface* surface = manager.GetSurfaceForId(surface_id); ASSERT_TRUE(!!surface); @@ -78,13 +78,15 @@ TEST(SurfaceTest, CopyRequestLifetime) { int max_frame = 3, start_id = 200; for (int i = 0; i < max_frame; ++i) { - CompositorFrame frame; + CompositorFrame frame = test::MakeEmptyCompositorFrame(); frame.render_pass_list.push_back(RenderPass::Create()); frame.render_pass_list.back()->id = i * 3 + start_id; frame.render_pass_list.push_back(RenderPass::Create()); frame.render_pass_list.back()->id = i * 3 + start_id + 1; frame.render_pass_list.push_back(RenderPass::Create()); - frame.render_pass_list.back()->id = i * 3 + start_id + 2; + frame.render_pass_list.back()->SetNew(i * 3 + start_id + 2, + gfx::Rect(0, 0, 20, 20), gfx::Rect(), + gfx::Transform()); support->SubmitCompositorFrame(local_surface_id, std::move(frame)); } @@ -105,7 +107,7 @@ TEST(SurfaceTest, CopyRequestLifetime) { copy_requests.find(last_pass_id)->second->SendEmptyResult(); EXPECT_TRUE(copy_called); - support->EvictFrame(); + support->EvictCurrentSurface(); } } // namespace diff --git a/chromium/cc/surfaces/surfaces_pixeltest.cc b/chromium/cc/surfaces/surfaces_pixeltest.cc index 0b81474ccb5..2917c086e42 100644 --- a/chromium/cc/surfaces/surfaces_pixeltest.cc +++ b/chromium/cc/surfaces/surfaces_pixeltest.cc @@ -11,6 +11,7 @@ #include "cc/surfaces/surface.h" #include "cc/surfaces/surface_aggregator.h" #include "cc/surfaces/surface_manager.h" +#include "cc/test/compositor_frame_helpers.h" #include "cc/test/pixel_comparator.h" #include "cc/test/pixel_test.h" #include "testing/gtest/include/gtest/gtest.h" @@ -39,7 +40,7 @@ class SurfacesPixelTest : public RendererPixelTest<GLRenderer> { kIsRoot, kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)) {} - ~SurfacesPixelTest() override { support_->EvictFrame(); } + ~SurfacesPixelTest() override { support_->EvictCurrentSurface(); } protected: SurfaceManager manager_; @@ -51,14 +52,14 @@ SharedQuadState* CreateAndAppendTestSharedQuadState( RenderPass* render_pass, const gfx::Transform& transform, const gfx::Size& size) { - const gfx::Size layer_bounds = size; + const gfx::Rect layer_rect = gfx::Rect(size); const gfx::Rect visible_layer_rect = gfx::Rect(size); const gfx::Rect clip_rect = gfx::Rect(size); bool is_clipped = false; float opacity = 1.f; const SkBlendMode blend_mode = SkBlendMode::kSrcOver; SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState(); - shared_state->SetAll(transform, layer_bounds, visible_layer_rect, clip_rect, + shared_state->SetAll(transform, layer_rect, visible_layer_rect, clip_rect, is_clipped, opacity, blend_mode, 0); return shared_state; } @@ -82,8 +83,7 @@ TEST_F(SurfacesPixelTest, DrawSimpleFrame) { SK_ColorGREEN, force_anti_aliasing_off); - - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeCompositorFrame(); root_frame.render_pass_list.push_back(std::move(pass)); LocalSurfaceId root_local_surface_id = allocator_.GenerateId(); @@ -140,7 +140,7 @@ TEST_F(SurfacesPixelTest, DrawSimpleAggregatedFrame) { SK_ColorYELLOW, force_anti_aliasing_off); - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeCompositorFrame(); root_frame.render_pass_list.push_back(std::move(pass)); support_->SubmitCompositorFrame(root_local_surface_id, @@ -165,7 +165,7 @@ TEST_F(SurfacesPixelTest, DrawSimpleAggregatedFrame) { SK_ColorBLUE, force_anti_aliasing_off); - CompositorFrame child_frame; + CompositorFrame child_frame = test::MakeCompositorFrame(); child_frame.render_pass_list.push_back(std::move(pass)); child_support->SubmitCompositorFrame(child_local_surface_id, @@ -182,7 +182,7 @@ TEST_F(SurfacesPixelTest, DrawSimpleAggregatedFrame) { base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")), pixel_comparator)); - child_support->EvictFrame(); + child_support->EvictCurrentSurface(); } // Tests a surface quad that has a non-identity transform into its pass. @@ -240,7 +240,7 @@ TEST_F(SurfacesPixelTest, DrawAggregatedFrameWithSurfaceTransforms) { right_child_id, SurfaceDrawQuadType::PRIMARY, nullptr); - CompositorFrame root_frame; + CompositorFrame root_frame = test::MakeCompositorFrame(); root_frame.render_pass_list.push_back(std::move(pass)); support_->SubmitCompositorFrame(root_local_surface_id, @@ -273,7 +273,7 @@ TEST_F(SurfacesPixelTest, DrawAggregatedFrameWithSurfaceTransforms) { SK_ColorBLUE, force_anti_aliasing_off); - CompositorFrame child_frame; + CompositorFrame child_frame = test::MakeCompositorFrame(); child_frame.render_pass_list.push_back(std::move(pass)); left_support->SubmitCompositorFrame(left_child_local_id, @@ -306,7 +306,7 @@ TEST_F(SurfacesPixelTest, DrawAggregatedFrameWithSurfaceTransforms) { SK_ColorGREEN, force_anti_aliasing_off); - CompositorFrame child_frame; + CompositorFrame child_frame = test::MakeCompositorFrame(); child_frame.render_pass_list.push_back(std::move(pass)); right_support->SubmitCompositorFrame(right_child_local_id, @@ -324,8 +324,8 @@ TEST_F(SurfacesPixelTest, DrawAggregatedFrameWithSurfaceTransforms) { base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")), pixel_comparator)); - left_support->EvictFrame(); - right_support->EvictFrame(); + left_support->EvictCurrentSurface(); + right_support->EvictCurrentSurface(); } } // namespace diff --git a/chromium/cc/tiles/checker_image_tracker.cc b/chromium/cc/tiles/checker_image_tracker.cc index 8bbcbebe839..022269d87a9 100644 --- a/chromium/cc/tiles/checker_image_tracker.cc +++ b/chromium/cc/tiles/checker_image_tracker.cc @@ -5,6 +5,7 @@ #include "cc/tiles/checker_image_tracker.h" #include "base/bind.h" +#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" @@ -30,35 +31,23 @@ CheckerImageTracker::CheckerImageTracker(ImageController* image_controller, enable_checker_imaging_(enable_checker_imaging), weak_factory_(this) {} -CheckerImageTracker::~CheckerImageTracker() { - // Unlock all images pending decode requests. - for (auto it : image_id_to_decode_request_id_) - image_controller_->UnlockImageDecode(it.second); -} +CheckerImageTracker::~CheckerImageTracker() = default; -void CheckerImageTracker::FilterImagesForCheckeringForTile( - std::vector<DrawImage>* images, - ImageIdFlatSet* checkered_images, - WhichTree tree) { - TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "CheckerImageTracker::FilterImagesForCheckeringForTile", "tree", - tree); - DCHECK(checkered_images->empty()); - - base::EraseIf(*images, - [this, tree, &checkered_images](const DrawImage& draw_image) { - const sk_sp<const SkImage>& image = draw_image.image(); - DCHECK(image->isLazyGenerated()); - if (ShouldCheckerImage(image, tree)) { - ScheduleImageDecodeIfNecessary(image); - checkered_images->insert(image->uniqueID()); - return true; - } - return false; - }); +void CheckerImageTracker::ScheduleImageDecodeQueue( + ImageDecodeQueue image_decode_queue) { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "CheckerImageTracker::ScheduleImageDecodeQueue"); + // Only checker-imaged (async updated) images are decoded using the image + // decode service. If |enable_checker_imaging_| is false, no image should + // be checkered. + DCHECK(image_decode_queue.empty() || enable_checker_imaging_); + + image_decode_queue_ = std::move(image_decode_queue); + ScheduleNextImageDecode(); } -const ImageIdFlatSet& CheckerImageTracker::TakeImagesToInvalidateOnSyncTree() { +const PaintImageIdFlatSet& +CheckerImageTracker::TakeImagesToInvalidateOnSyncTree() { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "CheckerImageTracker::TakeImagesToInvalidateOnSyncTree"); DCHECK_EQ(invalidated_images_on_current_sync_tree_.size(), 0u) @@ -72,17 +61,45 @@ const ImageIdFlatSet& CheckerImageTracker::TakeImagesToInvalidateOnSyncTree() { void CheckerImageTracker::DidActivateSyncTree() { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "CheckerImageTracker::DidActivateSyncTree"); - for (auto image_id : invalidated_images_on_current_sync_tree_) { - auto it = image_id_to_decode_request_id_.find(image_id); - image_controller_->UnlockImageDecode(it->second); - image_id_to_decode_request_id_.erase(it); - } - + for (auto image_id : invalidated_images_on_current_sync_tree_) + image_id_to_decode_.erase(image_id); invalidated_images_on_current_sync_tree_.clear(); } +void CheckerImageTracker::ClearTracker(bool can_clear_decode_policy_tracking) { + // Unlock all images and tracking for images pending invalidation. The + // |images_invalidated_on_current_sync_tree_| will be cleared when the sync + // tree is activated. + // + // Note that we assume that any images with DecodePolicy::ASYNC, which may be + // checkered, are safe to stop tracking here and will either be re-checkered + // and invalidated when the decode completes or be invalidated externally. + // This is because the policy decision for checkering an image is based on + // inputs received from a PaintImage in the DisplayItemList. The policy chosen + // for a PaintImage should remain unchanged. + // If the external inputs for deciding the decode policy for an image change, + // they should be accompanied with an invalidation during paint. + image_id_to_decode_.clear(); + + if (can_clear_decode_policy_tracking) { + image_async_decode_state_.clear(); + } else { + // If we can't clear the decode policy, we need to make sure we still + // re-decode and checker images that were pending invalidation. + for (auto image_id : images_pending_invalidation_) { + auto it = image_async_decode_state_.find(image_id); + + DCHECK(it != image_async_decode_state_.end()); + DCHECK_EQ(it->second, DecodePolicy::SYNC_DECODED_ONCE); + + it->second = DecodePolicy::ASYNC; + } + } + images_pending_invalidation_.clear(); +} + void CheckerImageTracker::DidFinishImageDecode( - ImageId image_id, + PaintImage::Id image_id, ImageController::ImageDecodeRequestId request_id, ImageController::ImageDecodeResult result) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), @@ -90,75 +107,114 @@ void CheckerImageTracker::DidFinishImageDecode( TRACE_EVENT_ASYNC_END0("cc", "CheckerImageTracker::DeferImageDecode", image_id); - DCHECK_NE(result, ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED); - DCHECK_NE(pending_image_decodes_.count(image_id), 0u); - pending_image_decodes_.erase(image_id); + DCHECK_NE(ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED, result); + DCHECK_EQ(outstanding_image_decode_.value().stable_id(), image_id); + outstanding_image_decode_.reset(); + + // The async decode state may have been cleared if the tracker was cleared + // before this decode could be finished. + auto it = image_async_decode_state_.find(image_id); + if (it == image_async_decode_state_.end()) { + DCHECK_EQ(image_id_to_decode_.count(image_id), 0u); + return; + } - images_decoded_once_.insert(image_id); + it->second = DecodePolicy::SYNC_DECODED_ONCE; images_pending_invalidation_.insert(image_id); + ScheduleNextImageDecode(); client_->NeedsInvalidationForCheckerImagedTiles(); } -bool CheckerImageTracker::ShouldCheckerImage(const sk_sp<const SkImage>& image, - WhichTree tree) const { +bool CheckerImageTracker::ShouldCheckerImage(const PaintImage& image, + WhichTree tree) { TRACE_EVENT1("cc", "CheckerImageTracker::ShouldCheckerImage", "image_id", - image->uniqueID()); + image.stable_id()); if (!enable_checker_imaging_) return false; + PaintImage::Id image_id = image.stable_id(); + // If the image was invalidated on the current sync tree and the tile is // for the active tree, continue checkering it on the active tree to ensure // the image update is atomic for the frame. - if (invalidated_images_on_current_sync_tree_.count(image->uniqueID()) != 0 && + if (invalidated_images_on_current_sync_tree_.count(image_id) != 0 && tree == WhichTree::ACTIVE_TREE) { return true; } - // If a decode request is pending for this image, continue checkering it. - if (pending_image_decodes_.find(image->uniqueID()) != - pending_image_decodes_.end()) { - return true; - } - // If the image is pending invalidation, continue checkering it. All tiles // for these images will be invalidated on the next pending tree. - if (images_pending_invalidation_.find(image->uniqueID()) != + if (images_pending_invalidation_.find(image_id) != images_pending_invalidation_.end()) { return true; } - // If the image has been decoded once before, don't checker it again. - if (images_decoded_once_.find(image->uniqueID()) != - images_decoded_once_.end()) { - return false; + auto insert_result = + image_async_decode_state_.insert(std::pair<PaintImage::Id, DecodePolicy>( + image_id, DecodePolicy::SYNC_PERMANENT)); + auto it = insert_result.first; + if (insert_result.second) { + bool can_checker_image = + image.animation_type() == PaintImage::AnimationType::STATIC && + image.completion_state() == PaintImage::CompletionState::DONE; + if (can_checker_image) { + size_t size = SafeSizeOfImage(image.sk_image().get()); + it->second = (size >= kMinImageSizeToCheckerBytes && + size <= image_controller_->image_cache_max_limit_bytes()) + ? DecodePolicy::ASYNC + : DecodePolicy::SYNC_PERMANENT; + } } - return SafeSizeOfImage(image.get()) >= kMinImageSizeToCheckerBytes; + return it->second == DecodePolicy::ASYNC; } -void CheckerImageTracker::ScheduleImageDecodeIfNecessary( - const sk_sp<const SkImage>& image) { +void CheckerImageTracker::ScheduleNextImageDecode() { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "CheckerImageTracker::ScheduleImageDecodeIfNecessary"); - ImageId image_id = image->uniqueID(); + "CheckerImageTracker::ScheduleNextImageDecode"); + // We can have only one outsanding decode pending completion with the decode + // service. We'll come back here when it is completed. + if (outstanding_image_decode_.has_value()) + return; + + while (!image_decode_queue_.empty()) { + auto candidate = std::move(image_decode_queue_.front()); + image_decode_queue_.erase(image_decode_queue_.begin()); + + // Once an image has been decoded, it can still be present in the decode + // queue (duplicate entries), or while an image is still being skipped on + // the active tree. Check if the image is still ASYNC to see if a decode is + // needed. + PaintImage::Id image_id = candidate.stable_id(); + auto it = image_async_decode_state_.find(image_id); + DCHECK(it != image_async_decode_state_.end()); + if (it->second != DecodePolicy::ASYNC) + continue; + + outstanding_image_decode_.emplace(candidate); + break; + } - // If the image has already been decoded, or a decode request is pending, we - // don't need to schedule another decode. - if (images_decoded_once_.count(image_id) != 0 || - pending_image_decodes_.count(image_id) != 0) { + // We either found an image to decode or we reached the end of the queue. If + // we couldn't find an image, we're done. + if (!outstanding_image_decode_.has_value()) { + DCHECK(image_decode_queue_.empty()); return; } + PaintImage::Id image_id = outstanding_image_decode_.value().stable_id(); + DCHECK_EQ(image_id_to_decode_.count(image_id), 0u); TRACE_EVENT_ASYNC_BEGIN0("cc", "CheckerImageTracker::DeferImageDecode", image_id); - DCHECK_EQ(image_id_to_decode_request_id_.count(image_id), 0U); - - image_id_to_decode_request_id_[image_id] = + ImageController::ImageDecodeRequestId request_id = image_controller_->QueueImageDecode( - image, base::Bind(&CheckerImageTracker::DidFinishImageDecode, - weak_factory_.GetWeakPtr(), image_id)); - pending_image_decodes_.insert(image_id); + outstanding_image_decode_.value().sk_image(), + base::Bind(&CheckerImageTracker::DidFinishImageDecode, + weak_factory_.GetWeakPtr(), image_id)); + + image_id_to_decode_.emplace(image_id, base::MakeUnique<ScopedDecodeHolder>( + image_controller_, request_id)); } } // namespace cc diff --git a/chromium/cc/tiles/checker_image_tracker.h b/chromium/cc/tiles/checker_image_tracker.h index 3bc29f55294..9f64d8d3f06 100644 --- a/chromium/cc/tiles/checker_image_tracker.h +++ b/chromium/cc/tiles/checker_image_tracker.h @@ -8,6 +8,7 @@ #include <unordered_map> #include <vector> +#include "base/optional.h" #include "cc/cc_export.h" #include "cc/paint/image_id.h" #include "cc/tiles/image_controller.h" @@ -37,33 +38,62 @@ class CC_EXPORT CheckerImageTracker { bool enable_checker_imaging); ~CheckerImageTracker(); - // Given the |images| for a tile, filters the images which will be deferred - // asynchronously using the image decoded service, eliminating them from - // |images| adds them to the |checkered_images| set, so they can be skipped - // during the rasterization of this tile. - // The entries remaining in |images| are for images for which a cached decode - // from the image decode service is available, or which must be decoded before - // before this tile can be rasterized. - void FilterImagesForCheckeringForTile(std::vector<DrawImage>* images, - ImageIdFlatSet* checkered_images, - WhichTree tree); + // Returns true if the decode for |image| will be deferred to the image decode + // service and it should be be skipped during raster. + bool ShouldCheckerImage(const PaintImage& image, WhichTree tree); + + using ImageDecodeQueue = std::vector<PaintImage>; + void ScheduleImageDecodeQueue(ImageDecodeQueue image_decode_queue); // Returns the set of images to invalidate on the sync tree. - const ImageIdFlatSet& TakeImagesToInvalidateOnSyncTree(); + const PaintImageIdFlatSet& TakeImagesToInvalidateOnSyncTree(); + // Called when the sync tree is activated. Each call to + // TakeImagesToInvalidateOnSyncTree() must be followed by this when the + // invalidated sync tree is activated. void DidActivateSyncTree(); + // Called to reset the tracker state on navigation. This will release all + // cached images. Setting |can_clear_decode_policy_tracking| will also result + // in re-checkering any images already decoded by the tracker. + void ClearTracker(bool can_clear_decode_policy_tracking); + private: - void DidFinishImageDecode(ImageId image_id, + enum class DecodePolicy { + // The image can be decoded asynchronously from raster. When set, the image + // is always skipped during rasterization of content that includes this + // image until it has been decoded using the decode service. + ASYNC, + // The image has been decoded asynchronously once and should now be + // synchronously rasterized with the content. + SYNC_DECODED_ONCE, + // The image has been permanently vetoed from being decoded async. + SYNC_PERMANENT, + }; + + // Wrapper to unlock an image decode requested from the ImageController on + // destruction. + class ScopedDecodeHolder { + public: + ScopedDecodeHolder(ImageController* controller, + ImageController::ImageDecodeRequestId request_id) + : controller_(controller), request_id_(request_id) {} + ~ScopedDecodeHolder() { controller_->UnlockImageDecode(request_id_); } + + private: + ImageController* controller_; + ImageController::ImageDecodeRequestId request_id_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDecodeHolder); + }; + + void DidFinishImageDecode(PaintImage::Id image_id, ImageController::ImageDecodeRequestId request_id, ImageController::ImageDecodeResult result); - // Returns true if the decode for |image| will be deferred to the image decode - // service and it should be be skipped during raster. - bool ShouldCheckerImage(const sk_sp<const SkImage>& image, - WhichTree tree) const; - - void ScheduleImageDecodeIfNecessary(const sk_sp<const SkImage>& image); + // Called when the next request in the |image_decode_queue_| should be + // scheduled with the image decode service. + void ScheduleNextImageDecode(); ImageController* image_controller_; CheckerImageTrackerClient* client_; @@ -71,27 +101,26 @@ class CC_EXPORT CheckerImageTracker { // A set of images which have been decoded and are pending invalidation for // raster on the checkered tiles. - ImageIdFlatSet images_pending_invalidation_; + PaintImageIdFlatSet images_pending_invalidation_; // A set of images which were invalidated on the current sync tree. - ImageIdFlatSet invalidated_images_on_current_sync_tree_; + PaintImageIdFlatSet invalidated_images_on_current_sync_tree_; + + // The queue of images pending decode. We maintain a queue to ensure that the + // order in which images are decoded is aligned with the priority of the tiles + // dependent on these images. + ImageDecodeQueue image_decode_queue_; - // A set of images which are currently pending decode from the image decode - // service. - // TODO(khushalsagar): This should be a queue that gets re-built each time we - // do a PrepareTiles? See crbug.com/689184. - ImageIdFlatSet pending_image_decodes_; + // The currently outstanding image decode that has been scheduled with the + // decode service. There can be only one outstanding decode at a time. + base::Optional<PaintImage> outstanding_image_decode_; - // A set of images which have been decoded at least once from the - // ImageDecodeService and should not be checkered again. - // TODO(khushalsagar): Limit the size of this set. - // TODO(khushalsagar): Plumb navigation changes here to reset this. See - // crbug.com/693228. - std::unordered_set<ImageId> images_decoded_once_; + // A map of ImageId to its DecodePolicy. + std::unordered_map<PaintImage::Id, DecodePolicy> image_async_decode_state_; // A map of image id to image decode request id for images to be unlocked. - std::unordered_map<ImageId, ImageController::ImageDecodeRequestId> - image_id_to_decode_request_id_; + std::unordered_map<PaintImage::Id, std::unique_ptr<ScopedDecodeHolder>> + image_id_to_decode_; base::WeakPtrFactory<CheckerImageTracker> weak_factory_; }; diff --git a/chromium/cc/tiles/checker_image_tracker_unittest.cc b/chromium/cc/tiles/checker_image_tracker_unittest.cc index 9ca8d39f344..38de60dfd80 100644 --- a/chromium/cc/tiles/checker_image_tracker_unittest.cc +++ b/chromium/cc/tiles/checker_image_tracker_unittest.cc @@ -15,8 +15,13 @@ namespace cc { namespace { +// 5MB max image cache size. +const size_t kMaxImageCacheSizeBytes = 5 * 1024 * 1024; + const int kCheckerableImageDimension = 512; -const int kNonCheckerableImageDimension = 16; +// This size will result in an image just over kMaxImageCacheSizeBytes. +const int kLargeNonCheckerableImageDimension = 1145; +const int kSmallNonCheckerableImageDimension = 16; class TestImageController : public ImageController { public: @@ -24,11 +29,16 @@ class TestImageController : public ImageController { // the ImageController is over-ridden here. TestImageController() : ImageController(base::ThreadTaskRunnerHandle::Get().get(), - base::ThreadTaskRunnerHandle::Get()) {} + base::ThreadTaskRunnerHandle::Get()) { + SetMaxImageCacheLimitBytesForTesting(kMaxImageCacheSizeBytes); + } ~TestImageController() override { DCHECK_EQ(locked_images_.size(), 0U); } int num_of_locked_images() const { return locked_images_.size(); } + const PaintImageIdFlatSet& decodes_requested() const { + return decodes_requested_; + } void UnlockImageDecode(ImageDecodeRequestId id) override { DCHECK_EQ(locked_images_.count(id), 1U); @@ -40,10 +50,7 @@ class TestImageController : public ImageController { const ImageDecodedCallback& callback) override { ImageDecodeRequestId request_id = next_image_request_id_++; - // The tracker should request a decode only once. - EXPECT_EQ(decodes_requested_.count(image->uniqueID()), 0u); decodes_requested_.insert(image->uniqueID()); - locked_images_.insert(request_id); // Post the callback asynchronously to match the behaviour in @@ -58,13 +65,17 @@ class TestImageController : public ImageController { private: ImageDecodeRequestId next_image_request_id_ = 1U; std::unordered_set<ImageDecodeRequestId> locked_images_; - ImageIdFlatSet decodes_requested_; + PaintImageIdFlatSet decodes_requested_; }; class CheckerImageTrackerTest : public testing::Test, public CheckerImageTrackerClient { public: - enum class ImageType { CHECKERABLE, NON_CHECKERABLE }; + enum class ImageType { + CHECKERABLE, + SMALL_NON_CHECKERABLE, + LARGE_NON_CHECKERABLE + }; void SetUpTracker(bool checker_images_enabled) { checker_image_tracker_ = base::MakeUnique<CheckerImageTracker>( @@ -73,15 +84,38 @@ class CheckerImageTrackerTest : public testing::Test, void TearDown() override { checker_image_tracker_.reset(); } - DrawImage CreateImage(ImageType image_type) { - int dimension = image_type == ImageType::CHECKERABLE - ? kCheckerableImageDimension - : kNonCheckerableImageDimension; + PaintImage CreateImage( + ImageType image_type, + PaintImage::AnimationType animation = PaintImage::AnimationType::STATIC, + PaintImage::CompletionState completion = + PaintImage::CompletionState::DONE) { + int dimension = 0; + switch (image_type) { + case ImageType::CHECKERABLE: + dimension = kCheckerableImageDimension; + break; + case ImageType::SMALL_NON_CHECKERABLE: + dimension = kSmallNonCheckerableImageDimension; + break; + case ImageType::LARGE_NON_CHECKERABLE: + dimension = kLargeNonCheckerableImageDimension; + break; + } + sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(dimension, dimension)); - gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); - return DrawImage(image, SkIRect::MakeWH(image->width(), image->height()), - kNone_SkFilterQuality, SkMatrix::I(), target_color_space); + return PaintImage(PaintImage::GetNextId(), image, animation, completion); + } + + CheckerImageTracker::ImageDecodeQueue BuildImageDecodeQueue( + std::vector<PaintImage> images, + WhichTree tree) { + CheckerImageTracker::ImageDecodeQueue decode_queue; + for (const auto& image : images) { + if (checker_image_tracker_->ShouldCheckerImage(image, tree)) + decode_queue.push_back(image); + } + return decode_queue; } // CheckerImageTrackerClient implementation. @@ -101,13 +135,10 @@ TEST_F(CheckerImageTrackerTest, CheckerImagesDisabled) { // disabled. SetUpTracker(false); - std::vector<DrawImage> draw_images; - ImageIdFlatSet checkered_images; - draw_images.push_back(CreateImage(ImageType::CHECKERABLE)); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(checkered_images.size(), 0U); + PaintImageIdFlatSet checkered_images; + PaintImage paint_image = CreateImage(ImageType::CHECKERABLE); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + paint_image, WhichTree::PENDING_TREE)); EXPECT_EQ(image_controller_.num_of_locked_images(), 0); } @@ -115,22 +146,25 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) { // Ensures that the tracker updates images atomically for each frame. SetUpTracker(true); - DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE); - DrawImage non_checkerable_image = CreateImage(ImageType::NON_CHECKERABLE); - ImageIdFlatSet checkered_images; - std::vector<DrawImage> draw_images; + PaintImage checkerable_image = CreateImage(ImageType::CHECKERABLE); + PaintImage small_non_checkerable_image = + CreateImage(ImageType::SMALL_NON_CHECKERABLE); + PaintImage large_non_checkerable_image = + CreateImage(ImageType::LARGE_NON_CHECKERABLE); + CheckerImageTracker::ImageDecodeQueue image_decode_queue; // First request to filter images. - draw_images.push_back(checkerable_image); - draw_images.push_back(non_checkerable_image); - draw_images.push_back(checkerable_image); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(draw_images[0].image(), non_checkerable_image.image()); - EXPECT_EQ(checkered_images.size(), 1U); - EXPECT_EQ(checkered_images.count(checkerable_image.image()->uniqueID()), 1U); + std::vector<PaintImage> paint_images = { + checkerable_image, small_non_checkerable_image, + large_non_checkerable_image, checkerable_image}; + image_decode_queue = + BuildImageDecodeQueue(paint_images, WhichTree::PENDING_TREE); + + ASSERT_EQ(2u, image_decode_queue.size()); + EXPECT_EQ(checkerable_image, image_decode_queue[0]); + EXPECT_EQ(checkerable_image, image_decode_queue[1]); + + checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue); EXPECT_EQ(image_controller_.num_of_locked_images(), 1); // Run pending task to indicate completion of decode request to the tracker. @@ -143,44 +177,31 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) { // Continue checkering the image until the set of images to invalidate is // pulled. - draw_images.clear(); - draw_images.push_back(checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); - EXPECT_EQ(image_controller_.num_of_locked_images(), 1); + EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image, WhichTree::PENDING_TREE)); - ImageIdFlatSet invalidated_images = + PaintImageIdFlatSet invalidated_images = checker_image_tracker_->TakeImagesToInvalidateOnSyncTree(); EXPECT_EQ(invalidated_images.size(), 1U); - EXPECT_EQ(invalidated_images.count(checkerable_image.image()->uniqueID()), - 1U); + EXPECT_EQ(invalidated_images.count(checkerable_image.stable_id()), 1U); // Use the same set of draw images to ensure that they are not checkered on // the pending tree now. - draw_images.clear(); - draw_images.push_back(checkerable_image); - draw_images.push_back(non_checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 2U); - EXPECT_EQ(checkered_images.size(), 0U); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image, WhichTree::PENDING_TREE)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + small_non_checkerable_image, WhichTree::PENDING_TREE)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + large_non_checkerable_image, WhichTree::PENDING_TREE)); // Use this set to make the same request from the active tree, we should // continue checkering this image on the active tree until activation. - draw_images.clear(); - draw_images.push_back(checkerable_image); - draw_images.push_back(non_checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::ACTIVE_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(draw_images[0].image(), non_checkerable_image.image()); - EXPECT_EQ(checkered_images.size(), 1U); - EXPECT_EQ(checkered_images.count(checkerable_image.image()->uniqueID()), 1U); + EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image, WhichTree::ACTIVE_TREE)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + small_non_checkerable_image, WhichTree::ACTIVE_TREE)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + large_non_checkerable_image, WhichTree::ACTIVE_TREE)); // Activate the sync tree. The images should be unlocked upon activation. EXPECT_EQ(image_controller_.num_of_locked_images(), 1); @@ -192,18 +213,13 @@ TEST_F(CheckerImageTrackerTest, NoConsecutiveCheckeringForImage) { // checkered again in subsequent frames. SetUpTracker(true); - DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE); - DrawImage non_checkerable_image = CreateImage(ImageType::NON_CHECKERABLE); - ImageIdFlatSet checkered_images; - std::vector<DrawImage> draw_images; + PaintImage checkerable_image = CreateImage(ImageType::CHECKERABLE); + std::vector<PaintImage> paint_images = {checkerable_image}; - draw_images.clear(); - draw_images.push_back(checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + CheckerImageTracker::ImageDecodeQueue image_decode_queue = + BuildImageDecodeQueue(paint_images, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 1U); + checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue); // Trigger decode completion, take images to invalidate and activate the sync // tree. @@ -212,13 +228,8 @@ TEST_F(CheckerImageTrackerTest, NoConsecutiveCheckeringForImage) { checker_image_tracker_->DidActivateSyncTree(); // Subsequent requests for this image should not be checkered. - draw_images.clear(); - draw_images.push_back(checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(checkered_images.size(), 0U); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image, WhichTree::PENDING_TREE)); } TEST_F(CheckerImageTrackerTest, @@ -227,72 +238,182 @@ TEST_F(CheckerImageTrackerTest, // active tree are tracked correctly. SetUpTracker(true); - DrawImage checkerable_image1 = CreateImage(ImageType::CHECKERABLE); - ImageIdFlatSet checkered_images; - std::vector<DrawImage> draw_images; + PaintImage checkerable_image1 = CreateImage(ImageType::CHECKERABLE); + std::vector<PaintImage> paint_images; + CheckerImageTracker::ImageDecodeQueue image_decode_queue; // First request to filter images on the pending and active tree. - draw_images.push_back(checkerable_image1); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + paint_images.push_back(checkerable_image1); + image_decode_queue = + BuildImageDecodeQueue(paint_images, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 1U); + checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue); // The image is also checkered on the active tree while a decode request is // pending. - draw_images.clear(); - checkered_images.clear(); - draw_images.push_back(checkerable_image1); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::ACTIVE_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image1, WhichTree::ACTIVE_TREE)); // Trigger decode completion and take images to invalidate on the sync tree. base::RunLoop().RunUntilIdle(); EXPECT_TRUE(invalidation_request_pending_); - ImageIdFlatSet invalidated_images = + PaintImageIdFlatSet invalidated_images = checker_image_tracker_->TakeImagesToInvalidateOnSyncTree(); EXPECT_EQ(invalidated_images.size(), 1U); - EXPECT_EQ(invalidated_images.count(checkerable_image1.image()->uniqueID()), - 1U); + EXPECT_EQ(invalidated_images.count(checkerable_image1.stable_id()), 1U); // Second request to filter the same image on the pending and active tree. It // should be checkered on the active tree, but not the pending tree. - draw_images.clear(); - checkered_images.clear(); - draw_images.push_back(checkerable_image1); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(checkered_images.size(), 0U); - - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::ACTIVE_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image1, WhichTree::ACTIVE_TREE)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image1, WhichTree::PENDING_TREE)); // New checkerable image on the pending tree. - DrawImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE); - draw_images.clear(); - checkered_images.clear(); - draw_images.push_back(checkerable_image2); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + PaintImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE); + EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image2, WhichTree::PENDING_TREE)); // Activate the sync tree. The initial image should no longer be checkered on // the active tree. checker_image_tracker_->DidActivateSyncTree(); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image1, WhichTree::ACTIVE_TREE)); +} + +TEST_F(CheckerImageTrackerTest, CancelsScheduledDecodes) { + SetUpTracker(true); + + PaintImage checkerable_image1 = CreateImage(ImageType::CHECKERABLE); + PaintImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE); + std::vector<PaintImage> paint_images = {checkerable_image1, + checkerable_image2}; + + CheckerImageTracker::ImageDecodeQueue image_decode_queue; + image_decode_queue = + BuildImageDecodeQueue(paint_images, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 2U); + checker_image_tracker_->ScheduleImageDecodeQueue( + std::move(image_decode_queue)); + + // Only the first image in the queue should have been decoded. + EXPECT_EQ(image_controller_.decodes_requested().size(), 1U); + EXPECT_EQ(image_controller_.decodes_requested().count( + checkerable_image1.sk_image()->uniqueID()), + 1U); + + // Rebuild the queue before the tracker is notified of decode completion, + // removing the second image and adding a new one. + PaintImage checkerable_image3 = CreateImage(ImageType::CHECKERABLE); + paint_images = {checkerable_image1, checkerable_image3}; + image_decode_queue = + BuildImageDecodeQueue(paint_images, WhichTree::PENDING_TREE); + + // The queue has 2 decodes because we are still checkering on the first one. + EXPECT_EQ(image_decode_queue.size(), 2U); + checker_image_tracker_->ScheduleImageDecodeQueue( + std::move(image_decode_queue)); + + // We still have only one decode because the tracker keeps only one decode + // pending at a time. + EXPECT_EQ(image_controller_.decodes_requested().size(), 1U); + EXPECT_EQ(image_controller_.decodes_requested().count( + checkerable_image1.sk_image()->uniqueID()), + 1U); + + // Trigger completion for all decodes. Only 2 images should have been decoded + // since the second image was cancelled. + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(image_controller_.decodes_requested().size(), 2U); + EXPECT_EQ(image_controller_.decodes_requested().count( + checkerable_image3.sk_image()->uniqueID()), + 1U); + EXPECT_EQ(image_controller_.num_of_locked_images(), 2); +} + +TEST_F(CheckerImageTrackerTest, ClearsTracker) { + SetUpTracker(true); + + PaintImage checkerable_image = CreateImage(ImageType::CHECKERABLE); + CheckerImageTracker::ImageDecodeQueue image_decode_queue = + BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 1U); + checker_image_tracker_->ScheduleImageDecodeQueue( + std::move(image_decode_queue)); + base::RunLoop().RunUntilIdle(); + checker_image_tracker_->TakeImagesToInvalidateOnSyncTree(); + + // The image is no longer checkered on the pending tree. + image_decode_queue = + BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 0U); + EXPECT_EQ(image_controller_.num_of_locked_images(), 1); + + // Clear the tracker without clearing the async decode tracking. This should + // drop the decode but the image should not be checkered. + bool can_clear_decode_policy_tracking = false; + checker_image_tracker_->ClearTracker(can_clear_decode_policy_tracking); + EXPECT_EQ(image_controller_.num_of_locked_images(), 0); + image_decode_queue = + BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 0U); + checker_image_tracker_->DidActivateSyncTree(); + + // Now clear the decode tracking as well. The image will be re-checkered. + can_clear_decode_policy_tracking = true; + checker_image_tracker_->ClearTracker(can_clear_decode_policy_tracking); + image_decode_queue = + BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE); + image_decode_queue = + BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 1U); + + // If an image had been decoded and tracker was cleared after it, we should + // continue checkering it. + PaintImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE); + image_decode_queue = + BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 1U); + checker_image_tracker_->ScheduleImageDecodeQueue( + std::move(image_decode_queue)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(image_controller_.num_of_locked_images(), 1); + can_clear_decode_policy_tracking = false; + checker_image_tracker_->ClearTracker(can_clear_decode_policy_tracking); + EXPECT_EQ(image_controller_.num_of_locked_images(), 0); + image_decode_queue = + BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 1U); +} + +TEST_F(CheckerImageTrackerTest, CheckersOnlyStaticCompletedImages) { + SetUpTracker(true); - draw_images.clear(); - checkered_images.clear(); - draw_images.push_back(checkerable_image1); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::ACTIVE_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(checkered_images.size(), 0U); + PaintImage static_image = CreateImage(ImageType::CHECKERABLE); + PaintImage animated_image = + CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::ANIMATED); + PaintImage partial_image = + CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::STATIC, + PaintImage::CompletionState::PARTIALLY_DONE); + PaintImage video_image = + CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::VIDEO); + std::vector<PaintImage> paint_images = {static_image, animated_image, + partial_image, video_image}; + + CheckerImageTracker::ImageDecodeQueue image_decode_queue = + BuildImageDecodeQueue(paint_images, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 1U); + EXPECT_EQ(image_decode_queue[0], static_image); + + // Change the partial image to complete and try again. It should still not + // be checkered. + gfx::Size image_size = gfx::Size(partial_image.sk_image()->width(), + partial_image.sk_image()->height()); + PaintImage completed_paint_image = + PaintImage(partial_image.stable_id(), CreateDiscardableImage(image_size)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + completed_paint_image, WhichTree::PENDING_TREE)); } } // namespace diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc index 39a822617db..b30d9da1c6f 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache.cc @@ -110,6 +110,38 @@ gfx::Size CalculateSizeForMipLevel(const DrawImage& draw_image, int mip_level) { return MipMapUtil::GetSizeForLevel(base_size, mip_level); } +// Draws and scales the provided |draw_image| into the |target_pixmap|. If the +// draw/scale can be done directly, calls directly into SkImage::scalePixels, +// if not, decodes to a compatible temporary pixmap and then converts that into +// the |target_pixmap|. +bool DrawAndScaleImage(const DrawImage& draw_image, SkPixmap* target_pixmap) { + const SkImage* image = draw_image.image().get(); + if (image->dimensions() == target_pixmap->bounds().size() || + target_pixmap->info().colorType() == kN32_SkColorType) { + // If no scaling is occurring, or if the target colortype is already N32, + // just scale directly. + return image->scalePixels(*target_pixmap, + CalculateUploadScaleFilterQuality(draw_image), + SkImage::kDisallow_CachingHint); + } + + // If the target colortype is not N32, it may be impossible to scale + // directly. Instead scale into an N32 pixmap, and convert that into the + // |target_pixmap|. + SkImageInfo decode_info = + target_pixmap->info().makeColorType(kN32_SkColorType); + SkBitmap decode_bitmap; + if (!decode_bitmap.tryAllocPixels(decode_info)) + return false; + SkPixmap decode_pixmap(decode_bitmap.info(), decode_bitmap.getPixels(), + decode_bitmap.rowBytes()); + if (!image->scalePixels(decode_pixmap, + CalculateUploadScaleFilterQuality(draw_image), + SkImage::kDisallow_CachingHint)) + return false; + return decode_pixmap.readPixels(*target_pixmap); +} + } // namespace // static @@ -616,6 +648,10 @@ void GpuImageDecodeCache::ClearCache() { } } +size_t GpuImageDecodeCache::GetMaximumMemoryLimitBytes() const { + return normal_max_cache_bytes_; +} + bool GpuImageDecodeCache::OnMemoryDump( const base::trace_event::MemoryDumpArgs& args, base::trace_event::ProcessMemoryDump* pmd) { @@ -1100,13 +1136,11 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, // In order to match GPU scaling quality (which uses mip-maps at high // quality), we want to use at most medium filter quality for the // scale. - SkPixmap image_pixmap(image_info, backing_memory->data(), - image_info.minRowBytes()); - // Note that scalePixels falls back to readPixels if the sale is 1x, so + SkPixmap image_pixmap(image_info.makeColorSpace(nullptr), + backing_memory->data(), image_info.minRowBytes()); + // Note that scalePixels falls back to readPixels if the scale is 1x, so // no need to special case that as an optimization. - if (!draw_image.image()->scalePixels( - image_pixmap, CalculateUploadScaleFilterQuality(draw_image), - SkImage::kDisallow_CachingHint)) { + if (!DrawAndScaleImage(draw_image, &image_pixmap)) { DLOG(ERROR) << "scalePixels failed."; backing_memory->Unlock(); backing_memory.reset(); @@ -1119,7 +1153,8 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, // DCHECKs here to enforce this. if (!draw_image.image()->getDeferredTextureImageData( *context_threadsafe_proxy_.get(), &image_data->upload_params, 1, - backing_memory->data(), nullptr)) { + backing_memory->data(), nullptr, + ResourceFormatToClosestSkColorType(format_))) { DLOG(ERROR) << "getDeferredTextureImageData failed despite params " << "having validated."; backing_memory->Unlock(); @@ -1219,7 +1254,8 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image) { draw_image.matrix(), CalculateUploadScaleFilterQuality(draw_image), upload_scale_mip_level); size_t data_size = draw_image.image()->getDeferredTextureImageData( - *context_threadsafe_proxy_.get(), ¶ms, 1, nullptr, nullptr); + *context_threadsafe_proxy_.get(), ¶ms, 1, nullptr, nullptr, + ResourceFormatToClosestSkColorType(format_)); if (data_size == 0) { // Can't upload image, too large or other failure. Try to use SW fallback. diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h index 391f048c2e9..f60157742b4 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.h +++ b/chromium/cc/tiles/gpu_image_decode_cache.h @@ -125,6 +125,7 @@ class CC_EXPORT GpuImageDecodeCache void SetShouldAggressivelyFreeResources( bool aggressively_free_resources) override; void ClearCache() override; + size_t GetMaximumMemoryLimitBytes() const override; // MemoryDumpProvider overrides. bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, @@ -230,7 +231,7 @@ class CC_EXPORT GpuImageDecodeCache UsageStats usage_stats_; }; - struct ImageData : public base::RefCounted<ImageData> { + struct ImageData : public base::RefCountedThreadSafe<ImageData> { ImageData(DecodedDataMode mode, size_t size, const gfx::ColorSpace& target_color_space, @@ -250,7 +251,7 @@ class CC_EXPORT GpuImageDecodeCache UploadedImageData upload; private: - friend class base::RefCounted<ImageData>; + friend class base::RefCountedThreadSafe<ImageData>; ~ImageData(); }; @@ -347,6 +348,8 @@ class CC_EXPORT GpuImageDecodeCache sk_sp<GrContextThreadSafeProxy> context_threadsafe_proxy_; // All members below this point must only be accessed while holding |lock_|. + // The exception are const members like |normal_max_cache_bytes_| that can + // be accessed without a lock since they are thread safe. base::Lock lock_; // |persistent_cache_| represents the long-lived cache, keeping a certain diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc index 91a08d1f40c..fb2f91bceaf 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc @@ -8,6 +8,7 @@ #include "cc/test/test_context_provider.h" #include "cc/test/test_tile_task_runner.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkImageGenerator.h" #include "third_party/skia/include/core/SkRefCnt.h" namespace cc { @@ -17,22 +18,48 @@ gfx::ColorSpace DefaultColorSpace() { return gfx::ColorSpace::CreateSRGB(); } +PaintImage::Id s_paint_image_id = PaintImage::GetNextId(); + +PaintImage CreatePaintImage(sk_sp<SkImage> image) { + return PaintImage(s_paint_image_id, image); +} + size_t kGpuMemoryLimitBytes = 96 * 1024 * 1024; class TestGpuImageDecodeCache : public GpuImageDecodeCache { public: - explicit TestGpuImageDecodeCache(ContextProvider* context) + explicit TestGpuImageDecodeCache(ContextProvider* context, + ResourceFormat format) : GpuImageDecodeCache(context, - ResourceFormat::RGBA_8888, + format, kGpuMemoryLimitBytes, kGpuMemoryLimitBytes) {} }; +class TestImageGenerator : public SkImageGenerator { + public: + explicit TestImageGenerator(const SkImageInfo& info) + : SkImageGenerator(info), + image_backing_memory_(info.getSafeSize(info.minRowBytes()), 0), + image_pixmap_(info, image_backing_memory_.data(), info.minRowBytes()) {} + + protected: + bool onGetPixels(const SkImageInfo& info, + void* pixels, + size_t rowBytes, + const Options&) override { + return image_pixmap_.readPixels(info, pixels, rowBytes, 0, 0); + } + + private: + std::vector<uint8_t> image_backing_memory_; + SkPixmap image_pixmap_; +}; + sk_sp<SkImage> CreateImage(int width, int height) { - SkBitmap bitmap; gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB(); - bitmap.allocPixels( - SkImageInfo::MakeN32Premul(width, height, color_space.ToSkColorSpace())); - return SkImage::MakeFromBitmap(bitmap); + std::unique_ptr<TestImageGenerator> generator(new TestImageGenerator( + SkImageInfo::MakeN32Premul(width, height, color_space.ToSkColorSpace()))); + return SkImage::MakeFromGenerator(std::move(generator)); } SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { @@ -47,18 +74,20 @@ SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { return matrix; } -TEST(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { +using GpuImageDecodeCacheTest = ::testing::TestWithParam<ResourceFormat>; + +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -66,8 +95,8 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { EXPECT_TRUE(task); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> another_task; need_unref = cache.GetTaskForImageAndRef( @@ -82,18 +111,18 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -101,8 +130,8 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { EXPECT_TRUE(task); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> another_task; need_unref = cache.GetTaskForImageAndRef( @@ -117,21 +146,16 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { cache.UnrefImage(another_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageLowerQuality DISABLED_GetTaskForImageLowerQuality -#else -#define MAYBE_GetTaskForImageLowerQuality GetTaskForImageLowerQuality -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLowerQuality) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLowerQuality) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), kHigh_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -140,7 +164,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLowerQuality) { EXPECT_TRUE(task); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> another_task; need_unref = cache.GetTaskForImageAndRef( @@ -155,24 +179,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLowerQuality) { cache.UnrefImage(another_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageDifferentImage \ - DISABLED_GetTaskForImageDifferentImage -#else -#define MAYBE_GetTaskForImageDifferentImage GetTaskForImageDifferentImage -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentImage) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageDifferentImage) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -182,7 +200,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentImage) { sk_sp<SkImage> second_image = CreateImage(100, 100); DrawImage second_draw_image( - second_image, + CreatePaintImage(second_image), SkIRect::MakeWH(second_image->width(), second_image->height()), quality, CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), DefaultColorSpace()); @@ -202,23 +220,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentImage) { cache.UnrefImage(second_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageLargerScale DISABLED_GetTaskForImageLargerScale -#else -#define MAYBE_GetTaskForImageLargerScale GetTaskForImageLargerScale -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScale) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScale) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -232,8 +245,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScale) { cache.UnrefImage(first_draw_image); DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -243,8 +257,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScale) { EXPECT_TRUE(first_task.get() != second_task.get()); DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> third_task; need_unref = cache.GetTaskForImageAndRef( @@ -259,25 +274,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScale) { cache.UnrefImage(third_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageLargerScaleNoReuse \ - DISABLED_GetTaskForImageLargerScaleNoReuse -#else -#define MAYBE_GetTaskForImageLargerScaleNoReuse \ - GetTaskForImageLargerScaleNoReuse -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScaleNoReuse) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScaleNoReuse) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -286,8 +294,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScaleNoReuse) { EXPECT_TRUE(first_task); DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -297,8 +306,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScaleNoReuse) { EXPECT_TRUE(first_task.get() != second_task.get()); DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> third_task; need_unref = cache.GetTaskForImageAndRef( @@ -316,22 +326,17 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScaleNoReuse) { cache.UnrefImage(third_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageHigherQuality DISABLED_GetTaskForImageHigherQuality -#else -#define MAYBE_GetTaskForImageHigherQuality GetTaskForImageHigherQuality -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageHigherQuality) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageHigherQuality) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -345,7 +350,8 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageHigherQuality) { cache.UnrefImage(first_draw_image); DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), kHigh_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -360,26 +366,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageHigherQuality) { cache.UnrefImage(second_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageAlreadyDecodedAndLocked \ - DISABLED_GetTaskForImageAlreadyDecodedAndLocked -#else -#define MAYBE_GetTaskForImageAlreadyDecodedAndLocked \ - GetTaskForImageAlreadyDecodedAndLocked -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyDecodedAndLocked) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyDecodedAndLocked) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -414,26 +412,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyDecodedAndLocked) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageAlreadyDecodedNotLocked \ - DISABLED_GetTaskForImageAlreadyDecodedNotLocked -#else -#define MAYBE_GetTaskForImageAlreadyDecodedNotLocked \ - GetTaskForImageAlreadyDecodedNotLocked -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyDecodedNotLocked) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyDecodedNotLocked) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -468,25 +458,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyDecodedNotLocked) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageAlreadyUploaded \ - DISABLED_GetTaskForImageAlreadyUploaded -#else -#define MAYBE_GetTaskForImageAlreadyUploaded GetTaskForImageAlreadyUploaded -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyUploaded) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyUploaded) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -511,26 +494,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyUploaded) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageCanceledGetsNewTask \ - DISABLED_GetTaskForImageCanceledGetsNewTask -#else -#define MAYBE_GetTaskForImageCanceledGetsNewTask \ - GetTaskForImageCanceledGetsNewTask -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageCanceledGetsNewTask) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageCanceledGetsNewTask) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -567,27 +542,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageCanceledGetsNewTask) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageCanceledWhileReffedGetsNewTask \ - DISABLED_GetTaskForImageCanceledWhileReffedGetsNewTask -#else -#define MAYBE_GetTaskForImageCanceledWhileReffedGetsNewTask \ - GetTaskForImageCanceledWhileReffedGetsNewTask -#endif -TEST(GpuImageDecodeCacheTest, - MAYBE_GetTaskForImageCanceledWhileReffedGetsNewTask) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageCanceledWhileReffedGetsNewTask) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -628,26 +594,18 @@ TEST(GpuImageDecodeCacheTest, cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_NoTaskForImageAlreadyFailedDecoding \ - DISABLED_NoTaskForImageAlreadyFailedDecoding -#else -#define MAYBE_NoTaskForImageAlreadyFailedDecoding \ - NoTaskForImageAlreadyFailedDecoding -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_NoTaskForImageAlreadyFailedDecoding) { +TEST_P(GpuImageDecodeCacheTest, NoTaskForImageAlreadyFailedDecoding) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -670,24 +628,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_NoTaskForImageAlreadyFailedDecoding) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDraw DISABLED_GetDecodedImageForDraw -#else -#define MAYBE_GetDecodedImageForDraw GetDecodedImageForDraw -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDraw) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDraw) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -711,18 +663,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDraw) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { +TEST_P(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(1, 24000); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -747,20 +699,20 @@ TEST(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { EXPECT_FALSE(cache.DiscardableIsLockedForTesting(draw_image)); } -TEST(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; cache.SetAllByteLimitsForTesting(0); sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -781,26 +733,18 @@ TEST(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { cache.DrawWithImageFinished(draw_image, decoded_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDrawLargerScale \ - DISABLED_GetDecodedImageForDrawLargerScale -#else -#define MAYBE_GetDecodedImageForDrawLargerScale \ - GetDecodedImageForDrawLargerScale -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawLargerScale) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawLargerScale) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -811,8 +755,8 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawLargerScale) { TestTileTaskRunner::ProcessTask(task.get()); DrawImage larger_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> larger_task; bool larger_need_unref = cache.GetTaskForImageAndRef( @@ -848,23 +792,16 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawLargerScale) { cache.UnrefImage(larger_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDrawHigherQuality \ - DISABLED_GetDecodedImageForDrawHigherQuality -#else -#define MAYBE_GetDecodedImageForDrawHigherQuality \ - GetDecodedImageForDrawHigherQuality -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawHigherQuality) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawHigherQuality) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable); sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -876,7 +813,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawHigherQuality) { TestTileTaskRunner::ProcessTask(task.get()); DrawImage higher_quality_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kHigh_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> hq_task; bool hq_needs_unref = cache.GetTaskForImageAndRef( @@ -913,25 +850,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawHigherQuality) { cache.UnrefImage(higher_quality_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDrawNegative \ - DISABLED_GetDecodedImageForDrawNegative -#else -#define MAYBE_GetDecodedImageForDrawNegative GetDecodedImageForDrawNegative -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawNegative) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawNegative) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(-0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(-0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -957,26 +887,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawNegative) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetLargeScaledDecodedImageForDraw \ - DISABLED_GetLargeScaledDecodedImageForDraw -#else -#define MAYBE_GetLargeScaledDecodedImageForDraw \ - GetLargeScaledDecodedImageForDraw -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetLargeScaledDecodedImageForDraw) { +TEST_P(GpuImageDecodeCacheTest, GetLargeScaledDecodedImageForDraw) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(1, 48000); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -1004,28 +926,20 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetLargeScaledDecodedImageForDraw) { EXPECT_FALSE(cache.DiscardableIsLockedForTesting(draw_image)); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_AtRasterUsedDirectlyIfSpaceAllows \ - DISABLED_AtRasterUsedDirectlyIfSpaceAllows -#else -#define MAYBE_AtRasterUsedDirectlyIfSpaceAllows \ - AtRasterUsedDirectlyIfSpaceAllows -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_AtRasterUsedDirectlyIfSpaceAllows) { +TEST_P(GpuImageDecodeCacheTest, AtRasterUsedDirectlyIfSpaceAllows) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; cache.SetAllByteLimitsForTesting(0); sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1057,29 +971,21 @@ TEST(GpuImageDecodeCacheTest, MAYBE_AtRasterUsedDirectlyIfSpaceAllows) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDrawAtRasterDecodeMultipleTimes \ - DISABLED_GetDecodedImageForDrawAtRasterDecodeMultipleTimes -#else -#define MAYBE_GetDecodedImageForDrawAtRasterDecodeMultipleTimes \ - GetDecodedImageForDrawAtRasterDecodeMultipleTimes -#endif -TEST(GpuImageDecodeCacheTest, - MAYBE_GetDecodedImageForDrawAtRasterDecodeMultipleTimes) { +TEST_P(GpuImageDecodeCacheTest, + GetDecodedImageForDrawAtRasterDecodeMultipleTimes) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; cache.SetAllByteLimitsForTesting(0); sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. @@ -1100,19 +1006,19 @@ TEST(GpuImageDecodeCacheTest, cache.DrawWithImageFinished(draw_image, another_decoded_draw_image); } -TEST(GpuImageDecodeCacheTest, - GetLargeDecodedImageForDrawAtRasterDecodeMultipleTimes) { +TEST_P(GpuImageDecodeCacheTest, + GetLargeDecodedImageForDrawAtRasterDecodeMultipleTimes) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(1, 24000); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. @@ -1138,18 +1044,18 @@ TEST(GpuImageDecodeCacheTest, EXPECT_FALSE(cache.DiscardableIsLockedForTesting(draw_image)); } -TEST(GpuImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { +TEST_P(GpuImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1167,17 +1073,18 @@ TEST(GpuImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { cache.DrawWithImageFinished(draw_image, decoded_draw_image); } -TEST(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { +TEST_P(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeXYWH(150, 150, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(150, 150, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1196,18 +1103,19 @@ TEST(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { cache.DrawWithImageFinished(draw_image, decoded_draw_image); } -TEST(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { +TEST_P(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image( - image, SkIRect::MakeXYWH(0, 0, image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeXYWH(0, 0, image->width(), image->height()), + quality, + CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1225,25 +1133,18 @@ TEST(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { EXPECT_EQ(0u, cache.GetBytesUsedForTesting()); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_ShouldAggressivelyFreeResources \ - DISABLED_ShouldAggressivelyFreeResources -#else -#define MAYBE_ShouldAggressivelyFreeResources ShouldAggressivelyFreeResources -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_ShouldAggressivelyFreeResources) { +TEST_P(GpuImageDecodeCacheTest, ShouldAggressivelyFreeResources) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; { bool need_unref = cache.GetTaskForImageAndRef( @@ -1296,26 +1197,19 @@ TEST(GpuImageDecodeCacheTest, MAYBE_ShouldAggressivelyFreeResources) { } } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_OrphanedImagesFreeOnReachingZeroRefs \ - DISABLED_OrphanedImagesFreeOnReachingZeroRefs -#else -#define MAYBE_OrphanedImagesFreeOnReachingZeroRefs \ - OrphanedImagesFreeOnReachingZeroRefs -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedImagesFreeOnReachingZeroRefs) { +TEST_P(GpuImageDecodeCacheTest, OrphanedImagesFreeOnReachingZeroRefs) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; // Create a downscaled image. sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1330,8 +1224,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedImagesFreeOnReachingZeroRefs) { // Create a larger version of |first_image|, this should immediately free the // memory used by |first_image| for the smaller scale. DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -1361,26 +1256,19 @@ TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedImagesFreeOnReachingZeroRefs) { cache.GetDrawImageSizeForTesting(second_draw_image)); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_OrphanedZeroRefImagesImmediatelyDeleted \ - DISABLED_OrphanedZeroRefImagesImmediatelyDeleted -#else -#define MAYBE_OrphanedZeroRefImagesImmediatelyDeleted \ - OrphanedZeroRefImagesImmediatelyDeleted -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedZeroRefImagesImmediatelyDeleted) { +TEST_P(GpuImageDecodeCacheTest, OrphanedZeroRefImagesImmediatelyDeleted) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; // Create a downscaled image. sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1399,8 +1287,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedZeroRefImagesImmediatelyDeleted) { // Create a larger version of |first_image|, this should immediately free the // memory used by |first_image| for the smaller scale. DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -1419,22 +1308,16 @@ TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedZeroRefImagesImmediatelyDeleted) { cache.GetDrawImageSizeForTesting(second_draw_image)); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_QualityCappedAtMedium DISABLED_QualityCappedAtMedium -#else -#define MAYBE_QualityCappedAtMedium QualityCappedAtMedium -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_QualityCappedAtMedium) { +TEST_P(GpuImageDecodeCacheTest, QualityCappedAtMedium) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); // Create an image with kLow_FilterQuality. - DrawImage low_draw_image(image, + DrawImage low_draw_image(CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> low_task; @@ -1446,7 +1329,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_QualityCappedAtMedium) { // Get the same image at kMedium_FilterQuality. We can't re-use low, so we // should get a new task/ref. DrawImage medium_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kMedium_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> medium_task; need_unref = cache.GetTaskForImageAndRef( @@ -1457,7 +1340,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_QualityCappedAtMedium) { // Get the same image at kHigh_FilterQuality. We should re-use medium. DrawImage large_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kHigh_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> large_task; need_unref = cache.GetTaskForImageAndRef( @@ -1477,19 +1360,19 @@ TEST(GpuImageDecodeCacheTest, MAYBE_QualityCappedAtMedium) { // Ensure that switching to a mipped version of an image after the initial // cache entry creation doesn't cause a buffer overflow/crash. -TEST(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; // Create an image decode task and cache entry that does not need mips. sk_sp<SkImage> image = CreateImage(4000, 4000); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -1510,24 +1393,25 @@ TEST(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { // Do an at-raster decode of the above image that *does* require mips. DrawImage draw_image_mips( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image_mips); cache.DrawWithImageFinished(draw_image_mips, decoded_draw_image); } -TEST(GpuImageDecodeCacheTest, MemoryStateSuspended) { +TEST_P(GpuImageDecodeCacheTest, MemoryStateSuspended) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); // First Insert an image into our cache. sk_sp<SkImage> image = CreateImage(1, 1); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1588,15 +1472,16 @@ TEST(GpuImageDecodeCacheTest, MemoryStateSuspended) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { +TEST_P(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(1, 1); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1614,7 +1499,7 @@ TEST(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { +TEST_P(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { // Setup - Image cache has a normal working set, but zero cache size. auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); @@ -1626,10 +1511,10 @@ TEST(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { // Add an image to the cache. Due to normal working set, this should produce // a task and a ref. sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -1669,7 +1554,7 @@ TEST(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { +TEST_P(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { // Cache will fit one (but not two) 100x100 images. size_t cache_size = 190 * 100 * 4; @@ -1681,16 +1566,17 @@ TEST(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); sk_sp<SkImage> image2 = CreateImage(100, 100); - DrawImage draw_image2( - image2, SkIRect::MakeWH(image2->width(), image2->height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image2(CreatePaintImage(image2), + SkIRect::MakeWH(image2->width(), image2->height()), + quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); // Add an image to the cache and un-ref it. { @@ -1762,17 +1648,18 @@ TEST(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { } } -TEST(GpuImageDecodeCacheTest, ClearCache) { +TEST_P(GpuImageDecodeCacheTest, ClearCache) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; for (int i = 0; i < 10; ++i) { sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, + CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1797,10 +1684,10 @@ TEST(GpuImageDecodeCacheTest, ClearCache) { EXPECT_EQ(cache.GetNumCacheEntriesForTesting(), 0u); } -TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; @@ -1809,9 +1696,9 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - color_space_a); + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_a); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo(), &first_task); @@ -1819,9 +1706,9 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { EXPECT_TRUE(first_task); DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - color_space_b); + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_b); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo(), &second_task); @@ -1830,9 +1717,9 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { EXPECT_TRUE(first_task.get() != second_task.get()); DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - color_space_a); + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_a); scoped_refptr<TileTask> third_task; need_unref = cache.GetTaskForImageAndRef( third_draw_image, ImageDecodeCache::TracingInfo(), &third_task); @@ -1849,5 +1736,10 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { cache.UnrefImage(third_draw_image); } +INSTANTIATE_TEST_CASE_P(GpuImageDecodeCacheTests, + GpuImageDecodeCacheTest, + ::testing::Values(ResourceFormat::RGBA_8888, + ResourceFormat::RGBA_4444)); + } // namespace } // namespace cc diff --git a/chromium/cc/tiles/image_controller.cc b/chromium/cc/tiles/image_controller.cc index ae81e64f96d..e8021e4264e 100644 --- a/chromium/cc/tiles/image_controller.cc +++ b/chromium/cc/tiles/image_controller.cc @@ -127,12 +127,15 @@ void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { SetPredecodeImages(std::vector<DrawImage>(), ImageDecodeCache::TracingInfo()); StopWorkerTasks(); + image_cache_max_limit_bytes_ = 0u; } cache_ = cache; - if (cache_) + if (cache_) { + image_cache_max_limit_bytes_ = cache_->GetMaximumMemoryLimitBytes(); GenerateTasksForOrphanedRequests(); + } } void ImageController::GetTasksForImagesAndRef( @@ -192,8 +195,12 @@ ImageController::ImageDecodeRequestId ImageController::QueueImageDecode( DCHECK(image); bool is_image_lazy = image->isLazyGenerated(); auto image_bounds = image->bounds(); - DrawImage draw_image(std::move(image), image_bounds, kNone_SkFilterQuality, - SkMatrix::I(), target_color_space); + // TODO(khushalsagar): Eliminate the use of an incorrect id here and have all + // call-sites provide PaintImage to the ImageController. + DrawImage draw_image( + PaintImage(PaintImage::kUnknownStableId, + sk_sp<SkImage>(const_cast<SkImage*>(image.release()))), + image_bounds, kNone_SkFilterQuality, SkMatrix::I(), target_color_space); // Get the tasks for this decode. scoped_refptr<TileTask> task; diff --git a/chromium/cc/tiles/image_controller.h b/chromium/cc/tiles/image_controller.h index d9253368bb8..30e93f68b18 100644 --- a/chromium/cc/tiles/image_controller.h +++ b/chromium/cc/tiles/image_controller.h @@ -9,9 +9,11 @@ #include <vector> #include "base/callback.h" +#include "base/containers/flat_map.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" #include "base/threading/simple_thread.h" #include "cc/base/unique_notifier.h" #include "cc/cc_export.h" @@ -55,6 +57,13 @@ class CC_EXPORT ImageController { virtual ImageDecodeRequestId QueueImageDecode( sk_sp<const SkImage> image, const ImageDecodedCallback& callback); + size_t image_cache_max_limit_bytes() const { + return image_cache_max_limit_bytes_; + } + + void SetMaxImageCacheLimitBytesForTesting(size_t bytes) { + image_cache_max_limit_bytes_ = bytes; + } protected: scoped_refptr<base::SequencedTaskRunner> worker_task_runner_; @@ -93,9 +102,10 @@ class CC_EXPORT ImageController { std::vector<DrawImage> predecode_locked_images_; static ImageDecodeRequestId s_next_image_decode_queue_id_; - std::unordered_map<ImageDecodeRequestId, DrawImage> requested_locked_images_; + base::flat_map<ImageDecodeRequestId, DrawImage> requested_locked_images_; base::SequencedTaskRunner* origin_task_runner_ = nullptr; + size_t image_cache_max_limit_bytes_ = 0u; // The variables defined below this lock (aside from weak_ptr_factory_) can // only be accessed when the lock is acquired. diff --git a/chromium/cc/tiles/image_controller_unittest.cc b/chromium/cc/tiles/image_controller_unittest.cc index 3a89afb2b64..a4a565774a4 100644 --- a/chromium/cc/tiles/image_controller_unittest.cc +++ b/chromium/cc/tiles/image_controller_unittest.cc @@ -80,7 +80,7 @@ class WorkerTaskRunner : public base::SequencedTaskRunner { return true; } - bool RunsTasksOnCurrentThread() const override { return false; } + bool RunsTasksInCurrentSequence() const override { return false; } protected: ~WorkerTaskRunner() override { @@ -129,6 +129,9 @@ class TestableCache : public ImageDecodeCache { void SetShouldAggressivelyFreeResources( bool aggressively_free_resources) override {} void ClearCache() override {} + size_t GetMaximumMemoryLimitBytes() const override { + return 256 * 1024 * 1024; + } int number_of_refs() const { return number_of_refs_; } void SetTaskToUse(scoped_refptr<TileTask> task) { task_to_use_ = task; } diff --git a/chromium/cc/tiles/image_decode_cache.h b/chromium/cc/tiles/image_decode_cache.h index ca1f41e4cd9..1b550a6b06e 100644 --- a/chromium/cc/tiles/image_decode_cache.h +++ b/chromium/cc/tiles/image_decode_cache.h @@ -96,6 +96,12 @@ class CC_EXPORT ImageDecodeCache { // Clears all elements from the cache. virtual void ClearCache() = 0; + + // Returns the maximum amount of memory we would be able to lock. This ignores + // any temporary states, such as throttled, and return the maximum possible + // memory. It is used as an esimate of whether an image can fit into the + // locked budget before creating a task. + virtual size_t GetMaximumMemoryLimitBytes() const = 0; }; } // namespace cc diff --git a/chromium/cc/tiles/picture_layer_tiling.cc b/chromium/cc/tiles/picture_layer_tiling.cc index 344cb32b6c0..2a20ddcc2e9 100644 --- a/chromium/cc/tiles/picture_layer_tiling.cc +++ b/chromium/cc/tiles/picture_layer_tiling.cc @@ -11,7 +11,7 @@ #include <limits> #include <set> -#include "base/containers/small_map.h" +#include "base/containers/flat_map.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" @@ -257,12 +257,8 @@ void PictureLayerTiling::RemoveTilesInRegion(const Region& layer_invalidation, // twin, so it's slated for removal in the future. if (live_tiles_rect_.IsEmpty()) return; - // Pick 16 for the size of the SmallMap before it promotes to a unordered_map. - // 4x4 tiles should cover most small invalidations, and walking a vector of - // 16 is fast enough. If an invalidation is huge we will fall back to a - // unordered_map instead of a vector in the SmallMap. - base::SmallMap<std::unordered_map<TileMapKey, gfx::Rect, TileMapKeyHash>, 16> - remove_tiles; + + base::flat_map<TileMapKey, gfx::Rect> remove_tiles; gfx::Rect expanded_live_tiles_rect = tiling_data_.ExpandRectToTileBounds(live_tiles_rect_); for (Region::Iterator iter(layer_invalidation); iter.has_rect(); @@ -380,9 +376,8 @@ PictureLayerTiling::CoverageIterator::CoverageIterator( const gfx::Rect& coverage_rect) : tiling_(tiling), coverage_rect_(coverage_rect), - coverage_to_content_( - gfx::PreScaleAxisTransform2d(tiling->raster_transform(), - 1.f / coverage_scale)) { + coverage_to_content_(tiling->raster_transform().scale() / coverage_scale, + tiling->raster_transform().translation()) { DCHECK(tiling_); // In order to avoid artifacts in geometry_rect scaling and clamping to ints, // the |coverage_scale| should always be at least as big as the tiling's @@ -439,25 +434,70 @@ PictureLayerTiling::CoverageIterator::operator++() { return *this; bool first_time = tile_i_ < left_; - bool new_row = false; - tile_i_++; - if (tile_i_ > right_) { - tile_i_ = left_; - tile_j_++; - new_row = true; - if (tile_j_ > bottom_) { - current_tile_ = NULL; - return *this; + while (true) { + bool new_row = false; + tile_i_++; + if (tile_i_ > right_) { + tile_i_ = left_; + tile_j_++; + new_row = true; + if (tile_j_ > bottom_) { + current_tile_ = NULL; + break; + } } - } - current_tile_ = tiling_->TileAt(tile_i_, tile_j_); + DCHECK_LT(tile_i_, tiling_->tiling_data_.num_tiles_x()); + DCHECK_LT(tile_j_, tiling_->tiling_data_.num_tiles_y()); + current_tile_ = tiling_->TileAt(tile_i_, tile_j_); + + gfx::Rect geometry_rect_candidate = ComputeGeometryRect(); + + // This can happen due to floating point inprecision when calculating the + // |wanted_texels| area in the constructor. + if (geometry_rect_candidate.IsEmpty()) + continue; + + gfx::Rect last_geometry_rect = current_geometry_rect_; + current_geometry_rect_ = geometry_rect_candidate; + + if (first_time) + break; + + // Iteration happens left->right, top->bottom. Running off the bottom-right + // edge is handled by the intersection above with dest_rect_. Here we make + // sure that the new current geometry rect doesn't overlap with the last. + int min_left; + int min_top; + if (new_row) { + min_left = coverage_rect_.x(); + min_top = last_geometry_rect.bottom(); + } else { + min_left = last_geometry_rect.right(); + min_top = last_geometry_rect.y(); + } + + int inset_left = std::max(0, min_left - current_geometry_rect_.x()); + int inset_top = std::max(0, min_top - current_geometry_rect_.y()); + current_geometry_rect_.Inset(inset_left, inset_top, 0, 0); + +#if DCHECK_IS_ON() + if (!new_row) { + DCHECK_EQ(last_geometry_rect.right(), current_geometry_rect_.x()); + DCHECK_EQ(last_geometry_rect.bottom(), current_geometry_rect_.bottom()); + DCHECK_EQ(last_geometry_rect.y(), current_geometry_rect_.y()); + } +#endif + break; + } + return *this; +} + +gfx::Rect PictureLayerTiling::CoverageIterator::ComputeGeometryRect() const { // Calculate the current geometry rect. As we reserved overlap between tiles // to accommodate bilinear filtering and rounding errors in destination // space, the geometry rect might overlap on the edges. - gfx::Rect last_geometry_rect = current_geometry_rect_; - gfx::RectF texel_extent = tiling_->tiling_data_.TexelExtent(tile_i_, tile_j_); { // Adjust tile extent to accommodate numerical errors. @@ -474,7 +514,7 @@ PictureLayerTiling::CoverageIterator::operator++() { // Convert texel_extent to coverage scale, which is what we have to report // geometry_rect in. - current_geometry_rect_ = + gfx::Rect candidate = gfx::ToEnclosedRect(coverage_to_content_.InverseMapRect(texel_extent)); { // Adjust external edges to cover the whole layer in dest space. @@ -486,48 +526,18 @@ PictureLayerTiling::CoverageIterator::operator++() { // sampled as the AA fragment shader clamps sample coordinate and // antialiasing itself. const TilingData& data = tiling_->tiling_data_; - current_geometry_rect_.Inset(tile_i_ ? 0 : -current_geometry_rect_.x(), - tile_j_ ? 0 : -current_geometry_rect_.y(), - (tile_i_ != data.num_tiles_x() - 1) - ? 0 - : current_geometry_rect_.right() - - coverage_rect_max_bounds_.width(), - (tile_j_ != data.num_tiles_y() - 1) - ? 0 - : current_geometry_rect_.bottom() - - coverage_rect_max_bounds_.height()); + candidate.Inset( + tile_i_ ? 0 : -candidate.x(), tile_j_ ? 0 : -candidate.y(), + (tile_i_ != data.num_tiles_x() - 1) + ? 0 + : candidate.right() - coverage_rect_max_bounds_.width(), + (tile_j_ != data.num_tiles_y() - 1) + ? 0 + : candidate.bottom() - coverage_rect_max_bounds_.height()); } - current_geometry_rect_.Intersect(coverage_rect_); - DCHECK(!current_geometry_rect_.IsEmpty()); - - if (first_time) - return *this; - - // Iteration happens left->right, top->bottom. Running off the bottom-right - // edge is handled by the intersection above with dest_rect_. Here we make - // sure that the new current geometry rect doesn't overlap with the last. - int min_left; - int min_top; - if (new_row) { - min_left = coverage_rect_.x(); - min_top = last_geometry_rect.bottom(); - } else { - min_left = last_geometry_rect.right(); - min_top = last_geometry_rect.y(); - } - - int inset_left = std::max(0, min_left - current_geometry_rect_.x()); - int inset_top = std::max(0, min_top - current_geometry_rect_.y()); - current_geometry_rect_.Inset(inset_left, inset_top, 0, 0); - - if (!new_row) { - DCHECK_EQ(last_geometry_rect.right(), current_geometry_rect_.x()); - DCHECK_EQ(last_geometry_rect.bottom(), current_geometry_rect_.bottom()); - DCHECK_EQ(last_geometry_rect.y(), current_geometry_rect_.y()); - } - - return *this; + candidate.Intersect(coverage_rect_); + return candidate; } gfx::Rect PictureLayerTiling::CoverageIterator::geometry_rect() const { @@ -808,6 +818,39 @@ bool PictureLayerTiling::IsTileRequiredForDraw(const Tile* tile) const { return true; } +bool PictureLayerTiling::ShouldDecodeCheckeredImagesForTile( + const Tile* tile) const { + // If this is the pending tree and the tile is not occluded, any checkered + // images on this tile should be decoded. + if (tree_ == PENDING_TREE) + return !IsTileOccludedOnCurrentTree(tile); + + DCHECK_EQ(tree_, ACTIVE_TREE); + const PictureLayerTiling* pending_twin = + client_->GetPendingOrActiveTwinTiling(this); + + // If we don't have a pending twin, then 2 cases are possible. Either we don't + // have a pending tree, in which case we should be decoding images for tiles + // which are unoccluded. + // If we do have a pending tree, then not having a twin implies that this + // tiling will be evicted upon activation. TODO(khushalsagar): Plumb this + // information here and return false for this case. + if (!pending_twin) + return !IsTileOccludedOnCurrentTree(tile); + + // If the tile will be replaced upon activation, then we don't need to process + // it for checkered images. Since once the pending tree is activated, it is + // the new active tree's content that we will invalidate and replace once the + // decode finishes. + if (!TilingMatchesTileIndices(pending_twin) || + pending_twin->TileAt(tile->tiling_i_index(), tile->tiling_j_index())) { + return false; + } + + // Ask the pending twin if this tile will become occluded upon activation. + return !pending_twin->IsTileOccludedOnCurrentTree(tile); +} + void PictureLayerTiling::UpdateRequiredStatesOnTile(Tile* tile) const { tile->set_required_for_activation(IsTileRequiredForActivation(tile)); tile->set_required_for_draw(IsTileRequiredForDraw(tile)); @@ -836,7 +879,8 @@ PrioritizedTile PictureLayerTiling::MakePrioritizedTile( tile_priority.distance_to_visible > 0.5f * max_skewport_extent_in_screen_space_); return PrioritizedTile(tile, this, tile_priority, IsTileOccluded(tile), - process_for_images_only); + process_for_images_only, + ShouldDecodeCheckeredImagesForTile(tile)); } std::map<const Tile*, PrioritizedTile> diff --git a/chromium/cc/tiles/picture_layer_tiling.h b/chromium/cc/tiles/picture_layer_tiling.h index ecf1974efa8..d49d22ed23f 100644 --- a/chromium/cc/tiles/picture_layer_tiling.h +++ b/chromium/cc/tiles/picture_layer_tiling.h @@ -63,6 +63,9 @@ struct TileMapKey { bool operator==(const TileMapKey& other) const { return index_x == other.index_x && index_y == other.index_y; } + bool operator<(const TileMapKey& other) const { + return std::tie(index_x, index_y) < std::tie(other.index_x, other.index_y); + } int index_x; int index_y; @@ -105,6 +108,10 @@ class CC_EXPORT PictureLayerTiling { bool IsTileRequiredForActivation(const Tile* tile) const; bool IsTileRequiredForDraw(const Tile* tile) const; + // Returns true if the tile should be processed for decoding images skipped + // during rasterization. + bool ShouldDecodeCheckeredImagesForTile(const Tile* tile) const; + void set_resolution(TileResolution resolution) { resolution_ = resolution; may_contain_low_resolution_tiles_ |= resolution == LOW_RESOLUTION; @@ -230,6 +237,8 @@ class CC_EXPORT PictureLayerTiling { int j() const { return tile_j_; } private: + gfx::Rect ComputeGeometryRect() const; + const PictureLayerTiling* tiling_ = nullptr; gfx::Size coverage_rect_max_bounds_; gfx::Rect coverage_rect_; diff --git a/chromium/cc/tiles/picture_layer_tiling_set.h b/chromium/cc/tiles/picture_layer_tiling_set.h index 30c1d2967d7..ed71afa177d 100644 --- a/chromium/cc/tiles/picture_layer_tiling_set.h +++ b/chromium/cc/tiles/picture_layer_tiling_set.h @@ -107,6 +107,9 @@ class CC_EXPORT PictureLayerTilingSet { // the aspect ratio. float GetMaximumContentsScale() const; + // Remove one tiling. + void Remove(PictureLayerTiling* tiling); + // Removes all tilings with a contents scale key < |minimum_scale_key|. void RemoveTilingsBelowScaleKey(float minimum_scale_key); @@ -226,8 +229,6 @@ class CC_EXPORT PictureLayerTilingSet { scoped_refptr<RasterSource> raster_source, const Region& layer_invalidation); - // Remove one tiling. - void Remove(PictureLayerTiling* tiling); void VerifyTilings(const PictureLayerTilingSet* pending_twin_set) const; bool TilingsNeedUpdate(const gfx::Rect& required_rect_in_layer_space, diff --git a/chromium/cc/tiles/picture_layer_tiling_unittest.cc b/chromium/cc/tiles/picture_layer_tiling_unittest.cc index f6185bf3638..39d5867a0a9 100644 --- a/chromium/cc/tiles/picture_layer_tiling_unittest.cc +++ b/chromium/cc/tiles/picture_layer_tiling_unittest.cc @@ -1255,5 +1255,33 @@ TEST_F(PictureLayerTilingIteratorTest, FractionalTranslatedTilingOverflow) { EXPECT_FALSE(++iter); } +TEST_F(PictureLayerTilingIteratorTest, EdgeCaseLargeIntBounds) { + gfx::Size tile_size(256, 256); + float scale = 7352.331055f; + gfx::Size layer_bounds(292082, 26910); + gfx::Rect coverage_rect(2104641536, 522015, 29440, 66172); + Initialize(tile_size, scale, layer_bounds); + int count = 0; + for (PictureLayerTiling::CoverageIterator + iter(tiling_.get(), scale, coverage_rect); + iter && count < 200; ++count, ++iter) { + EXPECT_FALSE(iter.geometry_rect().IsEmpty()); + } +} + +TEST_F(PictureLayerTilingIteratorTest, EdgeCaseLargeIntBounds2) { + gfx::RectF rect(2104670720.f, 522014.5f, 192.f, 1.f); + gfx::Size tile_size(256, 256); + float scale = 7352.331055f; + gfx::Size layer_bounds(292082, 26910); + gfx::Rect coverage_rect(2104670720, 522015, 192, 1); + Initialize(tile_size, scale, layer_bounds); + for (PictureLayerTiling::CoverageIterator iter(tiling_.get(), scale, + coverage_rect); + iter; ++iter) { + EXPECT_FALSE(iter.geometry_rect().IsEmpty()); + } +} + } // namespace } // namespace cc diff --git a/chromium/cc/tiles/prioritized_tile.cc b/chromium/cc/tiles/prioritized_tile.cc index a76963d340e..cd6e901959d 100644 --- a/chromium/cc/tiles/prioritized_tile.cc +++ b/chromium/cc/tiles/prioritized_tile.cc @@ -15,12 +15,15 @@ PrioritizedTile::PrioritizedTile(Tile* tile, const PictureLayerTiling* source_tiling, const TilePriority& priority, bool is_occluded, - bool is_process_for_images_only) + bool is_process_for_images_only, + bool should_decode_checkered_images_for_tile) : tile_(tile), source_tiling_(source_tiling), priority_(priority), is_occluded_(is_occluded), - is_process_for_images_only_(is_process_for_images_only) {} + is_process_for_images_only_(is_process_for_images_only), + should_decode_checkered_images_for_tile_( + should_decode_checkered_images_for_tile) {} PrioritizedTile::~PrioritizedTile() = default; diff --git a/chromium/cc/tiles/prioritized_tile.h b/chromium/cc/tiles/prioritized_tile.h index 7decc73cca1..a2996602c7f 100644 --- a/chromium/cc/tiles/prioritized_tile.h +++ b/chromium/cc/tiles/prioritized_tile.h @@ -24,7 +24,8 @@ class CC_EXPORT PrioritizedTile { const PictureLayerTiling* source_tiling, const TilePriority& priority, bool is_occluded, - bool is_process_for_images_only); + bool is_process_for_images_only, + bool should_decode_checkered_images_for_tile); ~PrioritizedTile(); Tile* tile() const { return tile_; } @@ -36,6 +37,9 @@ class CC_EXPORT PrioritizedTile { bool is_process_for_images_only() const { return is_process_for_images_only_; } + bool should_decode_checkered_images_for_tile() const { + return should_decode_checkered_images_for_tile_; + } void AsValueInto(base::trace_event::TracedValue* value) const; @@ -45,6 +49,7 @@ class CC_EXPORT PrioritizedTile { TilePriority priority_; bool is_occluded_ = false; bool is_process_for_images_only_ = false; + bool should_decode_checkered_images_for_tile_ = false; }; } // namespace cc diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc index 716d1f45ef3..c3b6eb7d4f0 100644 --- a/chromium/cc/tiles/software_image_decode_cache.cc +++ b/chromium/cc/tiles/software_image_decode_cache.cc @@ -250,7 +250,7 @@ bool SoftwareImageDecodeCache::GetTaskForImageAndRefInternal( // image does not fit into the budget, then we don't ref this image, since it // will be decoded at raster time which is when it will be temporarily put in // the cache. - ImageKey key = ImageKey::FromDrawImage(image); + ImageKey key = ImageKey::FromDrawImage(image, format_); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::GetTaskForImageAndRef", "key", key.ToString()); @@ -345,7 +345,7 @@ void SoftwareImageDecodeCache::UnrefImage(const DrawImage& image) { // 2a. The image isn't in the locked cache because we didn't get to decode // it yet (or failed to decode it). // 2b. Unlock the image but keep it in list. - const ImageKey& key = ImageKey::FromDrawImage(image); + const ImageKey& key = ImageKey::FromDrawImage(image, format_); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::UnrefImage", "key", key.ToString()); @@ -445,11 +445,11 @@ SoftwareImageDecodeCache::DecodeImageInternal(const ImageKey& key, case kNone_SkFilterQuality: case kLow_SkFilterQuality: if (key.should_use_subrect()) - return GetSubrectImageDecode(key, std::move(image)); + return GetSubrectImageDecode(key, draw_image.paint_image()); return GetOriginalSizeImageDecode(key, std::move(image)); case kMedium_SkFilterQuality: case kHigh_SkFilterQuality: - return GetScaledImageDecode(key, std::move(image)); + return GetScaledImageDecode(key, draw_image.paint_image()); default: NOTREACHED(); return nullptr; @@ -458,7 +458,7 @@ SoftwareImageDecodeCache::DecodeImageInternal(const ImageKey& key, DecodedDrawImage SoftwareImageDecodeCache::GetDecodedImageForDraw( const DrawImage& draw_image) { - ImageKey key = ImageKey::FromDrawImage(draw_image); + ImageKey key = ImageKey::FromDrawImage(draw_image, format_); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::GetDecodedImageForDraw", "key", key.ToString()); @@ -611,15 +611,16 @@ SoftwareImageDecodeCache::GetOriginalSizeImageDecode( std::unique_ptr<SoftwareImageDecodeCache::DecodedImage> SoftwareImageDecodeCache::GetSubrectImageDecode(const ImageKey& key, - sk_sp<const SkImage> image) { + const PaintImage& image) { // Construct a key to use in GetDecodedImageForDrawInternal(). // This allows us to reuse an image in any cache if available. - gfx::Rect full_image_rect(image->width(), image->height()); - DrawImage original_size_draw_image( - std::move(image), gfx::RectToSkIRect(full_image_rect), - kNone_SkFilterQuality, SkMatrix::I(), key.target_color_space()); + gfx::Rect full_image_rect(image.sk_image()->width(), + image.sk_image()->height()); + DrawImage original_size_draw_image(image, gfx::RectToSkIRect(full_image_rect), + kNone_SkFilterQuality, SkMatrix::I(), + key.target_color_space()); ImageKey original_size_key = - ImageKey::FromDrawImage(original_size_draw_image); + ImageKey::FromDrawImage(original_size_draw_image, format_); sk_sp<SkColorSpace> target_color_space = key.target_color_space().ToSkColorSpace(); @@ -674,15 +675,16 @@ SoftwareImageDecodeCache::GetSubrectImageDecode(const ImageKey& key, std::unique_ptr<SoftwareImageDecodeCache::DecodedImage> SoftwareImageDecodeCache::GetScaledImageDecode(const ImageKey& key, - sk_sp<const SkImage> image) { + const PaintImage& image) { // Construct a key to use in GetDecodedImageForDrawInternal(). // This allows us to reuse an image in any cache if available. - gfx::Rect full_image_rect(image->width(), image->height()); - DrawImage original_size_draw_image( - std::move(image), gfx::RectToSkIRect(full_image_rect), - kNone_SkFilterQuality, SkMatrix::I(), key.target_color_space()); + gfx::Rect full_image_rect(image.sk_image()->width(), + image.sk_image()->height()); + DrawImage original_size_draw_image(image, gfx::RectToSkIRect(full_image_rect), + kNone_SkFilterQuality, SkMatrix::I(), + key.target_color_space()); ImageKey original_size_key = - ImageKey::FromDrawImage(original_size_draw_image); + ImageKey::FromDrawImage(original_size_draw_image, format_); sk_sp<SkColorSpace> target_color_space = key.target_color_space().ToSkColorSpace(); @@ -745,8 +747,8 @@ void SoftwareImageDecodeCache::DrawWithImageFinished( const DecodedDrawImage& decoded_image) { TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::DrawWithImageFinished", "key", - ImageKey::FromDrawImage(image).ToString()); - ImageKey key = ImageKey::FromDrawImage(image); + ImageKey::FromDrawImage(image, format_).ToString()); + ImageKey key = ImageKey::FromDrawImage(image, format_); if (!decoded_image.image()) return; @@ -839,6 +841,10 @@ void SoftwareImageDecodeCache::ClearCache() { ReduceCacheUsageUntilWithinLimit(0); } +size_t SoftwareImageDecodeCache::GetMaximumMemoryLimitBytes() const { + return locked_images_budget_.total_limit_bytes(); +} + void SoftwareImageDecodeCache::RemovePendingTask(const ImageKey& key, DecodeTaskType task_type) { base::AutoLock lock(lock_); @@ -898,7 +904,8 @@ void SoftwareImageDecodeCache::DumpImageMemoryForCache( } // SoftwareImageDecodeCacheKey -ImageDecodeCacheKey ImageDecodeCacheKey::FromDrawImage(const DrawImage& image) { +ImageDecodeCacheKey ImageDecodeCacheKey::FromDrawImage(const DrawImage& image, + ResourceFormat format) { const SkSize& scale = image.scale(); // If the src_rect falls outside of the image, we need to clip it since // otherwise we might end up with uninitialized memory in the decode process. @@ -915,16 +922,22 @@ ImageDecodeCacheKey ImageDecodeCacheKey::FromDrawImage(const DrawImage& image) { // If we're not going to do a scale, we can use low filter quality. Note that // checking if the sizes are the same is better than checking if scale is 1.f, // because even non-1 scale can result in the same (rounded) width/height. - // If either dimension is a downscale, then use mipmaps (medium filter - // quality). + // If either dimension is a downscale, and the quality is not None (in which + // case we need to preserve the pixelated scale), then use mipmaps (medium + // filter quality). if (target_size.width() == src_rect.width() && target_size.height() == src_rect.height()) { quality = std::min(quality, kLow_SkFilterQuality); - } else if (target_size.width() < src_rect.width() || - target_size.height() < src_rect.height()) { - quality = std::min(quality, kMedium_SkFilterQuality); + } else if (quality != kNone_SkFilterQuality && + (target_size.width() < src_rect.width() || + target_size.height() < src_rect.height())) { + quality = kMedium_SkFilterQuality; } + // Skia doesn't scale an RGBA_4444 format, so always use the original decode. + if (format == RGBA_4444) + quality = std::min(quality, kLow_SkFilterQuality); + // Drop from high to medium if the the matrix we applied wasn't decomposable, // or if the scaled image will be too large. if (quality == kHigh_SkFilterQuality) { diff --git a/chromium/cc/tiles/software_image_decode_cache.h b/chromium/cc/tiles/software_image_decode_cache.h index 1d3e2ac75c4..755770b4bce 100644 --- a/chromium/cc/tiles/software_image_decode_cache.h +++ b/chromium/cc/tiles/software_image_decode_cache.h @@ -36,7 +36,8 @@ namespace cc { // in the cache multiple times at different scales and filter qualities. class CC_EXPORT ImageDecodeCacheKey { public: - static ImageDecodeCacheKey FromDrawImage(const DrawImage& image); + static ImageDecodeCacheKey FromDrawImage(const DrawImage& image, + ResourceFormat format); ImageDecodeCacheKey(const ImageDecodeCacheKey& other); @@ -140,6 +141,7 @@ class CC_EXPORT SoftwareImageDecodeCache void SetShouldAggressivelyFreeResources( bool aggressively_free_resources) override {} void ClearCache() override; + size_t GetMaximumMemoryLimitBytes() const override; // Decode the given image and store it in the cache. This is only called by an // image decode task from a worker thread. @@ -220,7 +222,7 @@ class CC_EXPORT SoftwareImageDecodeCache size_t GetCurrentUsageSafe() const; private: - size_t limit_bytes_; + const size_t limit_bytes_; base::CheckedNumeric<size_t> current_usage_bytes_; }; @@ -263,17 +265,15 @@ class CC_EXPORT SoftwareImageDecodeCache // data, which ensures that we cache an unlocked version of the original image // in case we need to extract multiple subrects (as would be the case in an // atlas). - std::unique_ptr<DecodedImage> GetSubrectImageDecode( - const ImageKey& key, - sk_sp<const SkImage> image); + std::unique_ptr<DecodedImage> GetSubrectImageDecode(const ImageKey& key, + const PaintImage& image); // GetScaledImageDecode is called by DecodeImageInternal when the quality // requires the image be scaled. Like DecodeImageInternal, it should be // called with no lock acquired and it returns nullptr if the decoding or // scaling failed. - std::unique_ptr<DecodedImage> GetScaledImageDecode( - const ImageKey& key, - sk_sp<const SkImage> image); + std::unique_ptr<DecodedImage> GetScaledImageDecode(const ImageKey& key, + const PaintImage& image); void RefImage(const ImageKey& key); void RefAtRasterImage(const ImageKey& key); @@ -306,6 +306,8 @@ class CC_EXPORT SoftwareImageDecodeCache // The members below this comment can only be accessed if the lock is held to // ensure that they are safe to access on multiple threads. + // The exception is accessing |locked_images_budget_.total_limit_bytes()|, + // which is const and thread safe. base::Lock lock_; // Decoded images and ref counts (predecode path). diff --git a/chromium/cc/tiles/software_image_decode_cache_perftest.cc b/chromium/cc/tiles/software_image_decode_cache_perftest.cc index 0267f20d4f4..3a4b93ff28a 100644 --- a/chromium/cc/tiles/software_image_decode_cache_perftest.cc +++ b/chromium/cc/tiles/software_image_decode_cache_perftest.cc @@ -56,7 +56,9 @@ class SoftwareImageDecodeCachePerfTest : public testing::Test { auto& subrect = rect_subrect.second; for (auto& scale : scales) { images.emplace_back( - CreateImage(rect.width(), rect.height()), subrect, quality, + PaintImage(PaintImage::GetNextId(), + CreateImage(rect.width(), rect.height())), + subrect, quality, CreateMatrix(SkSize::Make(scale.first, scale.second)), gfx::ColorSpace()); } @@ -66,7 +68,7 @@ class SoftwareImageDecodeCachePerfTest : public testing::Test { timer_.Reset(); do { for (auto& image : images) - ImageDecodeCacheKey::FromDrawImage(image); + ImageDecodeCacheKey::FromDrawImage(image, RGBA_8888); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); diff --git a/chromium/cc/tiles/software_image_decode_cache_unittest.cc b/chromium/cc/tiles/software_image_decode_cache_unittest.cc index 7d26359ba3d..90aa66e48a2 100644 --- a/chromium/cc/tiles/software_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/software_image_decode_cache_unittest.cc @@ -50,39 +50,116 @@ SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { return matrix; } -TEST(SoftwareImageDecodeCacheTest, ImageKeyLowQuality) { +PaintImage::Id s_paint_image_id = PaintImage::GetNextId(); + +PaintImage CreatePaintImage(sk_sp<SkImage> image) { + return PaintImage(s_paint_image_id, image); +} + +TEST(SoftwareImageDecodeCacheTest, ImageKeyNoneQuality) { sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; - SkFilterQuality qualities[] = {kNone_SkFilterQuality, kLow_SkFilterQuality}; - for (auto quality : qualities) { - DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kNone_SkFilterQuality, + CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); - EXPECT_EQ(image->uniqueID(), key.image_id()); - EXPECT_EQ(quality, key.filter_quality()); - EXPECT_EQ(100, key.target_size().width()); - EXPECT_EQ(100, key.target_size().height()); - EXPECT_TRUE(key.can_use_original_size_decode()); - // Since the original decode will be used, the locked_bytes is that of the - // original image. - EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); - } + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kNone_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_TRUE(key.can_use_original_size_decode()); + // Since the original decode will be used, the locked_bytes is that of the + // original image. + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); } -TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQuality) { +TEST(SoftwareImageDecodeCacheTest, + ImageKeyLowQualityIncreasedToMediumIfDownscale) { sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kLow_SkFilterQuality, + CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_FALSE(key.can_use_original_size_decode()); + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); +} + +TEST(SoftwareImageDecodeCacheTest, LowUnscalableFormatStaysLow) { + sk_sp<SkImage> image = CreateImage(100, 100); + bool is_decomposable = true; + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kLow_SkFilterQuality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_4444); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_TRUE(key.can_use_original_size_decode()); + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); +} + +TEST(SoftwareImageDecodeCacheTest, HighUnscalableFormatBecomesLow) { + sk_sp<SkImage> image = CreateImage(100, 100); + bool is_decomposable = true; + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kHigh_SkFilterQuality, + CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); + + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_4444); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_TRUE(key.can_use_original_size_decode()); + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); +} + +TEST(SoftwareImageDecodeCacheTest, ImageKeyLowQualityKeptLowIfUpscale) { + sk_sp<SkImage> image = CreateImage(100, 100); + bool is_decomposable = true; + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kLow_SkFilterQuality, + CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); + + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_TRUE(key.can_use_original_size_decode()); + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); +} + +TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQuality) { + sk_sp<SkImage> image = CreateImage(100, 100); + bool is_decomposable = true; + SkFilterQuality quality = kMedium_SkFilterQuality; + + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); + + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -96,12 +173,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfEnlarging) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -115,12 +192,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfIdentity) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -136,11 +213,11 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -156,11 +233,11 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -175,12 +252,12 @@ TEST(SoftwareImageDecodeCacheTest, bool is_decomposable = false; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -194,12 +271,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt1_5Scale) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(500, key.target_size().width()); @@ -213,12 +290,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt1_0cale) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(500, key.target_size().width()); @@ -233,11 +310,11 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_75Scale) { SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(500, key.target_size().width()); @@ -251,12 +328,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_5Scale) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(250, key.target_size().width()); @@ -271,11 +348,11 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_49Scale) { SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(250, key.target_size().width()); @@ -289,12 +366,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_1Scale) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(62, key.target_size().width()); @@ -309,11 +386,11 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_01Scale) { SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(7, key.target_size().width()); @@ -328,12 +405,12 @@ TEST(SoftwareImageDecodeCacheTest, bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -348,12 +425,12 @@ TEST(SoftwareImageDecodeCacheTest, bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.2f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.2f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); EXPECT_EQ(50, key.target_size().width()); @@ -367,12 +444,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyDowscalesHighQuality) { bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(2.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(2.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(250, key.target_size().width()); @@ -389,12 +466,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToMediumIfTooLarge) { // At least one dimension should scale down, so that medium quality doesn't // become low. - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.9f, 2.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.9f, 2.f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); EXPECT_EQ(4555, key.target_size().width()); @@ -409,12 +486,12 @@ TEST(SoftwareImageDecodeCacheTest, bool is_decomposable = false; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -428,12 +505,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToLowIfIdentity) { bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -449,11 +526,11 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -469,11 +546,11 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -485,29 +562,30 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, OriginalDecodesAreEqual) { sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kLow_SkFilterQuality; + SkFilterQuality quality = kNone_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); - EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(kNone_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); EXPECT_EQ(100, key.target_size().height()); EXPECT_TRUE(key.can_use_original_size_decode()); EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.5f, 1.5), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5), is_decomposable), DefaultColorSpace()); - auto another_key = ImageDecodeCacheKey::FromDrawImage(another_draw_image); + auto another_key = + ImageDecodeCacheKey::FromDrawImage(another_draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), another_key.image_id()); - EXPECT_EQ(kLow_SkFilterQuality, another_key.filter_quality()); + EXPECT_EQ(kNone_SkFilterQuality, another_key.filter_quality()); EXPECT_EQ(100, another_key.target_size().width()); EXPECT_EQ(100, another_key.target_size().height()); EXPECT_TRUE(another_key.can_use_original_size_decode()); @@ -522,11 +600,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageRectDoesNotContainSrcRect) { SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeXYWH(25, 35, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(25, 35, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -541,11 +620,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageRectDoesNotContainSrcRectWithScale) { SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeXYWH(20, 30, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(20, 30, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); EXPECT_EQ(40, key.target_size().width()); @@ -560,10 +640,10 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImage) { bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -571,8 +651,8 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImage) { EXPECT_TRUE(task); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> another_task; need_unref = cache.GetTaskForImageAndRef( @@ -592,7 +672,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentQuality) { bool is_decomposable = true; DrawImage high_quality_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kHigh_SkFilterQuality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); @@ -603,24 +683,24 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentQuality) { EXPECT_TRUE(need_unref); EXPECT_TRUE(high_quality_task); - DrawImage low_quality_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), - kLow_SkFilterQuality, + DrawImage none_quality_draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + kNone_SkFilterQuality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); - scoped_refptr<TileTask> low_quality_task; - need_unref = cache.GetTaskForImageAndRef(low_quality_draw_image, + scoped_refptr<TileTask> none_quality_task; + need_unref = cache.GetTaskForImageAndRef(none_quality_draw_image, ImageDecodeCache::TracingInfo(), - &low_quality_task); + &none_quality_task); EXPECT_TRUE(need_unref); - EXPECT_TRUE(low_quality_task); - EXPECT_TRUE(high_quality_task.get() != low_quality_task.get()); + EXPECT_TRUE(none_quality_task); + EXPECT_TRUE(high_quality_task.get() != none_quality_task.get()); TestTileTaskRunner::ProcessTask(high_quality_task.get()); - TestTileTaskRunner::ProcessTask(low_quality_task.get()); + TestTileTaskRunner::ProcessTask(none_quality_task.get()); cache.UnrefImage(high_quality_draw_image); - cache.UnrefImage(low_quality_draw_image); + cache.UnrefImage(none_quality_draw_image); } TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentSize) { @@ -630,8 +710,8 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentSize) { SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage half_size_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> half_size_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -640,8 +720,8 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentSize) { EXPECT_TRUE(half_size_task); DrawImage quarter_size_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> quarter_size_task; need_unref = cache.GetTaskForImageAndRef(quarter_size_draw_image, @@ -665,8 +745,9 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentImage) { sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -676,7 +757,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentImage) { sk_sp<SkImage> second_image = CreateImage(100, 100); DrawImage second_draw_image( - second_image, + CreatePaintImage(second_image), SkIRect::MakeWH(second_image->width(), second_image->height()), quality, CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), DefaultColorSpace()); @@ -694,7 +775,15 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentImage) { cache.UnrefImage(second_draw_image); } -TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { +// crbug.com/709341 +#if defined(MEMORY_SANITIZER) +#define MAYBE_GetTaskForImageDifferentColorSpace \ + DISABLED_GetTaskForImageDifferentColorSpace +#else +#define MAYBE_GetTaskForImageDifferentColorSpace \ + GetTaskForImageDifferentColorSpace +#endif +TEST(SoftwareImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentColorSpace) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; @@ -707,8 +796,9 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { sk_sp<SkImage> image = CreateImageWithColorSpace(100, 100, color_space_a); DrawImage first_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_b); + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + color_space_b); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo(), &first_task); @@ -716,8 +806,9 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { EXPECT_TRUE(first_task); DrawImage second_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_c); + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + color_space_c); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo(), &second_task); @@ -726,8 +817,9 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { EXPECT_TRUE(first_task.get() != second_task.get()); DrawImage third_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_b); + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + color_space_b); scoped_refptr<TileTask> third_task; need_unref = cache.GetTaskForImageAndRef( third_draw_image, ImageDecodeCache::TracingInfo(), &third_task); @@ -749,10 +841,10 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageAlreadyDecoded) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -780,10 +872,10 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageAlreadyPrerolled) { SkFilterQuality quality = kLow_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -818,10 +910,10 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageCanceledGetsNewTask) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -862,10 +954,10 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -905,10 +997,10 @@ TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDraw) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -940,8 +1032,9 @@ TEST(SoftwareImageDecodeCacheTest, sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeXYWH(20, 30, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(20, 30, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -972,10 +1065,10 @@ TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image); @@ -998,10 +1091,10 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image); @@ -1030,10 +1123,10 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image); @@ -1076,10 +1169,10 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image); @@ -1122,10 +1215,10 @@ TEST(SoftwareImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1147,8 +1240,9 @@ TEST(SoftwareImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeXYWH(150, 150, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(150, 150, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1170,10 +1264,10 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityFilterIsHandled) { SkFilterQuality quality = kLow_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1200,7 +1294,8 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityScaledSubrectIsHandled) { SkFilterQuality quality = kLow_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeXYWH(10, 10, 80, 80), quality, + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeXYWH(10, 10, 80, 80), quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); @@ -1219,7 +1314,10 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityScaledSubrectIsHandled) { // SkImage object. EXPECT_TRUE(decoded_draw_image.image() != image); EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); - EXPECT_TRUE(decoded_draw_image.is_scale_adjustment_identity()); + // Low quality will be upgraded to medium and mip-mapped. + EXPECT_FALSE(decoded_draw_image.is_scale_adjustment_identity()); + EXPECT_EQ(0.5f, decoded_draw_image.scale_adjustment().width()); + EXPECT_EQ(0.5f, decoded_draw_image.scale_adjustment().height()); cache.DrawWithImageFinished(draw_image, decoded_draw_image); cache.UnrefImage(draw_image); @@ -1231,7 +1329,8 @@ TEST(SoftwareImageDecodeCacheTest, NoneQualityScaledSubrectIsHandled) { SkFilterQuality quality = kNone_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeXYWH(10, 10, 80, 80), quality, + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeXYWH(10, 10, 80, 80), quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); @@ -1262,10 +1361,10 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt01_5ScaleIsHandled) { SkFilterQuality quality = kMedium_SkFilterQuality; sk_sp<SkImage> image = CreateImage(500, 200); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1295,10 +1394,10 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt1_0ScaleIsHandled) { SkFilterQuality quality = kMedium_SkFilterQuality; sk_sp<SkImage> image = CreateImage(500, 200); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1329,8 +1428,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_75ScaleIsHandled) { sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1361,10 +1460,10 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_5ScaleIsHandled) { SkFilterQuality quality = kMedium_SkFilterQuality; sk_sp<SkImage> image = CreateImage(500, 200); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1395,8 +1494,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_49ScaleIsHandled) { sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1427,10 +1526,10 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_1ScaleIsHandled) { SkFilterQuality quality = kMedium_SkFilterQuality; sk_sp<SkImage> image = CreateImage(500, 200); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1461,8 +1560,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_01ScaleIsHandled) { sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1494,8 +1593,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_001ScaleIsHandled) { sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.001f, 0.001f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.001f, 0.001f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1519,12 +1618,12 @@ TEST(SoftwareImageDecodeCacheTest, sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image_50( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); DrawImage draw_image_49( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task_50; @@ -1573,7 +1672,8 @@ TEST(SoftwareImageDecodeCacheTest, ClearCache) { for (int i = 0; i < 10; ++i) { sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, + CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; diff --git a/chromium/cc/tiles/tile.h b/chromium/cc/tiles/tile.h index 435d5f0552e..66dfadae0a6 100644 --- a/chromium/cc/tiles/tile.h +++ b/chromium/cc/tiles/tile.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "cc/paint/draw_image.h" #include "cc/raster/tile_task.h" #include "cc/tiles/tile_draw_info.h" #include "ui/gfx/geometry/axis_transform2d.h" @@ -117,6 +118,15 @@ class CC_EXPORT Tile { return is_solid_color_analysis_performed_; } + bool set_raster_task_scheduled_with_checker_images(bool has_checker_images) { + bool previous_value = raster_task_scheduled_with_checker_images_; + raster_task_scheduled_with_checker_images_ = has_checker_images; + return previous_value; + } + bool raster_task_scheduled_with_checker_images() const { + return raster_task_scheduled_with_checker_images_; + } + const PictureLayerTiling* tiling() const { return tiling_; } void set_tiling(const PictureLayerTiling* tiling) { tiling_ = tiling; } @@ -158,6 +168,10 @@ class CC_EXPORT Tile { Id invalidated_id_; unsigned scheduled_priority_; + + // Set to true if there is a raster task scheduled for this tile that will + // rasterize a resource with checker images. + bool raster_task_scheduled_with_checker_images_ = false; scoped_refptr<TileTask> raster_task_; DISALLOW_COPY_AND_ASSIGN(Tile); diff --git a/chromium/cc/tiles/tile_draw_info.cc b/chromium/cc/tiles/tile_draw_info.cc index 2e4eebb726c..d27059f5949 100644 --- a/chromium/cc/tiles/tile_draw_info.cc +++ b/chromium/cc/tiles/tile_draw_info.cc @@ -22,7 +22,7 @@ void TileDrawInfo::AsValueInto(base::trace_event::TracedValue* state) const { Resource* TileDrawInfo::TakeResource() { Resource* resource = resource_; - set_resource(nullptr); + set_resource(nullptr, false); return resource; } diff --git a/chromium/cc/tiles/tile_draw_info.h b/chromium/cc/tiles/tile_draw_info.h index 8ba08857dd2..dc1a8855506 100644 --- a/chromium/cc/tiles/tile_draw_info.h +++ b/chromium/cc/tiles/tile_draw_info.h @@ -77,6 +77,11 @@ class CC_EXPORT TileDrawInfo { return resource_ ? IsResourceFormatCompressed(resource_->format()) : false; } + bool is_checker_imaged() const { + DCHECK(!resource_is_checker_imaged_ || resource_); + return resource_is_checker_imaged_; + } + void SetSolidColorForTesting(SkColor color) { set_solid_color(color); } void AsValueInto(base::trace_event::TracedValue* state) const; @@ -87,9 +92,13 @@ class CC_EXPORT TileDrawInfo { const Resource* resource() const { return resource_; } - void set_resource(Resource* resource) { + void set_resource(Resource* resource, bool resource_is_checker_imaged) { + DCHECK(!resource_is_checker_imaged || resource) + << "Need to have a resource for it to be checker-imaged"; + mode_ = RESOURCE_MODE; is_resource_ready_to_draw_ = false; + resource_is_checker_imaged_ = resource_is_checker_imaged; resource_ = resource; } @@ -111,6 +120,10 @@ class CC_EXPORT TileDrawInfo { Resource* resource_ = nullptr; bool contents_swizzled_ = false; bool is_resource_ready_to_draw_ = false; + + // Set to true if |resource_| was rasterized with checker-imaged content. The + // flag can only be true iff we have a valid |resource_|. + bool resource_is_checker_imaged_ = false; }; } // namespace cc diff --git a/chromium/cc/tiles/tile_manager.cc b/chromium/cc/tiles/tile_manager.cc index 5639661f713..b34cbb8f2e4 100644 --- a/chromium/cc/tiles/tile_manager.cc +++ b/chromium/cc/tiles/tile_manager.cc @@ -425,6 +425,10 @@ void TileManager::SetResources(ResourcePool* resource_pool, } void TileManager::Release(Tile* tile) { + if (tile->raster_task_scheduled_with_checker_images()) + num_of_tiles_with_checker_images_--; + DCHECK_GE(num_of_tiles_with_checker_images_, 0); + FreeResourcesForTile(tile); tiles_.erase(tile->id()); } @@ -522,6 +526,10 @@ void TileManager::Flush() { tile_task_manager_->CheckForCompletedTasks(); did_check_for_completed_tasks_since_last_schedule_tasks_ = true; + + // Actually flush. + raster_buffer_provider_->Flush(); + CheckPendingGpuWorkTiles(true /* issue_signals */); TRACE_EVENT_INSTANT1("cc", "DidFlush", TRACE_EVENT_SCOPE_THREAD, "stats", @@ -639,6 +647,8 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { MemoryUsage memory_usage(resource_pool_->memory_usage_bytes(), resource_pool_->resource_count()); + gfx::ColorSpace raster_color_space = client_->GetRasterColorSpace(); + std::unique_ptr<RasterTilePriorityQueue> raster_priority_queue( client_->BuildRasterQueue(global_state_.tree_priority, RasterTilePriorityQueue::Type::ALL)); @@ -684,6 +694,21 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { continue; } + // Tiles in the raster queue should either require raster or decode for + // checker-images. If this tile does not need raster, process it only to + // build the decode queue for checkered images. + // Note that performing this check after the solid color analysis is not + // necessary for correctness. + if (!tile->draw_info().NeedsRaster()) { + DCHECK(tile->draw_info().is_checker_imaged()); + DCHECK(prioritized_tile.should_decode_checkered_images_for_tile()); + + AddCheckeredImagesToDecodeQueue( + prioritized_tile, raster_color_space, + &work_to_schedule.checker_image_decode_queue); + continue; + } + // We won't be able to schedule this tile, so break out early. if (work_to_schedule.tiles_to_raster.size() >= scheduled_raster_task_limit_) { @@ -729,6 +754,25 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { break; } + // If the tile has a scheduled task that will rasterize a resource with + // checker-imaged content, add those images to the decode queue. Note that + // we add all images as we process the raster priority queue to ensure that + // images are added to the decode queue in raster priority order. + if (tile->HasRasterTask()) { + if (tile->raster_task_scheduled_with_checker_images() && + prioritized_tile.should_decode_checkered_images_for_tile()) { + AddCheckeredImagesToDecodeQueue( + prioritized_tile, raster_color_space, + &work_to_schedule.checker_image_decode_queue); + } + } else { + // Creating the raster task here will acquire resources, but + // this resource usage has already been accounted for above. + tile->raster_task_ = + CreateRasterTask(prioritized_tile, client_->GetRasterColorSpace(), + &work_to_schedule.checker_image_decode_queue); + } + memory_usage += memory_required_by_tile_to_be_scheduled; work_to_schedule.tiles_to_raster.push_back(prioritized_tile); } @@ -739,6 +783,33 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { eviction_priority_queue = FreeTileResourcesUntilUsageIsWithinLimit( std::move(eviction_priority_queue), hard_memory_limit, &memory_usage); + // At this point, if we ran out of memory when allocating resources and we + // couldn't go past even the NOW bin, this means we have evicted resources + // from all tiles with a lower priority while we still might have resources + // holding checker-imaged content. The invalidations for these resources will + // be generated only if the skipped images are decoded. So we must schedule + // decodes for these tiles to update their content. + if (!had_enough_memory_to_schedule_tiles_needed_now && + num_of_tiles_with_checker_images_ > 0) { + for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) { + const PrioritizedTile& prioritized_tile = raster_priority_queue->Top(); + + if (prioritized_tile.priority().priority_bin > TilePriority::NOW) + break; + + if (!prioritized_tile.should_decode_checkered_images_for_tile()) + continue; + + Tile* tile = prioritized_tile.tile(); + if (tile->draw_info().is_checker_imaged() || + tile->raster_task_scheduled_with_checker_images()) { + AddCheckeredImagesToDecodeQueue( + prioritized_tile, raster_color_space, + &work_to_schedule.checker_image_decode_queue); + } + } + } + UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget", !had_enough_memory_to_schedule_tiles_needed_now); did_oom_on_last_assign_ = !had_enough_memory_to_schedule_tiles_needed_now; @@ -760,6 +831,11 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { void TileManager::FreeResourcesForTile(Tile* tile) { TileDrawInfo& draw_info = tile->draw_info(); + + if (draw_info.is_checker_imaged()) + num_of_tiles_with_checker_images_--; + DCHECK_GE(num_of_tiles_with_checker_images_, 0); + Resource* resource = draw_info.TakeResource(); if (resource) { resource_pool_->ReleaseResource(resource); @@ -775,6 +851,45 @@ void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw( client_->NotifyTileStateChanged(tile); } +void TileManager::PartitionImagesForCheckering( + const PrioritizedTile& prioritized_tile, + const gfx::ColorSpace& raster_color_space, + std::vector<DrawImage>* sync_decoded_images, + std::vector<PaintImage>* checkered_images) { + Tile* tile = prioritized_tile.tile(); + std::vector<DrawImage> images_in_tile; + prioritized_tile.raster_source()->GetDiscardableImagesInRect( + tile->enclosing_layer_rect(), tile->raster_transform().scale(), + raster_color_space, &images_in_tile); + WhichTree tree = tile->tiling()->tree(); + + for (auto& draw_image : images_in_tile) { + if (checker_image_tracker_.ShouldCheckerImage(draw_image.paint_image(), + tree)) + checkered_images->push_back(draw_image.paint_image()); + else + sync_decoded_images->push_back(draw_image); + } +} + +void TileManager::AddCheckeredImagesToDecodeQueue( + const PrioritizedTile& prioritized_tile, + const gfx::ColorSpace& raster_color_space, + CheckerImageTracker::ImageDecodeQueue* image_decode_queue) { + Tile* tile = prioritized_tile.tile(); + std::vector<DrawImage> images_in_tile; + prioritized_tile.raster_source()->GetDiscardableImagesInRect( + tile->enclosing_layer_rect(), tile->raster_transform().scale(), + raster_color_space, &images_in_tile); + WhichTree tree = tile->tiling()->tree(); + + for (auto& draw_image : images_in_tile) { + if (checker_image_tracker_.ShouldCheckerImage(draw_image.paint_image(), + tree)) + image_decode_queue->push_back(draw_image.paint_image()); + } +} + void TileManager::ScheduleTasks( const PrioritizedWorkToSchedule& work_to_schedule) { const std::vector<PrioritizedTile>& tiles_that_need_to_be_rasterized = @@ -823,11 +938,7 @@ void TileManager::ScheduleTasks( DCHECK(tile->draw_info().requires_resource()); DCHECK(!tile->draw_info().resource()); - - if (!tile->raster_task_) { - tile->raster_task_ = - CreateRasterTask(prioritized_tile, raster_color_space); - } + DCHECK(tile->HasRasterTask()); TileTask* task = tile->raster_task_.get(); @@ -880,7 +991,6 @@ void TileManager::ScheduleTasks( std::vector<scoped_refptr<TileTask>> new_locked_image_tasks = image_controller_.SetPredecodeImages(std::move(new_locked_images), tracing_info); - for (auto& task : new_locked_image_tasks) { auto decode_it = std::find_if(graph_.nodes.begin(), graph_.nodes.end(), [&task](const TaskGraph::Node& node) { @@ -929,6 +1039,12 @@ void TileManager::ScheduleTasks( // in |raster_queue_|. tile_task_manager_->ScheduleTasks(&graph_); + // Schedule running of the checker-image decode queue. This replaces the + // previously scheduled queue and effectively cancels image decodes from the + // previous queue, if not already started. + checker_image_tracker_.ScheduleImageDecodeQueue( + std::move(work_to_schedule.checker_image_decode_queue)); + did_check_for_completed_tasks_since_last_schedule_tasks_ = false; TRACE_EVENT_ASYNC_STEP_INTO1("cc", "ScheduledTasks", this, "running", "state", @@ -937,8 +1053,13 @@ void TileManager::ScheduleTasks( scoped_refptr<TileTask> TileManager::CreateRasterTask( const PrioritizedTile& prioritized_tile, - const gfx::ColorSpace& color_space) { + const gfx::ColorSpace& color_space, + CheckerImageTracker::ImageDecodeQueue* checker_image_decode_queue) { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "TileManager::CreateRasterTask"); Tile* tile = prioritized_tile.tile(); + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "TileManager::CreateRasterTask", "Tile", tile->id()); // Get the resource. uint64_t resource_content_id = 0; @@ -964,29 +1085,44 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask( playback_settings.skip_images = prioritized_tile.priority().resolution == LOW_RESOLUTION; - // Create and queue all image decode tasks that this tile depends on. + // Create and queue all image decode tasks that this tile depends on. Note + // that we need to store the images for decode tasks in + // |scheduled_draw_images_| since the tile might have been destroyed by the + // time the raster task finishes. TileTask::Vector decode_tasks; - std::vector<DrawImage>& images = scheduled_draw_images_[tile->id()]; - ImageIdFlatSet images_to_skip; - images.clear(); + std::vector<DrawImage>& sync_decoded_images = + scheduled_draw_images_[tile->id()]; + sync_decoded_images.clear(); if (!playback_settings.skip_images) { - prioritized_tile.raster_source()->GetDiscardableImagesInRect( - tile->enclosing_layer_rect(), tile->raster_transform().scale(), - color_space, &images); - checker_image_tracker_.FilterImagesForCheckeringForTile( - &images, &images_to_skip, prioritized_tile.tile()->tiling()->tree()); + std::vector<PaintImage> checkered_images; + PartitionImagesForCheckering(prioritized_tile, color_space, + &sync_decoded_images, &checkered_images); + for (const auto& image : checkered_images) { + playback_settings.images_to_skip.insert(image.sk_image()->uniqueID()); + + // This can be the case for tiles on the active tree that will be replaced + // or are occluded on the pending tree. While we still need to continue + // skipping images for these tiles, we don't need to decode them since + // they will not be required on the next active tree. + if (prioritized_tile.should_decode_checkered_images_for_tile()) + checker_image_decode_queue->push_back(image); + } } // We can skip the image hijack canvas if we have no images, or no images to // skip during raster. playback_settings.use_image_hijack_canvas = - !images.empty() || !images_to_skip.empty(); - playback_settings.images_to_skip = std::move(images_to_skip); + !sync_decoded_images.empty() || !playback_settings.images_to_skip.empty(); + + bool has_checker_images = !playback_settings.images_to_skip.empty(); + tile->set_raster_task_scheduled_with_checker_images(has_checker_images); + if (has_checker_images) + num_of_tiles_with_checker_images_++; // Get the tasks for the required images. ImageDecodeCache::TracingInfo tracing_info( prepare_tiles_count_, prioritized_tile.priority().priority_bin); - image_controller_.GetTasksForImagesAndRef(&images, &decode_tasks, + image_controller_.GetTasksForImagesAndRef(&sync_decoded_images, &decode_tasks, tracing_info); std::unique_ptr<RasterBuffer> raster_buffer = @@ -1012,10 +1148,15 @@ void TileManager::OnRasterTaskCompleted( auto found = tiles_.find(tile_id); Tile* tile = nullptr; + bool raster_task_was_scheduled_with_checker_images = false; if (found != tiles_.end()) { tile = found->second; DCHECK(tile->raster_task_.get()); tile->raster_task_ = nullptr; + raster_task_was_scheduled_with_checker_images = + tile->set_raster_task_scheduled_with_checker_images(false); + if (raster_task_was_scheduled_with_checker_images) + num_of_tiles_with_checker_images_--; } // Unref all the images. @@ -1038,8 +1179,11 @@ void TileManager::OnRasterTaskCompleted( } TileDrawInfo& draw_info = tile->draw_info(); - draw_info.set_resource(resource); + draw_info.set_resource(resource, + raster_task_was_scheduled_with_checker_images); draw_info.contents_swizzled_ = DetermineResourceRequiresSwizzle(tile); + if (raster_task_was_scheduled_with_checker_images) + num_of_tiles_with_checker_images_++; // In SMOOTHNESS_TAKES_PRIORITY mode, we wait for GPU work to complete for a // tile before setting it as ready to draw. @@ -1248,7 +1392,7 @@ void TileManager::MarkTilesOutOfMemory( } } -const ImageIdFlatSet& TileManager::TakeImagesToInvalidateOnSyncTree() { +const PaintImageIdFlatSet& TileManager::TakeImagesToInvalidateOnSyncTree() { return checker_image_tracker_.TakeImagesToInvalidateOnSyncTree(); } @@ -1256,6 +1400,11 @@ void TileManager::DidActivateSyncTree() { checker_image_tracker_.DidActivateSyncTree(); } +void TileManager::ClearCheckerImageTracking( + bool can_clear_decode_policy_tracking) { + checker_image_tracker_.ClearTracker(can_clear_decode_policy_tracking); +} + void TileManager::NeedsInvalidationForCheckerImagedTiles() { client_->RequestImplSideInvalidation(); } @@ -1287,6 +1436,11 @@ bool TileManager::UsePartialRaster() const { } void TileManager::CheckPendingGpuWorkTiles(bool issue_signals) { + TRACE_EVENT2("cc", "TileManager::CheckPendingGpuWorkTiles", + "pending_gpu_work_tiles", pending_gpu_work_tiles_.size(), + "tree_priority", + TreePriorityToString(global_state_.tree_priority)); + ResourceProvider::ResourceIdArray required_for_activation_ids; ResourceProvider::ResourceIdArray required_for_draw_ids; @@ -1364,6 +1518,12 @@ scoped_refptr<TileTask> TileManager::CreateTaskSetFinishedTask( std::unique_ptr<base::trace_event::ConvertableToTraceFormat> TileManager::ActivationStateAsValue() { auto state = base::MakeUnique<base::trace_event::TracedValue>(); + ActivationStateAsValueInto(state.get()); + return std::move(state); +} + +void TileManager::ActivationStateAsValueInto( + base::trace_event::TracedValue* state) { state->SetString("tree_priority", TreePriorityToString(global_state_.tree_priority)); state->SetInteger("soft_memory_limit", @@ -1402,7 +1562,7 @@ TileManager::ActivationStateAsValue() { state->BeginArray("raster_tiles"); for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) { state->BeginDictionary(); - tile_as_value(raster_priority_queue->Top(), state.get()); + tile_as_value(raster_priority_queue->Top(), state); state->EndDictionary(); } state->EndArray(); @@ -1414,12 +1574,10 @@ TileManager::ActivationStateAsValue() { state->BeginArray("activation_tiles"); for (; !required_priority_queue->IsEmpty(); required_priority_queue->Pop()) { state->BeginDictionary(); - tile_as_value(required_priority_queue->Top(), state.get()); + tile_as_value(required_priority_queue->Top(), state); state->EndDictionary(); } state->EndArray(); - - return std::move(state); } TileManager::MemoryUsage::MemoryUsage() diff --git a/chromium/cc/tiles/tile_manager.h b/chromium/cc/tiles/tile_manager.h index 3327cf1bf99..9b503e6af37 100644 --- a/chromium/cc/tiles/tile_manager.h +++ b/chromium/cc/tiles/tile_manager.h @@ -15,6 +15,7 @@ #include <vector> #include "base/macros.h" +#include "base/sequenced_task_runner.h" #include "base/values.h" #include "cc/base/unique_notifier.h" #include "cc/raster/raster_buffer_provider.h" @@ -150,8 +151,9 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { bool IsReadyToActivate() const; bool IsReadyToDraw() const; - const ImageIdFlatSet& TakeImagesToInvalidateOnSyncTree(); + const PaintImageIdFlatSet& TakeImagesToInvalidateOnSyncTree(); void DidActivateSyncTree(); + void ClearCheckerImageTracking(bool can_clear_decode_policy_tracking); std::unique_ptr<base::trace_event::ConvertableToTraceFormat> BasicStateAsValue() const; @@ -164,10 +166,12 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { void InitializeTilesWithResourcesForTesting(const std::vector<Tile*>& tiles) { for (size_t i = 0; i < tiles.size(); ++i) { TileDrawInfo& draw_info = tiles[i]->draw_info(); - draw_info.set_resource(resource_pool_->AcquireResource( - tiles[i]->desired_texture_size(), - raster_buffer_provider_->GetResourceFormat(false), - client_->GetRasterColorSpace())); + draw_info.set_resource( + resource_pool_->AcquireResource( + tiles[i]->desired_texture_size(), + raster_buffer_provider_->GetResourceFormat(false), + client_->GetRasterColorSpace()), + false); draw_info.set_resource_ready_for_draw(); } } @@ -234,6 +238,11 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { std::unique_ptr<base::trace_event::ConvertableToTraceFormat> ActivationStateAsValue(); + void ActivationStateAsValueInto(base::trace_event::TracedValue* state); + int num_of_tiles_with_checker_images() const { + return num_of_tiles_with_checker_images_; + } + protected: friend class Tile; // Must be called by tile during destruction. @@ -281,13 +290,15 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { std::vector<PrioritizedTile> tiles_to_raster; std::vector<PrioritizedTile> tiles_to_process_for_images; + CheckerImageTracker::ImageDecodeQueue checker_image_decode_queue; }; void FreeResourcesForTile(Tile* tile); void FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(Tile* tile); scoped_refptr<TileTask> CreateRasterTask( const PrioritizedTile& prioritized_tile, - const gfx::ColorSpace& color_space); + const gfx::ColorSpace& color_space, + CheckerImageTracker::ImageDecodeQueue* checker_image_decode_queue); std::unique_ptr<EvictionTilePriorityQueue> FreeTileResourcesUntilUsageIsWithinLimit( @@ -319,6 +330,15 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { PrioritizedWorkToSchedule AssignGpuMemoryToTiles(); void ScheduleTasks(const PrioritizedWorkToSchedule& work_to_schedule); + void PartitionImagesForCheckering(const PrioritizedTile& prioritized_tile, + const gfx::ColorSpace& raster_color_space, + std::vector<DrawImage>* sync_decoded_images, + std::vector<PaintImage>* checkered_images); + void AddCheckeredImagesToDecodeQueue( + const PrioritizedTile& prioritized_tile, + const gfx::ColorSpace& raster_color_space, + CheckerImageTracker::ImageDecodeQueue* image_decode_queue); + std::unique_ptr<base::trace_event::ConvertableToTraceFormat> ScheduledTasksStateAsValue() const; @@ -373,6 +393,10 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { std::unordered_map<Tile::Id, std::vector<DrawImage>> scheduled_draw_images_; std::vector<scoped_refptr<TileTask>> locked_image_tasks_; + // Number of tiles with a checker-imaged resource or active raster tasks which + // will create a checker-imaged resource. + int num_of_tiles_with_checker_images_ = 0; + // We need two WeakPtrFactory objects as the invalidation pattern of each is // different. The |task_set_finished_weak_ptr_factory_| is invalidated any // time new tasks are scheduled, preventing a race when the callback has diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc index b410cc006af..4cfe0123e32 100644 --- a/chromium/cc/tiles/tile_manager_unittest.cc +++ b/chromium/cc/tiles/tile_manager_unittest.cc @@ -29,6 +29,7 @@ #include "cc/test/fake_recording_source.h" #include "cc/test/fake_tile_manager.h" #include "cc/test/fake_tile_task_manager.h" +#include "cc/test/skia_common.h" #include "cc/test/test_layer_tree_host_base.h" #include "cc/test/test_task_graph_runner.h" #include "cc/test/test_tile_priorities.h" @@ -1493,7 +1494,7 @@ TEST_F(TileManagerTilePriorityQueueTest, NoRasterTasksforSolidColorTiles) { std::unique_ptr<PictureLayerImpl> layer_impl = PictureLayerImpl::Create( host_impl()->active_tree(), 1, Layer::LayerMaskType::NOT_MASK); - layer_impl->set_is_drawn_render_surface_layer_list_member(true); + layer_impl->set_contributes_to_drawn_render_surface(true); PictureLayerTilingSet* tiling_set = layer_impl->picture_layer_tiling_set(); PictureLayerTiling* tiling = @@ -1698,7 +1699,7 @@ TEST_F(TileManagerTest, LowResHasNoImage) { std::unique_ptr<PictureLayerImpl> layer = PictureLayerImpl::Create( host_impl()->active_tree(), 1, Layer::LayerMaskType::NOT_MASK); PictureLayerTilingSet* tiling_set = layer->picture_layer_tiling_set(); - layer->set_is_drawn_render_surface_layer_list_member(true); + layer->set_contributes_to_drawn_render_surface(true); auto* tiling = tiling_set->AddTiling(gfx::AxisTransform2d(), raster); tiling->set_resolution(resolutions[i]); @@ -2320,14 +2321,14 @@ class CheckerImagingTileManagerTest : public TestLayerTreeHostBase { SkImageInfo::MakeN32Premul(size.width(), size.height())) {} protected: - MOCK_METHOD5(onGetPixels, - bool(const SkImageInfo&, void*, size_t, SkPMColor[], int*)); + MOCK_METHOD4(onGetPixels, + bool(const SkImageInfo&, void*, size_t, const Options&)); }; void TearDown() override { // Allow all tasks on the image worker to run now. Any scheduled decodes // will be aborted. - image_worker_task_runner()->set_run_tasks_synchronously(true); + task_runner_->set_run_tasks_synchronously(true); } LayerTreeSettings CreateSettings() override { @@ -2351,8 +2352,11 @@ class CheckerImagingTileManagerTest : public TestLayerTreeHostBase { return base::MakeUnique<SynchronousTaskGraphRunner>(); } - SynchronousSimpleTaskRunner* image_worker_task_runner() const { - return task_runner_.get(); + void FlushDecodeTasks() { + while (task_runner_->HasPendingTask()) { + task_runner_->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + } } private: @@ -2366,7 +2370,7 @@ TEST_F(CheckerImagingTileManagerTest, std::unique_ptr<FakeRecordingSource> recording_source = FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); - recording_source->SetGenerateDiscardableImagesMetadata(true); + recording_source->set_fill_with_nonsolid_color(true); sk_sp<SkImage> image = SkImage::MakeFromGenerator( base::MakeUnique<testing::StrictMock<MockImageGenerator>>( @@ -2379,7 +2383,7 @@ TEST_F(CheckerImagingTileManagerTest, std::unique_ptr<PictureLayerImpl> layer_impl = PictureLayerImpl::Create( host_impl()->active_tree(), 1, Layer::LayerMaskType::NOT_MASK); - layer_impl->set_is_drawn_render_surface_layer_list_member(true); + layer_impl->set_contributes_to_drawn_render_surface(true); PictureLayerTilingSet* tiling_set = layer_impl->picture_layer_tiling_set(); PictureLayerTiling* tiling = @@ -2402,5 +2406,252 @@ TEST_F(CheckerImagingTileManagerTest, EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); } +TEST_F(CheckerImagingTileManagerTest, BuildsImageDecodeQueueAsExpected) { + const gfx::Size layer_bounds(900, 900); + + std::unique_ptr<FakeRecordingSource> recording_source = + FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); + recording_source->set_fill_with_nonsolid_color(true); + + int dimension = 450; + sk_sp<SkImage> image1 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + sk_sp<SkImage> image2 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + sk_sp<SkImage> image3 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + recording_source->add_draw_image(image1, gfx::Point(0, 0)); + recording_source->add_draw_image(image2, gfx::Point(600, 0)); + recording_source->add_draw_image(image3, gfx::Point(0, 600)); + + recording_source->Rerecord(); + scoped_refptr<RasterSource> raster_source = + RasterSource::CreateFromRecordingSource(recording_source.get(), false); + + gfx::Size tile_size(500, 500); + Region invalidation((gfx::Rect(layer_bounds))); + SetupPendingTree(raster_source, tile_size, invalidation); + + PictureLayerTilingSet* tiling_set = + pending_layer()->picture_layer_tiling_set(); + PictureLayerTiling* pending_tiling = tiling_set->tiling_at(0); + pending_tiling->set_resolution(HIGH_RESOLUTION); + pending_tiling->CreateAllTilesForTesting(); + pending_tiling->SetTilePriorityRectsForTesting( + gfx::Rect(layer_bounds), // Visible rect. + gfx::Rect(layer_bounds), // Skewport rect. + gfx::Rect(layer_bounds), // Soon rect. + gfx::Rect(layer_bounds)); // Eventually rect. + + // PrepareTiles and make sure we account correctly for tiles that have been + // scheduled with checkered images. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + EXPECT_TRUE(tile->HasRasterTask()); + if (i == 1 && j == 1) + EXPECT_FALSE(tile->raster_task_scheduled_with_checker_images()); + else + EXPECT_TRUE(tile->raster_task_scheduled_with_checker_images()); + } + } + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 3); + + // Now raster all the tiles and make sure these tiles are still accounted for + // with checkered images. + static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + EXPECT_FALSE(tile->HasRasterTask()); + EXPECT_FALSE(tile->raster_task_scheduled_with_checker_images()); + EXPECT_TRUE(tile->draw_info().has_resource()); + if (i == 1 && j == 1) + EXPECT_FALSE(tile->draw_info().is_checker_imaged()); + else + EXPECT_TRUE(tile->draw_info().is_checker_imaged()); + } + } + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 3); + + // Activate the pending tree. + ActivateTree(); + + // Set empty tile priority rects so an empty image decode queue is used. + gfx::Rect empty_rect; + PictureLayerTiling* active_tiling = + active_layer()->picture_layer_tiling_set()->tiling_at(0); + active_tiling->SetTilePriorityRectsForTesting( + gfx::Rect(empty_rect), // Visible rect. + gfx::Rect(empty_rect), // Skewport rect. + gfx::Rect(empty_rect), // Soon rect. + gfx::Rect(empty_rect)); // Eventually rect. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + + // Run the decode tasks. Since the first decode is always scheduled, the + // completion for it should be triggered. + FlushDecodeTasks(); + + // Create a new pending tree to invalidate tiles for decoded images and verify + // that only tiles for |image1| are invalidated. + EXPECT_TRUE(host_impl()->client()->did_request_impl_side_invalidation()); + PerformImplSideInvalidation(); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + if (i == 0 && j == 0) + EXPECT_TRUE(tile); + else + EXPECT_FALSE(tile); + } + } + host_impl()->client()->reset_did_request_impl_side_invalidation(); + + // Activating the tree replaces the checker-imaged tile. + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 3); + ActivateTree(); + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 2); + + // Set the tile priority rects such that only the tile with the second image + // is scheduled for decodes, since it is checker-imaged. + gfx::Rect rect_to_raster(600, 0, 300, 900); + active_tiling->SetTilePriorityRectsForTesting( + gfx::Rect(rect_to_raster), // Visible rect. + gfx::Rect(rect_to_raster), // Skewport rect. + gfx::Rect(rect_to_raster), // Soon rect. + gfx::Rect(rect_to_raster)); // Eventually rect. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + + // Run decode tasks to trigger completion of any pending decodes. + FlushDecodeTasks(); + + // Create a new pending tree to invalidate tiles for decoded images and verify + // that only tiles for |image2| are invalidated. + EXPECT_TRUE(host_impl()->client()->did_request_impl_side_invalidation()); + PerformImplSideInvalidation(); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + if (i == 1 && j == 0) + EXPECT_TRUE(tile); + else + EXPECT_FALSE(tile); + } + } + host_impl()->client()->reset_did_request_impl_side_invalidation(); + + // Activating the tree replaces the checker-imaged tile. + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 2); + ActivateTree(); + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 1); + + // Set the tile priority rects to cover the complete tiling and change the + // visibility. While |image3| has not yet been decoded, since we are + // invisible no decodes should have been scheduled. + active_tiling->SetTilePriorityRectsForTesting( + gfx::Rect(layer_bounds), // Visible rect. + gfx::Rect(layer_bounds), // Skewport rect. + gfx::Rect(layer_bounds), // Soon rect. + gfx::Rect(layer_bounds)); // Eventually rect. + host_impl()->SetVisible(false); + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + FlushDecodeTasks(); + EXPECT_FALSE(host_impl()->client()->did_request_impl_side_invalidation()); +} + +class CheckerImagingTileManagerMemoryTest + : public CheckerImagingTileManagerTest { + public: + std::unique_ptr<FakeLayerTreeHostImpl> CreateHostImpl( + const LayerTreeSettings& settings, + TaskRunnerProvider* task_runner_provider, + TaskGraphRunner* task_graph_runner) override { + LayerTreeSettings new_settings = settings; + new_settings.gpu_memory_policy.num_resources_limit = 4; + return CheckerImagingTileManagerTest::CreateHostImpl( + new_settings, task_runner_provider, task_graph_runner); + } +}; + +TEST_F(CheckerImagingTileManagerMemoryTest, AddsAllNowTilesToImageDecodeQueue) { + const gfx::Size layer_bounds(900, 1400); + + std::unique_ptr<FakeRecordingSource> recording_source = + FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); + recording_source->set_fill_with_nonsolid_color(true); + + int dimension = 450; + sk_sp<SkImage> image1 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + sk_sp<SkImage> image2 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + recording_source->add_draw_image(image1, gfx::Point(0, 515)); + recording_source->add_draw_image(image2, gfx::Point(515, 515)); + + recording_source->Rerecord(); + scoped_refptr<RasterSource> raster_source = + RasterSource::CreateFromRecordingSource(recording_source.get(), false); + + gfx::Size tile_size(500, 500); + Region invalidation((gfx::Rect(layer_bounds))); + SetupPendingTree(raster_source, tile_size, invalidation); + + PictureLayerTilingSet* tiling_set = + pending_layer()->picture_layer_tiling_set(); + PictureLayerTiling* pending_tiling = tiling_set->tiling_at(0); + pending_tiling->set_resolution(HIGH_RESOLUTION); + pending_tiling->CreateAllTilesForTesting(); + + // Use a rect that only rasterizes the bottom 2 rows of tiles. + gfx::Rect rect_to_raster(0, 500, 900, 900); + pending_tiling->SetTilePriorityRectsForTesting( + rect_to_raster, // Visible rect. + rect_to_raster, // Skewport rect. + rect_to_raster, // Soon rect. + rect_to_raster); // Eventually rect. + + // PrepareTiles, rasterize all scheduled tiles and activate while no images + // have been decoded. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + ActivateTree(); + + // Expand the visible rect to include the complete tiling. The tile iteration + // will not go beyond the first tile since there are no resources with a lower + // priority that can be evicted. But we should still see image decodes + // scheduled for all visible tiles. + gfx::Rect complete_tiling_rect(layer_bounds); + PictureLayerTiling* active_tiling = + active_layer()->picture_layer_tiling_set()->tiling_at(0); + active_tiling->SetTilePriorityRectsForTesting( + complete_tiling_rect, // Visible rect. + complete_tiling_rect, // Skewport rect. + complete_tiling_rect, // Soon rect. + complete_tiling_rect); // Eventually rect. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + + // Flush all decode tasks. The tiles with checkered images should be + // invalidated. + FlushDecodeTasks(); + EXPECT_TRUE(host_impl()->client()->did_request_impl_side_invalidation()); + PerformImplSideInvalidation(); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + if (j == 1) + EXPECT_TRUE(tile); + else + EXPECT_FALSE(tile); + } + } + host_impl()->client()->reset_did_request_impl_side_invalidation(); +} + } // namespace } // namespace cc diff --git a/chromium/cc/tiles/tiling_set_raster_queue_all.cc b/chromium/cc/tiles/tiling_set_raster_queue_all.cc index 74bf1b2f144..adae8a8dfbe 100644 --- a/chromium/cc/tiles/tiling_set_raster_queue_all.cc +++ b/chromium/cc/tiles/tiling_set_raster_queue_all.cc @@ -200,8 +200,23 @@ bool TilingSetRasterQueueAll::OnePriorityRectIterator:: bool TilingSetRasterQueueAll::OnePriorityRectIterator::IsTileValid( const Tile* tile) const { - if (!tile || !TileNeedsRaster(tile)) + if (!tile) return false; + + // A tile is valid for raster if it needs raster and is unoccluded. + bool tile_is_valid_for_raster = + tile->draw_info().NeedsRaster() && !tiling_->IsTileOccluded(tile); + + // A tile is not valid for the raster queue if it is not valid for raster or + // processing for checker-images. + if (!tile_is_valid_for_raster) { + bool tile_is_valid_for_checker_images = + tile->draw_info().is_checker_imaged() && + tiling_->ShouldDecodeCheckeredImagesForTile(tile); + if (!tile_is_valid_for_checker_images) + return false; + } + // After the pending visible rect has been processed, we must return false // for pending visible rect tiles as tiling iterators do not ignore those // tiles. diff --git a/chromium/cc/tiles/tiling_set_raster_queue_all.h b/chromium/cc/tiles/tiling_set_raster_queue_all.h index 318156380a1..73bb4c9e17b 100644 --- a/chromium/cc/tiles/tiling_set_raster_queue_all.h +++ b/chromium/cc/tiles/tiling_set_raster_queue_all.h @@ -44,9 +44,6 @@ class CC_EXPORT TilingSetRasterQueueAll { protected: ~OnePriorityRectIterator() = default; - bool TileNeedsRaster(const Tile* tile) const { - return tile->draw_info().NeedsRaster() && !tiling_->IsTileOccluded(tile); - } template <typename TilingIteratorType> void AdvanceToNextTile(TilingIteratorType* iterator); diff --git a/chromium/cc/trees/damage_tracker.cc b/chromium/cc/trees/damage_tracker.cc index aa4a6236801..d740521408d 100644 --- a/chromium/cc/trees/damage_tracker.cc +++ b/chromium/cc/trees/damage_tracker.cc @@ -14,6 +14,7 @@ #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/layers/layer_impl.h" #include "cc/layers/render_surface_impl.h" +#include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_impl.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -29,18 +30,14 @@ DamageTracker::DamageTracker() DamageTracker::~DamageTracker() {} -void DamageTracker::UpdateDamageTrackingState( - const LayerImplList& layer_list, - const RenderSurfaceImpl* target_surface, - bool target_surface_property_changed_only_from_descendant, - const gfx::Rect& target_surface_content_rect, - LayerImpl* target_surface_mask_layer, - const FilterOperations& filters) { +void DamageTracker::UpdateDamageTracking( + LayerTreeImpl* layer_tree_impl, + const RenderSurfaceList& render_surface_list) { // - // This function computes the "damage rect" of a target surface, and updates - // the state that is used to correctly track damage across frames. The damage - // rect is the region of the surface that may have changed and needs to be - // redrawn. This can be used to scissor what is actually drawn, to save GPU + // This function computes the "damage rect" of each target surface, and + // updates the state that is used to correctly track damage across frames. The + // damage rect is the region of the surface that may have changed and needs to + // be redrawn. This can be used to scissor what is actually drawn, to save GPU // computation and bandwidth. // // The surface's damage rect is computed as the union of all possible changes @@ -52,25 +49,30 @@ void DamageTracker::UpdateDamageTrackingState( // // The basic algorithm for computing the damage region is as follows: // - // 1. compute damage caused by changes in active/new layers - // for each layer in the layer_list: - // if the layer is actually a render_surface: - // add the surface's damage to our target surface. - // else - // add the layer's damage to the target surface. + // 1. compute damage caused by changes in contributing layers or surfaces + // for each contributing layer or render surface: + // add the layer's or surface's damage to the target surface. // // 2. compute damage caused by the target surface's mask, if it exists. // // 3. compute damage caused by old layers/surfaces that no longer exist - // for each leftover layer: + // for each leftover layer or render surface: // add the old layer/surface bounds to the target surface damage. // // 4. combine all partial damage rects to get the full damage rect. // // Additional important points: // - // - This algorithm is implicitly recursive; it assumes that descendant - // surfaces have already computed their damage. + // - This algorithm requires that descendant surfaces compute their damage + // before ancestor surfaces. Further, since contributing surfaces with + // background filters can expand the damage caused by contributors + // underneath them (that is, before them in draw order), the exact damage + // caused by these contributors must be computed before computing the damage + // caused by the contributing surface. This is implemented by visiting + // layers in draw order, computing the damage caused by each one to their + // target; during this walk, as soon as all of a surface's contributors have + // been visited, the surface's own damage is computed and then added to its + // target's accumulated damage. // // - Changes to layers/surfaces indicate "damage" to the target surface; If a // layer is not changed, it does NOT mean that the layer can skip drawing. @@ -104,40 +106,98 @@ void DamageTracker::UpdateDamageTrackingState( // erased from map. // - PrepareRectHistoryForUpdate(); + for (RenderSurfaceImpl* render_surface : render_surface_list) { + render_surface->damage_tracker()->PrepareForUpdate(); + } + + EffectTree& effect_tree = layer_tree_impl->property_trees()->effect_tree; + int current_target_effect_id = EffectTree::kContentsRootNodeId; + DCHECK(effect_tree.GetRenderSurface(current_target_effect_id)); + for (LayerImpl* layer : *layer_tree_impl) { + if (!layer->contributes_to_drawn_render_surface()) + continue; + + int next_target_effect_id = layer->render_target_effect_tree_index(); + if (next_target_effect_id != current_target_effect_id) { + int lowest_common_ancestor_id = + effect_tree.LowestCommonAncestorWithRenderSurface( + current_target_effect_id, next_target_effect_id); + while (current_target_effect_id != lowest_common_ancestor_id) { + // Moving to a non-descendant target surface. This implies that the + // current target doesn't have any more contributors, since only + // descendants can contribute to a target, and the each's target's + // content (including content contributed by descendants) is contiguous + // in draw order. + RenderSurfaceImpl* current_target = + effect_tree.GetRenderSurface(current_target_effect_id); + current_target->damage_tracker()->ComputeSurfaceDamage(current_target); + RenderSurfaceImpl* parent_target = current_target->render_target(); + parent_target->damage_tracker()->AccumulateDamageFromRenderSurface( + current_target); + current_target_effect_id = + effect_tree.Node(current_target_effect_id)->target_id; + } + current_target_effect_id = next_target_effect_id; + } + + RenderSurfaceImpl* target_surface = layer->render_target(); + + // We skip damage from the HUD layer because (a) the HUD layer damages the + // whole frame and (b) we don't want HUD layer damage to be shown by the + // HUD damage rect visualization. + if (layer != layer_tree_impl->hud_layer()) { + target_surface->damage_tracker()->AccumulateDamageFromLayer(layer); + } + } + + DCHECK_GE(current_target_effect_id, EffectTree::kContentsRootNodeId); + RenderSurfaceImpl* current_target = + effect_tree.GetRenderSurface(current_target_effect_id); + while (true) { + current_target->damage_tracker()->ComputeSurfaceDamage(current_target); + if (current_target->EffectTreeIndex() == EffectTree::kContentsRootNodeId) + break; + RenderSurfaceImpl* next_target = current_target->render_target(); + next_target->damage_tracker()->AccumulateDamageFromRenderSurface( + current_target); + current_target = next_target; + } +} + +void DamageTracker::ComputeSurfaceDamage(RenderSurfaceImpl* render_surface) { + // All damage from contributing layers and surfaces must already have been + // added to damage_for_this_update_ through calls to AccumulateDamageFromLayer + // and AccumulateDamageFromRenderSurface. + // These functions cannot be bypassed with early-exits, even if we know what // the damage will be for this frame, because we need to update the damage // tracker state to correctly track the next frame. - DamageAccumulator damage_from_active_layers = - TrackDamageFromActiveLayers(layer_list, target_surface); DamageAccumulator damage_from_surface_mask = - TrackDamageFromSurfaceMask(target_surface_mask_layer); + TrackDamageFromSurfaceMask(render_surface->MaskLayer()); DamageAccumulator damage_from_leftover_rects = TrackDamageFromLeftoverRects(); - DamageAccumulator damage_for_this_update; - - if (target_surface_property_changed_only_from_descendant) { - damage_for_this_update.Union(target_surface_content_rect); + if (render_surface->SurfacePropertyChangedOnlyFromDescendant()) { + damage_for_this_update_ = DamageAccumulator(); + damage_for_this_update_.Union(render_surface->content_rect()); } else { // TODO(shawnsingh): can we clamp this damage to the surface's content rect? // (affects performance, but not correctness) - damage_for_this_update.Union(damage_from_active_layers); - damage_for_this_update.Union(damage_from_surface_mask); - damage_for_this_update.Union(damage_from_leftover_rects); + damage_for_this_update_.Union(damage_from_surface_mask); + damage_for_this_update_.Union(damage_from_leftover_rects); gfx::Rect damage_rect; - bool is_rect_valid = damage_for_this_update.GetAsRect(&damage_rect); + bool is_rect_valid = damage_for_this_update_.GetAsRect(&damage_rect); if (is_rect_valid) { - damage_rect = - filters.MapRect(damage_rect, target_surface->SurfaceScale().matrix()); - damage_for_this_update = DamageAccumulator(); - damage_for_this_update.Union(damage_rect); + damage_rect = render_surface->Filters().MapRect( + damage_rect, render_surface->SurfaceScale().matrix()); + damage_for_this_update_ = DamageAccumulator(); + damage_for_this_update_.Union(damage_rect); } } // Damage accumulates until we are notified that we actually did draw on that // frame. - current_damage_.Union(damage_for_this_update); + current_damage_.Union(damage_for_this_update_); } bool DamageTracker::GetDamageRectIfValid(gfx::Rect* rect) { @@ -177,31 +237,6 @@ DamageTracker::SurfaceRectMapData& DamageTracker::RectDataForSurface( return *it; } -DamageTracker::DamageAccumulator DamageTracker::TrackDamageFromActiveLayers( - const LayerImplList& layer_list, - const RenderSurfaceImpl* target_surface) { - DamageAccumulator damage; - - for (size_t layer_index = 0; layer_index < layer_list.size(); ++layer_index) { - // Visit layers in back-to-front order. - LayerImpl* layer = layer_list[layer_index]; - - // We skip damage from the HUD layer because (a) the HUD layer damages the - // whole frame and (b) we don't want HUD layer damage to be shown by the - // HUD damage rect visualization. - if (layer == layer->layer_tree_impl()->hud_layer()) - continue; - - RenderSurfaceImpl* render_surface = layer->GetRenderSurface(); - if (render_surface && render_surface != target_surface) - ExtendDamageForRenderSurface(render_surface, &damage); - else - ExtendDamageForLayer(layer, &damage); - } - - return damage; -} - DamageTracker::DamageAccumulator DamageTracker::TrackDamageFromSurfaceMask( LayerImpl* target_surface_mask_layer) { DamageAccumulator damage; @@ -220,8 +255,9 @@ DamageTracker::DamageAccumulator DamageTracker::TrackDamageFromSurfaceMask( return damage; } -void DamageTracker::PrepareRectHistoryForUpdate() { +void DamageTracker::PrepareForUpdate() { mailboxId_++; + damage_for_this_update_ = DamageAccumulator(); } DamageTracker::DamageAccumulator DamageTracker::TrackDamageFromLeftoverRects() { @@ -292,12 +328,11 @@ DamageTracker::DamageAccumulator DamageTracker::TrackDamageFromLeftoverRects() { void DamageTracker::ExpandDamageInsideRectWithFilters( const gfx::Rect& pre_filter_rect, - const FilterOperations& filters, - DamageAccumulator* damage) { + const FilterOperations& filters) { gfx::Rect damage_rect; - bool is_valid_rect = damage->GetAsRect(&damage_rect); - // If the input isn't a valid rect, then there is no point in trying to make - // it bigger. + bool is_valid_rect = damage_for_this_update_.GetAsRect(&damage_rect); + // If the damage accumulated so far isn't a valid rect, then there is no point + // in trying to make it bigger. if (!is_valid_rect) return; @@ -308,11 +343,10 @@ void DamageTracker::ExpandDamageInsideRectWithFilters( // Restrict it to the rectangle in which the background filter is shown. expanded_damage_rect.Intersect(pre_filter_rect); - damage->Union(expanded_damage_rect); + damage_for_this_update_.Union(expanded_damage_rect); } -void DamageTracker::ExtendDamageForLayer(LayerImpl* layer, - DamageAccumulator* target_damage) { +void DamageTracker::AccumulateDamageFromLayer(LayerImpl* layer) { // There are two ways that a layer can damage a region of the target surface: // 1. Property change (e.g. opacity, position, transforms): // - the entire region of the layer itself damages the surface. @@ -341,11 +375,11 @@ void DamageTracker::ExtendDamageForLayer(LayerImpl* layer, if (layer_is_new || layer->LayerPropertyChanged()) { // If a layer is new or has changed, then its entire layer rect affects the // target surface. - target_damage->Union(rect_in_target_space); + damage_for_this_update_.Union(rect_in_target_space); // The layer's old region is now exposed on the target surface, too. // Note old_rect_in_target_space is already in target space. - target_damage->Union(old_rect_in_target_space); + damage_for_this_update_.Union(old_rect_in_target_space); return; } @@ -357,13 +391,12 @@ void DamageTracker::ExtendDamageForLayer(LayerImpl* layer, if (!damage_rect.IsEmpty()) { gfx::Rect damage_rect_in_target_space = MathUtil::MapEnclosingClippedRect(layer->DrawTransform(), damage_rect); - target_damage->Union(damage_rect_in_target_space); + damage_for_this_update_.Union(damage_rect_in_target_space); } } -void DamageTracker::ExtendDamageForRenderSurface( - RenderSurfaceImpl* render_surface, - DamageAccumulator* target_damage) { +void DamageTracker::AccumulateDamageFromRenderSurface( + RenderSurfaceImpl* render_surface) { // There are two ways a "descendant surface" can damage regions of the "target // surface": // 1. Property change: @@ -390,10 +423,10 @@ void DamageTracker::ExtendDamageForRenderSurface( if (surface_is_new || render_surface->SurfacePropertyChanged()) { // The entire surface contributes damage. - target_damage->Union(surface_rect_in_target_space); + damage_for_this_update_.Union(surface_rect_in_target_space); // The surface's old region is now exposed on the target surface, too. - target_damage->Union(old_surface_rect); + damage_for_this_update_.Union(old_surface_rect); } else { // Only the surface's damage_rect will damage the target surface. gfx::Rect damage_rect_in_local_space; @@ -405,9 +438,9 @@ void DamageTracker::ExtendDamageForRenderSurface( const gfx::Transform& draw_transform = render_surface->draw_transform(); gfx::Rect damage_rect_in_target_space = MathUtil::MapEnclosingClippedRect( draw_transform, damage_rect_in_local_space); - target_damage->Union(damage_rect_in_target_space); + damage_for_this_update_.Union(damage_rect_in_target_space); } else if (!is_valid_rect) { - target_damage->Union(surface_rect_in_target_space); + damage_for_this_update_.Union(surface_rect_in_target_space); } } @@ -421,7 +454,7 @@ void DamageTracker::ExtendDamageForRenderSurface( render_surface->BackgroundFilters(); if (background_filters.HasFilterThatMovesPixels()) { ExpandDamageInsideRectWithFilters(surface_rect_in_target_space, - background_filters, target_damage); + background_filters); } } diff --git a/chromium/cc/trees/damage_tracker.h b/chromium/cc/trees/damage_tracker.h index 6ddc504dafa..751eeb977e5 100644 --- a/chromium/cc/trees/damage_tracker.h +++ b/chromium/cc/trees/damage_tracker.h @@ -21,6 +21,7 @@ namespace cc { class FilterOperations; class LayerImpl; +class LayerTreeImpl; class RenderSurfaceImpl; // Computes the region where pixels have actually changed on a @@ -31,15 +32,12 @@ class CC_EXPORT DamageTracker { static std::unique_ptr<DamageTracker> Create(); ~DamageTracker(); + static void UpdateDamageTracking( + LayerTreeImpl* layer_tree_impl, + const RenderSurfaceList& render_surface_list); + void DidDrawDamagedArea() { current_damage_ = DamageAccumulator(); } void AddDamageNextUpdate(const gfx::Rect& dmg) { current_damage_.Union(dmg); } - void UpdateDamageTrackingState( - const LayerImplList& layer_list, - const RenderSurfaceImpl* target_surface, - bool target_surface_property_changed_only_from_descendant, - const gfx::Rect& target_surface_content_rect, - LayerImpl* target_surface_mask_layer, - const FilterOperations& filters); bool GetDamageRectIfValid(gfx::Rect* rect); @@ -84,21 +82,17 @@ class CC_EXPORT DamageTracker { int bottom_ = 0; }; - DamageAccumulator TrackDamageFromActiveLayers( - const LayerImplList& layer_list, - const RenderSurfaceImpl* target_surface); DamageAccumulator TrackDamageFromSurfaceMask( LayerImpl* target_surface_mask_layer); DamageAccumulator TrackDamageFromLeftoverRects(); - void PrepareRectHistoryForUpdate(); - // These helper functions are used only in TrackDamageFromActiveLayers(). - void ExtendDamageForLayer(LayerImpl* layer, DamageAccumulator* target_damage); - void ExtendDamageForRenderSurface(RenderSurfaceImpl* render_surface, - DamageAccumulator* target_damage); + // These helper functions are used only during UpdateDamageTracking(). + void PrepareForUpdate(); + void AccumulateDamageFromLayer(LayerImpl* layer); + void AccumulateDamageFromRenderSurface(RenderSurfaceImpl* render_surface); + void ComputeSurfaceDamage(RenderSurfaceImpl* render_surface); void ExpandDamageInsideRectWithFilters(const gfx::Rect& pre_filter_rect, - const FilterOperations& filters, - DamageAccumulator* damage); + const FilterOperations& filters); struct LayerRectMapData { LayerRectMapData() : layer_id_(0), mailboxId_(0) {} @@ -147,6 +141,9 @@ class CC_EXPORT DamageTracker { unsigned int mailboxId_; DamageAccumulator current_damage_; + // Damage accumulated since the last call to PrepareForUpdate(). + DamageAccumulator damage_for_this_update_; + DISALLOW_COPY_AND_ASSIGN(DamageTracker); }; diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc index 91cec6dc030..d036723038e 100644 --- a/chromium/cc/trees/damage_tracker_unittest.cc +++ b/chromium/cc/trees/damage_tracker_unittest.cc @@ -13,6 +13,7 @@ #include "cc/test/fake_impl_task_runner_provider.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" +#include "cc/test/layer_test_common.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_impl.h" @@ -27,22 +28,22 @@ namespace { void ExecuteCalculateDrawProperties(LayerImpl* root, float device_scale_factor, - LayerImplList* render_surface_layer_list) { + RenderSurfaceList* render_surface_list) { // Sanity check: The test itself should create the root layer's render // surface, so that the surface (and its damage tracker) can // persist across multiple calls to this function. - ASSERT_FALSE(render_surface_layer_list->size()); + ASSERT_FALSE(render_surface_list->size()); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), device_scale_factor, render_surface_layer_list); + root, root->bounds(), device_scale_factor, render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - ASSERT_TRUE(root->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(root)); } void ClearDamageForAllSurfaces(LayerImpl* root) { for (auto* layer : *root->layer_tree_impl()) { - if (layer->GetRenderSurface()) - layer->GetRenderSurface()->damage_tracker()->DidDrawDamagedArea(); + if (GetRenderSurface(layer)) + GetRenderSurface(layer)->damage_tracker()->DidDrawDamagedArea(); } } @@ -53,23 +54,12 @@ void EmulateDrawingOneFrame(LayerImpl* root, float device_scale_factor = 1.f) { // 3. resetting all update_rects and property_changed flags for all layers // and surfaces. - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; ExecuteCalculateDrawProperties(root, device_scale_factor, - &render_surface_layer_list); - - // Iterate back-to-front, so that damage correctly propagates from descendant - // surfaces to ancestors. - size_t render_surface_layer_list_size = render_surface_layer_list.size(); - for (size_t i = 0; i < render_surface_layer_list_size; ++i) { - size_t index = render_surface_layer_list_size - 1 - i; - RenderSurfaceImpl* target_surface = - render_surface_layer_list[index]->GetRenderSurface(); - target_surface->damage_tracker()->UpdateDamageTrackingState( - target_surface->layer_list(), target_surface, - target_surface->SurfacePropertyChangedOnlyFromDescendant(), - target_surface->content_rect(), target_surface->MaskLayer(), - target_surface->Filters()); - } + &render_surface_list); + + DamageTracker::UpdateDamageTracking(root->layer_tree_impl(), + render_surface_list); root->layer_tree_impl()->ResetAllChangeTracking(); } @@ -185,16 +175,17 @@ class DamageTrackerTest : public testing::Test { TEST_F(DamageTrackerTest, SanityCheckTestTreeWithOneSurface) { // Sanity check that the simple test tree will actually produce the expected - // render surfaces and layer lists. + // render surfaces. LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(); + LayerImpl* child = root->test_properties()->children[0]; - EXPECT_EQ(2u, root->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(1, root->GetRenderSurface()->layer_list()[0]->id()); - EXPECT_EQ(2, root->GetRenderSurface()->layer_list()[1]->id()); + EXPECT_EQ(2, GetRenderSurface(root)->num_contributors()); + EXPECT_TRUE(root->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child->contributes_to_drawn_render_surface()); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(500, 500).ToString(), root_damage_rect.ToString()); @@ -202,7 +193,7 @@ TEST_F(DamageTrackerTest, SanityCheckTestTreeWithOneSurface) { TEST_F(DamageTrackerTest, SanityCheckTestTreeWithTwoSurfaces) { // Sanity check that the complex test tree will actually produce the expected - // render surfaces and layer lists. + // render surfaces. LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces(); @@ -210,17 +201,16 @@ TEST_F(DamageTrackerTest, SanityCheckTestTreeWithTwoSurfaces) { LayerImpl* child2 = root->test_properties()->children[1]; gfx::Rect child_damage_rect; - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); - ASSERT_TRUE(child1->GetRenderSurface()); - EXPECT_FALSE(child2->GetRenderSurface()); - EXPECT_EQ(3u, root->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(2u, child1->GetRenderSurface()->layer_list().size()); + EXPECT_NE(GetRenderSurface(child1), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(child2), GetRenderSurface(root)); + EXPECT_EQ(3, GetRenderSurface(root)->num_contributors()); + EXPECT_EQ(2, GetRenderSurface(child1)->num_contributors()); // The render surface for child1 only has a content_rect that encloses // grand_child1 and grand_child2, because child1 does not draw content. @@ -243,7 +233,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForUpdateRects) { // Damage position on the surface should be: position of update_rect (10, 11) // relative to the child (100, 100). gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(110, 111, 12, 13).ToString(), root_damage_rect.ToString()); @@ -254,7 +244,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForUpdateRects) { child->SetUpdateRect(gfx::Rect(10, 11, 12, 13)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(110, 111, 12, 13).ToString(), root_damage_rect.ToString()); @@ -268,7 +258,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForUpdateRects) { // Damage position on the surface should be: position of update_rect (20, 25) // relative to the child (100, 100). - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(120, 125, 1, 2).ToString(), root_damage_rect.ToString()); } @@ -287,7 +277,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) { // Damage position on the surface should be: position of layer damage_rect // (10, 11) relative to the child (100, 100). gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(true, root_damage_rect.Contains(gfx::Rect(110, 111, 12, 13))); @@ -297,7 +287,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) { child->AddDamageRect(gfx::Rect(10, 11, 12, 13)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(true, root_damage_rect.Contains(gfx::Rect(110, 111, 12, 13))); @@ -310,7 +300,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) { // Damage position on the surface should be: position of layer damage_rect // (20, 25) relative to the child (100, 100). - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(true, root_damage_rect.Contains(gfx::Rect(120, 125, 1, 2))); @@ -324,7 +314,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) { // Damage position on the surface should be: position of layer damage_rect // (20, 25) relative to the child (100, 100). - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(true, root_damage_rect.Contains(gfx::Rect(120, 125, 1, 2))); EXPECT_EQ(true, root_damage_rect.Contains(gfx::Rect(110, 115, 3, 4))); @@ -346,7 +336,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForLayerUpdateAndDamageRects) { // damage_rect and update rect (5, 6) // relative to the child (100, 100). gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(true, root_damage_rect.Contains(gfx::Rect(105, 106, 24, 20))); @@ -357,7 +347,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForLayerUpdateAndDamageRects) { child->SetUpdateRect(gfx::Rect(10, 11, 14, 15)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(true, root_damage_rect.Contains(gfx::Rect(110, 111, 14, 15))); @@ -371,7 +361,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForLayerUpdateAndDamageRects) { // Damage position on the surface should be: position of unified layer damage // rect and update rect (5, 10) relative to the child (100, 100). - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(true, root_damage_rect.Contains(gfx::Rect(105, 110, 17, 18))); } @@ -390,12 +380,12 @@ TEST_F(DamageTrackerTest, VerifyDamageForPropertyChanges) { root->layer_tree_impl()->SetOpacityMutated(child->element_id(), 0.5f); EmulateDrawingOneFrame(root); - ASSERT_EQ(2u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(2, GetRenderSurface(root)->num_contributors()); // Damage should be the entire child layer in target_surface space. gfx::Rect expected_rect = gfx::Rect(100, 100, 30, 30); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(expected_rect.ToString(), root_damage_rect.ToString()); @@ -408,7 +398,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForPropertyChanges) { ClearDamageForAllSurfaces(root); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_TRUE(root_damage_rect.IsEmpty()); @@ -422,7 +412,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForPropertyChanges) { // Expect damage to be the combination of the previous one and the new one. expected_rect.Union(gfx::Rect(200, 230, 30, 30)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_FLOAT_RECT_EQ(expected_rect, root_damage_rect); } @@ -440,7 +430,7 @@ TEST_F(DamageTrackerTest, VerifyDamageWhenSurfaceRemoved) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(290, 290, 16, 18).ToString(), root_damage_rect.ToString()); @@ -468,7 +458,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForTransformedLayer) { // Sanity check that the layer actually moved to (85, 85), damaging its old // location and new location. gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(85, 85, 45, 45).ToString(), root_damage_rect.ToString()); @@ -486,7 +476,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForTransformedLayer) { float expected_position = 100.f - 0.5f * expected_width; gfx::Rect expected_rect = gfx::ToEnclosingRect(gfx::RectF( expected_position, expected_position, expected_width, expected_width)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(expected_rect.ToString(), root_damage_rect.ToString()); } @@ -539,7 +529,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForPerspectiveClippedLayer) { // don't care whether the damage rect was clamped or is larger than the // surface for this test. gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); gfx::Rect damage_we_care_about = gfx::Rect(gfx::Size(500, 500)); EXPECT_TRUE(root_damage_rect.Contains(damage_we_care_about)); @@ -570,7 +560,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForBlurredSurface) { // relative to the child (300, 300), but expanded by the blur outsets // (15, since the blur radius is 5). gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(286, 287, 33, 34), root_damage_rect); } @@ -594,9 +584,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForImageFilter) { EmulateDrawingOneFrame(root); child->layer_tree_impl()->SetFilterMutated(child->element_id(), filters); EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); // gfx::Rect(100, 100, 30, 30), expanded by 6px for the 2px blur filter. @@ -610,9 +600,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForImageFilter) { child->SetUpdateRect(gfx::Rect(1, 1)); EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); // gfx::Rect(100, 100, 1, 1), expanded by 6px for the 2px blur filter. @@ -644,9 +634,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForTransformedImageFilter) { EmulateDrawingOneFrame(root); child->layer_tree_impl()->SetFilterMutated(child->element_id(), filters); EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); // Blur outset is 6px for a 2px blur. @@ -666,9 +656,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForTransformedImageFilter) { child->SetUpdateRect(gfx::Rect(30, 30)); EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); int expect_width = 30 + 2 * blur_outset; @@ -699,9 +689,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForHighDPIImageFilter) { EmulateDrawingOneFrame(root, device_scale_factor); child->layer_tree_impl()->SetFilterMutated(child->element_id(), filters); EmulateDrawingOneFrame(root, device_scale_factor); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); // Blur outset is 9px for a 3px blur, scaled up by DSF. @@ -722,9 +712,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForHighDPIImageFilter) { child->SetUpdateRect(gfx::Rect(30, 30)); EmulateDrawingOneFrame(root, device_scale_factor); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); EXPECT_EQ(expected_root_damage_rect, root_damage_rect); @@ -759,7 +749,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForBackgroundBlurredChild) { EmulateDrawingOneFrame(root); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); // Damage position on the surface should be a composition of the damage on // the root and on child2. Damage on the root should be: position of @@ -779,7 +769,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForBackgroundBlurredChild) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); // Damage position on the surface should be a composition of the damage on // the root and on child2. Damage on the root should be: position of @@ -797,7 +787,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForBackgroundBlurredChild) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); // Damage on the root should be: position of update_rect (30, 30), not // expanded. @@ -813,7 +803,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForBackgroundBlurredChild) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); // Damage on the root should be: the originally damaged rect (99,99 1x1) // plus the rect that can influence with a 2px blur (93,93 13x13) intersected @@ -830,7 +820,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForBackgroundBlurredChild) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); // Damage on child2 should be: position of update_rect offset by the child's // position (11, 11), and not expanded by anything. @@ -846,7 +836,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForBackgroundBlurredChild) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); // Damage on child1 should be: position of update_rect offset by the child's // position (100, 100), and expanded by the damage. @@ -879,10 +869,10 @@ TEST_F(DamageTrackerTest, VerifyDamageForAddingAndRemovingLayer) { // Sanity check - all 3 layers should be on the same render surface; render // surfaces are tested elsewhere. - ASSERT_EQ(3u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(3, GetRenderSurface(root)->num_contributors()); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(400, 380, 6, 8).ToString(), root_damage_rect.ToString()); @@ -895,7 +885,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForAddingAndRemovingLayer) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_TRUE(root_damage_rect.IsEmpty()); @@ -905,7 +895,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForAddingAndRemovingLayer) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(100, 100, 30, 30).ToString(), root_damage_rect.ToString()); @@ -939,10 +929,10 @@ TEST_F(DamageTrackerTest, VerifyDamageForNewUnchangedLayer) { // Sanity check - all 3 layers should be on the same render surface; render // surfaces are tested elsewhere. - ASSERT_EQ(3u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(3, GetRenderSurface(root)->num_contributors()); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(400, 380, 6, 8).ToString(), root_damage_rect.ToString()); } @@ -975,7 +965,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForMultipleLayers) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(100, 100, 303, 284).ToString(), root_damage_rect.ToString()); @@ -999,10 +989,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForNestedSurfaces) { ClearDamageForAllSurfaces(root); root->layer_tree_impl()->SetOpacityMutated(grand_child1->element_id(), 0.5f); EmulateDrawingOneFrame(root); - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(200, 200, 6, 8).ToString(), child_damage_rect.ToString()); EXPECT_EQ(gfx::Rect(300, 300, 6, 8).ToString(), root_damage_rect.ToString()); @@ -1017,10 +1006,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForNestedSurfaces) { root->layer_tree_impl()->SetOpacityMutated(grand_child1->element_id(), 0.7f); root->layer_tree_impl()->SetOpacityMutated(child2->element_id(), 0.7f); EmulateDrawingOneFrame(root); - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(200, 200, 6, 8).ToString(), child_damage_rect.ToString()); EXPECT_EQ(gfx::Rect(11, 11, 295, 297).ToString(), @@ -1046,10 +1034,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForSurfaceChangeFromDescendantLayer) { grand_child1->SetPosition(gfx::PointF(195.f, 205.f)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); // The new surface bounds should be damaged entirely, even though only one of @@ -1087,10 +1074,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForSurfaceChangeFromAncestorLayer) { root->layer_tree_impl()->SetTransformMutated(child1->element_id(), translation); EmulateDrawingOneFrame(root); - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); // The new surface bounds should be damaged entirely. @@ -1119,10 +1105,10 @@ TEST_F(DamageTrackerTest, VerifyDamageForAddingAndRemovingRenderSurfaces) { EmulateDrawingOneFrame(root); // Sanity check that there is only one surface now. - ASSERT_FALSE(child1->GetRenderSurface()); - ASSERT_EQ(4u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(GetRenderSurface(child1), GetRenderSurface(root)); + ASSERT_EQ(4, GetRenderSurface(root)->num_contributors()); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(290, 290, 16, 18).ToString(), root_damage_rect.ToString()); @@ -1135,7 +1121,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForAddingAndRemovingRenderSurfaces) { ClearDamageForAllSurfaces(root); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_TRUE(root_damage_rect.IsEmpty()); @@ -1147,14 +1133,13 @@ TEST_F(DamageTrackerTest, VerifyDamageForAddingAndRemovingRenderSurfaces) { EmulateDrawingOneFrame(root); // Sanity check that there is a new surface now. - ASSERT_TRUE(child1->GetRenderSurface()); - EXPECT_EQ(3u, root->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(2u, child1->GetRenderSurface()->layer_list().size()); + ASSERT_TRUE(GetRenderSurface(child1)); + EXPECT_EQ(3, GetRenderSurface(root)->num_contributors()); + EXPECT_EQ(2, GetRenderSurface(child1)->num_contributors()); - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(190, 190, 16, 18).ToString(), child_damage_rect.ToString()); @@ -1173,10 +1158,9 @@ TEST_F(DamageTrackerTest, VerifyNoDamageWhenNothingChanged) { ClearDamageForAllSurfaces(root); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_TRUE(child_damage_rect.IsEmpty()); EXPECT_TRUE(root_damage_rect.IsEmpty()); @@ -1187,10 +1171,9 @@ TEST_F(DamageTrackerTest, VerifyNoDamageWhenNothingChanged) { ClearDamageForAllSurfaces(root); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_TRUE(child_damage_rect.IsEmpty()); EXPECT_TRUE(root_damage_rect.IsEmpty()); @@ -1208,10 +1191,9 @@ TEST_F(DamageTrackerTest, VerifyNoDamageForUpdateRectThatDoesNotDrawContent) { child1->SetUpdateRect(gfx::Rect(1, 2)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &child_damage_rect)); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &child_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_TRUE(child_damage_rect.IsEmpty()); EXPECT_TRUE(root_damage_rect.IsEmpty()); @@ -1257,7 +1239,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForMask) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); gfx::Rect child_damage_rect; - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); EXPECT_EQ(gfx::Rect(30, 30).ToString(), child_damage_rect.ToString()); @@ -1269,7 +1251,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForMask) { ClearDamageForAllSurfaces(root); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); EXPECT_TRUE(child_damage_rect.IsEmpty()); @@ -1279,7 +1261,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForMask) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); EXPECT_EQ(gfx::Rect(30, 30).ToString(), child_damage_rect.ToString()); @@ -1291,7 +1273,7 @@ TEST_F(DamageTrackerTest, VerifyDamageForMask) { ClearDamageForAllSurfaces(root); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); EXPECT_TRUE(child_damage_rect.IsEmpty()); @@ -1304,9 +1286,9 @@ TEST_F(DamageTrackerTest, VerifyDamageForMask) { EmulateDrawingOneFrame(root); // Sanity check that a render surface still exists. - ASSERT_TRUE(child->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(child)); - EXPECT_TRUE(child->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(child)->damage_tracker()->GetDamageRectIfValid( &child_damage_rect)); EXPECT_EQ(gfx::Rect(30, 30).ToString(), child_damage_rect.ToString()); } @@ -1320,12 +1302,12 @@ TEST_F(DamageTrackerTest, DamageWhenAddedExternally) { // ClearDamageForAllSurfaces(root); child->SetUpdateRect(gfx::Rect(10, 11, 12, 13)); - root->GetRenderSurface()->damage_tracker()->AddDamageNextUpdate( + GetRenderSurface(root)->damage_tracker()->AddDamageNextUpdate( gfx::Rect(15, 16, 32, 33)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::UnionRects(gfx::Rect(15, 16, 32, 33), gfx::Rect(100 + 10, 100 + 11, 12, 13)).ToString(), @@ -1335,19 +1317,16 @@ TEST_F(DamageTrackerTest, DamageWhenAddedExternally) { // nothing on the layer tree changed. // ClearDamageForAllSurfaces(root); - root->GetRenderSurface()->damage_tracker()->AddDamageNextUpdate( + GetRenderSurface(root)->damage_tracker()->AddDamageNextUpdate( gfx::Rect(30, 31, 14, 15)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(30, 31, 14, 15).ToString(), root_damage_rect.ToString()); } -TEST_F(DamageTrackerTest, VerifyDamageForEmptyLayerList) { - // Though it should never happen, its a good idea to verify that the damage - // tracker does not crash when it receives an empty layer_list. - +TEST_F(DamageTrackerTest, VerifyDamageWithNoContributingLayers) { std::unique_ptr<LayerImpl> root = LayerImpl::Create(host_impl_.active_tree(), 1); root->test_properties()->force_render_surface = true; @@ -1356,13 +1335,8 @@ TEST_F(DamageTrackerTest, VerifyDamageForEmptyLayerList) { root_ptr->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root_ptr); - DCHECK_EQ(root_ptr->GetRenderSurface(), root_ptr->render_target()); - RenderSurfaceImpl* target_surface = root_ptr->GetRenderSurface(); - - LayerImplList empty_list; - target_surface->damage_tracker()->UpdateDamageTrackingState( - empty_list, target_surface, false, gfx::Rect(), NULL, FilterOperations()); - + DCHECK_EQ(GetRenderSurface(root_ptr), root_ptr->render_target()); + RenderSurfaceImpl* target_surface = GetRenderSurface(root_ptr); gfx::Rect damage_rect; EXPECT_TRUE( target_surface->damage_tracker()->GetDamageRectIfValid(&damage_rect)); @@ -1382,7 +1356,7 @@ TEST_F(DamageTrackerTest, VerifyDamageAccumulatesUntilReset) { // Sanity check damage after the first frame; this isnt the actual test yet. gfx::Rect root_damage_rect; - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(110, 111, 1, 2).ToString(), root_damage_rect.ToString()); @@ -1391,15 +1365,15 @@ TEST_F(DamageTrackerTest, VerifyDamageAccumulatesUntilReset) { child->SetUpdateRect(gfx::Rect(20, 25, 1, 2)); root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_EQ(gfx::Rect(110, 111, 11, 16).ToString(), root_damage_rect.ToString()); // If we notify the damage tracker that we drew the damaged area, then damage // should be emptied. - root->GetRenderSurface()->damage_tracker()->DidDrawDamagedArea(); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + GetRenderSurface(root)->damage_tracker()->DidDrawDamagedArea(); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_TRUE(root_damage_rect.IsEmpty()); @@ -1407,7 +1381,7 @@ TEST_F(DamageTrackerTest, VerifyDamageAccumulatesUntilReset) { // damage. root->layer_tree_impl()->property_trees()->needs_rebuild = true; EmulateDrawingOneFrame(root); - EXPECT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &root_damage_rect)); EXPECT_TRUE(root_damage_rect.IsEmpty()); } @@ -1441,9 +1415,8 @@ TEST_F(DamageTrackerTest, HugeDamageRect) { // The expected damage should cover the visible part of the child layer, // which is (0, 0, i, i) in the viewport. gfx::Rect root_damage_rect; - EXPECT_TRUE( - root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &root_damage_rect)); + EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( + &root_damage_rect)); gfx::Rect damage_we_care_about = gfx::Rect(i, i); EXPECT_LE(damage_we_care_about.right(), root_damage_rect.right()); EXPECT_LE(damage_we_care_about.bottom(), root_damage_rect.bottom()); @@ -1470,10 +1443,10 @@ TEST_F(DamageTrackerTest, DamageRectTooBig) { // The expected damage would be too large to store in a gfx::Rect, so we // should damage everything (ie, we don't have a valid rect). gfx::Rect damage_rect; - EXPECT_FALSE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_FALSE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &damage_rect)); - EXPECT_EQ(root->GetRenderSurface()->content_rect(), - root->GetRenderSurface()->GetDamageRect()); + EXPECT_EQ(GetRenderSurface(root)->content_rect(), + GetRenderSurface(root)->GetDamageRect()); } TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) { @@ -1501,10 +1474,10 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) { // The expected damage would be too large to store in a gfx::Rect, so we // should damage everything (ie, we don't have a valid rect). gfx::Rect damage_rect; - EXPECT_FALSE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + EXPECT_FALSE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &damage_rect)); - EXPECT_EQ(root->GetRenderSurface()->content_rect(), - root->GetRenderSurface()->GetDamageRect()); + EXPECT_EQ(GetRenderSurface(root)->content_rect(), + GetRenderSurface(root)->GetDamageRect()); } TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) { @@ -1527,36 +1500,31 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; float device_scale_factor = 1.f; - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; ExecuteCalculateDrawProperties(root, device_scale_factor, - &render_surface_layer_list); - - auto* surface = child1->GetRenderSurface(); - surface->damage_tracker()->UpdateDamageTrackingState( - surface->layer_list(), surface, false, surface->content_rect(), - surface->MaskLayer(), surface->Filters()); - surface = root->GetRenderSurface(); - surface->damage_tracker()->UpdateDamageTrackingState( - surface->layer_list(), surface, false, surface->content_rect(), - surface->MaskLayer(), surface->Filters()); + &render_surface_list); + // Avoid the descendant-only property change path that skips unioning damage + // from descendant layers. + GetRenderSurface(child1)->NoteAncestorPropertyChanged(); + DamageTracker::UpdateDamageTracking(host_impl_.active_tree(), + render_surface_list); // The expected damage would be too large to store in a gfx::Rect, so we // should damage everything on child1. gfx::Rect damage_rect; - EXPECT_FALSE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &damage_rect)); - EXPECT_EQ(child1->GetRenderSurface()->content_rect(), - child1->GetRenderSurface()->GetDamageRect()); + EXPECT_FALSE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &damage_rect)); + EXPECT_EQ(GetRenderSurface(child1)->content_rect(), + GetRenderSurface(child1)->GetDamageRect()); // However, the root should just use the child1 render surface's content rect // as damage. - ASSERT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + ASSERT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &damage_rect)); - EXPECT_TRUE(damage_rect.Contains(root->GetRenderSurface()->content_rect())); + EXPECT_TRUE(damage_rect.Contains(GetRenderSurface(root)->content_rect())); EXPECT_TRUE(damage_rect.Contains( - gfx::ToEnclosingRect(child1->GetRenderSurface()->DrawableContentRect()))); - EXPECT_EQ(damage_rect, root->GetRenderSurface()->GetDamageRect()); + gfx::ToEnclosingRect(GetRenderSurface(child1)->DrawableContentRect()))); + EXPECT_EQ(damage_rect, GetRenderSurface(root)->GetDamageRect()); // Add new damage, without changing properties, which goes down a different // path in the damage tracker. @@ -1565,34 +1533,27 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) { grandchild2->AddDamageRect(gfx::Rect(grandchild1->bounds())); // Recompute all damage / properties. - render_surface_layer_list.clear(); + render_surface_list.clear(); ExecuteCalculateDrawProperties(root, device_scale_factor, - &render_surface_layer_list); - surface = child1->GetRenderSurface(); - surface->damage_tracker()->UpdateDamageTrackingState( - surface->layer_list(), surface, false, surface->content_rect(), - surface->MaskLayer(), surface->Filters()); - surface = root->GetRenderSurface(); - surface->damage_tracker()->UpdateDamageTrackingState( - surface->layer_list(), surface, false, surface->content_rect(), - surface->MaskLayer(), surface->Filters()); + &render_surface_list); + DamageTracker::UpdateDamageTracking(host_impl_.active_tree(), + render_surface_list); // Child1 should still not have a valid rect, since the union of the damage of // its children is not representable by a single rect. - EXPECT_FALSE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &damage_rect)); - EXPECT_EQ(child1->GetRenderSurface()->content_rect(), - child1->GetRenderSurface()->GetDamageRect()); + EXPECT_FALSE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &damage_rect)); + EXPECT_EQ(GetRenderSurface(child1)->content_rect(), + GetRenderSurface(child1)->GetDamageRect()); // Root should have valid damage and contain both its content rect and the // drawable content rect of child1. - ASSERT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + ASSERT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &damage_rect)); - EXPECT_TRUE(damage_rect.Contains(root->GetRenderSurface()->content_rect())); + EXPECT_TRUE(damage_rect.Contains(GetRenderSurface(root)->content_rect())); EXPECT_TRUE(damage_rect.Contains( - gfx::ToEnclosingRect(child1->GetRenderSurface()->DrawableContentRect()))); - EXPECT_EQ(damage_rect, root->GetRenderSurface()->GetDamageRect()); + gfx::ToEnclosingRect(GetRenderSurface(child1)->DrawableContentRect()))); + EXPECT_EQ(damage_rect, GetRenderSurface(root)->GetDamageRect()); } TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) { @@ -1621,36 +1582,31 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; float device_scale_factor = 1.f; - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; ExecuteCalculateDrawProperties(root, device_scale_factor, - &render_surface_layer_list); - - auto* surface = child1->GetRenderSurface(); - surface->damage_tracker()->UpdateDamageTrackingState( - surface->layer_list(), surface, false, surface->content_rect(), - surface->MaskLayer(), surface->Filters()); - surface = root->GetRenderSurface(); - surface->damage_tracker()->UpdateDamageTrackingState( - surface->layer_list(), surface, false, surface->content_rect(), - surface->MaskLayer(), surface->Filters()); + &render_surface_list); + // Avoid the descendant-only property change path that skips unioning damage + // from descendant layers. + GetRenderSurface(child1)->NoteAncestorPropertyChanged(); + DamageTracker::UpdateDamageTracking(host_impl_.active_tree(), + render_surface_list); // The expected damage would be too large to store in a gfx::Rect, so we // should damage everything on child1. gfx::Rect damage_rect; - EXPECT_FALSE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &damage_rect)); - EXPECT_EQ(child1->GetRenderSurface()->content_rect(), - child1->GetRenderSurface()->GetDamageRect()); + EXPECT_FALSE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &damage_rect)); + EXPECT_EQ(GetRenderSurface(child1)->content_rect(), + GetRenderSurface(child1)->GetDamageRect()); // However, the root should just use the child1 render surface's content rect // as damage. - ASSERT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + ASSERT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &damage_rect)); - EXPECT_TRUE(damage_rect.Contains(root->GetRenderSurface()->content_rect())); + EXPECT_TRUE(damage_rect.Contains(GetRenderSurface(root)->content_rect())); EXPECT_TRUE(damage_rect.Contains( - gfx::ToEnclosingRect(child1->GetRenderSurface()->DrawableContentRect()))); - EXPECT_EQ(damage_rect, root->GetRenderSurface()->GetDamageRect()); + gfx::ToEnclosingRect(GetRenderSurface(child1)->DrawableContentRect()))); + EXPECT_EQ(damage_rect, GetRenderSurface(root)->GetDamageRect()); // Add new damage, without changing properties, which goes down a different // path in the damage tracker. @@ -1659,34 +1615,27 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) { grandchild2->AddDamageRect(gfx::Rect(grandchild1->bounds())); // Recompute all damage / properties. - render_surface_layer_list.clear(); + render_surface_list.clear(); ExecuteCalculateDrawProperties(root, device_scale_factor, - &render_surface_layer_list); - surface = child1->GetRenderSurface(); - surface->damage_tracker()->UpdateDamageTrackingState( - surface->layer_list(), surface, false, surface->content_rect(), - surface->MaskLayer(), surface->Filters()); - surface = root->GetRenderSurface(); - surface->damage_tracker()->UpdateDamageTrackingState( - surface->layer_list(), surface, false, surface->content_rect(), - surface->MaskLayer(), surface->Filters()); + &render_surface_list); + DamageTracker::UpdateDamageTracking(host_impl_.active_tree(), + render_surface_list); // Child1 should still not have a valid rect, since the union of the damage of // its children is not representable by a single rect. - EXPECT_FALSE( - child1->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( - &damage_rect)); - EXPECT_EQ(child1->GetRenderSurface()->content_rect(), - child1->GetRenderSurface()->GetDamageRect()); + EXPECT_FALSE(GetRenderSurface(child1)->damage_tracker()->GetDamageRectIfValid( + &damage_rect)); + EXPECT_EQ(GetRenderSurface(child1)->content_rect(), + GetRenderSurface(child1)->GetDamageRect()); // Root should have valid damage and contain both its content rect and the // drawable content rect of child1. - ASSERT_TRUE(root->GetRenderSurface()->damage_tracker()->GetDamageRectIfValid( + ASSERT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( &damage_rect)); - EXPECT_TRUE(damage_rect.Contains(root->GetRenderSurface()->content_rect())); + EXPECT_TRUE(damage_rect.Contains(GetRenderSurface(root)->content_rect())); EXPECT_TRUE(damage_rect.Contains( - gfx::ToEnclosingRect(child1->GetRenderSurface()->DrawableContentRect()))); - EXPECT_EQ(damage_rect, root->GetRenderSurface()->GetDamageRect()); + gfx::ToEnclosingRect(GetRenderSurface(child1)->DrawableContentRect()))); + EXPECT_EQ(damage_rect, GetRenderSurface(root)->GetDamageRect()); } } // namespace diff --git a/chromium/cc/trees/debug_rect_history.cc b/chromium/cc/trees/debug_rect_history.cc index 243c610214c..35c8db1073d 100644 --- a/chromium/cc/trees/debug_rect_history.cc +++ b/chromium/cc/trees/debug_rect_history.cc @@ -32,7 +32,7 @@ DebugRectHistory::~DebugRectHistory() {} void DebugRectHistory::SaveDebugRectsForCurrentFrame( LayerTreeImpl* tree_impl, LayerImpl* hud_layer, - const LayerImplList& render_surface_layer_list, + const RenderSurfaceList& render_surface_list, const LayerTreeDebugState& debug_state) { // For now, clear all rects from previous frames. In the future we may want to // store all debug rects for a history of many frames. @@ -54,13 +54,13 @@ void DebugRectHistory::SaveDebugRectsForCurrentFrame( SavePaintRects(tree_impl); if (debug_state.show_property_changed_rects) - SavePropertyChangedRects(render_surface_layer_list, hud_layer); + SavePropertyChangedRects(tree_impl, hud_layer); if (debug_state.show_surface_damage_rects) - SaveSurfaceDamageRects(render_surface_layer_list); + SaveSurfaceDamageRects(render_surface_list); if (debug_state.show_screen_space_rects) - SaveScreenSpaceRects(render_surface_layer_list); + SaveScreenSpaceRects(render_surface_list); if (debug_state.show_layer_animation_bounds_rects) SaveLayerAnimationBoundsRects(tree_impl); @@ -84,46 +84,27 @@ void DebugRectHistory::SavePaintRects(LayerTreeImpl* tree_impl) { } } -void DebugRectHistory::SavePropertyChangedRects( - const LayerImplList& render_surface_layer_list, - LayerImpl* hud_layer) { - for (size_t i = 0; i < render_surface_layer_list.size(); ++i) { - size_t surface_index = render_surface_layer_list.size() - 1 - i; - LayerImpl* render_surface_layer = render_surface_layer_list[surface_index]; - RenderSurfaceImpl* render_surface = - render_surface_layer->GetRenderSurface(); - DCHECK(render_surface); - - const LayerImplList& layer_list = render_surface->layer_list(); - for (unsigned layer_index = 0; layer_index < layer_list.size(); - ++layer_index) { - LayerImpl* layer = layer_list[layer_index]; - - if (layer->GetRenderSurface() && - layer->GetRenderSurface() != render_surface) - continue; - - if (layer == hud_layer) - continue; +void DebugRectHistory::SavePropertyChangedRects(LayerTreeImpl* tree_impl, + LayerImpl* hud_layer) { + for (LayerImpl* layer : *tree_impl) { + if (layer == hud_layer) + continue; - if (!layer->LayerPropertyChanged()) - continue; + if (!layer->LayerPropertyChanged()) + continue; - debug_rects_.push_back(DebugRect( - PROPERTY_CHANGED_RECT_TYPE, - MathUtil::MapEnclosingClippedRect(layer->ScreenSpaceTransform(), - gfx::Rect(layer->bounds())))); - } + debug_rects_.push_back(DebugRect( + PROPERTY_CHANGED_RECT_TYPE, + MathUtil::MapEnclosingClippedRect(layer->ScreenSpaceTransform(), + gfx::Rect(layer->bounds())))); } } void DebugRectHistory::SaveSurfaceDamageRects( - const LayerImplList& render_surface_layer_list) { - for (size_t i = 0; i < render_surface_layer_list.size(); ++i) { - size_t surface_index = render_surface_layer_list.size() - 1 - i; - LayerImpl* render_surface_layer = render_surface_layer_list[surface_index]; - RenderSurfaceImpl* render_surface = - render_surface_layer->GetRenderSurface(); + const RenderSurfaceList& render_surface_list) { + for (size_t i = 0; i < render_surface_list.size(); ++i) { + size_t surface_index = render_surface_list.size() - 1 - i; + RenderSurfaceImpl* render_surface = render_surface_list[surface_index]; DCHECK(render_surface); debug_rects_.push_back(DebugRect( @@ -134,12 +115,10 @@ void DebugRectHistory::SaveSurfaceDamageRects( } void DebugRectHistory::SaveScreenSpaceRects( - const LayerImplList& render_surface_layer_list) { - for (size_t i = 0; i < render_surface_layer_list.size(); ++i) { - size_t surface_index = render_surface_layer_list.size() - 1 - i; - LayerImpl* render_surface_layer = render_surface_layer_list[surface_index]; - RenderSurfaceImpl* render_surface = - render_surface_layer->GetRenderSurface(); + const RenderSurfaceList& render_surface_list) { + for (size_t i = 0; i < render_surface_list.size(); ++i) { + size_t surface_index = render_surface_list.size() - 1 - i; + RenderSurfaceImpl* render_surface = render_surface_list[surface_index]; DCHECK(render_surface); debug_rects_.push_back(DebugRect( @@ -218,7 +197,7 @@ void DebugRectHistory::SaveNonFastScrollableRectsCallback(LayerImpl* layer) { void DebugRectHistory::SaveLayerAnimationBoundsRects(LayerTreeImpl* tree_impl) { for (auto it = tree_impl->rbegin(); it != tree_impl->rend(); ++it) { - if (!(*it)->is_drawn_render_surface_layer_list_member()) + if (!(*it)->contributes_to_drawn_render_surface()) continue; // TODO(avallee): Figure out if we should show something for a layer who's diff --git a/chromium/cc/trees/debug_rect_history.h b/chromium/cc/trees/debug_rect_history.h index 183cddeb775..7728237eb54 100644 --- a/chromium/cc/trees/debug_rect_history.h +++ b/chromium/cc/trees/debug_rect_history.h @@ -67,7 +67,7 @@ class DebugRectHistory { void SaveDebugRectsForCurrentFrame( LayerTreeImpl* tree_impl, LayerImpl* hud_layer, - const LayerImplList& render_surface_layer_list, + const RenderSurfaceList& render_surface_list, const LayerTreeDebugState& debug_state); const std::vector<DebugRect>& debug_rects() { return debug_rects_; } @@ -76,10 +76,9 @@ class DebugRectHistory { DebugRectHistory(); void SavePaintRects(LayerTreeImpl* tree_impl); - void SavePropertyChangedRects(const LayerImplList& render_surface_layer_list, - LayerImpl* hud_layer); - void SaveSurfaceDamageRects(const LayerImplList& render_surface_layer_list); - void SaveScreenSpaceRects(const LayerImplList& render_surface_layer_list); + void SavePropertyChangedRects(LayerTreeImpl* tree_impl, LayerImpl* hud_layer); + void SaveSurfaceDamageRects(const RenderSurfaceList& render_surface_list); + void SaveScreenSpaceRects(const RenderSurfaceList& render_surface_list); void SaveTouchEventHandlerRects(LayerTreeImpl* layer); void SaveTouchEventHandlerRectsCallback(LayerImpl* layer); void SaveWheelEventHandlerRects(LayerTreeImpl* tree_impl); diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index 5af6686aa61..43120259e3d 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -458,14 +458,11 @@ static inline bool LayerShouldBeSkippedInternal( transform_tree.Node(layer->transform_tree_index()); const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index()); - if (effect_node->has_render_surface && - effect_node->num_copy_requests_in_subtree > 0) + if (effect_node->has_render_surface && effect_node->subtree_has_copy_request) return false; - // If the layer transform is not invertible, it should be skipped. - // TODO(ajuma): Correctly process subtrees with singular transform for the - // case where we may animate to a non-singular transform and wish to - // pre-raster. + // If the layer transform is not invertible, it should be skipped. In case the + // transform is animating and singular, we should not skip it. return !transform_node->node_and_ancestors_are_animated_or_invertible || effect_node->hidden_by_backface_visibility || !effect_node->is_drawn; } @@ -672,9 +669,10 @@ static void SetSurfaceDrawTransform(const PropertyTrees* property_trees, static gfx::Rect LayerVisibleRect(PropertyTrees* property_trees, LayerImpl* layer) { + const EffectNode* effect_node = + property_trees->effect_tree.Node(layer->effect_tree_index()); int effect_ancestor_with_copy_request = - property_trees->effect_tree.ClosestAncestorWithCopyRequest( - layer->effect_tree_index()); + effect_node->closest_ancestor_with_copy_request_id; bool non_root_copy_request = effect_ancestor_with_copy_request > EffectTree::kContentsRootNodeId; gfx::Rect layer_content_rect = gfx::Rect(layer->bounds()); @@ -722,27 +720,18 @@ static ConditionalClip LayerClipRect(PropertyTrees* property_trees, effect_node->has_render_surface ? effect_node : effect_tree->Node(effect_node->target_id); - // TODO(weiliangc): When effect node has up to date render surface info on - // compositor thread, no need to check for resourceless draw mode - if (!property_trees->non_root_surfaces_enabled) { - target_node = effect_tree->Node(1); - } - bool include_expanding_clips = false; return ComputeAccumulatedClip(property_trees, include_expanding_clips, layer->clip_tree_index(), target_node->id); } -static void UpdateRenderTarget(EffectTree* effect_tree, - bool can_render_to_separate_surface) { +static void UpdateRenderTarget(EffectTree* effect_tree) { for (int i = EffectTree::kContentsRootNodeId; i < static_cast<int>(effect_tree->size()); ++i) { EffectNode* node = effect_tree->Node(i); if (i == EffectTree::kContentsRootNodeId) { // Render target of the node corresponding to root is itself. node->target_id = EffectTree::kContentsRootNodeId; - } else if (!can_render_to_separate_surface) { - node->target_id = EffectTree::kContentsRootNodeId; } else if (effect_tree->parent(node)->has_render_surface) { node->target_id = node->parent_id; } else { @@ -809,15 +798,17 @@ void ConcatInverseSurfaceContentsScale(const EffectNode* effect_node, 1.0 / effect_node->surface_contents_scale.y()); } -bool LayerShouldBeSkipped(LayerImpl* layer, - const TransformTree& transform_tree, - const EffectTree& effect_tree) { +bool LayerShouldBeSkippedForDrawPropertiesComputation( + LayerImpl* layer, + const TransformTree& transform_tree, + const EffectTree& effect_tree) { return LayerShouldBeSkippedInternal(layer, transform_tree, effect_tree); } -bool LayerShouldBeSkipped(Layer* layer, - const TransformTree& transform_tree, - const EffectTree& effect_tree) { +bool LayerShouldBeSkippedForDrawPropertiesComputation( + Layer* layer, + const TransformTree& transform_tree, + const EffectTree& effect_tree) { return LayerShouldBeSkippedInternal(layer, transform_tree, effect_tree); } @@ -827,8 +818,8 @@ void FindLayersThatNeedUpdates(LayerTreeHost* layer_tree_host, const TransformTree& transform_tree = property_trees->transform_tree; const EffectTree& effect_tree = property_trees->effect_tree; for (auto* layer : *layer_tree_host) { - if (!IsRootLayer(layer) && - LayerShouldBeSkipped(layer, transform_tree, effect_tree)) + if (!IsRootLayer(layer) && LayerShouldBeSkippedForDrawPropertiesComputation( + layer, transform_tree, effect_tree)) continue; bool layer_is_drawn = @@ -852,16 +843,9 @@ void FindLayersThatNeedUpdates(LayerTreeImpl* layer_tree_impl, const EffectTree& effect_tree = property_trees->effect_tree; for (auto* layer_impl : *layer_tree_impl) { - DCHECK(layer_impl); - DCHECK(layer_impl->layer_tree_impl()); - // TODO(crbug.com/726423) : This is a workaround for crbug.com/725851 to - // avoid crashing when layer_impl is nullptr. This workaround should be - // removed as layer_impl should not be nullptr here. - if (!layer_impl || !layer_impl->HasValidPropertyTreeIndices()) - continue; - if (!IsRootLayer(layer_impl) && - LayerShouldBeSkipped(layer_impl, transform_tree, effect_tree)) + LayerShouldBeSkippedForDrawPropertiesComputation( + layer_impl, transform_tree, effect_tree)) continue; bool layer_is_drawn = @@ -891,16 +875,10 @@ void ComputeEffects(EffectTree* effect_tree) { } void UpdatePropertyTrees(LayerTreeHost* layer_tree_host, - PropertyTrees* property_trees, - bool can_render_to_separate_surface) { + PropertyTrees* property_trees) { DCHECK(layer_tree_host); DCHECK(property_trees); DCHECK_EQ(layer_tree_host->property_trees(), property_trees); - if (property_trees->non_root_surfaces_enabled != - can_render_to_separate_surface) { - property_trees->non_root_surfaces_enabled = can_render_to_separate_surface; - property_trees->transform_tree.set_needs_update(true); - } if (property_trees->transform_tree.needs_update()) { property_trees->clip_tree.set_needs_update(true); property_trees->effect_tree.set_needs_update(true); @@ -915,15 +893,8 @@ void UpdatePropertyTrees(LayerTreeHost* layer_tree_host, void UpdatePropertyTreesAndRenderSurfaces(LayerImpl* root_layer, PropertyTrees* property_trees, - bool can_render_to_separate_surface, bool can_adjust_raster_scales) { bool render_surfaces_need_update = false; - if (property_trees->non_root_surfaces_enabled != - can_render_to_separate_surface) { - property_trees->non_root_surfaces_enabled = can_render_to_separate_surface; - property_trees->transform_tree.set_needs_update(true); - render_surfaces_need_update = true; - } if (property_trees->can_adjust_raster_scales != can_adjust_raster_scales) { property_trees->can_adjust_raster_scales = can_adjust_raster_scales; property_trees->transform_tree.set_needs_update(true); @@ -935,11 +906,9 @@ void UpdatePropertyTreesAndRenderSurfaces(LayerImpl* root_layer, } if (render_surfaces_need_update) { property_trees->effect_tree.UpdateRenderSurfaces( - root_layer->layer_tree_impl(), - property_trees->non_root_surfaces_enabled); + root_layer->layer_tree_impl()); } - UpdateRenderTarget(&property_trees->effect_tree, - property_trees->non_root_surfaces_enabled); + UpdateRenderTarget(&property_trees->effect_tree); ComputeTransforms(&property_trees->transform_tree); ComputeEffects(&property_trees->effect_tree); @@ -967,12 +936,9 @@ gfx::Transform DrawTransform(const LayerImpl* layer, // node and surface's transform node and scales it by the surface's content // scale. gfx::Transform xform; - if (transform_tree.property_trees()->non_root_surfaces_enabled) - transform_tree.property_trees()->GetToTarget( - layer->transform_tree_index(), layer->render_target_effect_tree_index(), - &xform); - else - xform = transform_tree.ToScreen(layer->transform_tree_index()); + transform_tree.property_trees()->GetToTarget( + layer->transform_tree_index(), layer->render_target_effect_tree_index(), + &xform); if (layer->should_flatten_transform_from_property_tree()) xform.FlattenTo2d(); xform.Translate(layer->offset_to_transform_parent().x(), @@ -1060,8 +1026,7 @@ void ComputeMaskDrawProperties(LayerImpl* mask_layer, } void ComputeSurfaceDrawProperties(PropertyTrees* property_trees, - RenderSurfaceImpl* render_surface, - const bool use_layer_lists) { + RenderSurfaceImpl* render_surface) { SetSurfaceIsClipped(property_trees->clip_tree, render_surface); SetSurfaceDrawOpacity(property_trees->effect_tree, render_surface); SetSurfaceDrawTransform(property_trees, render_surface); diff --git a/chromium/cc/trees/draw_property_utils.h b/chromium/cc/trees/draw_property_utils.h index b5ff981c579..45ed01f6e88 100644 --- a/chromium/cc/trees/draw_property_utils.h +++ b/chromium/cc/trees/draw_property_utils.h @@ -37,15 +37,12 @@ void CC_EXPORT ComputeTransforms(TransformTree* transform_tree); // Computes screen space opacity for every node in the opacity tree. void CC_EXPORT ComputeEffects(EffectTree* effect_tree); - void CC_EXPORT UpdatePropertyTrees(LayerTreeHost* layer_tree_host, - PropertyTrees* property_trees, - bool can_render_to_separate_surface); + PropertyTrees* property_trees); void CC_EXPORT UpdatePropertyTreesAndRenderSurfaces(LayerImpl* root_layer, PropertyTrees* property_trees, - bool can_render_to_separate_surface, bool can_adjust_raster_scales); void CC_EXPORT FindLayersThatNeedUpdates(LayerTreeHost* layer_tree_host, @@ -65,12 +62,12 @@ void CC_EXPORT ComputeMaskDrawProperties(LayerImpl* mask_layer, const PropertyTrees* property_trees); void CC_EXPORT ComputeSurfaceDrawProperties(PropertyTrees* property_trees, - RenderSurfaceImpl* render_surface, - const bool use_layer_lists); + RenderSurfaceImpl* render_surface); -bool CC_EXPORT LayerShouldBeSkipped(LayerImpl* layer, - const TransformTree& transform_tree, - const EffectTree& effect_tree); +bool CC_EXPORT LayerShouldBeSkippedForDrawPropertiesComputation( + LayerImpl* layer, + const TransformTree& transform_tree, + const EffectTree& effect_tree); bool CC_EXPORT LayerNeedsUpdate(Layer* layer, bool layer_is_drawn, diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc index 142cb08db41..7a556277530 100644 --- a/chromium/cc/trees/effect_node.cc +++ b/chromium/cc/trees/effect_node.cc @@ -27,11 +27,12 @@ EffectNode::EffectNode() is_currently_animating_filter(false), is_currently_animating_opacity(false), effect_changed(false), - num_copy_requests_in_subtree(0), + subtree_has_copy_request(false), transform_id(0), clip_id(0), target_id(1), - mask_layer_id(-1) {} + mask_layer_id(Layer::INVALID_ID), + closest_ancestor_with_copy_request_id(-1) {} EffectNode::EffectNode(const EffectNode& other) = default; @@ -58,9 +59,11 @@ bool EffectNode::operator==(const EffectNode& other) const { is_currently_animating_opacity == other.is_currently_animating_opacity && effect_changed == other.effect_changed && - num_copy_requests_in_subtree == other.num_copy_requests_in_subtree && + subtree_has_copy_request == other.subtree_has_copy_request && transform_id == other.transform_id && clip_id == other.clip_id && - target_id == other.target_id && mask_layer_id == other.mask_layer_id; + target_id == other.target_id && mask_layer_id == other.mask_layer_id && + closest_ancestor_with_copy_request_id == + other.closest_ancestor_with_copy_request_id; } void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { @@ -77,12 +80,13 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { value->SetBoolean("has_potential_opacity_animation", has_potential_opacity_animation); value->SetBoolean("effect_changed", effect_changed); - value->SetInteger("num_copy_requests_in_subtree", - num_copy_requests_in_subtree); + value->SetInteger("subtree_has_copy_request", subtree_has_copy_request); value->SetInteger("transform_id", transform_id); value->SetInteger("clip_id", clip_id); value->SetInteger("target_id", target_id); value->SetInteger("mask_layer_id", mask_layer_id); + value->SetInteger("closest_ancestor_with_copy_request_id", + closest_ancestor_with_copy_request_id); } } // namespace cc diff --git a/chromium/cc/trees/effect_node.h b/chromium/cc/trees/effect_node.h index 48997c03823..8079f898283 100644 --- a/chromium/cc/trees/effect_node.h +++ b/chromium/cc/trees/effect_node.h @@ -43,27 +43,42 @@ struct CC_EXPORT EffectNode { gfx::Size unscaled_mask_target_size; - bool has_render_surface; - bool has_copy_request; - bool hidden_by_backface_visibility; - bool double_sided; - bool is_drawn; + bool has_render_surface : 1; + bool has_copy_request : 1; + bool hidden_by_backface_visibility : 1; + bool double_sided : 1; + bool is_drawn : 1; // TODO(jaydasika) : Delete this after implementation of // SetHideLayerAndSubtree is cleaned up. (crbug.com/595843) - bool subtree_hidden; - bool has_potential_filter_animation; - bool has_potential_opacity_animation; - bool is_currently_animating_filter; - bool is_currently_animating_opacity; - // We need to track changes to effects on the compositor to compute damage - // rect. - bool effect_changed; - int num_copy_requests_in_subtree; + bool subtree_hidden : 1; + // Whether this node has a potentially running (i.e., irrespective + // of exact timeline) filter animation. + bool has_potential_filter_animation : 1; + // Whether this node has a potentially running (i.e., irrespective + // of exact timeline) opacity animation. + bool has_potential_opacity_animation : 1; + // Whether this node has a currently running filter animation. + bool is_currently_animating_filter : 1; + // Whether this node has a currently running opacity animation. + bool is_currently_animating_opacity : 1; + // Whether this node's effect has been changed since the last + // frame. Needed in order to compute damage rect. + bool effect_changed : 1; + bool subtree_has_copy_request : 1; + // The transform node index of the transform to apply to this effect + // node's content when rendering to a surface. int transform_id; + // The clip node index of the clip to apply to this effect node's + // content when rendering to a surface. int clip_id; - // Effect node id of which this effect contributes to. + + // This is the id of the ancestor effect node that induces a + // RenderSurfaceImpl. int target_id; + // The layer id of the mask layer, if any, to apply to this effect + // node's content when rendering to a surface. int mask_layer_id; + int closest_ancestor_with_copy_request_id; bool operator==(const EffectNode& other) const; diff --git a/chromium/cc/trees/element_id.cc b/chromium/cc/trees/element_id.cc index 45913303c10..6e32b660aa5 100644 --- a/chromium/cc/trees/element_id.cc +++ b/chromium/cc/trees/element_id.cc @@ -13,7 +13,7 @@ namespace cc { bool ElementId::operator==(const ElementId& o) const { - return primaryId == o.primaryId && secondaryId == o.secondaryId; + return id_ == o.id_; } bool ElementId::operator!=(const ElementId& o) const { @@ -21,36 +21,33 @@ bool ElementId::operator!=(const ElementId& o) const { } bool ElementId::operator<(const ElementId& o) const { - return std::tie(primaryId, secondaryId) < - std::tie(o.primaryId, o.secondaryId); + return id_ < o.id_; } ElementId::operator bool() const { - return !!primaryId; + return !!id_; } ElementId LayerIdToElementIdForTesting(int layer_id) { - return ElementId(std::numeric_limits<int>::max() - layer_id, 0); + return ElementId(std::numeric_limits<int>::max() - layer_id); } void ElementId::AddToTracedValue(base::trace_event::TracedValue* res) const { - res->SetInteger("primaryId", primaryId); - res->SetInteger("secondaryId", secondaryId); + res->SetInteger("id_", id_); } std::unique_ptr<base::Value> ElementId::AsValue() const { std::unique_ptr<base::DictionaryValue> res(new base::DictionaryValue()); - res->SetInteger("primaryId", primaryId); - res->SetInteger("secondaryId", secondaryId); + res->SetInteger("id_", id_); return std::move(res); } size_t ElementIdHash::operator()(ElementId key) const { - return base::HashInts(key.primaryId, key.secondaryId); + return std::hash<int>()(key.id_); } std::ostream& operator<<(std::ostream& out, const ElementId& id) { - return out << "(" << id.primaryId << ", " << id.secondaryId << ")"; + return out << "(" << id.id_ << ")"; } } // namespace cc diff --git a/chromium/cc/trees/element_id.h b/chromium/cc/trees/element_id.h index 74de4c9995f..059cf2b36d7 100644 --- a/chromium/cc/trees/element_id.h +++ b/chromium/cc/trees/element_id.h @@ -24,20 +24,32 @@ class TracedValue; namespace cc { -// An "element" is really an animation target. It retains the name element to be -// symmetric with ElementAnimations and blink::ElementAnimations, but is not -// in fact tied to the notion of a blink element. It is also not associated with -// the notion of a Layer. Ultimately, these ids will be used to look up the -// property tree node associated with the given animation. +using ElementIdType = uint64_t; + +static const ElementIdType kInvalidElementId = 0; + +// Element ids are chosen by cc's clients and can be used as a stable identifier +// across updates. +// +// Historically, the layer tree stored all compositing data but this has been +// refactored over time into auxilliary structures such as property trees. +// +// In composited scrolling, Layers directly reference scroll tree nodes +// (Layer::scroll_tree_index) but scroll tree nodes are being refactored to +// reference stable element ids instead of layers. Scroll property nodes have +// unique element ids that blink creates from scrollable areas (though this is +// opaque to the compositor). This refactoring of scroll nodes keeping a +// scrolling element id instead of a scrolling layer id allows for more general +// compositing where, for example, multiple layers scroll with one scroll node. // -// These ids are chosen by cc's clients to permit the destruction and -// restoration of cc entities (when visuals are hidden and shown) but maintain -// stable identifiers. There will be a single layer for an ElementId, but -// not every layer will have an id. +// The animation system (see: ElementAnimations and blink::ElementAnimations) is +// another auxilliary structure to the layer tree and uses element ids as a +// stable identifier for animation targets. A Layer's element id can change over +// the Layer's lifetime because non-default ElementIds are only set during an +// animation's lifetime. struct CC_EXPORT ElementId { - ElementId(int primaryId, int secondaryId) - : primaryId(primaryId), secondaryId(secondaryId) {} - ElementId() : ElementId(0, 0) {} + explicit ElementId(int id) : id_(id) {} + ElementId() : ElementId(kInvalidElementId) {} bool operator==(const ElementId& o) const; bool operator!=(const ElementId& o) const; @@ -52,11 +64,10 @@ struct CC_EXPORT ElementId { // The compositor treats this as an opaque handle and should not know how to // interpret these bits. Non-blink cc clients typically operate in terms of // layers and may set this value to match the client's layer id. - int primaryId; - int secondaryId; + ElementIdType id_; }; -CC_EXPORT ElementId LayerIdToElementIdForTesting(int layer_id); +ElementId CC_EXPORT LayerIdToElementIdForTesting(int layer_id); struct CC_EXPORT ElementIdHash { size_t operator()(ElementId key) const; diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index e8a6f4b0c07..8db96130c74 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -46,7 +46,6 @@ #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_host_impl.h" -#include "cc/trees/layer_tree_impl.h" #include "cc/trees/mutator_host.h" #include "cc/trees/property_tree_builder.h" #include "cc/trees/proxy_main.h" @@ -108,8 +107,6 @@ LayerTreeHost::LayerTreeHost(InitParams* params, CompositorMode mode) mutator_host_(params->mutator_host) { DCHECK(task_graph_runner_); DCHECK(!settings_.enable_checker_imaging || image_worker_task_runner_); - DCHECK(!settings_.enable_checker_imaging || - settings_.image_decode_tasks_enabled); mutator_host_->SetMutatorHostClient(this); @@ -168,14 +165,14 @@ void LayerTreeHost::InitializeProxy(std::unique_ptr<Proxy> proxy) { LayerTreeHost::~LayerTreeHost() { // Track when we're inside a main frame to see if compositor is being // destroyed midway which causes a crash. crbug.com/654672 - CHECK(!inside_main_frame_); + DCHECK(!inside_main_frame_); TRACE_EVENT0("cc", "LayerTreeHostInProcess::~LayerTreeHostInProcess"); // Clear any references into the LayerTreeHost. mutator_host_->SetMutatorHostClient(nullptr); // We must clear any pointers into the layer tree prior to destroying it. - RegisterViewportLayers(nullptr, nullptr, nullptr, nullptr); + RegisterViewportLayers(ViewportLayers()); if (root_layer_) { root_layer_->SetLayerTreeHost(nullptr); @@ -249,6 +246,10 @@ void LayerTreeHost::BeginMainFrameNotExpectedSoon() { client_->BeginMainFrameNotExpectedSoon(); } +void LayerTreeHost::BeginMainFrameNotExpectedUntil(base::TimeTicks time) { + client_->BeginMainFrameNotExpectedUntil(time); +} + void LayerTreeHost::BeginMainFrame(const BeginFrameArgs& args) { client_->BeginMainFrame(args); } @@ -288,6 +289,7 @@ void LayerTreeHost::FinishCommitOnImplThread( } LayerTreeImpl* sync_tree = host_impl->sync_tree(); + sync_tree->lifecycle().AdvanceTo(LayerTreeLifecycle::kBeginningSync); if (next_commit_forces_redraw_) { sync_tree->ForceRedrawNextActivation(); @@ -303,34 +305,37 @@ void LayerTreeHost::FinishCommitOnImplThread( if (needs_full_tree_sync_) TreeSynchronizer::SynchronizeTrees(root_layer(), sync_tree); - PushPropertiesTo(sync_tree); + // Track the navigation state before pushing properties since it overwrites + // the |content_source_id_| on the sync tree. + bool did_navigate = content_source_id_ != sync_tree->content_source_id(); + if (did_navigate) + host_impl->ClearImageCacheOnNavigation(); - sync_tree->PassSwapPromises(swap_promise_manager_.TakeSwapPromises()); + { + TRACE_EVENT0("cc", "LayerTreeHostInProcess::PushProperties"); - host_impl->SetHasGpuRasterizationTrigger(has_gpu_rasterization_trigger_); - host_impl->SetContentIsSuitableForGpuRasterization( - content_is_suitable_for_gpu_rasterization_); - RecordGpuRasterizationHistogram(host_impl); + PushPropertyTreesTo(sync_tree); + sync_tree->lifecycle().AdvanceTo(LayerTreeLifecycle::kSyncedPropertyTrees); - host_impl->SetViewportSize(device_viewport_size_); - sync_tree->SetDeviceScaleFactor(device_scale_factor_); - host_impl->SetDebugState(debug_state_); + TreeSynchronizer::PushLayerProperties(this, sync_tree); + sync_tree->lifecycle().AdvanceTo( + LayerTreeLifecycle::kSyncedLayerProperties); - if (did_navigate_) { - did_navigate_ = false; - host_impl->ClearImageCacheOnNavigation(); - } + PushLayerTreePropertiesTo(sync_tree); + PushLayerTreeHostPropertiesTo(host_impl); - sync_tree->set_ui_resource_request_queue( - ui_resource_manager_->TakeUIResourcesRequests()); + sync_tree->PassSwapPromises(swap_promise_manager_.TakeSwapPromises()); - { - TRACE_EVENT0("cc", "LayerTreeHostInProcess::PushProperties"); + // TODO(pdr): Move this into PushPropertyTreesTo or introduce a lifecycle + // state for it. + sync_tree->SetDeviceScaleFactor(device_scale_factor_); - TreeSynchronizer::PushLayerProperties(this, sync_tree); + sync_tree->set_ui_resource_request_queue( + ui_resource_manager_->TakeUIResourcesRequests()); // This must happen after synchronizing property trees and after pushing // properties, which updates the clobber_active_value flag. + // TODO(pdr): Enforce this comment with DCHECKS and a lifecycle state. sync_tree->property_trees()->scroll_tree.PushScrollUpdatesFromMainThread( property_trees(), sync_tree); @@ -339,11 +344,16 @@ void LayerTreeHost::FinishCommitOnImplThread( // host pushes properties as animation host push properties can change // Animation::InEffect and we want the old InEffect value for updating // property tree scrolling and animation. - sync_tree->UpdatePropertyTreeScrollingAndAnimationFromMainThread(); + // TODO(pdr): Enforce this comment with DCHECKS and a lifecycle state. + bool is_impl_side_update = false; + sync_tree->UpdatePropertyTreeScrollingAndAnimationFromMainThread( + is_impl_side_update); TRACE_EVENT0("cc", "LayerTreeHostInProcess::AnimationHost::PushProperties"); DCHECK(host_impl->mutator_host()); mutator_host_->PushPropertiesTo(host_impl->mutator_host()); + + sync_tree->lifecycle().AdvanceTo(LayerTreeLifecycle::kNotSyncing); } // Transfer image decode requests to the impl thread. @@ -355,6 +365,23 @@ void LayerTreeHost::FinishCommitOnImplThread( property_trees_.ResetAllChangeTracking(); } +void LayerTreeHost::PushPropertyTreesTo(LayerTreeImpl* tree_impl) { + bool property_trees_changed_on_active_tree = + tree_impl->IsActiveTree() && tree_impl->property_trees()->changed; + // Property trees may store damage status. We preserve the sync tree damage + // status by pushing the damage status from sync tree property trees to main + // thread property trees or by moving it onto the layers. + if (root_layer_ && property_trees_changed_on_active_tree) { + if (property_trees_.sequence_number == + tree_impl->property_trees()->sequence_number) + tree_impl->property_trees()->PushChangeTrackingTo(&property_trees_); + else + tree_impl->MoveChangeTrackingToLayers(); + } + + tree_impl->SetPropertyTrees(&property_trees_); +} + void LayerTreeHost::WillCommit() { swap_promise_manager_.WillCommit(); client_->WillCommit(); @@ -652,7 +679,7 @@ bool LayerTreeHost::DoUpdateLayers(Layer* root_layer) { Layer* root_scroll = PropertyTreeBuilder::FindFirstScrollableLayer(root_layer); - Layer* page_scale_layer = page_scale_layer_.get(); + Layer* page_scale_layer = viewport_layers_.page_scale.get(); if (!page_scale_layer && root_scroll) page_scale_layer = root_scroll->parent(); @@ -682,7 +709,6 @@ bool LayerTreeHost::DoUpdateLayers(Layer* root_layer) { TRACE_EVENT0( TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), "LayerTreeHostInProcessCommon::ComputeVisibleRectsWithPropertyTrees"); - bool can_render_to_separate_surface = true; PropertyTrees* property_trees = &property_trees_; if (!settings_.use_layer_lists) { // If use_layer_lists is set, then the property trees should have been @@ -702,15 +728,11 @@ bool LayerTreeHost::DoUpdateLayers(Layer* root_layer) { TRACE_EVENT_SCOPE_THREAD, "property_trees", property_trees->AsTracedValue()); } - draw_property_utils::UpdatePropertyTrees(this, property_trees, - can_render_to_separate_surface); + draw_property_utils::UpdatePropertyTrees(this, property_trees); draw_property_utils::FindLayersThatNeedUpdates(this, property_trees, &update_layer_list); } - for (const auto& layer : update_layer_list) - layer->SavePaintProperties(); - bool content_is_suitable_for_gpu = true; bool did_paint_content = PaintContent(update_layer_list, &content_is_suitable_for_gpu); @@ -740,10 +762,10 @@ void LayerTreeHost::ApplyViewportDeltas(ScrollAndScaleSet* info) { // Preemptively apply the scroll offset and scale delta here before sending // it to the client. If the client comes back and sets it to the same // value, then the layer can early out without needing a full commit. - if (inner_viewport_scroll_layer_) { - inner_viewport_scroll_layer_->SetScrollOffsetFromImplSide( + if (viewport_layers_.inner_viewport_scroll) { + viewport_layers_.inner_viewport_scroll->SetScrollOffsetFromImplSide( gfx::ScrollOffsetWithDelta( - inner_viewport_scroll_layer_->scroll_offset(), + viewport_layers_.inner_viewport_scroll->scroll_offset(), inner_viewport_scroll_delta)); } @@ -788,7 +810,7 @@ void LayerTreeHost::ApplyScrollAndScale(ScrollAndScaleSet* info) { SetNeedsUpdateLayers(); } for (size_t i = 0; i < info->scrollbars.size(); ++i) { - Layer* layer = LayerById(info->scrollbars[i].layer_id); + Layer* layer = LayerByElementId(info->scrollbars[i].element_id); if (!layer) continue; layer->SetScrollbarsHiddenFromImplSide(info->scrollbars[i].hidden); @@ -878,20 +900,21 @@ void LayerTreeHost::SetRootLayer(scoped_refptr<Layer> root_layer) { ResetGpuRasterizationTracking(); SetNeedsFullTreeSync(); - did_navigate_ = true; } -void LayerTreeHost::RegisterViewportLayers( - scoped_refptr<Layer> overscroll_elasticity_layer, - scoped_refptr<Layer> page_scale_layer, - scoped_refptr<Layer> inner_viewport_scroll_layer, - scoped_refptr<Layer> outer_viewport_scroll_layer) { - DCHECK(!inner_viewport_scroll_layer || - inner_viewport_scroll_layer != outer_viewport_scroll_layer); - overscroll_elasticity_layer_ = overscroll_elasticity_layer; - page_scale_layer_ = page_scale_layer; - inner_viewport_scroll_layer_ = inner_viewport_scroll_layer; - outer_viewport_scroll_layer_ = outer_viewport_scroll_layer; +LayerTreeHost::ViewportLayers::ViewportLayers() {} + +LayerTreeHost::ViewportLayers::~ViewportLayers() {} + +void LayerTreeHost::RegisterViewportLayers(const ViewportLayers& layers) { + DCHECK(!layers.inner_viewport_scroll || + layers.inner_viewport_scroll != layers.outer_viewport_scroll); + viewport_layers_.overscroll_elasticity = layers.overscroll_elasticity; + viewport_layers_.page_scale = layers.page_scale; + viewport_layers_.inner_viewport_container = layers.inner_viewport_container; + viewport_layers_.outer_viewport_container = layers.outer_viewport_container; + viewport_layers_.inner_viewport_scroll = layers.inner_viewport_scroll; + viewport_layers_.outer_viewport_scroll = layers.outer_viewport_scroll; } void LayerTreeHost::RegisterSelection(const LayerSelection& selection) { @@ -1123,7 +1146,7 @@ void LayerTreeHost::SetPropertyTreesNeedRebuild() { SetNeedsUpdateLayers(); } -void LayerTreeHost::PushPropertiesTo(LayerTreeImpl* tree_impl) { +void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) { tree_impl->set_needs_full_tree_sync(needs_full_tree_sync_); needs_full_tree_sync_ = false; @@ -1147,35 +1170,29 @@ void LayerTreeHost::PushPropertiesTo(LayerTreeImpl* tree_impl) { EventListenerClass::kTouchEndOrCancel, event_listener_properties(EventListenerClass::kTouchEndOrCancel)); - if (page_scale_layer_ && inner_viewport_scroll_layer_) { - tree_impl->SetViewportLayersFromIds( - overscroll_elasticity_layer_ ? overscroll_elasticity_layer_->id() - : Layer::INVALID_ID, - page_scale_layer_->id(), inner_viewport_scroll_layer_->id(), - outer_viewport_scroll_layer_ ? outer_viewport_scroll_layer_->id() - : Layer::INVALID_ID); - DCHECK(inner_viewport_scroll_layer_->IsContainerForFixedPositionLayers()); + if (viewport_layers_.page_scale && viewport_layers_.inner_viewport_scroll) { + LayerTreeImpl::ViewportLayerIds ids; + if (viewport_layers_.overscroll_elasticity) + ids.overscroll_elasticity = viewport_layers_.overscroll_elasticity->id(); + ids.page_scale = viewport_layers_.page_scale->id(); + if (viewport_layers_.inner_viewport_container) + ids.inner_viewport_container = + viewport_layers_.inner_viewport_container->id(); + if (viewport_layers_.outer_viewport_container) + ids.outer_viewport_container = + viewport_layers_.outer_viewport_container->id(); + ids.inner_viewport_scroll = viewport_layers_.inner_viewport_scroll->id(); + if (viewport_layers_.outer_viewport_scroll) + ids.outer_viewport_scroll = viewport_layers_.outer_viewport_scroll->id(); + tree_impl->SetViewportLayersFromIds(ids); + DCHECK(viewport_layers_.inner_viewport_scroll + ->IsContainerForFixedPositionLayers()); } else { tree_impl->ClearViewportLayers(); } tree_impl->RegisterSelection(selection_); - bool property_trees_changed_on_active_tree = - tree_impl->IsActiveTree() && tree_impl->property_trees()->changed; - // Property trees may store damage status. We preserve the sync tree damage - // status by pushing the damage status from sync tree property trees to main - // thread property trees or by moving it onto the layers. - if (root_layer_ && property_trees_changed_on_active_tree) { - if (property_trees_.sequence_number == - tree_impl->property_trees()->sequence_number) - tree_impl->property_trees()->PushChangeTrackingTo(&property_trees_); - else - tree_impl->MoveChangeTrackingToLayers(); - } - // Setting property trees must happen before pushing the page scale. - tree_impl->SetPropertyTrees(&property_trees_); - tree_impl->PushPageScaleFromMainThread( page_scale_factor_, min_page_scale_factor_, max_page_scale_factor_); @@ -1206,6 +1223,17 @@ void LayerTreeHost::PushPropertiesTo(LayerTreeImpl* tree_impl) { tree_impl->set_has_ever_been_drawn(false); } +void LayerTreeHost::PushLayerTreeHostPropertiesTo( + LayerTreeHostImpl* host_impl) { + host_impl->SetHasGpuRasterizationTrigger(has_gpu_rasterization_trigger_); + host_impl->SetContentIsSuitableForGpuRasterization( + content_is_suitable_for_gpu_rasterization_); + RecordGpuRasterizationHistogram(host_impl); + + host_impl->SetViewportSize(device_viewport_size_); + host_impl->SetDebugState(debug_state_); +} + Layer* LayerTreeHost::LayerByElementId(ElementId element_id) const { auto iter = element_layers_map_.find(element_id); return iter != element_layers_map_.end() ? iter->second : nullptr; @@ -1264,6 +1292,13 @@ void LayerTreeHost::SetMutatorsNeedRebuildPropertyTrees() { void LayerTreeHost::SetElementFilterMutated(ElementId element_id, ElementListType list_type, const FilterOperations& filters) { + if (settings_.use_layer_lists) { + // In SPv2 we always have property trees and can set the filter + // directly on the effect node. + property_trees_.effect_tree.OnFilterAnimated(element_id, filters); + return; + } + Layer* layer = LayerByElementId(element_id); DCHECK(layer); layer->OnFilterAnimated(filters); @@ -1272,10 +1307,16 @@ void LayerTreeHost::SetElementFilterMutated(ElementId element_id, void LayerTreeHost::SetElementOpacityMutated(ElementId element_id, ElementListType list_type, float opacity) { - Layer* layer = LayerByElementId(element_id); - DCHECK(layer); DCHECK_GE(opacity, 0.f); DCHECK_LE(opacity, 1.f); + + if (settings_.use_layer_lists) { + property_trees_.effect_tree.OnOpacityAnimated(element_id, opacity); + return; + } + + Layer* layer = LayerByElementId(element_id); + DCHECK(layer); layer->OnOpacityAnimated(opacity); if (EffectNode* node = @@ -1296,6 +1337,11 @@ void LayerTreeHost::SetElementTransformMutated( ElementId element_id, ElementListType list_type, const gfx::Transform& transform) { + if (settings_.use_layer_lists) { + property_trees_.transform_tree.OnTransformAnimated(element_id, transform); + return; + } + Layer* layer = LayerByElementId(element_id); DCHECK(layer); layer->OnTransformAnimated(transform); @@ -1441,4 +1487,8 @@ void LayerTreeHost::SetNeedsDisplayOnAllLayers() { layer->SetNeedsDisplay(); } +void LayerTreeHost::SetHasCopyRequest(bool has_copy_request) { + has_copy_request_ = has_copy_request; +} + } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index bf5520e2a39..51622245b1e 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -19,6 +19,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" #include "base/time/time.h" #include "cc/benchmarks/micro_benchmark.h" #include "cc/benchmarks/micro_benchmark_controller.h" @@ -67,8 +68,6 @@ struct ScrollAndScaleSet; class CC_EXPORT LayerTreeHost : public NON_EXPORTED_BASE(SurfaceReferenceOwner), public NON_EXPORTED_BASE(MutatorHostClient) { public: - // TODO(sad): InitParams should be a movable type so that it can be - // std::move()d to the Create* functions. struct CC_EXPORT InitParams { LayerTreeHostClient* client = nullptr; TaskGraphRunner* task_graph_runner = nullptr; @@ -242,20 +241,32 @@ class CC_EXPORT LayerTreeHost : public NON_EXPORTED_BASE(SurfaceReferenceOwner), Layer* root_layer() { return root_layer_.get(); } const Layer* root_layer() const { return root_layer_.get(); } - void RegisterViewportLayers(scoped_refptr<Layer> overscroll_elasticity_layer, - scoped_refptr<Layer> page_scale_layer, - scoped_refptr<Layer> inner_viewport_scroll_layer, - scoped_refptr<Layer> outer_viewport_scroll_layer); - + struct CC_EXPORT ViewportLayers { + ViewportLayers(); + ~ViewportLayers(); + scoped_refptr<Layer> overscroll_elasticity; + scoped_refptr<Layer> page_scale; + scoped_refptr<Layer> inner_viewport_container; + scoped_refptr<Layer> outer_viewport_container; + scoped_refptr<Layer> inner_viewport_scroll; + scoped_refptr<Layer> outer_viewport_scroll; + }; + void RegisterViewportLayers(const ViewportLayers& viewport_layers); Layer* overscroll_elasticity_layer() const { - return overscroll_elasticity_layer_.get(); + return viewport_layers_.overscroll_elasticity.get(); + } + Layer* page_scale_layer() const { return viewport_layers_.page_scale.get(); } + Layer* inner_viewport_container_layer() const { + return viewport_layers_.inner_viewport_container.get(); + } + Layer* outer_viewport_container_layer() const { + return viewport_layers_.outer_viewport_container.get(); } - Layer* page_scale_layer() const { return page_scale_layer_.get(); } Layer* inner_viewport_scroll_layer() const { - return inner_viewport_scroll_layer_.get(); + return viewport_layers_.inner_viewport_scroll.get(); } Layer* outer_viewport_scroll_layer() const { - return outer_viewport_scroll_layer_.get(); + return viewport_layers_.outer_viewport_scroll.get(); } void RegisterSelection(const LayerSelection& selection); @@ -341,6 +352,9 @@ class CC_EXPORT LayerTreeHost : public NON_EXPORTED_BASE(SurfaceReferenceOwner), bool* content_is_suitable_for_gpu); bool in_paint_layer_contents() const { return in_paint_layer_contents_; } + void SetHasCopyRequest(bool has_copy_request); + bool has_copy_request() const { return has_copy_request_; } + void AddLayerShouldPushProperties(Layer* layer); void RemoveLayerShouldPushProperties(Layer* layer); std::unordered_set<Layer*>& LayersThatShouldPushProperties(); @@ -358,7 +372,9 @@ class CC_EXPORT LayerTreeHost : public NON_EXPORTED_BASE(SurfaceReferenceOwner), void SetPropertyTreesNeedRebuild(); - void PushPropertiesTo(LayerTreeImpl* tree_impl); + void PushPropertyTreesTo(LayerTreeImpl* tree_impl); + void PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl); + void PushLayerTreeHostPropertiesTo(LayerTreeHostImpl* host_impl); MutatorHost* mutator_host() const { return mutator_host_; } @@ -384,6 +400,7 @@ class CC_EXPORT LayerTreeHost : public NON_EXPORTED_BASE(SurfaceReferenceOwner), void DidBeginMainFrame(); void BeginMainFrame(const BeginFrameArgs& args); void BeginMainFrameNotExpectedSoon(); + void BeginMainFrameNotExpectedUntil(base::TimeTicks time); void AnimateLayers(base::TimeTicks monotonic_frame_begin_time); void RequestMainFrameUpdate(); void FinishCommitOnImplThread(LayerTreeHostImpl* host_impl); @@ -557,10 +574,7 @@ class CC_EXPORT LayerTreeHost : public NON_EXPORTED_BASE(SurfaceReferenceOwner), scoped_refptr<Layer> root_layer_; - scoped_refptr<Layer> overscroll_elasticity_layer_; - scoped_refptr<Layer> page_scale_layer_; - scoped_refptr<Layer> inner_viewport_scroll_layer_; - scoped_refptr<Layer> outer_viewport_scroll_layer_; + ViewportLayers viewport_layers_; float top_controls_height_ = 0.f; float top_controls_shown_ratio_ = 0.f; @@ -611,13 +625,16 @@ class CC_EXPORT LayerTreeHost : public NON_EXPORTED_BASE(SurfaceReferenceOwner), bool in_paint_layer_contents_ = false; bool in_update_property_trees_ = false; + // This is true if atleast one layer in the layer tree has a copy request. We + // use this bool to decide whether we need to compute subtree has copy request + // for every layer during property tree building. + bool has_copy_request_ = false; + MutatorHost* mutator_host_; std::vector<std::pair<sk_sp<const SkImage>, base::Callback<void(bool)>>> queued_image_decodes_; - bool did_navigate_ = false; - DISALLOW_COPY_AND_ASSIGN(LayerTreeHost); }; diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h index 84416d60ef0..f54a69b0217 100644 --- a/chromium/cc/trees/layer_tree_host_client.h +++ b/chromium/cc/trees/layer_tree_host_client.h @@ -8,6 +8,7 @@ #include <memory> #include "base/memory/ref_counted.h" +#include "base/time/time.h" namespace gfx { class Vector2dF; @@ -24,6 +25,7 @@ class LayerTreeHostClient { // mode, this corresponds to DidCommit(). virtual void BeginMainFrame(const BeginFrameArgs& args) = 0; virtual void BeginMainFrameNotExpectedSoon() = 0; + virtual void BeginMainFrameNotExpectedUntil(base::TimeTicks time) = 0; virtual void DidBeginMainFrame() = 0; // A LayerTreeHost is bound to a LayerTreeHostClient. Visual frame-based // updates to the state of the LayerTreeHost are expected to happen only in diff --git a/chromium/cc/trees/layer_tree_host_common.cc b/chromium/cc/trees/layer_tree_host_common.cc index 8d09c07ec1d..e289a34db08 100644 --- a/chromium/cc/trees/layer_tree_host_common.cc +++ b/chromium/cc/trees/layer_tree_host_common.cc @@ -20,6 +20,7 @@ #include "cc/trees/layer_tree_impl.h" #include "cc/trees/property_tree_builder.h" #include "cc/trees/scroll_node.h" +#include "cc/trees/transform_node.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" #include "ui/gfx/transform.h" @@ -77,10 +78,8 @@ LayerTreeHostCommon::CalcDrawPropsImplInputs::CalcDrawPropsImplInputs( const gfx::Vector2dF& elastic_overscroll, const LayerImpl* elastic_overscroll_application_layer, int max_texture_size, - bool can_render_to_separate_surface, bool can_adjust_raster_scales, - bool use_layer_lists, - LayerImplList* render_surface_layer_list, + RenderSurfaceList* render_surface_list, PropertyTrees* property_trees) : root_layer(root_layer), device_viewport_size(device_viewport_size), @@ -94,10 +93,8 @@ LayerTreeHostCommon::CalcDrawPropsImplInputs::CalcDrawPropsImplInputs( elastic_overscroll_application_layer( elastic_overscroll_application_layer), max_texture_size(max_texture_size), - can_render_to_separate_surface(can_render_to_separate_surface), can_adjust_raster_scales(can_adjust_raster_scales), - use_layer_lists(use_layer_lists), - render_surface_layer_list(render_surface_layer_list), + render_surface_list(render_surface_list), property_trees(property_trees) {} LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: @@ -105,7 +102,7 @@ LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: const gfx::Size& device_viewport_size, const gfx::Transform& device_transform, float device_scale_factor, - LayerImplList* render_surface_layer_list) + RenderSurfaceList* render_surface_list) : CalcDrawPropsImplInputs(root_layer, device_viewport_size, device_transform, @@ -117,46 +114,44 @@ LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: gfx::Vector2dF(), NULL, std::numeric_limits<int>::max() / 2, - true, false, - false, - render_surface_layer_list, + render_surface_list, GetPropertyTrees(root_layer)) { DCHECK(root_layer); - DCHECK(render_surface_layer_list); + DCHECK(render_surface_list); } LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: CalcDrawPropsImplInputsForTesting(LayerImpl* root_layer, const gfx::Size& device_viewport_size, const gfx::Transform& device_transform, - LayerImplList* render_surface_layer_list) + RenderSurfaceList* render_surface_list) : CalcDrawPropsImplInputsForTesting(root_layer, device_viewport_size, device_transform, 1.f, - render_surface_layer_list) {} + render_surface_list) {} LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: CalcDrawPropsImplInputsForTesting(LayerImpl* root_layer, const gfx::Size& device_viewport_size, - LayerImplList* render_surface_layer_list) + RenderSurfaceList* render_surface_list) : CalcDrawPropsImplInputsForTesting(root_layer, device_viewport_size, gfx::Transform(), 1.f, - render_surface_layer_list) {} + render_surface_list) {} LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: CalcDrawPropsImplInputsForTesting(LayerImpl* root_layer, const gfx::Size& device_viewport_size, float device_scale_factor, - LayerImplList* render_surface_layer_list) + RenderSurfaceList* render_surface_list) : CalcDrawPropsImplInputsForTesting(root_layer, device_viewport_size, gfx::Transform(), device_scale_factor, - render_surface_layer_list) {} + render_surface_list) {} LayerTreeHostCommon::ScrollUpdateInfo::ScrollUpdateInfo() : layer_id(Layer::INVALID_ID) {} @@ -167,15 +162,15 @@ bool LayerTreeHostCommon::ScrollUpdateInfo::operator==( } LayerTreeHostCommon::ScrollbarsUpdateInfo::ScrollbarsUpdateInfo() - : layer_id(Layer::INVALID_ID), hidden(true) {} + : element_id(), hidden(true) {} -LayerTreeHostCommon::ScrollbarsUpdateInfo::ScrollbarsUpdateInfo(int layer_id, +LayerTreeHostCommon::ScrollbarsUpdateInfo::ScrollbarsUpdateInfo(ElementId id, bool hidden) - : layer_id(layer_id), hidden(hidden) {} + : element_id(id), hidden(hidden) {} bool LayerTreeHostCommon::ScrollbarsUpdateInfo::operator==( const LayerTreeHostCommon::ScrollbarsUpdateInfo& other) const { - return layer_id == other.layer_id && hidden == other.hidden; + return element_id == other.element_id && hidden == other.hidden; } ScrollAndScaleSet::ScrollAndScaleSet() @@ -186,28 +181,21 @@ ScrollAndScaleSet::ScrollAndScaleSet() ScrollAndScaleSet::~ScrollAndScaleSet() {} -static inline void SetMaskLayersAreDrawnRenderSurfaceLayerListMembers( +static inline void SetMaskLayersContributeToDrawnRenderSurface( RenderSurfaceImpl* surface, PropertyTrees* property_trees) { LayerImpl* mask_layer = surface->MaskLayer(); if (mask_layer) { - mask_layer->set_is_drawn_render_surface_layer_list_member(true); + mask_layer->set_contributes_to_drawn_render_surface(true); draw_property_utils::ComputeMaskDrawProperties(mask_layer, property_trees); } } -static inline void ClearMaskLayersAreDrawnRenderSurfaceLayerListMembers( +static inline void ClearMaskLayersContributeToDrawnRenderSurface( RenderSurfaceImpl* surface) { LayerImpl* mask_layer = surface->MaskLayer(); if (mask_layer) - mask_layer->set_is_drawn_render_surface_layer_list_member(false); -} - -static inline void ClearIsDrawnRenderSurfaceLayerListMember( - LayerImplList* layer_list, - ScrollTree* scroll_tree) { - for (LayerImpl* layer : *layer_list) - layer->set_is_drawn_render_surface_layer_list_member(false); + mask_layer->set_contributes_to_drawn_render_surface(false); } static bool CdpPerfTracingEnabled() { @@ -280,86 +268,120 @@ enum PropertyTreeOption { DONT_BUILD_PROPERTY_TREES }; -static void ComputeInitialRenderSurfaceLayerList( +static void AddSurfaceToRenderSurfaceList( + RenderSurfaceImpl* render_surface, + RenderSurfaceList* render_surface_list, + PropertyTrees* property_trees) { + // |render_surface| must appear after its target, so first make sure its + // target is in the list. + RenderSurfaceImpl* target = render_surface->render_target(); + bool is_root = + render_surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId; + if (!is_root && !target->is_render_surface_list_member()) { + AddSurfaceToRenderSurfaceList(target, render_surface_list, property_trees); + } + render_surface->ClearAccumulatedContentRect(); + render_surface_list->push_back(render_surface); + render_surface->set_is_render_surface_list_member(true); + if (is_root) { + // The root surface does not contribute to any other surface, it has no + // target. + render_surface->set_contributes_to_drawn_surface(false); + } else { + bool contributes_to_drawn_surface = + property_trees->effect_tree.ContributesToDrawnSurface( + render_surface->EffectTreeIndex()); + render_surface->set_contributes_to_drawn_surface( + contributes_to_drawn_surface); + } + + draw_property_utils::ComputeSurfaceDrawProperties(property_trees, + render_surface); + + // Ignore occlusion from outside the surface when surface contents need to be + // fully drawn. Layers with copy-request need to be complete. We could be + // smarter about layers with filters that move pixels and exclude regions + // where both layers and the filters are occluded, but this seems like + // overkill. + // TODO(senorblanco): make this smarter for the SkImageFilter case (check for + // pixel-moving filters) + const FilterOperations& filters = render_surface->Filters(); + bool is_occlusion_immune = render_surface->HasCopyRequest() || + filters.HasReferenceFilter() || + filters.HasFilterThatMovesPixels(); + if (is_occlusion_immune) { + render_surface->SetNearestOcclusionImmuneAncestor(render_surface); + } else if (is_root) { + render_surface->SetNearestOcclusionImmuneAncestor(nullptr); + } else { + render_surface->SetNearestOcclusionImmuneAncestor( + render_surface->render_target()->nearest_occlusion_immune_ancestor()); + } +} + +static bool SkipForInvertibility(const LayerImpl* layer, + PropertyTrees* property_trees) { + const TransformNode* transform_node = + property_trees->transform_tree.Node(layer->transform_tree_index()); + const EffectNode* effect_node = + property_trees->effect_tree.Node(layer->effect_tree_index()); + bool non_root_copy_request = + effect_node->closest_ancestor_with_copy_request_id > + EffectTree::kContentsRootNodeId; + gfx::Transform from_target; + // If there is a copy request, we check the invertibility of the transform + // between the node corresponding to the layer and the node corresponding to + // the copy request. Otherwise, we are interested in the invertibility of + // screen space transform which is already cached on the transform node. + return non_root_copy_request + ? !property_trees->GetFromTarget( + layer->transform_tree_index(), + effect_node->closest_ancestor_with_copy_request_id, + &from_target) + : !transform_node->ancestors_are_invertible; +} + +static void ComputeInitialRenderSurfaceList( LayerTreeImpl* layer_tree_impl, PropertyTrees* property_trees, - LayerImplList* render_surface_layer_list, - bool can_render_to_separate_surface, - bool use_layer_lists) { - // Add all non-skipped surfaces to the initial render surface layer list. Add - // all non-skipped layers to the layer list of their target surface, and - // add their content rect to their target surface's accumulated content rect. + RenderSurfaceList* render_surface_list) { + EffectTree& effect_tree = property_trees->effect_tree; + for (int i = EffectTree::kContentsRootNodeId; + i < static_cast<int>(effect_tree.size()); ++i) { + if (RenderSurfaceImpl* render_surface = effect_tree.GetRenderSurface(i)) { + render_surface->set_is_render_surface_list_member(false); + render_surface->reset_num_contributors(); + ClearMaskLayersContributeToDrawnRenderSurface(render_surface); + } + } + + RenderSurfaceImpl* root_surface = + effect_tree.GetRenderSurface(EffectTree::kContentsRootNodeId); + // The root surface always gets added to the render surface list. + AddSurfaceToRenderSurfaceList(root_surface, render_surface_list, + property_trees); + // For all non-skipped layers, add their target to the render surface list if + // it's not already been added, and add their content rect to the target + // surface's accumulated content rect. for (LayerImpl* layer : *layer_tree_impl) { - DCHECK(layer); + layer->set_contributes_to_drawn_render_surface(false); - // TODO(crbug.com/726423): LayerImpls should never have invalid PropertyTree - // indices. - if (!layer) - continue; + bool is_root = layer_tree_impl->IsRootLayer(layer); - layer->set_is_drawn_render_surface_layer_list_member(false); - if (!layer->HasValidPropertyTreeIndices()) - continue; + bool skip_draw_properties_computation = + draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation( + layer, property_trees->transform_tree, property_trees->effect_tree); - RenderSurfaceImpl* render_surface = layer->GetRenderSurface(); - if (render_surface) { - render_surface->ClearLayerLists(); - ClearMaskLayersAreDrawnRenderSurfaceLayerListMembers(render_surface); - } - layer->set_is_drawn_render_surface_layer_list_member(false); + bool skip_for_invertibility = SkipForInvertibility(layer, property_trees); - bool is_root = layer_tree_impl->IsRootLayer(layer); - bool skip_layer = !is_root && draw_property_utils::LayerShouldBeSkipped( - layer, property_trees->transform_tree, - property_trees->effect_tree); + bool skip_layer = !is_root && (skip_draw_properties_computation || + skip_for_invertibility); + + layer->set_raster_even_if_not_in_rsll(skip_for_invertibility && + !skip_draw_properties_computation); if (skip_layer) continue; - bool render_to_separate_surface = - is_root || (can_render_to_separate_surface && render_surface); - - if (render_to_separate_surface) { - DCHECK(render_surface); - DCHECK(layer->render_target() == render_surface); - render_surface->ClearAccumulatedContentRect(); - render_surface_layer_list->push_back(layer); - if (is_root) { - // The root surface does not contribute to any other surface, it has no - // target. - render_surface->set_contributes_to_drawn_surface(false); - } else { - render_surface->render_target()->layer_list().push_back(layer); - bool contributes_to_drawn_surface = - property_trees->effect_tree.ContributesToDrawnSurface( - layer->effect_tree_index()); - render_surface->set_contributes_to_drawn_surface( - contributes_to_drawn_surface); - } - - draw_property_utils::ComputeSurfaceDrawProperties( - property_trees, render_surface, use_layer_lists); - - // Ignore occlusion from outside the surface when surface contents need to - // be fully drawn. Layers with copy-request need to be complete. We could - // be smarter about layers with filters that move pixels and exclude - // regions where both layers and the filters are occluded, but this seems - // like overkill. - // TODO(senorblanco): make this smarter for the SkImageFilter case (check - // for pixel-moving filters) - const FilterOperations& filters = render_surface->Filters(); - bool is_occlusion_immune = render_surface->HasCopyRequest() || - filters.HasReferenceFilter() || - filters.HasFilterThatMovesPixels(); - if (is_occlusion_immune) { - render_surface->SetNearestOcclusionImmuneAncestor(render_surface); - } else if (is_root) { - render_surface->SetNearestOcclusionImmuneAncestor(nullptr); - } else { - render_surface->SetNearestOcclusionImmuneAncestor( - render_surface->render_target() - ->nearest_occlusion_immune_ancestor()); - } - } bool layer_is_drawn = property_trees->effect_tree.Node(layer->effect_tree_index())->is_drawn; bool layer_should_be_drawn = draw_property_utils::LayerNeedsUpdate( @@ -367,24 +389,30 @@ static void ComputeInitialRenderSurfaceLayerList( if (!layer_should_be_drawn) continue; - layer->set_is_drawn_render_surface_layer_list_member(true); - layer->render_target()->layer_list().push_back(layer); + RenderSurfaceImpl* render_target = layer->render_target(); + if (!render_target->is_render_surface_list_member()) { + AddSurfaceToRenderSurfaceList(render_target, render_surface_list, + property_trees); + } + + layer->set_contributes_to_drawn_render_surface(true); // The layer contributes its drawable content rect to its render target. - layer->render_target()->AccumulateContentRectFromContributingLayer(layer); + render_target->AccumulateContentRectFromContributingLayer(layer); + render_target->increment_num_contributors(); } } static void ComputeSurfaceContentRects(LayerTreeImpl* layer_tree_impl, PropertyTrees* property_trees, - LayerImplList* render_surface_layer_list, + RenderSurfaceList* render_surface_list, int max_texture_size) { // Walk the list backwards, accumulating each surface's content rect into its // target's content rect. - for (LayerImpl* layer : base::Reversed(*render_surface_layer_list)) { - RenderSurfaceImpl* render_surface = layer->GetRenderSurface(); - if (layer_tree_impl->IsRootLayer(layer)) { - // The root layer's surface content rect is always the entire viewport. + for (RenderSurfaceImpl* render_surface : + base::Reversed(*render_surface_list)) { + if (render_surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId) { + // The root surface's content rect is always the entire viewport. render_surface->SetContentRectToViewport(); continue; } @@ -396,82 +424,74 @@ static void ComputeSurfaceContentRects(LayerTreeImpl* layer_tree_impl, // Now the render surface's content rect is calculated correctly, it could // contribute to its render target. - render_surface->render_target() - ->AccumulateContentRectFromContributingRenderSurface(render_surface); + RenderSurfaceImpl* render_target = render_surface->render_target(); + DCHECK(render_target->is_render_surface_list_member()); + render_target->AccumulateContentRectFromContributingRenderSurface( + render_surface); + render_target->increment_num_contributors(); } } -static void ComputeListOfNonEmptySurfaces(LayerTreeImpl* layer_tree_impl, - PropertyTrees* property_trees, - LayerImplList* initial_surface_list, - LayerImplList* final_surface_list) { +static void ComputeListOfNonEmptySurfaces( + LayerTreeImpl* layer_tree_impl, + PropertyTrees* property_trees, + RenderSurfaceList* initial_surface_list, + RenderSurfaceList* final_surface_list) { // Walk the initial surface list forwards. The root surface and each // surface with a non-empty content rect go into the final render surface // layer list. Surfaces with empty content rects or whose target isn't in // the final list do not get added to the final list. - for (LayerImpl* layer : *initial_surface_list) { - bool is_root = layer_tree_impl->IsRootLayer(layer); - RenderSurfaceImpl* surface = layer->GetRenderSurface(); + bool removed_surface = false; + for (RenderSurfaceImpl* surface : *initial_surface_list) { + bool is_root = + surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId; RenderSurfaceImpl* target_surface = surface->render_target(); if (!is_root && (surface->content_rect().IsEmpty() || - target_surface->layer_list().empty())) { - ClearIsDrawnRenderSurfaceLayerListMember(&surface->layer_list(), - &property_trees->scroll_tree); - surface->ClearLayerLists(); - if (!is_root) { - LayerImplList& target_list = target_surface->layer_list(); - auto it = std::find(target_list.begin(), target_list.end(), layer); - if (it != target_list.end()) { - target_list.erase(it); - // This surface has an empty content rect. If its target's layer list - // had no other layers, then its target would also have had an empty - // content rect, meaning it would have been removed and had its layer - // list cleared when we visited it, unless the target surface is the - // root surface. - DCHECK(!target_surface->layer_list().empty() || - target_surface->render_target() == target_surface); - } else { - // This layer was removed when the target itself was cleared. - DCHECK(target_surface->layer_list().empty()); + !target_surface->is_render_surface_list_member())) { + surface->set_is_render_surface_list_member(false); + removed_surface = true; + target_surface->decrement_num_contributors(); + continue; + } + SetMaskLayersContributeToDrawnRenderSurface(surface, property_trees); + final_surface_list->push_back(surface); + } + if (removed_surface) { + for (LayerImpl* layer : *layer_tree_impl) { + if (layer->contributes_to_drawn_render_surface()) { + RenderSurfaceImpl* render_target = layer->render_target(); + if (!render_target->is_render_surface_list_member()) { + layer->set_contributes_to_drawn_render_surface(false); + render_target->decrement_num_contributors(); } } - continue; } - SetMaskLayersAreDrawnRenderSurfaceLayerListMembers(surface, property_trees); - final_surface_list->push_back(layer); } } static void CalculateRenderSurfaceLayerList( LayerTreeImpl* layer_tree_impl, PropertyTrees* property_trees, - LayerImplList* render_surface_layer_list, - const bool can_render_to_separate_surface, - const bool use_layer_lists, + RenderSurfaceList* render_surface_list, const int max_texture_size) { - // This calculates top level Render Surface Layer List, and Layer List for all - // Render Surfaces. - // |render_surface_layer_list| is the top level RenderSurfaceLayerList. + RenderSurfaceList initial_render_surface_list; - LayerImplList initial_render_surface_list; - - // First compute an RSLL that might include surfaces that later turn out to + // First compute a list that might include surfaces that later turn out to // have an empty content rect. After surface content rects are computed, - // produce a final RSLL that omits empty surfaces. - ComputeInitialRenderSurfaceLayerList( - layer_tree_impl, property_trees, &initial_render_surface_list, - can_render_to_separate_surface, use_layer_lists); + // produce a final list that omits empty surfaces. + ComputeInitialRenderSurfaceList(layer_tree_impl, property_trees, + &initial_render_surface_list); ComputeSurfaceContentRects(layer_tree_impl, property_trees, &initial_render_surface_list, max_texture_size); ComputeListOfNonEmptySurfaces(layer_tree_impl, property_trees, &initial_render_surface_list, - render_surface_layer_list); + render_surface_list); } void CalculateDrawPropertiesInternal( LayerTreeHostCommon::CalcDrawPropsImplInputs* inputs, PropertyTreeOption property_tree_option) { - inputs->render_surface_layer_list->clear(); + inputs->render_surface_list->clear(); const bool should_measure_property_tree_performance = property_tree_option == BUILD_PROPERTY_TREES_IF_NEEDED; @@ -498,7 +518,6 @@ void CalculateDrawPropertiesInternal( inputs->device_transform, inputs->property_trees); draw_property_utils::UpdatePropertyTreesAndRenderSurfaces( inputs->root_layer, inputs->property_trees, - inputs->can_render_to_separate_surface, inputs->can_adjust_raster_scales); // Property trees are normally constructed on the main thread and @@ -545,7 +564,6 @@ void CalculateDrawPropertiesInternal( inputs->device_transform, inputs->root_layer->position()); draw_property_utils::UpdatePropertyTreesAndRenderSurfaces( inputs->root_layer, inputs->property_trees, - inputs->can_render_to_separate_surface, inputs->can_adjust_raster_scales); break; } @@ -559,15 +577,12 @@ void CalculateDrawPropertiesInternal( draw_property_utils::FindLayersThatNeedUpdates( inputs->root_layer->layer_tree_impl(), inputs->property_trees, &visible_layer_list); - DCHECK(inputs->can_render_to_separate_surface == - inputs->property_trees->non_root_surfaces_enabled); draw_property_utils::ComputeDrawPropertiesOfVisibleLayers( &visible_layer_list, inputs->property_trees); CalculateRenderSurfaceLayerList( inputs->root_layer->layer_tree_impl(), inputs->property_trees, - inputs->render_surface_layer_list, inputs->can_render_to_separate_surface, - inputs->use_layer_lists, inputs->max_texture_size); + inputs->render_surface_list, inputs->max_texture_size); if (should_measure_property_tree_performance) { TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), @@ -576,13 +591,13 @@ void CalculateDrawPropertiesInternal( // A root layer render_surface should always exist after // CalculateDrawProperties. - DCHECK(inputs->root_layer->GetRenderSurface()); + DCHECK(inputs->property_trees->effect_tree.GetRenderSurface( + EffectTree::kContentsRootNodeId)); } void LayerTreeHostCommon::CalculateDrawPropertiesForTesting( CalcDrawPropsMainInputsForTesting* inputs) { LayerList update_layer_list; - bool can_render_to_separate_surface = true; PropertyTrees* property_trees = inputs->root_layer->layer_tree_host()->property_trees(); Layer* overscroll_elasticity_layer = nullptr; @@ -595,8 +610,7 @@ void LayerTreeHostCommon::CalculateDrawPropertiesForTesting( gfx::Rect(inputs->device_viewport_size), inputs->device_transform, property_trees); draw_property_utils::UpdatePropertyTrees( - inputs->root_layer->layer_tree_host(), property_trees, - can_render_to_separate_surface); + inputs->root_layer->layer_tree_host(), property_trees); draw_property_utils::FindLayersThatNeedUpdates( inputs->root_layer->layer_tree_host(), property_trees, &update_layer_list); diff --git a/chromium/cc/trees/layer_tree_host_common.h b/chromium/cc/trees/layer_tree_host_common.h index dd668caa88e..522ef93c050 100644 --- a/chromium/cc/trees/layer_tree_host_common.h +++ b/chromium/cc/trees/layer_tree_host_common.h @@ -72,10 +72,8 @@ class CC_EXPORT LayerTreeHostCommon { const gfx::Vector2dF& elastic_overscroll, const LayerImpl* elastic_overscroll_application_layer, int max_texture_size, - bool can_render_to_separate_surface, bool can_adjust_raster_scales, - bool use_layer_lists, - LayerImplList* render_surface_layer_list, + RenderSurfaceList* render_surface_list, PropertyTrees* property_trees); LayerImpl* root_layer; @@ -89,10 +87,8 @@ class CC_EXPORT LayerTreeHostCommon { gfx::Vector2dF elastic_overscroll; const LayerImpl* elastic_overscroll_application_layer; int max_texture_size; - bool can_render_to_separate_surface; bool can_adjust_raster_scales; - bool use_layer_lists; - LayerImplList* render_surface_layer_list; + RenderSurfaceList* render_surface_list; PropertyTrees* property_trees; }; @@ -102,18 +98,18 @@ class CC_EXPORT LayerTreeHostCommon { const gfx::Size& device_viewport_size, const gfx::Transform& device_transform, float device_scale_factor, - LayerImplList* render_surface_layer_list); + RenderSurfaceList* render_surface_list); CalcDrawPropsImplInputsForTesting(LayerImpl* root_layer, const gfx::Size& device_viewport_size, const gfx::Transform& device_transform, - LayerImplList* render_surface_layer_list); + RenderSurfaceList* render_surface_list); CalcDrawPropsImplInputsForTesting(LayerImpl* root_layer, const gfx::Size& device_viewport_size, - LayerImplList* render_surface_layer_list); + RenderSurfaceList* render_surface_list); CalcDrawPropsImplInputsForTesting(LayerImpl* root_layer, const gfx::Size& device_viewport_size, float device_scale_factor, - LayerImplList* render_surface_layer_list); + RenderSurfaceList* render_surface_list); }; static int CalculateLayerJitter(LayerImpl* scrolling_layer); @@ -149,11 +145,11 @@ class CC_EXPORT LayerTreeHostCommon { // to be told when they're faded out so it can stop handling input for // invisible scrollbars. struct CC_EXPORT ScrollbarsUpdateInfo { - int layer_id; + ElementId element_id; bool hidden; ScrollbarsUpdateInfo(); - ScrollbarsUpdateInfo(int layer_id, bool hidden); + ScrollbarsUpdateInfo(ElementId element_id, bool hidden); bool operator==(const ScrollbarsUpdateInfo& other) const; }; diff --git a/chromium/cc/trees/layer_tree_host_common_perftest.cc b/chromium/cc/trees/layer_tree_host_common_perftest.cc index 89946eea48a..c0b1e3254d2 100644 --- a/chromium/cc/trees/layer_tree_host_common_perftest.cc +++ b/chromium/cc/trees/layer_tree_host_common_perftest.cc @@ -86,12 +86,8 @@ class CalcDrawPropsTest : public LayerTreeHostCommonPerfTest { LayerTreeImpl* active_tree = host_impl->active_tree(); do { - bool can_render_to_separate_surface = true; int max_texture_size = 8096; - DoCalcDrawPropertiesImpl(can_render_to_separate_surface, - max_texture_size, - active_tree, - host_impl); + DoCalcDrawPropertiesImpl(max_texture_size, active_tree, host_impl); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); @@ -99,22 +95,20 @@ class CalcDrawPropsTest : public LayerTreeHostCommonPerfTest { EndTest(); } - void DoCalcDrawPropertiesImpl(bool can_render_to_separate_surface, - int max_texture_size, + void DoCalcDrawPropertiesImpl(int max_texture_size, LayerTreeImpl* active_tree, LayerTreeHostImpl* host_impl) { - LayerImplList update_list; + RenderSurfaceList update_list; LayerTreeHostCommon::CalcDrawPropsImplInputs inputs( - active_tree->root_layer_for_testing(), active_tree->DrawViewportSize(), - host_impl->DrawTransform(), active_tree->device_scale_factor(), + active_tree->root_layer_for_testing(), + active_tree->DeviceViewport().size(), host_impl->DrawTransform(), + active_tree->device_scale_factor(), active_tree->current_page_scale_factor(), active_tree->InnerViewportContainerLayer(), active_tree->InnerViewportScrollLayer(), active_tree->OuterViewportScrollLayer(), active_tree->elastic_overscroll()->Current(active_tree->IsActiveTree()), active_tree->OverscrollElasticityLayer(), max_texture_size, - can_render_to_separate_surface, - false, // don't use layer lists for perf tests host_impl->settings().layer_transforms_should_scale_layer_contents, &update_list, active_tree->property_trees()); LayerTreeHostCommon::CalculateDrawProperties(&inputs); diff --git a/chromium/cc/trees/layer_tree_host_common_unittest.cc b/chromium/cc/trees/layer_tree_host_common_unittest.cc index e6f69f6dd99..a26ee7f4559 100644 --- a/chromium/cc/trees/layer_tree_host_common_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_common_unittest.cc @@ -42,7 +42,6 @@ #include "cc/trees/clip_node.h" #include "cc/trees/draw_property_utils.h" #include "cc/trees/effect_node.h" -#include "cc/trees/layer_tree_impl.h" #include "cc/trees/property_tree_builder.h" #include "cc/trees/scroll_node.h" #include "cc/trees/single_thread_proxy.h" @@ -139,14 +138,13 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { gfx::Size(root_layer->bounds().width() * device_scale_factor, root_layer->bounds().height() * device_scale_factor); - render_surface_layer_list_impl_.reset(new LayerImplList); + render_surface_list_impl_.reset(new RenderSurfaceList); // We are probably not testing what is intended if the root_layer bounds are // empty. DCHECK(!root_layer->bounds().IsEmpty()); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, device_viewport_size, - render_surface_layer_list_impl_.get()); + root_layer, device_viewport_size, render_surface_list_impl_.get()); inputs.device_scale_factor = device_scale_factor; inputs.page_scale_factor = page_scale_factor; inputs.page_scale_layer = page_scale_layer; @@ -182,8 +180,6 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { void ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(Layer* root_layer) { DCHECK(root_layer->layer_tree_host()); - bool can_render_to_separate_surface = true; - const Layer* page_scale_layer = root_layer->layer_tree_host()->page_scale_layer(); Layer* inner_viewport_scroll_layer = @@ -208,8 +204,7 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { elastic_overscroll, page_scale_factor, device_scale_factor, gfx::Rect(device_viewport_size), gfx::Transform(), property_trees); draw_property_utils::UpdatePropertyTrees(root_layer->layer_tree_host(), - property_trees, - can_render_to_separate_surface); + property_trees); draw_property_utils::FindLayersThatNeedUpdates( root_layer->layer_tree_host(), property_trees, &update_layer_list_); } @@ -217,7 +212,6 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { void ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList( LayerImpl* root_layer) { DCHECK(root_layer->layer_tree_impl()); - bool can_render_to_separate_surface = true; bool can_adjust_raster_scales = true; const LayerImpl* page_scale_layer = nullptr; @@ -245,8 +239,7 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { elastic_overscroll, page_scale_factor, device_scale_factor, gfx::Rect(device_viewport_size), gfx::Transform(), property_trees); draw_property_utils::UpdatePropertyTreesAndRenderSurfaces( - root_layer, property_trees, can_render_to_separate_surface, - can_adjust_raster_scales); + root_layer, property_trees, can_adjust_raster_scales); draw_property_utils::FindLayersThatNeedUpdates( root_layer->layer_tree_impl(), property_trees, update_layer_list_impl_.get()); @@ -258,14 +251,12 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { LayerImpl* root_layer) { gfx::Size device_viewport_size = gfx::Size(root_layer->bounds().width(), root_layer->bounds().height()); - render_surface_layer_list_impl_.reset(new LayerImplList); + render_surface_list_impl_.reset(new RenderSurfaceList); DCHECK(!root_layer->bounds().IsEmpty()); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, device_viewport_size, - render_surface_layer_list_impl_.get()); + root_layer, device_viewport_size, render_surface_list_impl_.get()); inputs.can_adjust_raster_scales = true; - inputs.can_render_to_separate_surface = false; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); } @@ -274,13 +265,11 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { LayerImpl* root_layer) { gfx::Size device_viewport_size = gfx::Size(root_layer->bounds().width(), root_layer->bounds().height()); - render_surface_layer_list_impl_.reset(new LayerImplList); + render_surface_list_impl_.reset(new RenderSurfaceList); DCHECK(!root_layer->bounds().IsEmpty()); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, device_viewport_size, - render_surface_layer_list_impl_.get()); - inputs.can_render_to_separate_surface = true; + root_layer, device_viewport_size, render_surface_list_impl_.get()); inputs.can_adjust_raster_scales = false; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); @@ -302,8 +291,8 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { return false; } - const LayerImplList* render_surface_layer_list_impl() const { - return render_surface_layer_list_impl_.get(); + const RenderSurfaceList* render_surface_list_impl() const { + return render_surface_list_impl_.get(); } const LayerImplList* update_layer_list_impl() const { return update_layer_list_impl_.get(); @@ -317,7 +306,7 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { } private: - std::unique_ptr<std::vector<LayerImpl*>> render_surface_layer_list_impl_; + std::unique_ptr<RenderSurfaceList> render_surface_list_impl_; LayerList update_layer_list_; std::unique_ptr<LayerImplList> update_layer_list_impl_; }; @@ -613,9 +602,10 @@ TEST_F(LayerTreeHostCommonTest, TransformsAboutScrollOffset) { // Test that page scale is updated even when we don't rebuild property trees. page_scale = 1.888f; - root_layer->layer_tree_impl()->SetViewportLayersFromIds( - Layer::INVALID_ID, scroll_layer->test_properties()->parent->id(), - Layer::INVALID_ID, Layer::INVALID_ID); + + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = scroll_layer->test_properties()->parent->id(); + root_layer->layer_tree_impl()->SetViewportLayersFromIds(viewport_ids); root_layer->layer_tree_impl()->SetPageScaleOnActiveTree(page_scale); EXPECT_FALSE(root_layer->layer_tree_impl()->property_trees()->needs_rebuild); ExecuteCalculateDrawProperties(root_layer, kDeviceScale, page_scale, @@ -750,8 +740,8 @@ TEST_F(LayerTreeHostCommonTest, TransformsForSingleRenderSurface) { ExecuteCalculateDrawProperties(root); // Render surface should have been created now. - ASSERT_TRUE(child->GetRenderSurface()); - ASSERT_EQ(child->GetRenderSurface(), child->render_target()); + ASSERT_TRUE(GetRenderSurface(child)); + ASSERT_EQ(GetRenderSurface(child), child->render_target()); // The child layer's draw transform should refer to its new render surface. // The screen-space transform, however, should still refer to the root. @@ -773,54 +763,6 @@ TEST_F(LayerTreeHostCommonTest, TransformsForSingleRenderSurface) { child->render_target()->screen_space_transform()); } -TEST_F(LayerTreeHostCommonTest, TransformsWhenCannotRenderToSeparateSurface) { - LayerImpl* root = root_layer_for_testing(); - LayerImpl* parent = AddChildToRoot<LayerImpl>(); - LayerImpl* child = AddChild<LayerImpl>(parent); - LayerImpl* grand_child = AddChild<LayerImpl>(child); - - gfx::Transform parent_transform; - parent_transform.Translate(10.0, 10.0); - - gfx::Transform child_transform; - child_transform.Rotate(45.0); - - root->SetBounds(gfx::Size(100, 100)); - parent->test_properties()->transform = parent_transform; - parent->SetBounds(gfx::Size(10, 10)); - child->test_properties()->transform = child_transform; - child->SetBounds(gfx::Size(10, 10)); - child->test_properties()->force_render_surface = true; - grand_child->SetPosition(gfx::PointF(2.f, 2.f)); - grand_child->SetBounds(gfx::Size(20, 20)); - grand_child->SetDrawsContent(true); - - gfx::Transform expected_grand_child_screen_space_transform; - expected_grand_child_screen_space_transform.Translate(10.0, 10.0); - expected_grand_child_screen_space_transform.Rotate(45.0); - expected_grand_child_screen_space_transform.Translate(2.0, 2.0); - - // First compute draw properties with separate surfaces enabled. - ExecuteCalculateDrawProperties(root); - - // The grand child's draw transform should be its offset wrt the child. - gfx::Transform expected_grand_child_draw_transform; - expected_grand_child_draw_transform.Translate(2.0, 2.0); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_draw_transform, - grand_child->DrawTransform()); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_screen_space_transform, - grand_child->ScreenSpaceTransform()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - - // With separate surfaces disabled, the grand child's draw transform should be - // the same as its screen space transform. - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_screen_space_transform, - grand_child->DrawTransform()); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_screen_space_transform, - grand_child->ScreenSpaceTransform()); -} - TEST_F(LayerTreeHostCommonTest, TransformsForRenderSurfaceHierarchy) { // This test creates a more complex tree and verifies it all at once. This // covers the following cases: @@ -935,33 +877,37 @@ TEST_F(LayerTreeHostCommonTest, TransformsForRenderSurfaceHierarchy) { // Only layers that are associated with render surfaces should have an actual // RenderSurface() value. - ASSERT_TRUE(root->GetRenderSurface()); - ASSERT_FALSE(child_of_root->GetRenderSurface()); - ASSERT_FALSE(grand_child_of_root->GetRenderSurface()); - - ASSERT_TRUE(render_surface1->GetRenderSurface()); - ASSERT_FALSE(child_of_rs1->GetRenderSurface()); - ASSERT_FALSE(grand_child_of_rs1->GetRenderSurface()); - - ASSERT_TRUE(render_surface2->GetRenderSurface()); - ASSERT_FALSE(child_of_rs2->GetRenderSurface()); - ASSERT_FALSE(grand_child_of_rs2->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(root)); + ASSERT_EQ(GetRenderSurface(child_of_root), GetRenderSurface(root)); + ASSERT_EQ(GetRenderSurface(grand_child_of_root), GetRenderSurface(root)); + + ASSERT_NE(GetRenderSurface(render_surface1), GetRenderSurface(root)); + ASSERT_EQ(GetRenderSurface(child_of_rs1), GetRenderSurface(render_surface1)); + ASSERT_EQ(GetRenderSurface(grand_child_of_rs1), + GetRenderSurface(render_surface1)); + + ASSERT_NE(GetRenderSurface(render_surface2), GetRenderSurface(root)); + ASSERT_NE(GetRenderSurface(render_surface2), + GetRenderSurface(render_surface1)); + ASSERT_EQ(GetRenderSurface(child_of_rs2), GetRenderSurface(render_surface2)); + ASSERT_EQ(GetRenderSurface(grand_child_of_rs2), + GetRenderSurface(render_surface2)); // Verify all render target accessors - EXPECT_EQ(root->GetRenderSurface(), parent->render_target()); - EXPECT_EQ(root->GetRenderSurface(), child_of_root->render_target()); - EXPECT_EQ(root->GetRenderSurface(), grand_child_of_root->render_target()); + EXPECT_EQ(GetRenderSurface(root), parent->render_target()); + EXPECT_EQ(GetRenderSurface(root), child_of_root->render_target()); + EXPECT_EQ(GetRenderSurface(root), grand_child_of_root->render_target()); - EXPECT_EQ(render_surface1->GetRenderSurface(), + EXPECT_EQ(GetRenderSurface(render_surface1), render_surface1->render_target()); - EXPECT_EQ(render_surface1->GetRenderSurface(), child_of_rs1->render_target()); - EXPECT_EQ(render_surface1->GetRenderSurface(), + EXPECT_EQ(GetRenderSurface(render_surface1), child_of_rs1->render_target()); + EXPECT_EQ(GetRenderSurface(render_surface1), grand_child_of_rs1->render_target()); - EXPECT_EQ(render_surface2->GetRenderSurface(), + EXPECT_EQ(GetRenderSurface(render_surface2), render_surface2->render_target()); - EXPECT_EQ(render_surface2->GetRenderSurface(), child_of_rs2->render_target()); - EXPECT_EQ(render_surface2->GetRenderSurface(), + EXPECT_EQ(GetRenderSurface(render_surface2), child_of_rs2->render_target()); + EXPECT_EQ(GetRenderSurface(render_surface2), grand_child_of_rs2->render_target()); // Verify layer draw transforms note that draw transforms are described with @@ -1007,17 +953,16 @@ TEST_F(LayerTreeHostCommonTest, TransformsForRenderSurfaceHierarchy) { // // Draw transform of render surface 1 is described with respect to root. EXPECT_TRANSFORMATION_MATRIX_EQ( - A * A * S1, render_surface1->GetRenderSurface()->draw_transform()); + A * A * S1, GetRenderSurface(render_surface1)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( - A * A * S1, - render_surface1->GetRenderSurface()->screen_space_transform()); + A * A * S1, GetRenderSurface(render_surface1)->screen_space_transform()); // Draw transform of render surface 2 is described with respect to render // surface 1. EXPECT_TRANSFORMATION_MATRIX_EQ( - SS1 * A * S2, render_surface2->GetRenderSurface()->draw_transform()); + SS1 * A * S2, GetRenderSurface(render_surface2)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( A * A * A * S2, - render_surface2->GetRenderSurface()->screen_space_transform()); + GetRenderSurface(render_surface2)->screen_space_transform()); // Sanity check. If these fail there is probably a bug in the test itself. It // is expected that we correctly set up transforms so that the y-component of @@ -1088,12 +1033,12 @@ TEST_F(LayerTreeHostCommonTest, TransformsForFlatteningLayer) { ExecuteCalculateDrawProperties(root); // The child's draw transform should have been taken by its surface. - ASSERT_TRUE(child->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(child)); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_draw_transform, - child->GetRenderSurface()->draw_transform()); + GetRenderSurface(child)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_child_screen_space_transform, - child->GetRenderSurface()->screen_space_transform()); + GetRenderSurface(child)->screen_space_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), child->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_screen_space_transform, child->ScreenSpaceTransform()); @@ -1169,10 +1114,10 @@ TEST_F(LayerTreeHostCommonTest, TransformsForDegenerateIntermediateLayer) { grand_child->SetBounds(gfx::Size(10, 10)); ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(child->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(child)); // This is the real test, the rest are sanity checks. EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - child->GetRenderSurface()->draw_transform()); + GetRenderSurface(child)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), child->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), grand_child->DrawTransform()); @@ -1199,9 +1144,9 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceWithSublayerScale) { // render_surface will have a sublayer scale because of device scale factor. float device_scale_factor = 2.0f; - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), translate, &render_surface_layer_list_impl); + root, root->bounds(), translate, &render_surface_list_impl); inputs.device_scale_factor = device_scale_factor; inputs.property_trees->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); @@ -1229,9 +1174,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { gfx::Transform translate; translate.Translate(50, 50); { - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), translate, &render_surface_layer_list_impl); + root, root->bounds(), translate, &render_surface_list_impl); inputs.property_trees->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -1239,7 +1184,7 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_TRANSFORMATION_MATRIX_EQ( translate, child->draw_properties().target_space_transform); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - root->GetRenderSurface()->draw_transform()); + GetRenderSurface(root)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(translate, child->ScreenSpaceTransform()); EXPECT_EQ(gfx::Rect(50, 50, 100, 100), child->clip_rect()); } @@ -1247,9 +1192,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { gfx::Transform scale; scale.Scale(2, 2); { - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), scale, &render_surface_layer_list_impl); + root, root->bounds(), scale, &render_surface_list_impl); inputs.property_trees->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -1257,7 +1202,7 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_TRANSFORMATION_MATRIX_EQ( scale, child->draw_properties().target_space_transform); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - root->GetRenderSurface()->draw_transform()); + GetRenderSurface(root)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(scale, child->ScreenSpaceTransform()); EXPECT_EQ(gfx::Rect(0, 0, 200, 200), child->clip_rect()); } @@ -1265,9 +1210,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { gfx::Transform rotate; rotate.Rotate(2); { - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), rotate, &render_surface_layer_list_impl); + root, root->bounds(), rotate, &render_surface_list_impl); inputs.property_trees->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -1275,7 +1220,7 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_TRANSFORMATION_MATRIX_EQ( rotate, child->draw_properties().target_space_transform); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - root->GetRenderSurface()->draw_transform()); + GetRenderSurface(root)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(rotate, child->ScreenSpaceTransform()); EXPECT_EQ(gfx::Rect(-4, 0, 104, 104), child->clip_rect()); } @@ -1285,9 +1230,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { composite.ConcatTransform(scale); composite.ConcatTransform(rotate); { - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), composite, &render_surface_layer_list_impl); + root, root->bounds(), composite, &render_surface_list_impl); inputs.property_trees->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -1295,7 +1240,7 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_TRANSFORMATION_MATRIX_EQ( composite, child->draw_properties().target_space_transform); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - root->GetRenderSurface()->draw_transform()); + GetRenderSurface(root)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(composite, child->ScreenSpaceTransform()); EXPECT_EQ(gfx::Rect(89, 103, 208, 208), child->clip_rect()); } @@ -1304,9 +1249,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { float device_scale_factor = 1.5f; { - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), translate, &render_surface_layer_list_impl); + root, root->bounds(), translate, &render_surface_list_impl); inputs.device_scale_factor = device_scale_factor; inputs.property_trees->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); @@ -1319,7 +1264,7 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { device_scaled_translate, child->draw_properties().target_space_transform); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - root->GetRenderSurface()->draw_transform()); + GetRenderSurface(root)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(device_scaled_translate, child->ScreenSpaceTransform()); EXPECT_EQ(gfx::Rect(50, 50, 150, 150), child->clip_rect()); @@ -1329,9 +1274,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { float page_scale_factor = 2.f; { - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), translate, &render_surface_layer_list_impl); + root, root->bounds(), translate, &render_surface_list_impl); inputs.page_scale_factor = page_scale_factor; inputs.page_scale_layer = root; inputs.property_trees->needs_rebuild = true; @@ -1343,7 +1288,7 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_TRANSFORMATION_MATRIX_EQ( page_scaled_translate, child->draw_properties().target_space_transform); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - root->GetRenderSurface()->draw_transform()); + GetRenderSurface(root)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(page_scaled_translate, child->ScreenSpaceTransform()); EXPECT_EQ(gfx::Rect(50, 50, 200, 200), child->clip_rect()); @@ -1353,9 +1298,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { root->test_properties()->transform = composite; { - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), composite, &render_surface_layer_list_impl); + root, root->bounds(), composite, &render_surface_list_impl); inputs.property_trees->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); gfx::Transform compositeSquared = composite; @@ -1365,13 +1310,68 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_TRANSFORMATION_MATRIX_EQ( compositeSquared, child->draw_properties().target_space_transform); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - root->GetRenderSurface()->draw_transform()); + GetRenderSurface(root)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(compositeSquared, child->ScreenSpaceTransform()); EXPECT_EQ(gfx::Rect(254, 316, 428, 428), child->clip_rect()); } } +TEST_F(LayerTreeHostCommonTest, RenderSurfaceForNonAxisAlignedClipping) { + LayerImpl* root = root_layer_for_testing(); + LayerImpl* rotated_and_transparent = AddChildToRoot<LayerImpl>(); + LayerImpl* clips_subtree = AddChild<LayerImpl>(rotated_and_transparent); + LayerImpl* draws_content = AddChild<LayerImpl>(clips_subtree); + + root->SetBounds(gfx::Size(10, 10)); + rotated_and_transparent->SetBounds(gfx::Size(10, 10)); + rotated_and_transparent->test_properties()->opacity = 0.5f; + gfx::Transform rotate; + rotate.Rotate(2); + rotated_and_transparent->test_properties()->transform = rotate; + clips_subtree->SetBounds(gfx::Size(10, 10)); + clips_subtree->SetMasksToBounds(true); + draws_content->SetBounds(gfx::Size(10, 10)); + draws_content->SetDrawsContent(true); + + ExecuteCalculateDrawProperties(root); + EffectTree& effect_tree = + root->layer_tree_impl()->property_trees()->effect_tree; + EffectNode* node = effect_tree.Node(clips_subtree->effect_tree_index()); + EXPECT_TRUE(node->has_render_surface); +} + +TEST_F(LayerTreeHostCommonTest, EffectNodesForNonAxisAlignedClips) { + LayerImpl* root = root_layer_for_testing(); + LayerImpl* rotate_and_clip = AddChildToRoot<LayerImpl>(); + LayerImpl* only_clip = AddChild<LayerImpl>(rotate_and_clip); + LayerImpl* rotate_and_clip2 = AddChild<LayerImpl>(only_clip); + + gfx::Transform rotate; + rotate.Rotate(2); + root->SetBounds(gfx::Size(10, 10)); + rotate_and_clip->SetBounds(gfx::Size(10, 10)); + rotate_and_clip->test_properties()->transform = rotate; + rotate_and_clip->SetMasksToBounds(true); + only_clip->SetBounds(gfx::Size(10, 10)); + only_clip->SetMasksToBounds(true); + rotate_and_clip2->SetBounds(gfx::Size(10, 10)); + rotate_and_clip2->test_properties()->transform = rotate; + rotate_and_clip2->SetMasksToBounds(true); + + ExecuteCalculateDrawProperties(root); + // non-axis aligned clip should create an effect node + EXPECT_NE(root->effect_tree_index(), rotate_and_clip->effect_tree_index()); + // Since only_clip's clip is in the same non-axis aligned space as + // rotate_and_clip's clip, no new effect node should be created. + EXPECT_EQ(rotate_and_clip->effect_tree_index(), + only_clip->effect_tree_index()); + // rotate_and_clip2's clip and only_clip's clip are in different non-axis + // aligned spaces. So, new effect node should be created. + EXPECT_NE(rotate_and_clip2->effect_tree_index(), + only_clip->effect_tree_index()); +} + TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForRenderSurfaceWithClippedLayer) { LayerImpl* root = root_layer_for_testing(); @@ -1392,8 +1392,8 @@ TEST_F(LayerTreeHostCommonTest, // forced to be created. Render surfaces without children or visible content // are unexpected at draw time (e.g. we might try to create a content texture // of size 0). - ASSERT_TRUE(root->GetRenderSurface()); - EXPECT_EQ(1U, render_surface_layer_list_impl()->size()); + ASSERT_TRUE(GetRenderSurface(root)); + EXPECT_EQ(1U, render_surface_list_impl()->size()); } TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForTransparentChild) { @@ -1407,19 +1407,19 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForTransparentChild) { child->SetBounds(gfx::Size(10, 10)); child->SetDrawsContent(true); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), &render_surface_layer_list); + root, root->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // Since the layer is transparent, render_surface1->GetRenderSurface() should // not have gotten added anywhere. Also, the drawable content rect should not // have been extended by the children. - ASSERT_TRUE(root->GetRenderSurface()); - EXPECT_EQ(0U, root->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(1U, render_surface_layer_list.size()); - EXPECT_EQ(root->id(), render_surface_layer_list.at(0)->id()); + ASSERT_TRUE(GetRenderSurface(root)); + EXPECT_EQ(0, GetRenderSurface(root)->num_contributors()); + EXPECT_EQ(1U, render_surface_list.size()); + EXPECT_EQ(root->id(), render_surface_list.at(0)->id()); EXPECT_EQ(gfx::Rect(), root->drawable_content_rect()); } @@ -1442,19 +1442,19 @@ TEST_F(LayerTreeHostCommonTest, root->layer_tree_impl()->SetElementIdsForTesting(); { - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), &render_surface_layer_list); + root, root->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - EXPECT_EQ(2U, render_surface_layer_list.size()); + EXPECT_EQ(2U, render_surface_list.size()); } // The layer is fully transparent, but has a background filter, so it // shouldn't be skipped and should be drawn. - ASSERT_TRUE(root->GetRenderSurface()); - EXPECT_EQ(1U, root->GetRenderSurface()->layer_list().size()); + ASSERT_TRUE(GetRenderSurface(root)); + EXPECT_EQ(1, GetRenderSurface(root)->num_contributors()); EXPECT_EQ(gfx::RectF(0, 0, 10, 10), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); EffectTree& effect_tree = root->layer_tree_impl()->property_trees()->effect_tree; EffectNode* node = effect_tree.Node(render_surface1->effect_tree_index()); @@ -1466,9 +1466,9 @@ TEST_F(LayerTreeHostCommonTest, 1.f); render_surface1->set_visible_layer_rect(gfx::Rect()); { - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), &render_surface_layer_list); + root, root->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); } @@ -1501,21 +1501,21 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForFilter) { child2->SetDrawsContent(true); child2->test_properties()->force_render_surface = true; - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), &render_surface_layer_list); + root, root->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - ASSERT_TRUE(parent->GetRenderSurface()); - EXPECT_EQ(2U, parent->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(4U, render_surface_layer_list.size()); + ASSERT_TRUE(GetRenderSurface(parent)); + EXPECT_EQ(2, GetRenderSurface(parent)->num_contributors()); + EXPECT_EQ(4U, render_surface_list.size()); // The rectangle enclosing child1 and child2 (0,0 50x50), expanded for the // blur (-30,-30 110x110), and then scaled by the scale matrix // (-60,-60 220x220). EXPECT_EQ(gfx::RectF(-60, -60, 220, 220), - parent->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(parent)->DrawableContentRect()); } TEST_F(LayerTreeHostCommonTest, DrawableContentRectForReferenceFilter) { @@ -1535,9 +1535,9 @@ TEST_F(LayerTreeHostCommonTest, DrawableContentRectForReferenceFilter) { // The render surface's size should be unaffected by the offset image filter; // it need only have a drawable content rect large enough to contain the // contents (at the new offset). - ASSERT_TRUE(child->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(child)); EXPECT_EQ(gfx::RectF(50, 50, 25, 25), - child->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(child)->DrawableContentRect()); } TEST_F(LayerTreeHostCommonTest, DrawableContentRectForReferenceFilterHighDpi) { @@ -1562,9 +1562,9 @@ TEST_F(LayerTreeHostCommonTest, DrawableContentRectForReferenceFilterHighDpi) { // it need only have a drawable content rect large enough to contain the // contents (at the new offset). All coordinates should be scaled by 2, // corresponding to the device scale factor. - ASSERT_TRUE(child->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(child)); EXPECT_EQ(gfx::RectF(100, 100, 50, 50), - child->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(child)->DrawableContentRect()); } TEST_F(LayerTreeHostCommonTest, RenderSurfaceForBlendMode) { @@ -1581,10 +1581,10 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceForBlendMode) { // Since the child layer has a blend mode other than normal, it should get // its own render surface. - ASSERT_TRUE(child->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(child)); EXPECT_EQ(1.0f, child->draw_opacity()); - EXPECT_EQ(0.5f, child->GetRenderSurface()->draw_opacity()); - EXPECT_EQ(SkBlendMode::kMultiply, child->GetRenderSurface()->BlendMode()); + EXPECT_EQ(0.5f, GetRenderSurface(child)->draw_opacity()); + EXPECT_EQ(SkBlendMode::kMultiply, GetRenderSurface(child)->BlendMode()); } TEST_F(LayerTreeHostCommonTest, RenderSurfaceDrawOpacity) { @@ -1606,73 +1606,13 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceDrawOpacity) { surface2->test_properties()->force_render_surface = true; ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(surface1->GetRenderSurface()); - ASSERT_FALSE(not_surface->GetRenderSurface()); - ASSERT_TRUE(surface2->GetRenderSurface()); - EXPECT_EQ(0.5f, surface1->GetRenderSurface()->draw_opacity()); + ASSERT_TRUE(GetRenderSurface(surface1)); + ASSERT_EQ(GetRenderSurface(not_surface), GetRenderSurface(surface1)); + ASSERT_TRUE(GetRenderSurface(surface2)); + EXPECT_EQ(0.5f, GetRenderSurface(surface1)->draw_opacity()); // surface2's draw opacity should include the opacity of not-surface and // itself, but not the opacity of surface1. - EXPECT_EQ(0.25f, surface2->GetRenderSurface()->draw_opacity()); -} - -TEST_F(LayerTreeHostCommonTest, DrawOpacityWhenCannotRenderToSeparateSurface) { - // Tests that when separate surfaces are disabled, a layer's draw opacity is - // the product of all ancestor layer opacties and the layer's own opacity. - // (Rendering will still be incorrect in situations where we really do need - // surfaces to apply opacity, such as when we have overlapping layers with an - // ancestor whose opacity is <1.) - LayerImpl* root = root_layer_for_testing(); - LayerImpl* parent = AddChild<LayerImpl>(root); - LayerImpl* child1 = AddChild<LayerImpl>(parent); - LayerImpl* child2 = AddChild<LayerImpl>(parent); - LayerImpl* grand_child = AddChild<LayerImpl>(child1); - LayerImpl* leaf_node1 = AddChild<LayerImpl>(grand_child); - LayerImpl* leaf_node2 = AddChild<LayerImpl>(child2); - - root->SetBounds(gfx::Size(100, 100)); - root->SetDrawsContent(true); - parent->SetBounds(gfx::Size(100, 100)); - parent->SetDrawsContent(true); - child1->SetBounds(gfx::Size(100, 100)); - child1->SetDrawsContent(true); - child1->test_properties()->opacity = 0.5f; - child1->test_properties()->force_render_surface = true; - child2->SetBounds(gfx::Size(100, 100)); - child2->SetDrawsContent(true); - grand_child->SetBounds(gfx::Size(100, 100)); - grand_child->SetDrawsContent(true); - grand_child->test_properties()->opacity = 0.5f; - grand_child->test_properties()->force_render_surface = true; - leaf_node1->SetBounds(gfx::Size(100, 100)); - leaf_node1->SetDrawsContent(true); - leaf_node1->test_properties()->opacity = 0.5f; - leaf_node2->SetBounds(gfx::Size(100, 100)); - leaf_node2->SetDrawsContent(true); - leaf_node2->test_properties()->opacity = 0.5f; - - // With surfaces enabled, each layer's draw opacity is the product of layer - // opacities on the path from the layer to its render target, not including - // the opacity of the layer that owns the target surface (since that opacity - // is applied by the surface). - ExecuteCalculateDrawProperties(root); - EXPECT_EQ(1.f, root->draw_opacity()); - EXPECT_EQ(1.f, parent->draw_opacity()); - EXPECT_EQ(1.f, child1->draw_opacity()); - EXPECT_EQ(1.f, child2->draw_opacity()); - EXPECT_EQ(1.f, grand_child->draw_opacity()); - EXPECT_EQ(0.5f, leaf_node1->draw_opacity()); - EXPECT_EQ(0.5f, leaf_node2->draw_opacity()); - - // With surfaces disabled, each layer's draw opacity is the product of layer - // opacities on the path from the layer to the root. - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_EQ(1.f, root->draw_opacity()); - EXPECT_EQ(1.f, parent->draw_opacity()); - EXPECT_EQ(0.5f, child1->draw_opacity()); - EXPECT_EQ(1.f, child2->draw_opacity()); - EXPECT_EQ(0.25f, grand_child->draw_opacity()); - EXPECT_EQ(0.125f, leaf_node1->draw_opacity()); - EXPECT_EQ(0.5f, leaf_node2->draw_opacity()); + EXPECT_EQ(0.25f, GetRenderSurface(surface2)->draw_opacity()); } TEST_F(LayerTreeHostCommonTest, ForceRenderSurface) { @@ -1690,16 +1630,16 @@ TEST_F(LayerTreeHostCommonTest, ForceRenderSurface) { ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); // The root layer always creates a render surface - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_TRUE(render_surface1->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(render_surface1), GetRenderSurface(root)); } { render_surface1->test_properties()->force_render_surface = false; render_surface1->layer_tree_impl()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_FALSE(render_surface1->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(render_surface1), GetRenderSurface(root)); } } @@ -1725,9 +1665,9 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfacesFlattenScreenSpaceTransform) { grand_child->test_properties()->should_flatten_transform = false; ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(parent->GetRenderSurface()); - EXPECT_FALSE(child->GetRenderSurface()); - EXPECT_FALSE(grand_child->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(parent)); + EXPECT_EQ(GetRenderSurface(child), GetRenderSurface(parent)); + EXPECT_EQ(GetRenderSurface(grand_child), GetRenderSurface(parent)); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), child->DrawTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), @@ -1746,7 +1686,7 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfacesFlattenScreenSpaceTransform) { TEST_F(LayerTreeHostCommonTest, ClipRectCullsRenderSurfaces) { // The entire subtree of layers that are outside the clip rect should be - // culled away, and should not affect the render_surface_layer_list. + // culled away, and should not affect the render_surface_list. // // The test tree is set up as follows: // - all layers except the leaf_nodes are forced to be a new render surface @@ -1765,9 +1705,8 @@ TEST_F(LayerTreeHostCommonTest, ClipRectCullsRenderSurfaces) { LayerImpl* grand_child = AddChild<LayerImpl>(child); LayerImpl* great_grand_child = AddChild<LayerImpl>(grand_child); - // leaf_node1 ensures that root and child are kept on the - // render_surface_layer_list, even though grand_child and great_grand_child - // should be clipped. + // leaf_node1 ensures that root and child are kept on the render_surface_list, + // even though grand_child and great_grand_child should be clipped. LayerImpl* leaf_node1 = AddChild<LayerImpl>(child); LayerImpl* leaf_node2 = AddChild<LayerImpl>(great_grand_child); @@ -1784,9 +1723,9 @@ TEST_F(LayerTreeHostCommonTest, ClipRectCullsRenderSurfaces) { leaf_node2->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); - ASSERT_EQ(2U, render_surface_layer_list_impl()->size()); - EXPECT_EQ(root->id(), render_surface_layer_list_impl()->at(0)->id()); - EXPECT_EQ(child->id(), render_surface_layer_list_impl()->at(1)->id()); + ASSERT_EQ(2U, render_surface_list_impl()->size()); + EXPECT_EQ(root->id(), render_surface_list_impl()->at(0)->id()); + EXPECT_EQ(child->id(), render_surface_list_impl()->at(1)->id()); } TEST_F(LayerTreeHostCommonTest, ClipRectCullsSurfaceWithoutVisibleContent) { @@ -1802,7 +1741,7 @@ TEST_F(LayerTreeHostCommonTest, ClipRectCullsSurfaceWithoutVisibleContent) { // In this configuration, grand_child should be outside the clipped // content rect of the child, making grand_child not appear in the - // render_surface_layer_list. + // render_surface_list. LayerImpl* root = root_layer_for_testing(); LayerImpl* child = AddChildToRoot<LayerImpl>(); @@ -1821,9 +1760,9 @@ TEST_F(LayerTreeHostCommonTest, ClipRectCullsSurfaceWithoutVisibleContent) { ExecuteCalculateDrawProperties(root); // We should cull child and grand_child from the - // render_surface_layer_list. - ASSERT_EQ(1U, render_surface_layer_list_impl()->size()); - EXPECT_EQ(root->id(), render_surface_layer_list_impl()->at(0)->id()); + // render_surface_list. + ASSERT_EQ(1U, render_surface_list_impl()->size()); + EXPECT_EQ(root->id(), render_surface_list_impl()->at(0)->id()); } TEST_F(LayerTreeHostCommonTest, IsClippedIsSetCorrectlyLayerImpl) { @@ -1866,15 +1805,15 @@ TEST_F(LayerTreeHostCommonTest, IsClippedIsSetCorrectlyLayerImpl) { // Case 1: nothing is clipped except the root render surface. ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(root->GetRenderSurface()); - ASSERT_TRUE(child2->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(root)); + ASSERT_TRUE(GetRenderSurface(child2)); EXPECT_FALSE(root->is_clipped()); - EXPECT_TRUE(root->GetRenderSurface()->is_clipped()); + EXPECT_TRUE(GetRenderSurface(root)->is_clipped()); EXPECT_FALSE(parent->is_clipped()); EXPECT_FALSE(child1->is_clipped()); EXPECT_FALSE(child2->is_clipped()); - EXPECT_FALSE(child2->GetRenderSurface()->is_clipped()); + EXPECT_FALSE(GetRenderSurface(child2)->is_clipped()); EXPECT_FALSE(grand_child->is_clipped()); EXPECT_FALSE(leaf_node1->is_clipped()); EXPECT_FALSE(leaf_node2->is_clipped()); @@ -1888,15 +1827,15 @@ TEST_F(LayerTreeHostCommonTest, IsClippedIsSetCorrectlyLayerImpl) { ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(root->GetRenderSurface()); - ASSERT_TRUE(child2->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(root)); + ASSERT_TRUE(GetRenderSurface(child2)); EXPECT_FALSE(root->is_clipped()); - EXPECT_TRUE(root->GetRenderSurface()->is_clipped()); + EXPECT_TRUE(GetRenderSurface(root)->is_clipped()); EXPECT_TRUE(parent->is_clipped()); EXPECT_TRUE(child1->is_clipped()); EXPECT_FALSE(child2->is_clipped()); - EXPECT_TRUE(child2->GetRenderSurface()->is_clipped()); + EXPECT_TRUE(GetRenderSurface(child2)->is_clipped()); EXPECT_TRUE(grand_child->is_clipped()); EXPECT_TRUE(leaf_node1->is_clipped()); EXPECT_FALSE(leaf_node2->is_clipped()); @@ -1910,15 +1849,15 @@ TEST_F(LayerTreeHostCommonTest, IsClippedIsSetCorrectlyLayerImpl) { ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(root->GetRenderSurface()); - ASSERT_TRUE(child2->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(root)); + ASSERT_TRUE(GetRenderSurface(child2)); EXPECT_FALSE(root->is_clipped()); - EXPECT_TRUE(root->GetRenderSurface()->is_clipped()); + EXPECT_TRUE(GetRenderSurface(root)->is_clipped()); EXPECT_FALSE(parent->is_clipped()); EXPECT_FALSE(child1->is_clipped()); EXPECT_TRUE(child2->is_clipped()); - EXPECT_FALSE(child2->GetRenderSurface()->is_clipped()); + EXPECT_FALSE(GetRenderSurface(child2)->is_clipped()); EXPECT_FALSE(grand_child->is_clipped()); EXPECT_FALSE(leaf_node1->is_clipped()); EXPECT_TRUE(leaf_node2->is_clipped()); @@ -1958,154 +1897,6 @@ TEST_F(LayerTreeHostCommonTest, UpdateClipRectCorrectly) { EXPECT_EQ(gfx::Rect(), child->clip_rect()); } -TEST_F(LayerTreeHostCommonTest, IsClippedWhenCannotRenderToSeparateSurface) { - // Tests that when separate surfaces are disabled, is_clipped is true exactly - // when a layer or its ancestor has a clip; in particular, if a layer - // is_clipped, so is its entire subtree (since there are no render surfaces - // that can reset is_clipped). - LayerImpl* root = root_layer_for_testing(); - LayerImpl* parent = AddChild<LayerImpl>(root); - LayerImpl* child1 = AddChild<LayerImpl>(parent); - LayerImpl* child2 = AddChild<LayerImpl>(parent); - LayerImpl* grand_child = AddChild<LayerImpl>(child1); - LayerImpl* leaf_node1 = AddChild<LayerImpl>(grand_child); - LayerImpl* leaf_node2 = AddChild<LayerImpl>(child2); - - root->SetBounds(gfx::Size(100, 100)); - root->SetDrawsContent(true); - parent->SetBounds(gfx::Size(100, 100)); - parent->SetDrawsContent(true); - child1->SetBounds(gfx::Size(100, 100)); - child1->SetDrawsContent(true); - child1->test_properties()->force_render_surface = true; - child2->SetBounds(gfx::Size(100, 100)); - child2->SetDrawsContent(true); - grand_child->SetBounds(gfx::Size(100, 100)); - grand_child->SetDrawsContent(true); - grand_child->test_properties()->force_render_surface = true; - leaf_node1->SetBounds(gfx::Size(100, 100)); - leaf_node1->SetDrawsContent(true); - leaf_node2->SetBounds(gfx::Size(100, 100)); - leaf_node2->SetDrawsContent(true); - - // Case 1: Nothing is clipped. In this case, is_clipped is always false, with - // or without surfaces. - ExecuteCalculateDrawProperties(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_FALSE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_FALSE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_FALSE(leaf_node2->is_clipped()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_FALSE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_FALSE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_FALSE(leaf_node2->is_clipped()); - - // Case 2: The root is clipped. With surfaces, this only persists until the - // next render surface. Without surfaces, the entire tree is clipped. - root->SetMasksToBounds(true); - host_impl()->active_tree()->property_trees()->needs_rebuild = true; - ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->is_clipped()); - EXPECT_TRUE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_TRUE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_TRUE(root->is_clipped()); - EXPECT_TRUE(parent->is_clipped()); - EXPECT_TRUE(child1->is_clipped()); - EXPECT_TRUE(child2->is_clipped()); - EXPECT_TRUE(grand_child->is_clipped()); - EXPECT_TRUE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - - root->SetMasksToBounds(false); - - // Case 3: The parent is clipped. Again, with surfaces, this only persists - // until the next render surface. Without surfaces, parent's entire subtree is - // clipped. - parent->SetMasksToBounds(true); - host_impl()->active_tree()->property_trees()->needs_rebuild = true; - ExecuteCalculateDrawProperties(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_TRUE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_TRUE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_TRUE(parent->is_clipped()); - EXPECT_TRUE(child1->is_clipped()); - EXPECT_TRUE(child2->is_clipped()); - EXPECT_TRUE(grand_child->is_clipped()); - EXPECT_TRUE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - - parent->SetMasksToBounds(false); - - // Case 4: child1 is clipped. With surfaces, only child1 is_clipped, since it - // has no non-surface children. Without surfaces, child1's entire subtree is - // clipped. - child1->SetMasksToBounds(true); - host_impl()->active_tree()->property_trees()->needs_rebuild = true; - ExecuteCalculateDrawProperties(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_FALSE(parent->is_clipped()); - EXPECT_TRUE(child1->is_clipped()); - EXPECT_FALSE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_FALSE(leaf_node2->is_clipped()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_FALSE(parent->is_clipped()); - EXPECT_TRUE(child1->is_clipped()); - EXPECT_FALSE(child2->is_clipped()); - EXPECT_TRUE(grand_child->is_clipped()); - EXPECT_TRUE(leaf_node1->is_clipped()); - EXPECT_FALSE(leaf_node2->is_clipped()); - - child1->SetMasksToBounds(false); - - // Case 5: Only the leaf nodes are clipped. The behavior with and without - // surfaces is the same. - leaf_node1->SetMasksToBounds(true); - leaf_node2->SetMasksToBounds(true); - host_impl()->active_tree()->property_trees()->needs_rebuild = true; - ExecuteCalculateDrawProperties(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_FALSE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_FALSE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_TRUE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_FALSE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_FALSE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_TRUE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); -} - TEST_F(LayerTreeHostCommonTest, DrawableContentRectForLayers) { // Verify that layers get the appropriate DrawableContentRect when their // parent MasksToBounds is true. @@ -2201,286 +1992,18 @@ TEST_F(LayerTreeHostCommonTest, ClipRectIsPropagatedCorrectlyToSurfaces) { leaf_node4->SetDrawsContent(true); ExecuteCalculateDrawProperties(parent); - ASSERT_TRUE(grand_child1->GetRenderSurface()); - ASSERT_TRUE(grand_child2->GetRenderSurface()); - ASSERT_TRUE(grand_child3->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(grand_child1)); + ASSERT_TRUE(GetRenderSurface(grand_child2)); + ASSERT_TRUE(GetRenderSurface(grand_child3)); // Surfaces are clipped by their parent, but un-affected by the owning layer's // MasksToBounds. EXPECT_EQ(gfx::Rect(0, 0, 20, 20), - grand_child1->GetRenderSurface()->clip_rect()); + GetRenderSurface(grand_child1)->clip_rect()); EXPECT_EQ(gfx::Rect(0, 0, 20, 20), - grand_child2->GetRenderSurface()->clip_rect()); + GetRenderSurface(grand_child2)->clip_rect()); EXPECT_EQ(gfx::Rect(0, 0, 20, 20), - grand_child3->GetRenderSurface()->clip_rect()); -} - -TEST_F(LayerTreeHostCommonTest, ClipRectWhenCannotRenderToSeparateSurface) { - // Tests that when separate surfaces are disabled, a layer's clip_rect is the - // intersection of all ancestor clips in screen space; in particular, if a - // layer masks to bounds, it contributes to the clip_rect of all layers in its - // subtree (since there are no render surfaces that can reset the clip_rect). - LayerImpl* root = root_layer_for_testing(); - LayerImpl* parent = AddChild<LayerImpl>(root); - LayerImpl* child1 = AddChild<LayerImpl>(parent); - LayerImpl* child2 = AddChild<LayerImpl>(parent); - LayerImpl* grand_child = AddChild<LayerImpl>(child1); - LayerImpl* leaf_node1 = AddChild<LayerImpl>(grand_child); - LayerImpl* leaf_node2 = AddChild<LayerImpl>(child2); - - root->SetBounds(gfx::Size(100, 100)); - parent->SetPosition(gfx::PointF(2.f, 2.f)); - parent->SetBounds(gfx::Size(400, 400)); - child1->SetPosition(gfx::PointF(4.f, 4.f)); - child1->SetBounds(gfx::Size(800, 800)); - child2->SetPosition(gfx::PointF(3.f, 3.f)); - child2->SetBounds(gfx::Size(800, 800)); - grand_child->SetPosition(gfx::PointF(8.f, 8.f)); - grand_child->SetBounds(gfx::Size(1500, 1500)); - leaf_node1->SetPosition(gfx::PointF(16.f, 16.f)); - leaf_node1->SetBounds(gfx::Size(2000, 2000)); - leaf_node2->SetPosition(gfx::PointF(9.f, 9.f)); - leaf_node2->SetBounds(gfx::Size(2000, 2000)); - - root->SetDrawsContent(true); - parent->SetDrawsContent(true); - child1->SetDrawsContent(true); - child2->SetDrawsContent(true); - grand_child->SetDrawsContent(true); - leaf_node1->SetDrawsContent(true); - leaf_node2->SetDrawsContent(true); - - root->test_properties()->force_render_surface = true; - child1->test_properties()->force_render_surface = true; - grand_child->test_properties()->force_render_surface = true; - - // Case 1: Nothing is clipped. In this case, each layer's clip rect is its - // bounds in target space. The only thing that changes when surfaces are - // disabled is that target space is always screen space. - ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_FALSE(parent->GetRenderSurface()); - EXPECT_TRUE(child1->GetRenderSurface()); - EXPECT_FALSE(child2->GetRenderSurface()); - EXPECT_TRUE(grand_child->GetRenderSurface()); - EXPECT_FALSE(leaf_node1->GetRenderSurface()); - EXPECT_FALSE(leaf_node2->GetRenderSurface()); - EXPECT_FALSE(root->is_clipped()); - EXPECT_FALSE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_FALSE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_FALSE(leaf_node2->is_clipped()); - EXPECT_TRUE(root->GetRenderSurface()->is_clipped()); - EXPECT_FALSE(child1->GetRenderSurface()->is_clipped()); - EXPECT_FALSE(grand_child->GetRenderSurface()->is_clipped()); - EXPECT_EQ(gfx::Rect(100, 100), root->GetRenderSurface()->clip_rect()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_FALSE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_FALSE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_FALSE(leaf_node2->is_clipped()); - EXPECT_TRUE(root->GetRenderSurface()->is_clipped()); - EXPECT_EQ(gfx::Rect(100, 100), root->GetRenderSurface()->clip_rect()); - - // Case 2: The root is clipped. In this case, layers that draw into the root - // render surface are clipped by the root's bounds. - root->SetMasksToBounds(true); - host_impl()->active_tree()->property_trees()->needs_rebuild = true; - root->test_properties()->force_render_surface = true; - child1->test_properties()->force_render_surface = true; - grand_child->test_properties()->force_render_surface = true; - ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_FALSE(parent->GetRenderSurface()); - EXPECT_TRUE(child1->GetRenderSurface()); - EXPECT_FALSE(child2->GetRenderSurface()); - EXPECT_TRUE(grand_child->GetRenderSurface()); - EXPECT_FALSE(leaf_node1->GetRenderSurface()); - EXPECT_FALSE(leaf_node2->GetRenderSurface()); - EXPECT_TRUE(root->is_clipped()); - EXPECT_TRUE(parent->is_clipped()); - EXPECT_FALSE(child1->is_clipped()); - EXPECT_TRUE(child1->GetRenderSurface()->is_clipped()); - EXPECT_TRUE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_FALSE(grand_child->GetRenderSurface()->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - EXPECT_EQ(gfx::Rect(100, 100), root->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), parent->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), child1->GetRenderSurface()->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), child2->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), leaf_node2->clip_rect()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_TRUE(root->is_clipped()); - EXPECT_TRUE(parent->is_clipped()); - EXPECT_TRUE(child1->is_clipped()); - EXPECT_TRUE(child2->is_clipped()); - EXPECT_TRUE(grand_child->is_clipped()); - EXPECT_TRUE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - EXPECT_EQ(gfx::Rect(100, 100), root->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), parent->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), child1->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), child2->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), grand_child->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), leaf_node1->clip_rect()); - EXPECT_EQ(gfx::Rect(100, 100), leaf_node2->clip_rect()); - - root->SetMasksToBounds(false); - - // Case 3: The parent and child1 are clipped. When surfaces are enabled, the - // parent clip rect only contributes to the subtree rooted at child2, since - // the subtree rooted at child1 renders into a separate surface. Similarly, - // child1's clip rect doesn't contribute to its descendants, since its only - // child is a render surface. However, without surfaces, these clip rects - // contribute to all descendants. - parent->SetMasksToBounds(true); - child1->SetMasksToBounds(true); - host_impl()->active_tree()->property_trees()->needs_rebuild = true; - root->test_properties()->force_render_surface = true; - child1->test_properties()->force_render_surface = true; - grand_child->test_properties()->force_render_surface = true; - ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_FALSE(parent->GetRenderSurface()); - EXPECT_TRUE(child1->GetRenderSurface()); - EXPECT_FALSE(child2->GetRenderSurface()); - EXPECT_TRUE(grand_child->GetRenderSurface()); - EXPECT_FALSE(leaf_node1->GetRenderSurface()); - EXPECT_FALSE(leaf_node2->GetRenderSurface()); - EXPECT_FALSE(root->is_clipped()); - EXPECT_TRUE(root->GetRenderSurface()->is_clipped()); - EXPECT_TRUE(parent->is_clipped()); - EXPECT_TRUE(child1->is_clipped()); - EXPECT_TRUE(child2->is_clipped()); - EXPECT_FALSE(grand_child->is_clipped()); - EXPECT_TRUE(grand_child->GetRenderSurface()->is_clipped()); - EXPECT_FALSE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - EXPECT_EQ(gfx::Rect(100, 100), root->GetRenderSurface()->clip_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), parent->clip_rect()); - EXPECT_EQ(gfx::Rect(800, 800), child1->clip_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), child2->clip_rect()); - EXPECT_EQ(gfx::Rect(800, 800), grand_child->GetRenderSurface()->clip_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), leaf_node2->clip_rect()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_FALSE(root->is_clipped()); - EXPECT_TRUE(root->GetRenderSurface()->is_clipped()); - EXPECT_TRUE(parent->is_clipped()); - EXPECT_TRUE(child1->is_clipped()); - EXPECT_TRUE(child2->is_clipped()); - EXPECT_TRUE(grand_child->is_clipped()); - EXPECT_TRUE(leaf_node1->is_clipped()); - EXPECT_TRUE(leaf_node2->is_clipped()); - EXPECT_EQ(gfx::Rect(100, 100), root->GetRenderSurface()->clip_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), parent->clip_rect()); - EXPECT_EQ(gfx::Rect(6, 6, 396, 396), child1->clip_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), child2->clip_rect()); - EXPECT_EQ(gfx::Rect(6, 6, 396, 396), grand_child->clip_rect()); - EXPECT_EQ(gfx::Rect(6, 6, 396, 396), leaf_node1->clip_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), leaf_node2->clip_rect()); -} - -TEST_F(LayerTreeHostCommonTest, HitTestingWhenSurfacesDisabled) { - LayerImpl* root = root_layer_for_testing(); - LayerImpl* parent = AddChild<LayerImpl>(root); - LayerImpl* child = AddChild<LayerImpl>(parent); - LayerImpl* grand_child = AddChild<LayerImpl>(child); - LayerImpl* leaf_node = AddChild<LayerImpl>(grand_child); - - root->SetBounds(gfx::Size(100, 100)); - parent->SetPosition(gfx::PointF(2.f, 2.f)); - parent->SetBounds(gfx::Size(400, 400)); - parent->SetMasksToBounds(true); - child->SetPosition(gfx::PointF(4.f, 4.f)); - child->SetBounds(gfx::Size(800, 800)); - child->SetMasksToBounds(true); - child->test_properties()->force_render_surface = true; - grand_child->SetPosition(gfx::PointF(8.f, 8.f)); - grand_child->SetBounds(gfx::Size(1500, 1500)); - grand_child->test_properties()->force_render_surface = true; - leaf_node->SetPosition(gfx::PointF(16.f, 16.f)); - leaf_node->SetBounds(gfx::Size(2000, 2000)); - - root->SetDrawsContent(true); - parent->SetDrawsContent(true); - child->SetDrawsContent(true); - grand_child->SetDrawsContent(true); - leaf_node->SetDrawsContent(true); - - host_impl()->set_resourceless_software_draw_for_testing(); - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - gfx::PointF test_point(90.f, 90.f); - LayerImpl* result_layer = - root->layer_tree_impl()->FindLayerThatIsHitByPoint(test_point); - ASSERT_TRUE(result_layer); - EXPECT_EQ(leaf_node, result_layer); -} - -TEST_F(LayerTreeHostCommonTest, SurfacesDisabledAndReEnabled) { - // Tests that draw properties are computed correctly when we disable and then - // re-enable separate surfaces. - LayerImpl* root = root_layer_for_testing(); - LayerImpl* parent = AddChild<LayerImpl>(root); - LayerImpl* child = AddChild<LayerImpl>(parent); - LayerImpl* grand_child = AddChild<LayerImpl>(child); - LayerImpl* leaf_node = AddChild<LayerImpl>(grand_child); - - root->SetBounds(gfx::Size(100, 100)); - parent->SetPosition(gfx::PointF(2.f, 2.f)); - parent->SetBounds(gfx::Size(400, 400)); - parent->SetMasksToBounds(true); - child->SetPosition(gfx::PointF(4.f, 4.f)); - child->SetBounds(gfx::Size(800, 800)); - child->SetMasksToBounds(true); - child->test_properties()->force_render_surface = true; - grand_child->SetPosition(gfx::PointF(8.f, 8.f)); - grand_child->SetBounds(gfx::Size(1500, 1500)); - grand_child->test_properties()->force_render_surface = true; - leaf_node->SetPosition(gfx::PointF(16.f, 16.f)); - leaf_node->SetBounds(gfx::Size(2000, 2000)); - - root->SetDrawsContent(true); - parent->SetDrawsContent(true); - child->SetDrawsContent(true); - grand_child->SetDrawsContent(true); - leaf_node->SetDrawsContent(true); - - gfx::Transform expected_leaf_draw_transform_with_surfaces; - expected_leaf_draw_transform_with_surfaces.Translate(16.0, 16.0); - - gfx::Transform expected_leaf_draw_transform_without_surfaces; - expected_leaf_draw_transform_without_surfaces.Translate(30.0, 30.0); - - ExecuteCalculateDrawProperties(root); - EXPECT_FALSE(leaf_node->is_clipped()); - EXPECT_TRUE(leaf_node->render_target()->is_clipped()); - EXPECT_EQ(gfx::Rect(16, 16, 2000, 2000), leaf_node->drawable_content_rect()); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_leaf_draw_transform_with_surfaces, - leaf_node->DrawTransform()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_TRUE(leaf_node->is_clipped()); - EXPECT_EQ(gfx::Rect(6, 6, 396, 396), leaf_node->clip_rect()); - EXPECT_EQ(gfx::Rect(30, 30, 372, 372), leaf_node->drawable_content_rect()); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_leaf_draw_transform_without_surfaces, - leaf_node->DrawTransform()); - - ExecuteCalculateDrawProperties(root); - EXPECT_FALSE(leaf_node->is_clipped()); - EXPECT_TRUE(leaf_node->render_target()->is_clipped()); - EXPECT_EQ(gfx::Rect(16, 16, 2000, 2000), leaf_node->drawable_content_rect()); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_leaf_draw_transform_with_surfaces, - leaf_node->DrawTransform()); + GetRenderSurface(grand_child3)->clip_rect()); } TEST_F(LayerTreeHostCommonTest, AnimationsForRenderSurfaceHierarchy) { @@ -2581,33 +2104,37 @@ TEST_F(LayerTreeHostCommonTest, AnimationsForRenderSurfaceHierarchy) { // Only layers that are associated with render surfaces should have an actual // RenderSurface() value. - ASSERT_TRUE(root->GetRenderSurface()); - ASSERT_FALSE(child_of_root->GetRenderSurface()); - ASSERT_FALSE(grand_child_of_root->GetRenderSurface()); - - ASSERT_TRUE(render_surface1->GetRenderSurface()); - ASSERT_FALSE(child_of_rs1->GetRenderSurface()); - ASSERT_FALSE(grand_child_of_rs1->GetRenderSurface()); - - ASSERT_TRUE(render_surface2->GetRenderSurface()); - ASSERT_FALSE(child_of_rs2->GetRenderSurface()); - ASSERT_FALSE(grand_child_of_rs2->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(root)); + ASSERT_EQ(GetRenderSurface(child_of_root), GetRenderSurface(root)); + ASSERT_EQ(GetRenderSurface(grand_child_of_root), GetRenderSurface(root)); + + ASSERT_NE(GetRenderSurface(render_surface1), GetRenderSurface(root)); + ASSERT_EQ(GetRenderSurface(child_of_rs1), GetRenderSurface(render_surface1)); + ASSERT_EQ(GetRenderSurface(grand_child_of_rs1), + GetRenderSurface(render_surface1)); + + ASSERT_NE(GetRenderSurface(render_surface2), GetRenderSurface(root)); + ASSERT_NE(GetRenderSurface(render_surface2), + GetRenderSurface(render_surface1)); + ASSERT_EQ(GetRenderSurface(child_of_rs2), GetRenderSurface(render_surface2)); + ASSERT_EQ(GetRenderSurface(grand_child_of_rs2), + GetRenderSurface(render_surface2)); // Verify all render target accessors - EXPECT_EQ(root->GetRenderSurface(), root->render_target()); - EXPECT_EQ(root->GetRenderSurface(), child_of_root->render_target()); - EXPECT_EQ(root->GetRenderSurface(), grand_child_of_root->render_target()); + EXPECT_EQ(GetRenderSurface(root), root->render_target()); + EXPECT_EQ(GetRenderSurface(root), child_of_root->render_target()); + EXPECT_EQ(GetRenderSurface(root), grand_child_of_root->render_target()); - EXPECT_EQ(render_surface1->GetRenderSurface(), + EXPECT_EQ(GetRenderSurface(render_surface1), render_surface1->render_target()); - EXPECT_EQ(render_surface1->GetRenderSurface(), child_of_rs1->render_target()); - EXPECT_EQ(render_surface1->GetRenderSurface(), + EXPECT_EQ(GetRenderSurface(render_surface1), child_of_rs1->render_target()); + EXPECT_EQ(GetRenderSurface(render_surface1), grand_child_of_rs1->render_target()); - EXPECT_EQ(render_surface2->GetRenderSurface(), + EXPECT_EQ(GetRenderSurface(render_surface2), render_surface2->render_target()); - EXPECT_EQ(render_surface2->GetRenderSurface(), child_of_rs2->render_target()); - EXPECT_EQ(render_surface2->GetRenderSurface(), + EXPECT_EQ(GetRenderSurface(render_surface2), child_of_rs2->render_target()); + EXPECT_EQ(GetRenderSurface(render_surface2), grand_child_of_rs2->render_target()); // Verify screen_space_transform_is_animating values @@ -2662,6 +2189,11 @@ TEST_F(LayerTreeHostCommonTest, LargeTransforms) { EXPECT_EQ(gfx::Rect(), grand_child->visible_layer_rect()); } +static bool TransformIsAnimating(LayerImpl* layer) { + return layer->GetMutatorHost()->IsAnimatingTransformProperty( + layer->element_id(), layer->GetElementTypeForAnimation()); +} + TEST_F(LayerTreeHostCommonTest, ScreenSpaceTransformIsAnimatingWithDelayedAnimation) { LayerImpl* root = root_layer_for_testing(); @@ -2694,7 +2226,7 @@ TEST_F(LayerTreeHostCommonTest, EXPECT_FALSE(root->screen_space_transform_is_animating()); EXPECT_FALSE(child->screen_space_transform_is_animating()); - EXPECT_FALSE(grand_child->TransformIsAnimating()); + EXPECT_FALSE(TransformIsAnimating(grand_child)); EXPECT_TRUE(grand_child->HasPotentiallyRunningTransformAnimation()); EXPECT_TRUE(grand_child->screen_space_transform_is_animating()); EXPECT_TRUE(great_grand_child->screen_space_transform_is_animating()); @@ -3031,7 +2563,7 @@ TEST_F(LayerTreeHostCommonTest, ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::RectF(100.f, 100.f), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); // In target space, not clipped. EXPECT_EQ(gfx::Rect(60, 70, 100, 100), root->drawable_content_rect()); // In layer space, clipped. @@ -3056,7 +2588,7 @@ TEST_F(LayerTreeHostCommonTest, DrawableAndVisibleContentRectsForSimpleLayers) { ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::RectF(100.f, 100.f), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); // Layers that do not draw content should have empty visible_layer_rects. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect()); @@ -3095,7 +2627,7 @@ TEST_F(LayerTreeHostCommonTest, ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::RectF(100.f, 100.f), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); // Layers that do not draw content should have empty visible content rects. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect()); @@ -3152,7 +2684,7 @@ TEST_F(LayerTreeHostCommonTest, VisibleRectWithClippingAndFilters) { ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::Rect(50, 50, 10, 10), filter_child->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(10, 10), filter->GetRenderSurface()->content_rect()); + EXPECT_EQ(gfx::Rect(10, 10), GetRenderSurface(filter)->content_rect()); FilterOperations blur_filter; blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f)); @@ -3163,7 +2695,7 @@ TEST_F(LayerTreeHostCommonTest, VisibleRectWithClippingAndFilters) { EXPECT_EQ(gfx::Rect(38, 38, 34, 34), filter_child->visible_layer_rect()); EXPECT_EQ(gfx::Rect(-12, -12, 34, 34), - filter->GetRenderSurface()->content_rect()); + GetRenderSurface(filter)->content_rect()); gfx::Transform vertical_flip; vertical_flip.Scale(1, -1); @@ -3180,7 +2712,7 @@ TEST_F(LayerTreeHostCommonTest, VisibleRectWithClippingAndFilters) { EXPECT_EQ(gfx::Rect(50, 40, 10, 20), filter_child->visible_layer_rect()); EXPECT_EQ(gfx::Rect(0, -10, 10, 20), - filter->GetRenderSurface()->content_rect()); + GetRenderSurface(filter)->content_rect()); } TEST_F(LayerTreeHostCommonTest, VisibleRectWithScalingClippingAndFilters) { @@ -3205,7 +2737,7 @@ TEST_F(LayerTreeHostCommonTest, VisibleRectWithScalingClippingAndFilters) { ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::Rect(50, 50, 10, 10), filter_child->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(30, 30), filter->GetRenderSurface()->content_rect()); + EXPECT_EQ(gfx::Rect(30, 30), GetRenderSurface(filter)->content_rect()); FilterOperations blur_filter; blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f)); @@ -3216,7 +2748,7 @@ TEST_F(LayerTreeHostCommonTest, VisibleRectWithScalingClippingAndFilters) { EXPECT_EQ(gfx::Rect(38, 38, 34, 34), filter_child->visible_layer_rect()); EXPECT_EQ(gfx::Rect(-36, -36, 102, 102), - filter->GetRenderSurface()->content_rect()); + GetRenderSurface(filter)->content_rect()); gfx::Transform vertical_flip; vertical_flip.Scale(1, -1); @@ -3233,7 +2765,7 @@ TEST_F(LayerTreeHostCommonTest, VisibleRectWithScalingClippingAndFilters) { EXPECT_EQ(gfx::Rect(50, 40, 10, 20), filter_child->visible_layer_rect()); EXPECT_EQ(gfx::Rect(0, -30, 30, 60), - filter->GetRenderSurface()->content_rect()); + GetRenderSurface(filter)->content_rect()); } TEST_F(LayerTreeHostCommonTest, ClipRectWithClipParentAndFilters) { @@ -3331,10 +2863,10 @@ TEST_F(LayerTreeHostCommonTest, child3->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(render_surface->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(render_surface)); EXPECT_EQ(gfx::RectF(100.f, 100.f), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); // Layers that do not draw content should have empty visible content rects. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect()); @@ -3343,7 +2875,7 @@ TEST_F(LayerTreeHostCommonTest, // An unclipped surface grows its DrawableContentRect to include all drawable // regions of the subtree. EXPECT_EQ(gfx::RectF(5.f, 5.f, 170.f, 170.f), - render_surface->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface)->DrawableContentRect()); // All layers that draw content into the unclipped surface are also unclipped. // Only the viewport clip should apply @@ -3357,184 +2889,6 @@ TEST_F(LayerTreeHostCommonTest, } TEST_F(LayerTreeHostCommonTest, - DrawableAndVisibleRectsWhenCannotRenderToSeparateSurface) { - LayerImpl* root = root_layer_for_testing(); - LayerImpl* parent = AddChild<LayerImpl>(root); - LayerImpl* child1 = AddChild<LayerImpl>(parent); - LayerImpl* child2 = AddChild<LayerImpl>(parent); - LayerImpl* grand_child1 = AddChild<LayerImpl>(child1); - LayerImpl* grand_child2 = AddChild<LayerImpl>(child2); - LayerImpl* leaf_node1 = AddChild<LayerImpl>(grand_child1); - LayerImpl* leaf_node2 = AddChild<LayerImpl>(grand_child2); - - root->SetBounds(gfx::Size(100, 100)); - parent->SetPosition(gfx::PointF(2.f, 2.f)); - parent->SetBounds(gfx::Size(400, 400)); - child1->SetPosition(gfx::PointF(4.f, 4.f)); - child1->SetBounds(gfx::Size(800, 800)); - child1->test_properties()->force_render_surface = true; - child2->SetPosition(gfx::PointF(3.f, 3.f)); - child2->SetBounds(gfx::Size(800, 800)); - child2->test_properties()->force_render_surface = true; - grand_child1->SetPosition(gfx::PointF(8.f, 8.f)); - grand_child1->SetBounds(gfx::Size(1500, 1500)); - grand_child2->SetPosition(gfx::PointF(7.f, 7.f)); - grand_child2->SetBounds(gfx::Size(1500, 1500)); - leaf_node1->SetPosition(gfx::PointF(16.f, 16.f)); - leaf_node1->SetBounds(gfx::Size(2000, 2000)); - leaf_node2->SetPosition(gfx::PointF(9.f, 9.f)); - leaf_node2->SetBounds(gfx::Size(2000, 2000)); - - root->SetDrawsContent(true); - parent->SetDrawsContent(true); - child1->SetDrawsContent(true); - child2->SetDrawsContent(true); - grand_child1->SetDrawsContent(true); - grand_child2->SetDrawsContent(true); - leaf_node1->SetDrawsContent(true); - leaf_node2->SetDrawsContent(true); - - // Case 1: No layers clip. Visible rects are clipped by the viewport. - // Each layer's drawable content rect is its bounds in target space; the only - // thing that changes with surfaces disabled is that target space is always - // screen space. - child1->test_properties()->force_render_surface = true; - child2->test_properties()->force_render_surface = true; - ExecuteCalculateDrawProperties(root); - EXPECT_EQ(gfx::Rect(100, 100), root->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(0, 0, 98, 98), parent->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(94, 94), child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(95, 95), child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(86, 86), grand_child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(88, 88), grand_child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(70, 70), leaf_node1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(79, 79), leaf_node2->visible_layer_rect()); - - EXPECT_EQ(gfx::Rect(100, 100), root->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), parent->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(800, 800), child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(800, 800), child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(8, 8, 1500, 1500), grand_child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(7, 7, 1500, 1500), grand_child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(24, 24, 2000, 2000), leaf_node1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(16, 16, 2000, 2000), leaf_node2->drawable_content_rect()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_EQ(gfx::Rect(100, 100), root->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(98, 98), parent->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(94, 94), child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(95, 95), child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(86, 86), grand_child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(88, 88), grand_child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(70, 70), leaf_node1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(79, 79), leaf_node2->visible_layer_rect()); - - EXPECT_EQ(gfx::Rect(100, 100), root->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), parent->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(6, 6, 800, 800), child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(5, 5, 800, 800), child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(14, 14, 1500, 1500), - grand_child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(12, 12, 1500, 1500), - grand_child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(30, 30, 2000, 2000), leaf_node1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(21, 21, 2000, 2000), leaf_node2->drawable_content_rect()); - - // Case 2: The parent clips. In this case, neither surface is unclipped, so - // all visible layer rects are clipped by the intersection of all ancestor - // clips, whether or not surfaces are disabled. However, drawable content - // rects are clipped only until the next render surface is reached, so - // descendants of parent have their drawable content rects clipped only when - // surfaces are disabled. - parent->SetMasksToBounds(true); - host_impl()->active_tree()->property_trees()->needs_rebuild = true; - ExecuteCalculateDrawProperties(root); - EXPECT_EQ(gfx::Rect(100, 100), root->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(98, 98), parent->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(94, 94), child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(95, 95), child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(86, 86), grand_child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(88, 88), grand_child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(70, 70), leaf_node1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(79, 79), leaf_node2->visible_layer_rect()); - - EXPECT_EQ(gfx::Rect(100, 100), root->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), parent->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(800, 800), child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(800, 800), child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(8, 8, 1500, 1500), grand_child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(7, 7, 1500, 1500), grand_child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(24, 24, 2000, 2000), leaf_node1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(16, 16, 2000, 2000), leaf_node2->drawable_content_rect()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_EQ(gfx::Rect(100, 100), root->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(98, 98), parent->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(94, 94), child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(95, 95), child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(86, 86), grand_child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(88, 88), grand_child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(70, 70), leaf_node1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(79, 79), leaf_node2->visible_layer_rect()); - - EXPECT_EQ(gfx::Rect(100, 100), root->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), parent->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(6, 6, 396, 396), child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(5, 5, 397, 397), child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(14, 14, 388, 388), grand_child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(12, 12, 390, 390), grand_child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(30, 30, 372, 372), leaf_node1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(21, 21, 381, 381), leaf_node2->drawable_content_rect()); - - parent->SetMasksToBounds(false); - - // Case 3: child1 and grand_child2 clip. In this case, descendants of these - // layers have their visible rects clipped by them; Similarly, descendants of - // these layers have their drawable content rects clipped by them. - child1->SetMasksToBounds(true); - grand_child2->SetMasksToBounds(true); - host_impl()->active_tree()->property_trees()->needs_rebuild = true; - ExecuteCalculateDrawProperties(root); - EXPECT_EQ(gfx::Rect(100, 100), root->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(98, 98), parent->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(94, 94), child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(95, 95), child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(86, 86), grand_child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(88, 88), grand_child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(70, 70), leaf_node1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(79, 79), leaf_node2->visible_layer_rect()); - - EXPECT_EQ(gfx::Rect(100, 100), root->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), parent->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(800, 800), child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(800, 800), child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(8, 8, 792, 792), grand_child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(7, 7, 1500, 1500), grand_child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(24, 24, 776, 776), leaf_node1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(16, 16, 1491, 1491), leaf_node2->drawable_content_rect()); - - ExecuteCalculateDrawPropertiesWithoutSeparateSurfaces(root); - EXPECT_EQ(gfx::Rect(100, 100), root->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(98, 98), parent->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(94, 94), child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(95, 95), child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(86, 86), grand_child1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(88, 88), grand_child2->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(70, 70), leaf_node1->visible_layer_rect()); - EXPECT_EQ(gfx::Rect(79, 79), leaf_node2->visible_layer_rect()); - - EXPECT_EQ(gfx::Rect(100, 100), root->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(2, 2, 400, 400), parent->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(6, 6, 800, 800), child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(5, 5, 800, 800), child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(14, 14, 792, 792), grand_child1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(12, 12, 1500, 1500), - grand_child2->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(30, 30, 776, 776), leaf_node1->drawable_content_rect()); - EXPECT_EQ(gfx::Rect(21, 21, 1491, 1491), leaf_node2->drawable_content_rect()); -} - -TEST_F(LayerTreeHostCommonTest, VisibleContentRectsForClippedSurfaceWithEmptyClip) { LayerImpl* root = root_layer_for_testing(); LayerImpl* child1 = AddChild<LayerImpl>(root); @@ -3552,18 +2906,18 @@ TEST_F(LayerTreeHostCommonTest, child3->SetBounds(gfx::Size(50, 50)); child3->SetDrawsContent(true); - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; // Now set the root render surface an empty clip. LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, gfx::Size(), &render_surface_layer_list_impl); + root, gfx::Size(), &render_surface_list_impl); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - ASSERT_TRUE(root->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(root)); EXPECT_FALSE(root->is_clipped()); gfx::Rect empty; - EXPECT_EQ(empty, root->GetRenderSurface()->clip_rect()); - EXPECT_TRUE(root->GetRenderSurface()->is_clipped()); + EXPECT_EQ(empty, GetRenderSurface(root)->clip_rect()); + EXPECT_TRUE(GetRenderSurface(root)->is_clipped()); // Visible content rect calculation will check if the target surface is // clipped or not. An empty clip rect does not indicate the render surface @@ -3717,11 +3071,11 @@ TEST_F(LayerTreeHostCommonTest, OcclusionBySiblingOfTarget) { host_impl.active_tree()->UpdateDrawProperties(update_lcd_text); EXPECT_TRANSFORMATION_MATRIX_EQ( - surface_ptr->GetRenderSurface()->draw_transform(), translate); + GetRenderSurface(surface_ptr)->draw_transform(), translate); // surface_sibling draws into the root render surface and occludes // surface_child's contents. Occlusion actual_occlusion = - surface_child_ptr->GetRenderSurface()->occlusion_in_content_space(); + GetRenderSurface(surface_child_ptr)->occlusion_in_content_space(); Occlusion expected_occlusion(translate, SimpleEnclosedRegion(gfx::Rect()), SimpleEnclosedRegion(gfx::Rect(200, 200))); EXPECT_TRUE(expected_occlusion.IsEqual(actual_occlusion)); @@ -3874,10 +3228,10 @@ TEST_F(LayerTreeHostCommonTest, child3->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(render_surface->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(render_surface)); EXPECT_EQ(gfx::RectF(100.f, 100.f), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); // Layers that do not draw content should have empty visible content rects. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect()); @@ -3886,7 +3240,7 @@ TEST_F(LayerTreeHostCommonTest, // A clipped surface grows its DrawableContentRect to include all drawable // regions of the subtree, but also gets clamped by the ancestor's clip. EXPECT_EQ(gfx::RectF(5.f, 5.f, 95.f, 95.f), - render_surface->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface)->DrawableContentRect()); // All layers that draw content into the surface have their visible content // rect clipped by the surface clip rect. @@ -3927,11 +3281,11 @@ TEST_F(LayerTreeHostCommonTest, child3->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(render_surface1->GetRenderSurface()); - ASSERT_TRUE(render_surface2->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(render_surface1)); + ASSERT_TRUE(GetRenderSurface(render_surface2)); EXPECT_EQ(gfx::RectF(100.f, 100.f), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); // Layers that do not draw content should have empty visible content rects. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect()); @@ -3941,13 +3295,13 @@ TEST_F(LayerTreeHostCommonTest, // A clipped surface grows its DrawableContentRect to include all drawable // regions of the subtree, but also gets clamped by the ancestor's clip. EXPECT_EQ(gfx::RectF(5.f, 5.f, 95.f, 95.f), - render_surface1->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface1)->DrawableContentRect()); // render_surface1 lives in the "unclipped universe" of render_surface1, and // is only implicitly clipped by render_surface1's content rect. So, // render_surface2 grows to enclose all drawable content of its subtree. EXPECT_EQ(gfx::RectF(5.f, 5.f, 170.f, 170.f), - render_surface2->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface2)->DrawableContentRect()); // All layers that draw content into render_surface2 think they are unclipped // by the surface. So, only the viewport clip applies. @@ -4058,12 +3412,11 @@ TEST_F(LayerTreeHostCommonTest, ClipRectOfSurfaceWhoseParentIsAClipChild) { float device_scale_factor = 1.f; ExecuteCalculateDrawProperties(root, device_scale_factor); - EXPECT_EQ(gfx::Rect(50, 50), - render_surface2->GetRenderSurface()->clip_rect()); + EXPECT_EQ(gfx::Rect(50, 50), GetRenderSurface(render_surface2)->clip_rect()); device_scale_factor = 2.f; ExecuteCalculateDrawProperties(root, device_scale_factor); EXPECT_EQ(gfx::Rect(100, 100), - render_surface2->GetRenderSurface()->clip_rect()); + GetRenderSurface(render_surface2)->clip_rect()); } TEST_F(LayerTreeHostCommonTest, RenderSurfaceContentRectWhenLayerNotDrawn) { @@ -4079,11 +3432,11 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceContentRectWhenLayerNotDrawn) { test_layer->SetBounds(gfx::Size(150, 150)); ExecuteCalculateDrawProperties(root); - EXPECT_EQ(gfx::Rect(100, 100), surface->GetRenderSurface()->content_rect()); + EXPECT_EQ(gfx::Rect(100, 100), GetRenderSurface(surface)->content_rect()); test_layer->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); - EXPECT_EQ(gfx::Rect(150, 150), surface->GetRenderSurface()->content_rect()); + EXPECT_EQ(gfx::Rect(150, 150), GetRenderSurface(surface)->content_rect()); } TEST_F(LayerTreeHostCommonTest, VisibleRectsMultipleSurfaces) { @@ -4191,10 +3544,10 @@ TEST_F(LayerTreeHostCommonTest, child1->test_properties()->transform_origin = gfx::Point3F(25.f, 25.f, 0.f); ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(render_surface->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(render_surface)); EXPECT_EQ(gfx::RectF(100.f, 100.f), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); // Layers that do not draw content should have empty visible content rects. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect()); @@ -4209,7 +3562,7 @@ TEST_F(LayerTreeHostCommonTest, diagonal_radius * 2, diagonal_radius * 2); EXPECT_EQ(gfx::RectF(expected_surface_drawable_content), - render_surface->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface)->DrawableContentRect()); // All layers that draw content into the unclipped surface are also unclipped. EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect()); @@ -4238,7 +3591,7 @@ TEST_F(LayerTreeHostCommonTest, child1->test_properties()->transform_origin = gfx::Point3F(25.f, 25.f, 0.f); ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(render_surface->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(render_surface)); // The clipped surface clamps the DrawableContentRect that encloses the // rotated layer. @@ -4250,7 +3603,7 @@ TEST_F(LayerTreeHostCommonTest, gfx::RectF expected_surface_drawable_content( gfx::IntersectRects(unclipped_surface_content, gfx::Rect(50, 50))); EXPECT_EQ(expected_surface_drawable_content, - render_surface->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface)->DrawableContentRect()); // On the clipped surface, only a quarter of the child1 is visible, but when // rotating it back to child1's content space, the actual enclosing rect ends @@ -4296,20 +3649,20 @@ TEST_F(LayerTreeHostCommonTest, DrawableAndVisibleContentRectsInHighDPI) { float device_scale_factor = 2.f; ExecuteCalculateDrawProperties(root, device_scale_factor); - ASSERT_TRUE(render_surface1->GetRenderSurface()); - ASSERT_TRUE(render_surface2->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(render_surface1)); + ASSERT_TRUE(GetRenderSurface(render_surface2)); // drawable_content_rects for all layers and surfaces are scaled by // device_scale_factor. EXPECT_EQ(gfx::RectF(200.f, 200.f), - root->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(root)->DrawableContentRect()); EXPECT_EQ(gfx::RectF(10.f, 10.f, 190.f, 190.f), - render_surface1->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface1)->DrawableContentRect()); // render_surface2 lives in the "unclipped universe" of render_surface1, and // is only implicitly clipped by render_surface1. EXPECT_EQ(gfx::RectF(10.f, 10.f, 350.f, 350.f), - render_surface2->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface2)->DrawableContentRect()); EXPECT_EQ(gfx::Rect(10, 10, 100, 100), child1->drawable_content_rect()); EXPECT_EQ(gfx::Rect(150, 150, 100, 100), child2->drawable_content_rect()); @@ -4401,14 +3754,20 @@ TEST_F(LayerTreeHostCommonTest, BackFaceCullingWithoutPreserves3d) { ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); // Verify which render surfaces were created. - EXPECT_FALSE(front_facing_child->GetRenderSurface()); - EXPECT_FALSE(back_facing_child->GetRenderSurface()); - EXPECT_TRUE(front_facing_surface->GetRenderSurface()); - EXPECT_TRUE(back_facing_surface->GetRenderSurface()); - EXPECT_FALSE(front_facing_child_of_front_facing_surface->GetRenderSurface()); - EXPECT_FALSE(back_facing_child_of_front_facing_surface->GetRenderSurface()); - EXPECT_FALSE(front_facing_child_of_back_facing_surface->GetRenderSurface()); - EXPECT_FALSE(back_facing_child_of_back_facing_surface->GetRenderSurface()); + EXPECT_EQ(GetRenderSurface(front_facing_child), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(back_facing_child), GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(front_facing_surface), GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(back_facing_surface), GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(back_facing_surface), + GetRenderSurface(front_facing_surface)); + EXPECT_EQ(GetRenderSurface(front_facing_child_of_front_facing_surface), + GetRenderSurface(front_facing_surface)); + EXPECT_EQ(GetRenderSurface(back_facing_child_of_front_facing_surface), + GetRenderSurface(front_facing_surface)); + EXPECT_EQ(GetRenderSurface(front_facing_child_of_back_facing_surface), + GetRenderSurface(back_facing_surface)); + EXPECT_EQ(GetRenderSurface(back_facing_child_of_back_facing_surface), + GetRenderSurface(back_facing_surface)); EXPECT_EQ(3u, update_layer_list_impl()->size()); EXPECT_TRUE(UpdateLayerListImplContains(front_facing_child->id())); @@ -4509,15 +3868,21 @@ TEST_F(LayerTreeHostCommonTest, BackFaceCullingWithPreserves3d) { ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); // Verify which render surfaces were created and used. - EXPECT_FALSE(front_facing_child->GetRenderSurface()); - EXPECT_FALSE(back_facing_child->GetRenderSurface()); - EXPECT_TRUE(front_facing_surface->GetRenderSurface()); + EXPECT_EQ(GetRenderSurface(front_facing_child), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(back_facing_child), GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(front_facing_surface), GetRenderSurface(root)); // We expect that a has_render_surface was created but not used. - EXPECT_TRUE(back_facing_surface->GetRenderSurface()); - EXPECT_FALSE(front_facing_child_of_front_facing_surface->GetRenderSurface()); - EXPECT_FALSE(back_facing_child_of_front_facing_surface->GetRenderSurface()); - EXPECT_FALSE(front_facing_child_of_back_facing_surface->GetRenderSurface()); - EXPECT_FALSE(back_facing_child_of_back_facing_surface->GetRenderSurface()); + EXPECT_NE(GetRenderSurface(back_facing_surface), GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(back_facing_surface), + GetRenderSurface(front_facing_surface)); + EXPECT_EQ(GetRenderSurface(front_facing_child_of_front_facing_surface), + GetRenderSurface(front_facing_surface)); + EXPECT_EQ(GetRenderSurface(back_facing_child_of_front_facing_surface), + GetRenderSurface(front_facing_surface)); + EXPECT_EQ(GetRenderSurface(front_facing_child_of_back_facing_surface), + GetRenderSurface(back_facing_surface)); + EXPECT_EQ(GetRenderSurface(back_facing_child_of_back_facing_surface), + GetRenderSurface(back_facing_surface)); EXPECT_EQ(3u, update_layer_list_impl()->size()); @@ -4583,11 +3948,12 @@ TEST_F(LayerTreeHostCommonTest, BackFaceCullingWithAnimatingTransforms) { ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); - EXPECT_FALSE(child->GetRenderSurface()); - EXPECT_TRUE(animating_surface->GetRenderSurface()); - EXPECT_FALSE(child_of_animating_surface->GetRenderSurface()); - EXPECT_FALSE(animating_child->GetRenderSurface()); - EXPECT_FALSE(child2->GetRenderSurface()); + EXPECT_EQ(GetRenderSurface(child), GetRenderSurface(root)); + EXPECT_TRUE(GetRenderSurface(animating_surface)); + EXPECT_EQ(GetRenderSurface(child_of_animating_surface), + GetRenderSurface(animating_surface)); + EXPECT_EQ(GetRenderSurface(animating_child), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(child2), GetRenderSurface(root)); EXPECT_EQ(1u, update_layer_list_impl()->size()); @@ -4643,12 +4009,12 @@ TEST_F(LayerTreeHostCommonTest, ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); // Verify which render surfaces were created and used. - EXPECT_TRUE(front_facing_surface->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(front_facing_surface)); // We expect the render surface to have been created, but remain unused. - EXPECT_TRUE(back_facing_surface->GetRenderSurface()); - EXPECT_FALSE(child1->GetRenderSurface()); - EXPECT_FALSE(child2->GetRenderSurface()); + EXPECT_NE(GetRenderSurface(back_facing_surface), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(child1), GetRenderSurface(front_facing_surface)); + EXPECT_EQ(GetRenderSurface(child2), GetRenderSurface(back_facing_surface)); EXPECT_EQ(2u, update_layer_list_impl()->size()); EXPECT_TRUE(UpdateLayerListImplContains(front_facing_surface->id())); @@ -4679,7 +4045,7 @@ TEST_F(LayerTreeHostCommonScalingTest, LayerTransformsInHighDPI) { EXPECT_FLOAT_EQ(device_scale_factor, child->GetIdealContentsScale()); EXPECT_FLOAT_EQ(device_scale_factor, child2->GetIdealContentsScale()); - EXPECT_EQ(1u, render_surface_layer_list_impl()->size()); + EXPECT_EQ(1u, render_surface_list_impl()->size()); // Verify root transforms gfx::Transform expected_root_transform; @@ -4774,9 +4140,9 @@ TEST_F(LayerTreeHostCommonScalingTest, SurfaceLayerTransformsInHighDPI) { float device_scale_factor = 2.5f; float page_scale_factor = 3.f; - root->layer_tree_impl()->SetViewportLayersFromIds( - Layer::INVALID_ID, page_scale->id(), Layer::INVALID_ID, - Layer::INVALID_ID); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = page_scale->id(); + root->layer_tree_impl()->SetViewportLayersFromIds(viewport_ids); root->layer_tree_impl()->BuildLayerListAndPropertyTreesForTesting(); root->layer_tree_impl()->SetPageScaleOnActiveTree(page_scale_factor); ExecuteCalculateDrawProperties(root, device_scale_factor, page_scale_factor, @@ -4806,7 +4172,7 @@ TEST_F(LayerTreeHostCommonScalingTest, SurfaceLayerTransformsInHighDPI) { std::max(target_space_transform_scales.x(), target_space_transform_scales.y())); - EXPECT_EQ(3u, render_surface_layer_list_impl()->size()); + EXPECT_EQ(3u, render_surface_list_impl()->size()); gfx::Transform expected_parent_draw_transform; expected_parent_draw_transform.Scale(device_scale_factor * page_scale_factor, @@ -4832,7 +4198,7 @@ TEST_F(LayerTreeHostCommonScalingTest, SurfaceLayerTransformsInHighDPI) { device_scale_factor * page_scale_factor); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_perspective_surface_draw_transform, - perspective_surface->GetRenderSurface()->draw_transform()); + GetRenderSurface(perspective_surface)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_perspective_surface_layer_draw_transform, perspective_surface->DrawTransform()); @@ -4937,7 +4303,7 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceTransformsInHighDPI) { // We should have two render surfaces. The root's render surface and child's // render surface (it needs one because of force_render_surface). - EXPECT_EQ(2u, render_surface_layer_list_impl()->size()); + EXPECT_EQ(2u, render_surface_list_impl()->size()); gfx::Transform expected_parent_transform; expected_parent_transform.Scale(device_scale_factor, device_scale_factor); @@ -4975,20 +4341,20 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceTransformsInHighDPI) { device_scale_factor * child->position().x(), device_scale_factor * child->position().y()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_render_surface_draw_transform, - child->GetRenderSurface()->draw_transform()); + GetRenderSurface(child)->draw_transform()); gfx::Transform expected_surface_draw_transform; expected_surface_draw_transform.Translate(device_scale_factor * 2.f, device_scale_factor * 2.f); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_surface_draw_transform, - child->GetRenderSurface()->draw_transform()); + GetRenderSurface(child)->draw_transform()); gfx::Transform expected_surface_screen_space_transform; expected_surface_screen_space_transform.Translate(device_scale_factor * 2.f, device_scale_factor * 2.f); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_surface_screen_space_transform, - child->GetRenderSurface()->screen_space_transform()); + GetRenderSurface(child)->screen_space_transform()); } TEST_F(LayerTreeHostCommonTest, @@ -5007,14 +4373,14 @@ TEST_F(LayerTreeHostCommonTest, // We should have two render surfaces. The root's render surface and child's // render surface (it needs one because of force_render_surface). - EXPECT_EQ(2u, render_surface_layer_list_impl()->size()); + EXPECT_EQ(2u, render_surface_list_impl()->size()); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - child->GetRenderSurface()->draw_transform()); + GetRenderSurface(child)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), - child->GetRenderSurface()->draw_transform()); + GetRenderSurface(child)->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( - gfx::Transform(), child->GetRenderSurface()->screen_space_transform()); + gfx::Transform(), GetRenderSurface(child)->screen_space_transform()); } TEST_F(LayerTreeHostCommonTest, LayerSearch) { @@ -5048,7 +4414,7 @@ TEST_F(LayerTreeHostCommonTest, TransparentChildRenderSurfaceCreation) { grand_child->SetBounds(gfx::Size(10, 10)); grand_child->SetDrawsContent(true); ExecuteCalculateDrawProperties(root); - EXPECT_FALSE(child->GetRenderSurface()); + EXPECT_EQ(GetRenderSurface(child), GetRenderSurface(root)); } TEST_F(LayerTreeHostCommonTest, OpacityAnimatingOnPendingTree) { @@ -5085,24 +4451,24 @@ TEST_F(LayerTreeHostCommonTest, OpacityAnimatingOnPendingTree) { AddOpacityTransitionToElementWithPlayer(child_element_id, timeline, 10.0, 0.0f, 1.0f, false); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &render_surface_layer_list); + root_layer, root_layer->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // We should have one render surface and two layers. The child // layer should be included even though it is transparent. - ASSERT_EQ(1u, render_surface_layer_list.size()); - ASSERT_EQ(2u, root_layer->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, render_surface_list.size()); + ASSERT_EQ(2, GetRenderSurface(root_layer)->num_contributors()); // If the root itself is hidden, the child should not be drawn even if it has // an animating opacity. root_layer->test_properties()->opacity = 0.0f; root_layer->layer_tree_impl()->property_trees()->needs_rebuild = true; - LayerImplList render_surface_layer_list2; + RenderSurfaceList render_surface_list2; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs2( - root_layer, root_layer->bounds(), &render_surface_layer_list2); + root_layer, root_layer->bounds(), &render_surface_list2); inputs2.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs2); @@ -5117,9 +4483,9 @@ TEST_F(LayerTreeHostCommonTest, OpacityAnimatingOnPendingTree) { root_layer->test_properties()->opacity = 1.0f; child_ptr->test_properties()->opacity = 0.0f; root_layer->layer_tree_impl()->property_trees()->needs_rebuild = true; - LayerImplList render_surface_layer_list3; + RenderSurfaceList render_surface_list3; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs3( - root_layer, root_layer->bounds(), &render_surface_layer_list3); + root_layer, root_layer->bounds(), &render_surface_list3); inputs3.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs3); @@ -5384,29 +4750,32 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHidden_SingleLayerImpl) { LayerImpl::Create(host_impl.pending_tree(), 2); child->SetBounds(gfx::Size(40, 40)); child->SetDrawsContent(true); + LayerImpl* child_layer = child.get(); std::unique_ptr<LayerImpl> grand_child = LayerImpl::Create(host_impl.pending_tree(), 3); grand_child->SetBounds(gfx::Size(30, 30)); grand_child->SetDrawsContent(true); grand_child->test_properties()->hide_layer_and_subtree = true; + LayerImpl* grand_child_layer = grand_child.get(); child->test_properties()->AddChild(std::move(grand_child)); root->test_properties()->AddChild(std::move(child)); host_impl.pending_tree()->SetRootLayerForTesting(std::move(root)); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &render_surface_layer_list); + root_layer, root_layer->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // We should have one render surface and two layers. The grand child has // hidden itself. - ASSERT_EQ(1u, render_surface_layer_list.size()); - ASSERT_EQ(2u, root_layer->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(1, root_layer->GetRenderSurface()->layer_list().at(0)->id()); - EXPECT_EQ(2, root_layer->GetRenderSurface()->layer_list().at(1)->id()); + ASSERT_EQ(1u, render_surface_list.size()); + ASSERT_EQ(2, GetRenderSurface(root_layer)->num_contributors()); + EXPECT_TRUE(root_layer->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child_layer->contributes_to_drawn_render_surface()); } TEST_F(LayerTreeHostCommonTest, SubtreeHidden_TwoLayersImpl) { @@ -5426,27 +4795,31 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHidden_TwoLayersImpl) { child->SetBounds(gfx::Size(40, 40)); child->SetDrawsContent(true); child->test_properties()->hide_layer_and_subtree = true; + LayerImpl* child_layer = child.get(); std::unique_ptr<LayerImpl> grand_child = LayerImpl::Create(host_impl.pending_tree(), 3); grand_child->SetBounds(gfx::Size(30, 30)); grand_child->SetDrawsContent(true); + LayerImpl* grand_child_layer = grand_child.get(); child->test_properties()->AddChild(std::move(grand_child)); root->test_properties()->AddChild(std::move(child)); host_impl.pending_tree()->SetRootLayerForTesting(std::move(root)); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &render_surface_layer_list); + root_layer, root_layer->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - // We should have one render surface and one layers. The child has + // We should have one render surface and one layer. The child has // hidden itself and the grand child. - ASSERT_EQ(1u, render_surface_layer_list.size()); - ASSERT_EQ(1u, root_layer->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(1, root_layer->GetRenderSurface()->layer_list().at(0)->id()); + ASSERT_EQ(1u, render_surface_list.size()); + ASSERT_EQ(1, GetRenderSurface(root_layer)->num_contributors()); + EXPECT_TRUE(root_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(child_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child_layer->contributes_to_drawn_render_surface()); } void EmptyCopyOutputCallback(std::unique_ptr<CopyOutputResult> result) {} @@ -5532,46 +4905,45 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHiddenWithCopyRequest) { copy_layer->test_properties()->copy_requests.push_back( CopyOutputRequest::CreateRequest(base::Bind(&EmptyCopyOutputCallback))); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &render_surface_layer_list); + root_layer, root_layer->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - EXPECT_GT(root_layer->num_copy_requests_in_target_subtree(), 0); - EXPECT_GT(copy_grand_parent_layer->num_copy_requests_in_target_subtree(), 0); - EXPECT_GT(copy_parent_layer->num_copy_requests_in_target_subtree(), 0); - EXPECT_GT(copy_layer->num_copy_requests_in_target_subtree(), 0); + EXPECT_TRUE(root_layer->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(copy_grand_parent_layer->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(copy_parent_layer->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(copy_layer->has_copy_requests_in_target_subtree()); // We should have four render surfaces, one for the root, one for the grand // parent since it has opacity and two drawing descendants, one for the parent // since it owns a surface, and one for the copy_layer. - ASSERT_EQ(4u, render_surface_layer_list.size()); - EXPECT_EQ(root_layer->id(), render_surface_layer_list.at(0)->id()); - EXPECT_EQ(copy_grand_parent_layer->id(), - render_surface_layer_list.at(1)->id()); - EXPECT_EQ(copy_parent_layer->id(), render_surface_layer_list.at(2)->id()); - EXPECT_EQ(copy_layer->id(), render_surface_layer_list.at(3)->id()); + ASSERT_EQ(4u, render_surface_list.size()); + EXPECT_EQ(root_layer->id(), render_surface_list.at(0)->id()); + EXPECT_EQ(copy_grand_parent_layer->id(), render_surface_list.at(1)->id()); + EXPECT_EQ(copy_parent_layer->id(), render_surface_list.at(2)->id()); + EXPECT_EQ(copy_layer->id(), render_surface_list.at(3)->id()); // The root render surface should have 2 contributing layers. - ASSERT_EQ(2u, root_layer->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(root_layer->id(), - root_layer->GetRenderSurface()->layer_list().at(0)->id()); - EXPECT_EQ(copy_grand_parent_layer->id(), - root_layer->GetRenderSurface()->layer_list().at(1)->id()); + EXPECT_EQ(2, GetRenderSurface(root_layer)->num_contributors()); + EXPECT_TRUE(root_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(copy_grand_parent_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(copy_grand_parent_sibling_before_layer + ->contributes_to_drawn_render_surface()); + EXPECT_FALSE(copy_grand_parent_sibling_after_layer + ->contributes_to_drawn_render_surface()); // Nothing actually draws into the copy parent, so only the copy_layer will // appear in its list, since it needs to be drawn for the copy request. - ASSERT_EQ(1u, copy_parent_layer->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(copy_layer->id(), - copy_parent_layer->GetRenderSurface()->layer_list().at(0)->id()); + ASSERT_EQ(1, GetRenderSurface(copy_parent_layer)->num_contributors()); + EXPECT_FALSE(copy_parent_layer->contributes_to_drawn_render_surface()); - // The copy_layer's render surface should have two contributing layers. - ASSERT_EQ(2u, copy_layer->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(copy_layer->id(), - copy_layer->GetRenderSurface()->layer_list().at(0)->id()); - EXPECT_EQ(copy_child_layer->id(), - copy_layer->GetRenderSurface()->layer_list().at(1)->id()); + // The copy layer's render surface should have 2 contributing layers. + ASSERT_EQ(2, GetRenderSurface(copy_layer)->num_contributors()); + EXPECT_TRUE(copy_layer->contributes_to_drawn_render_surface()); + EXPECT_TRUE(copy_child_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(copy_grand_child_layer->contributes_to_drawn_render_surface()); // copy_grand_parent, copy_parent shouldn't be drawn because they are hidden, // but the copy_layer and copy_child should be drawn for the copy request. @@ -5592,7 +4964,7 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHiddenWithCopyRequest) { // Though copy_layer is drawn, it shouldn't contribute to drawn surface as its // actually hidden. - EXPECT_FALSE(copy_layer->GetRenderSurface()->contributes_to_drawn_surface()); + EXPECT_FALSE(GetRenderSurface(copy_layer)->contributes_to_drawn_surface()); } TEST_F(LayerTreeHostCommonTest, ClippedOutCopyRequest) { @@ -5629,23 +5001,66 @@ TEST_F(LayerTreeHostCommonTest, ClippedOutCopyRequest) { copy_parent->test_properties()->AddChild(std::move(copy_layer)); root->test_properties()->AddChild(std::move(copy_parent)); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerImpl* root_layer = root.get(); root_layer->layer_tree_impl()->SetRootLayerForTesting(std::move(root)); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &render_surface_layer_list); + root_layer, root_layer->bounds(), &render_surface_list); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // We should have two render surface, as the others are clipped out. - ASSERT_EQ(2u, render_surface_layer_list.size()); - EXPECT_EQ(root_layer->id(), render_surface_layer_list.at(0)->id()); + ASSERT_EQ(2u, render_surface_list.size()); + EXPECT_EQ(root_layer->id(), render_surface_list.at(0)->id()); + + // The root render surface should have only 2 contributing layer, since the + // other layers are clipped away. + ASSERT_EQ(2, GetRenderSurface(root_layer)->num_contributors()); + EXPECT_TRUE(root_layer->contributes_to_drawn_render_surface()); +} + +TEST_F(LayerTreeHostCommonTest, SingularTransformAndCopyRequests) { + LayerImpl* root = root_layer_for_testing(); + root->SetBounds(gfx::Size(50, 50)); + root->SetDrawsContent(true); - // The root render surface should only have 2 contributing layer, since the - // other layers are empty/clipped away. - ASSERT_EQ(2u, root_layer->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(root_layer->id(), - root_layer->GetRenderSurface()->layer_list().at(0)->id()); + LayerImpl* singular_transform_layer = AddChild<LayerImpl>(root); + singular_transform_layer->SetBounds(gfx::Size(100, 100)); + singular_transform_layer->SetDrawsContent(true); + gfx::Transform singular; + singular.Scale3d(6.f, 6.f, 0.f); + singular_transform_layer->test_properties()->transform = singular; + + LayerImpl* copy_layer = AddChild<LayerImpl>(singular_transform_layer); + copy_layer->SetBounds(gfx::Size(100, 100)); + copy_layer->SetDrawsContent(true); + copy_layer->test_properties()->copy_requests.push_back( + CopyOutputRequest::CreateRequest(base::Bind(&EmptyCopyOutputCallback))); + + LayerImpl* copy_child = AddChild<LayerImpl>(copy_layer); + copy_child->SetBounds(gfx::Size(100, 100)); + copy_child->SetDrawsContent(true); + + LayerImpl* copy_grand_child = AddChild<LayerImpl>(copy_child); + copy_grand_child->SetBounds(gfx::Size(100, 100)); + copy_grand_child->SetDrawsContent(true); + copy_grand_child->test_properties()->transform = singular; + + DCHECK(!copy_layer->test_properties()->copy_requests.empty()); + ExecuteCalculateDrawProperties(root); + DCHECK(copy_layer->test_properties()->copy_requests.empty()); + + // A layer with singular transform should not contribute to drawn render + // surface. + EXPECT_FALSE(singular_transform_layer->contributes_to_drawn_render_surface()); + // Even though copy_layer and copy_child have singular screen space transform, + // they still contribute to drawn render surface as their transform to the + // closest ancestor with copy request is not singular. + EXPECT_TRUE(copy_layer->contributes_to_drawn_render_surface()); + EXPECT_TRUE(copy_child->contributes_to_drawn_render_surface()); + // copy_grand_child's transform to its closest ancestor with copy request is + // also singular. So, it doesn't contribute to drawn render surface. + EXPECT_FALSE(copy_grand_child->contributes_to_drawn_render_surface()); } TEST_F(LayerTreeHostCommonTest, VisibleRectInNonRootCopyRequest) { @@ -5765,8 +5180,8 @@ TEST_F(LayerTreeHostCommonTest, TransformedClipParent) { clip_child->SetBounds(gfx::Size(10, 10)); ExecuteCalculateDrawProperties(root); - ASSERT_TRUE(root->GetRenderSurface()); - ASSERT_TRUE(render_surface->GetRenderSurface()); + ASSERT_TRUE(GetRenderSurface(root)); + ASSERT_TRUE(GetRenderSurface(render_surface)); // Ensure that we've inherited our clip parent's clip and weren't affected // by the intervening clip layer. @@ -5777,14 +5192,14 @@ TEST_F(LayerTreeHostCommonTest, TransformedClipParent) { // Ensure that the render surface reports a content rect that has been grown // to accomodate for the clip child. ASSERT_EQ(gfx::Rect(1, 1, 20, 20), - render_surface->GetRenderSurface()->content_rect()); + GetRenderSurface(render_surface)->content_rect()); // The above check implies the two below, but they nicely demonstrate that // we've grown, despite the intervening layer's clip. ASSERT_TRUE(clip_parent->clip_rect().Contains( - render_surface->GetRenderSurface()->content_rect())); + GetRenderSurface(render_surface)->content_rect())); ASSERT_FALSE(intervening->clip_rect().Contains( - render_surface->GetRenderSurface()->content_rect())); + GetRenderSurface(render_surface)->content_rect())); } TEST_F(LayerTreeHostCommonTest, ClipParentWithInterveningRenderSurface) { @@ -5830,16 +5245,16 @@ TEST_F(LayerTreeHostCommonTest, ClipParentWithInterveningRenderSurface) { clip_child->SetBounds(gfx::Size(60, 60)); ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_TRUE(render_surface1->GetRenderSurface()); - EXPECT_TRUE(render_surface2->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_TRUE(GetRenderSurface(render_surface1)); + EXPECT_TRUE(GetRenderSurface(render_surface2)); // render_surface1 should apply the clip from clip_parent. Though there is a // clip child, render_surface1 can apply the clip as there are no clips // between the clip parent and render_surface1 EXPECT_EQ(gfx::Rect(1, 1, 40, 40), - render_surface1->GetRenderSurface()->clip_rect()); - EXPECT_TRUE(render_surface1->GetRenderSurface()->is_clipped()); + GetRenderSurface(render_surface1)->clip_rect()); + EXPECT_TRUE(GetRenderSurface(render_surface1)->is_clipped()); EXPECT_EQ(gfx::Rect(), render_surface1->clip_rect()); EXPECT_FALSE(render_surface1->is_clipped()); @@ -5848,8 +5263,8 @@ TEST_F(LayerTreeHostCommonTest, ClipParentWithInterveningRenderSurface) { // So, it should not be clipped (their bounds would no longer be reliable). // We should resort to layer clipping in this case. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), - render_surface2->GetRenderSurface()->clip_rect()); - EXPECT_FALSE(render_surface2->GetRenderSurface()->is_clipped()); + GetRenderSurface(render_surface2)->clip_rect()); + EXPECT_FALSE(GetRenderSurface(render_surface2)->is_clipped()); // This value is inherited from the clipping ancestor layer, 'intervening'. EXPECT_EQ(gfx::Rect(0, 0, 5, 5), render_surface2->clip_rect()); @@ -5858,9 +5273,9 @@ TEST_F(LayerTreeHostCommonTest, ClipParentWithInterveningRenderSurface) { // The content rects of render_surface2 should have expanded to contain the // clip child. EXPECT_EQ(gfx::Rect(0, 0, 40, 40), - render_surface1->GetRenderSurface()->content_rect()); + GetRenderSurface(render_surface1)->content_rect()); EXPECT_EQ(gfx::Rect(-10, -10, 60, 60), - render_surface2->GetRenderSurface()->content_rect()); + GetRenderSurface(render_surface2)->content_rect()); // The clip child should have inherited the clip parent's clip (projected to // the right space, of course), but as render_surface1 already applies that @@ -5916,16 +5331,16 @@ TEST_F(LayerTreeHostCommonTest, ClipParentScrolledInterveningLayer) { clip_child->SetBounds(gfx::Size(60, 60)); ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_TRUE(render_surface1->GetRenderSurface()); - EXPECT_TRUE(render_surface2->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_TRUE(GetRenderSurface(render_surface1)); + EXPECT_TRUE(GetRenderSurface(render_surface2)); // render_surface1 should apply the clip from clip_parent. Though there is a // clip child, render_surface1 can apply the clip as there are no clips // between the clip parent and render_surface1 EXPECT_EQ(gfx::Rect(3, 3, 40, 40), - render_surface1->GetRenderSurface()->clip_rect()); - EXPECT_TRUE(render_surface1->GetRenderSurface()->is_clipped()); + GetRenderSurface(render_surface1)->clip_rect()); + EXPECT_TRUE(GetRenderSurface(render_surface1)->is_clipped()); EXPECT_EQ(gfx::Rect(), render_surface1->clip_rect()); EXPECT_FALSE(render_surface1->is_clipped()); @@ -5934,8 +5349,8 @@ TEST_F(LayerTreeHostCommonTest, ClipParentScrolledInterveningLayer) { // So, it should not be clipped (their bounds would no longer be reliable). // We should resort to layer clipping in this case. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), - render_surface2->GetRenderSurface()->clip_rect()); - EXPECT_FALSE(render_surface2->GetRenderSurface()->is_clipped()); + GetRenderSurface(render_surface2)->clip_rect()); + EXPECT_FALSE(GetRenderSurface(render_surface2)->is_clipped()); // This value is inherited from the clipping ancestor layer, 'intervening'. EXPECT_EQ(gfx::Rect(0, 0, 5, 5), render_surface2->clip_rect()); EXPECT_TRUE(render_surface2->is_clipped()); @@ -5943,9 +5358,9 @@ TEST_F(LayerTreeHostCommonTest, ClipParentScrolledInterveningLayer) { // The content rects of render_surface2 should have expanded to contain the // clip child. EXPECT_EQ(gfx::Rect(0, 0, 40, 40), - render_surface1->GetRenderSurface()->content_rect()); + GetRenderSurface(render_surface1)->content_rect()); EXPECT_EQ(gfx::Rect(-10, -10, 60, 60), - render_surface2->GetRenderSurface()->content_rect()); + GetRenderSurface(render_surface2)->content_rect()); // The clip child should have inherited the clip parent's clip (projected to // the right space, of course), but as render_surface1 already applies that @@ -5987,7 +5402,7 @@ TEST_F(LayerTreeHostCommonTest, DescendantsOfClipChildren) { ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); // Neither the clip child nor its descendant should have inherited the clip // from |intervening|. @@ -6046,9 +5461,9 @@ TEST_F(LayerTreeHostCommonTest, ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_TRUE(render_surface1->GetRenderSurface()); - EXPECT_TRUE(render_surface2->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_TRUE(GetRenderSurface(render_surface1)); + EXPECT_TRUE(GetRenderSurface(render_surface2)); EXPECT_EQ(gfx::Rect(-5, -5, 10, 10), render_surface1->clip_rect()); EXPECT_TRUE(render_surface1->is_clipped()); @@ -6056,23 +5471,23 @@ TEST_F(LayerTreeHostCommonTest, // The render surface should not clip (it has unclipped descendants), instead // it should rely on layer clipping. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), - render_surface1->GetRenderSurface()->clip_rect()); - EXPECT_FALSE(render_surface1->GetRenderSurface()->is_clipped()); + GetRenderSurface(render_surface1)->clip_rect()); + EXPECT_FALSE(GetRenderSurface(render_surface1)->is_clipped()); // That said, it should have grown to accomodate the unclipped descendant and // its own size. EXPECT_EQ(gfx::Rect(-1, 0, 6, 5), - render_surface1->GetRenderSurface()->content_rect()); + GetRenderSurface(render_surface1)->content_rect()); // This render surface should clip. It has no unclipped descendants. EXPECT_EQ(gfx::Rect(0, 0, 10, 10), - render_surface2->GetRenderSurface()->clip_rect()); - EXPECT_TRUE(render_surface2->GetRenderSurface()->is_clipped()); + GetRenderSurface(render_surface2)->clip_rect()); + EXPECT_TRUE(GetRenderSurface(render_surface2)->is_clipped()); EXPECT_FALSE(render_surface2->is_clipped()); // It also shouldn't have grown to accomodate the clip child. EXPECT_EQ(gfx::Rect(0, 0, 5, 5), - render_surface2->GetRenderSurface()->content_rect()); + GetRenderSurface(render_surface2)->content_rect()); } TEST_F(LayerTreeHostCommonTest, @@ -6101,113 +5516,10 @@ TEST_F(LayerTreeHostCommonTest, ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); // Verify which render surfaces were created. - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_FALSE(child1->GetRenderSurface()); - EXPECT_TRUE(child2->GetRenderSurface()); - EXPECT_FALSE(child3->GetRenderSurface()); -} - -TEST_F(LayerTreeHostCommonTest, CanRenderToSeparateSurface) { - FakeImplTaskRunnerProvider task_runner_provider; - TestTaskGraphRunner task_graph_runner; - FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); - - std::unique_ptr<LayerImpl> root = - LayerImpl::Create(host_impl.active_tree(), 12345); - std::unique_ptr<LayerImpl> child1 = - LayerImpl::Create(host_impl.active_tree(), 123456); - std::unique_ptr<LayerImpl> child2 = - LayerImpl::Create(host_impl.active_tree(), 1234567); - std::unique_ptr<LayerImpl> child3 = - LayerImpl::Create(host_impl.active_tree(), 12345678); - - gfx::Size bounds(100, 100); - - root->SetBounds(bounds); - root->SetDrawsContent(true); - - // This layer structure normally forces render surface due to preserves3d - // behavior. - child1->SetBounds(bounds); - child1->SetDrawsContent(true); - child1->test_properties()->should_flatten_transform = false; - child1->test_properties()->sorting_context_id = 1; - child2->SetBounds(bounds); - child2->SetDrawsContent(true); - child2->test_properties()->sorting_context_id = 1; - child3->SetBounds(bounds); - child3->SetDrawsContent(true); - child3->test_properties()->sorting_context_id = 1; - - child2->test_properties()->AddChild(std::move(child3)); - child1->test_properties()->AddChild(std::move(child2)); - root->test_properties()->AddChild(std::move(child1)); - LayerImpl* root_layer = root.get(); - root_layer->layer_tree_impl()->SetRootLayerForTesting(std::move(root)); - - { - LayerImplList render_surface_layer_list; - LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &render_surface_layer_list); - inputs.can_render_to_separate_surface = true; - LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - - EXPECT_EQ(2u, render_surface_layer_list.size()); - - int count_represents_target_render_surface = 0; - int count_represents_contributing_render_surface = 0; - int count_represents_itself = 0; - for (EffectTreeLayerListIterator it(host_impl.active_tree()); - it.state() != EffectTreeLayerListIterator::State::END; ++it) { - if (it.state() == EffectTreeLayerListIterator::State::TARGET_SURFACE) { - count_represents_target_render_surface++; - } else if (it.state() == - EffectTreeLayerListIterator::State::CONTRIBUTING_SURFACE) { - count_represents_contributing_render_surface++; - } else { - count_represents_itself++; - } - } - - // Two render surfaces. - EXPECT_EQ(2, count_represents_target_render_surface); - // Second render surface contributes to root render surface. - EXPECT_EQ(1, count_represents_contributing_render_surface); - // All 4 layers represent itself. - EXPECT_EQ(4, count_represents_itself); - } - - { - LayerImplList render_surface_layer_list; - LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &render_surface_layer_list); - inputs.can_render_to_separate_surface = false; - LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - - EXPECT_EQ(1u, render_surface_layer_list.size()); - - int count_represents_target_render_surface = 0; - int count_represents_contributing_render_surface = 0; - int count_represents_itself = 0; - for (EffectTreeLayerListIterator it(host_impl.active_tree()); - it.state() != EffectTreeLayerListIterator::State::END; ++it) { - if (it.state() == EffectTreeLayerListIterator::State::TARGET_SURFACE) { - count_represents_target_render_surface++; - } else if (it.state() == - EffectTreeLayerListIterator::State::CONTRIBUTING_SURFACE) { - count_represents_contributing_render_surface++; - } else { - count_represents_itself++; - } - } - - // Only root layer has a render surface. - EXPECT_EQ(1, count_represents_target_render_surface); - // No layer contributes a render surface to root render surface. - EXPECT_EQ(0, count_represents_contributing_render_surface); - // All 4 layers represent itself. - EXPECT_EQ(4, count_represents_itself); - } + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(child1), GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(child2), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(child3), GetRenderSurface(child2)); } TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleSurfaces) { @@ -6243,19 +5555,9 @@ TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleSurfaces) { ExecuteCalculateDrawProperties(root); - EXPECT_EQ(3u, render_surface_layer_list_impl()->size()); - EXPECT_EQ(2u, - render_surface_layer_list_impl() - ->at(0) - ->GetRenderSurface() - ->layer_list() - .size()); - EXPECT_EQ(1u, - render_surface_layer_list_impl() - ->at(1) - ->GetRenderSurface() - ->layer_list() - .size()); + EXPECT_EQ(3u, render_surface_list_impl()->size()); + EXPECT_EQ(2, render_surface_list_impl()->at(0)->num_contributors()); + EXPECT_EQ(1, render_surface_list_impl()->at(1)->num_contributors()); gfx::Transform rotation_transform; rotation_transform.RotateAboutXAxis(180.0); @@ -6269,13 +5571,8 @@ TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleSurfaces) { // not double sided, so it should not be in RSLL. render_surface2 is also not // double-sided, but will still be in RSLL as it's in a different 3d rendering // context. - EXPECT_EQ(2u, render_surface_layer_list_impl()->size()); - EXPECT_EQ(1u, - render_surface_layer_list_impl() - ->at(0) - ->GetRenderSurface() - ->layer_list() - .size()); + EXPECT_EQ(2u, render_surface_list_impl()->size()); + EXPECT_EQ(1, render_surface_list_impl()->at(0)->num_contributors()); } TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleLayers) { @@ -6294,12 +5591,8 @@ TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleLayers) { grand_child->test_properties()->should_flatten_transform = false; ExecuteCalculateDrawProperties(root); - EXPECT_EQ(1u, render_surface_layer_list_impl()->size()); - EXPECT_EQ(grand_child, - render_surface_layer_list_impl() - ->at(0) - ->GetRenderSurface() - ->layer_list()[0]); + EXPECT_EQ(1u, render_surface_list_impl()->size()); + EXPECT_TRUE(grand_child->contributes_to_drawn_render_surface()); // As all layers have identity transform, we shouldn't check for backface // visibility. @@ -6321,13 +5614,8 @@ TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleLayers) { child->layer_tree_impl()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); - EXPECT_EQ(1u, render_surface_layer_list_impl()->size()); - EXPECT_EQ(0u, - render_surface_layer_list_impl() - ->at(0) - ->GetRenderSurface() - ->layer_list() - .size()); + EXPECT_EQ(1u, render_surface_list_impl()->size()); + EXPECT_EQ(0, render_surface_list_impl()->at(0)->num_contributors()); // We should check for backface visibilty of child as it has a rotation // transform. We should also check for grand_child as it uses the backface @@ -6348,13 +5636,8 @@ TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleLayers) { grand_child->layer_tree_impl()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); - EXPECT_EQ(1u, render_surface_layer_list_impl()->size()); - EXPECT_EQ(0u, - render_surface_layer_list_impl() - ->at(0) - ->GetRenderSurface() - ->layer_list() - .size()); + EXPECT_EQ(1u, render_surface_list_impl()->size()); + EXPECT_EQ(0, render_surface_list_impl()->at(0)->num_contributors()); // We should check the backface visibility of child as it has a rotation // transform and for grand_child as it is in a 3d rendering context and not @@ -6456,7 +5739,7 @@ TEST_F(LayerTreeHostCommonTest, ClippedByScrollParent) { scroll_child->SetBounds(gfx::Size(50, 50)); ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); EXPECT_EQ(gfx::Rect(0, 0, 30, 30), scroll_child->clip_rect()); EXPECT_TRUE(scroll_child->is_clipped()); @@ -6490,13 +5773,12 @@ TEST_F(LayerTreeHostCommonTest, ScrollChildAndScrollParentDifferentTargets) { scroll_parent->SetBounds(gfx::Size(50, 50)); float device_scale_factor = 1.5f; - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; gfx::Size device_viewport_size = gfx::Size(root->bounds().width() * device_scale_factor, root->bounds().height() * device_scale_factor); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, device_viewport_size, gfx::Transform(), - &render_surface_layer_list_impl); + root, device_viewport_size, gfx::Transform(), &render_surface_list_impl); inputs.device_scale_factor = device_scale_factor; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); @@ -6527,7 +5809,7 @@ TEST_F(LayerTreeHostCommonTest, SingularTransformSubtreesDoNotDraw) { child->test_properties()->sorting_context_id = 1; ExecuteCalculateDrawProperties(root); - EXPECT_EQ(3u, render_surface_layer_list_impl()->size()); + EXPECT_EQ(3u, render_surface_list_impl()->size()); gfx::Transform singular_transform; singular_transform.Scale3d( @@ -6538,7 +5820,7 @@ TEST_F(LayerTreeHostCommonTest, SingularTransformSubtreesDoNotDraw) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); - EXPECT_EQ(2u, render_surface_layer_list_impl()->size()); + EXPECT_EQ(2u, render_surface_list_impl()->size()); // Ensure that the entire subtree under a layer with singular transform does // not get rendered. @@ -6548,7 +5830,7 @@ TEST_F(LayerTreeHostCommonTest, SingularTransformSubtreesDoNotDraw) { root->layer_tree_impl()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); - EXPECT_EQ(1u, render_surface_layer_list_impl()->size()); + EXPECT_EQ(1u, render_surface_list_impl()->size()); } TEST_F(LayerTreeHostCommonTest, ClippedByOutOfOrderScrollParent) { @@ -6584,7 +5866,7 @@ TEST_F(LayerTreeHostCommonTest, ClippedByOutOfOrderScrollParent) { ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); EXPECT_EQ(gfx::Rect(0, 0, 30, 30), scroll_child->clip_rect()); EXPECT_TRUE(scroll_child->is_clipped()); @@ -6642,17 +5924,17 @@ TEST_F(LayerTreeHostCommonTest, ClippedByOutOfOrderScrollGrandparent) { ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); EXPECT_EQ(gfx::Rect(0, 0, 20, 20), scroll_child->clip_rect()); EXPECT_TRUE(scroll_child->is_clipped()); // Despite the fact that we visited the above layers out of order to get the // correct clip, the layer lists should be unaffected. - EXPECT_EQ(3u, root->GetRenderSurface()->layer_list().size()); - EXPECT_EQ(scroll_child, root->GetRenderSurface()->layer_list().at(0)); - EXPECT_EQ(scroll_parent, root->GetRenderSurface()->layer_list().at(1)); - EXPECT_EQ(scroll_grandparent, root->GetRenderSurface()->layer_list().at(2)); + EXPECT_EQ(3, GetRenderSurface(root)->num_contributors()); + EXPECT_TRUE(scroll_child->contributes_to_drawn_render_surface()); + EXPECT_TRUE(scroll_parent->contributes_to_drawn_render_surface()); + EXPECT_TRUE(scroll_grandparent->contributes_to_drawn_render_surface()); } TEST_F(LayerTreeHostCommonTest, OutOfOrderClippingRequiresRSLLSorting) { @@ -6719,20 +6001,19 @@ TEST_F(LayerTreeHostCommonTest, OutOfOrderClippingRequiresRSLLSorting) { ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); EXPECT_EQ(gfx::Rect(0, 0, 20, 20), scroll_child->clip_rect()); EXPECT_TRUE(scroll_child->is_clipped()); // Despite the fact that we had to process the layers out of order to get the - // right clip, our render_surface_layer_list's order should be unaffected. - EXPECT_EQ(3u, render_surface_layer_list_impl()->size()); - EXPECT_EQ(root, render_surface_layer_list_impl()->at(0)); - EXPECT_EQ(render_surface2, render_surface_layer_list_impl()->at(1)); - EXPECT_EQ(render_surface1, render_surface_layer_list_impl()->at(2)); - EXPECT_TRUE(render_surface_layer_list_impl()->at(0)->GetRenderSurface()); - EXPECT_TRUE(render_surface_layer_list_impl()->at(1)->GetRenderSurface()); - EXPECT_TRUE(render_surface_layer_list_impl()->at(2)->GetRenderSurface()); + // right clip, our render_surface_list's order should be unaffected. + EXPECT_EQ(3u, render_surface_list_impl()->size()); + EXPECT_EQ(GetRenderSurface(root), render_surface_list_impl()->at(0)); + EXPECT_EQ(GetRenderSurface(render_surface2), + render_surface_list_impl()->at(1)); + EXPECT_EQ(GetRenderSurface(render_surface1), + render_surface_list_impl()->at(2)); } TEST_F(LayerTreeHostCommonTest, FixedPositionWithInterveningRenderSurface) { @@ -6858,9 +6139,9 @@ TEST_F(LayerTreeHostCommonTest, ScrollCompensationWithRounding) { gfx::Vector2dF scroll_delta(3.0, 5.0); SetScrollOffsetDelta(scroll_layer, scroll_delta); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), &render_surface_layer_list); + root, root->bounds(), &render_surface_list); root->layer_tree_impl() ->property_trees() ->transform_tree.set_source_to_parent_updates_allowed(false); @@ -6884,9 +6165,9 @@ TEST_F(LayerTreeHostCommonTest, ScrollCompensationWithRounding) { gfx::Vector2dF rounded_scroll_delta(4.f, 8.f); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), &render_surface_layer_list); + root, root->bounds(), &render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -6912,9 +6193,9 @@ TEST_F(LayerTreeHostCommonTest, ScrollCompensationWithRounding) { gfx::Vector2dF scroll_delta(4.5f, 8.5f); SetScrollOffsetDelta(scroll_layer, scroll_delta); - LayerImplList render_surface_layer_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), &render_surface_layer_list); + root, root->bounds(), &render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -7308,7 +6589,11 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomInnerViewportDelta) { scroller->AddChild(sticky_pos); host()->SetRootLayer(root); scroller->SetScrollClipLayerId(root->id()); - host()->RegisterViewportLayers(nullptr, root, scroller, nullptr); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = root; + viewport_layers.inner_viewport_container = root; + viewport_layers.inner_viewport_scroll = scroller; + host()->RegisterViewportLayers(viewport_layers); LayerStickyPositionConstraint sticky_position; sticky_position.is_sticky = true; @@ -7343,7 +6628,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomInnerViewportDelta) { // We start to hide the toolbar, but not far enough that the sticky element // should be moved up yet. - root_impl->SetBoundsDelta(gfx::Vector2dF(0.f, -10.f)); + root_impl->SetViewportBoundsDelta(gfx::Vector2dF(0.f, -10.f)); ExecuteCalculateDrawProperties(root_impl, 1.f, 1.f, root_impl, inner_scroll, nullptr); EXPECT_VECTOR2DF_EQ( @@ -7351,7 +6636,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomInnerViewportDelta) { sticky_pos_impl->ScreenSpaceTransform().To2dTranslation()); // On hiding more of the toolbar the sticky element starts to stick. - root_impl->SetBoundsDelta(gfx::Vector2dF(0.f, -20.f)); + root_impl->SetViewportBoundsDelta(gfx::Vector2dF(0.f, -20.f)); ExecuteCalculateDrawProperties(root_impl, 1.f, 1.f, root_impl, inner_scroll, nullptr); EXPECT_VECTOR2DF_EQ( @@ -7360,7 +6645,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomInnerViewportDelta) { // On hiding more the sticky element stops moving as it has reached its // limit. - root_impl->SetBoundsDelta(gfx::Vector2dF(0.f, -30.f)); + root_impl->SetViewportBoundsDelta(gfx::Vector2dF(0.f, -30.f)); ExecuteCalculateDrawProperties(root_impl, 1.f, 1.f, root_impl, inner_scroll, nullptr); EXPECT_VECTOR2DF_EQ( @@ -7381,7 +6666,13 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomOuterViewportDelta) { host()->SetRootLayer(root); scroller->SetScrollClipLayerId(root->id()); outer_viewport->SetScrollClipLayerId(outer_clip->id()); - host()->RegisterViewportLayers(nullptr, root, scroller, outer_viewport); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = root; + viewport_layers.inner_viewport_container = root; + viewport_layers.outer_viewport_container = outer_clip; + viewport_layers.inner_viewport_scroll = scroller; + viewport_layers.outer_viewport_scroll = outer_viewport; + host()->RegisterViewportLayers(viewport_layers); LayerStickyPositionConstraint sticky_position; sticky_position.is_sticky = true; @@ -7420,7 +6711,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomOuterViewportDelta) { // We start to hide the toolbar, but not far enough that the sticky element // should be moved up yet. - outer_clip_impl->SetBoundsDelta(gfx::Vector2dF(0.f, -10.f)); + outer_clip_impl->SetViewportBoundsDelta(gfx::Vector2dF(0.f, -10.f)); ExecuteCalculateDrawProperties(root_impl, 1.f, 1.f, root_impl, inner_scroll, outer_scroll); EXPECT_VECTOR2DF_EQ( @@ -7428,7 +6719,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomOuterViewportDelta) { sticky_pos_impl->ScreenSpaceTransform().To2dTranslation()); // On hiding more of the toolbar the sticky element starts to stick. - outer_clip_impl->SetBoundsDelta(gfx::Vector2dF(0.f, -20.f)); + outer_clip_impl->SetViewportBoundsDelta(gfx::Vector2dF(0.f, -20.f)); ExecuteCalculateDrawProperties(root_impl, 1.f, 1.f, root_impl, inner_scroll, outer_scroll); @@ -7438,7 +6729,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomOuterViewportDelta) { gfx::Vector2dF(0.f, 60.f), sticky_pos_impl->ScreenSpaceTransform().To2dTranslation()); - outer_clip_impl->SetBoundsDelta(gfx::Vector2dF(0.f, -30.f)); + outer_clip_impl->SetViewportBoundsDelta(gfx::Vector2dF(0.f, -30.f)); ExecuteCalculateDrawProperties(root_impl, 1.f, 1.f, root_impl, inner_scroll, outer_scroll); @@ -8402,11 +7693,11 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { // Start with nothing being drawn. ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_FALSE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(child_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(child_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child2_raw->contributes_to_drawn_render_surface()); std::set<LayerImpl*> expected; std::set<LayerImpl*> actual; @@ -8420,11 +7711,11 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_FALSE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(child_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(child_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child2_raw->contributes_to_drawn_render_surface()); expected.clear(); actual.clear(); @@ -8437,11 +7728,11 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_FALSE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(child_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(child_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child2_raw->contributes_to_drawn_render_surface()); expected.clear(); expected.insert(grand_child1_raw); @@ -8460,11 +7751,11 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_FALSE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(child_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(child_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(grand_child2_raw->contributes_to_drawn_render_surface()); expected.clear(); expected.insert(grand_child2_raw); @@ -8480,13 +7771,13 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_FALSE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(child_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(child_raw->contributes_to_drawn_render_surface()); EXPECT_TRUE(child_raw->test_properties() - ->mask_layer->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + ->mask_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(grand_child2_raw->contributes_to_drawn_render_surface()); expected.clear(); expected.insert(grand_child2_raw); @@ -8502,13 +7793,13 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_FALSE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(child_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(child_raw->contributes_to_drawn_render_surface()); EXPECT_TRUE(child_raw->test_properties() - ->mask_layer->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + ->mask_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(grand_child2_raw->contributes_to_drawn_render_surface()); expected.clear(); expected.insert(grand_child2_raw); @@ -8525,13 +7816,13 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_FALSE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(child_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(child_raw->contributes_to_drawn_render_surface()); EXPECT_FALSE(child_raw->test_properties() - ->mask_layer->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + ->mask_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child2_raw->contributes_to_drawn_render_surface()); expected.clear(); actual.clear(); @@ -8544,13 +7835,13 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_FALSE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(child_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child_raw->contributes_to_drawn_render_surface()); EXPECT_TRUE(child_raw->test_properties() - ->mask_layer->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_FALSE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + ->mask_layer->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_FALSE(grand_child2_raw->contributes_to_drawn_render_surface()); expected.clear(); expected.insert(child_raw); @@ -8571,11 +7862,11 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) { ExecuteCalculateDrawProperties(grand_parent_raw); - EXPECT_TRUE(grand_parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(parent_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(child_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(grand_child1_raw->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(grand_child2_raw->is_drawn_render_surface_layer_list_member()); + EXPECT_TRUE(grand_parent_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(parent_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(grand_child1_raw->contributes_to_drawn_render_surface()); + EXPECT_TRUE(grand_child2_raw->contributes_to_drawn_render_surface()); expected.clear(); expected.insert(grand_parent_raw); @@ -8659,12 +7950,12 @@ TEST_F(LayerTreeHostCommonTest, DrawPropertyScales) { float page_scale_factor = 3.f; float device_scale_factor = 1.0f; - std::vector<LayerImpl*> render_surface_layer_list; + RenderSurfaceList render_surface_list; gfx::Size device_viewport_size = gfx::Size(root_layer->bounds().width() * device_scale_factor, root_layer->bounds().height() * device_scale_factor); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, device_viewport_size, &render_surface_layer_list); + root_layer, device_viewport_size, &render_surface_list); inputs.page_scale_factor = page_scale_factor; inputs.can_adjust_raster_scales = true; @@ -8839,10 +8130,9 @@ TEST_F(LayerTreeHostCommonTest, VisibleContentRectInChildRenderSurface) { content->test_properties()->force_render_surface = true; gfx::Size device_viewport_size(768, 582); - LayerImplList render_surface_layer_list_impl; + RenderSurfaceList render_surface_list_impl; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, device_viewport_size, gfx::Transform(), - &render_surface_layer_list_impl); + root, device_viewport_size, gfx::Transform(), &render_surface_list_impl); inputs.device_scale_factor = 2.f; inputs.page_scale_factor = 1.f; inputs.page_scale_layer = nullptr; @@ -8857,7 +8147,7 @@ TEST_F(LayerTreeHostCommonTest, VisibleContentRectInChildRenderSurface) { EXPECT_EQ(gfx::Rect(768 / 2, 582 / 2), content->visible_layer_rect()); } -TEST_F(LayerTreeHostCommonTest, BoundsDeltaAffectVisibleContentRect) { +TEST_F(LayerTreeHostCommonTest, ViewportBoundsDeltaAffectVisibleContentRect) { FakeImplTaskRunnerProvider task_runner_provider; TestTaskGraphRunner task_graph_runner; FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); @@ -8881,6 +8171,12 @@ TEST_F(LayerTreeHostCommonTest, BoundsDeltaAffectVisibleContentRect) { root->SetBounds(root_size); root->SetMasksToBounds(true); + // Make root the inner viewport scroll layer. This ensures the later call to + // |SetViewportBoundsDelta| will be on a viewport layer. + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.inner_viewport_scroll = root->id(); + host_impl.active_tree()->SetViewportLayersFromIds(viewport_ids); + root->test_properties()->AddChild( LayerImpl::Create(host_impl.active_tree(), 2)); @@ -8890,14 +8186,14 @@ TEST_F(LayerTreeHostCommonTest, BoundsDeltaAffectVisibleContentRect) { host_impl.active_tree()->BuildPropertyTreesForTesting(); - LayerImplList layer_impl_list; + RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, device_viewport_size, &layer_impl_list); + root, device_viewport_size, &render_surface_list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_EQ(gfx::Rect(root_size), sublayer->visible_layer_rect()); - root->SetBoundsDelta(gfx::Vector2dF(0.0, 50.0)); + root->SetViewportBoundsDelta(gfx::Vector2dF(0.0, 50.0)); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); gfx::Rect affected_by_delta(0, 0, root_size.width(), @@ -8905,7 +8201,7 @@ TEST_F(LayerTreeHostCommonTest, BoundsDeltaAffectVisibleContentRect) { EXPECT_EQ(affected_by_delta, sublayer->visible_layer_rect()); } -TEST_F(LayerTreeHostCommonTest, NodesAffectedByBoundsDeltaGetUpdated) { +TEST_F(LayerTreeHostCommonTest, NodesAffectedByViewportBoundsDeltaGetUpdated) { scoped_refptr<Layer> root = Layer::Create(); scoped_refptr<Layer> inner_viewport_container_layer = Layer::Create(); scoped_refptr<Layer> inner_viewport_scroll_layer = Layer::Create(); @@ -8926,8 +8222,13 @@ TEST_F(LayerTreeHostCommonTest, NodesAffectedByBoundsDeltaGetUpdated) { outer_viewport_scroll_layer->SetIsContainerForFixedPositionLayers(true); host()->SetRootLayer(root); - host()->RegisterViewportLayers(nullptr, root, inner_viewport_scroll_layer, - outer_viewport_scroll_layer); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = root; + viewport_layers.inner_viewport_container = inner_viewport_container_layer; + viewport_layers.outer_viewport_container = outer_viewport_container_layer; + viewport_layers.inner_viewport_scroll = inner_viewport_scroll_layer; + viewport_layers.outer_viewport_scroll = outer_viewport_scroll_layer; + host()->RegisterViewportLayers(viewport_layers); scoped_refptr<Layer> fixed_to_inner = Layer::Create(); scoped_refptr<Layer> fixed_to_outer = Layer::Create(); @@ -8994,14 +8295,31 @@ TEST_F(LayerTreeHostCommonTest, VisibleContentRectForAnimatedLayer) { TEST_F(LayerTreeHostCommonTest, VisibleContentRectForAnimatedLayerWithSingularTransform) { - LayerImpl* root = root_layer_for_testing(); - LayerImpl* clip = AddChild<LayerImpl>(root); - LayerImpl* animated = AddChild<LayerImpl>(clip); - LayerImpl* surface = AddChild<LayerImpl>(animated); - LayerImpl* descendant_of_animation = AddChild<LayerImpl>(surface); + host_impl()->CreatePendingTree(); + std::unique_ptr<LayerImpl> root_ptr = + LayerImpl::Create(host_impl()->pending_tree(), 1); + LayerImpl* root = root_ptr.get(); + host_impl()->pending_tree()->SetRootLayerForTesting(std::move(root_ptr)); + std::unique_ptr<LayerImpl> clip_ptr = + LayerImpl::Create(host_impl()->pending_tree(), 2); + LayerImpl* clip = clip_ptr.get(); + root->test_properties()->AddChild(std::move(clip_ptr)); + std::unique_ptr<LayerImpl> animated_ptr = + LayerImpl::Create(host_impl()->pending_tree(), 3); + LayerImpl* animated = animated_ptr.get(); + clip->test_properties()->AddChild(std::move(animated_ptr)); + std::unique_ptr<LayerImpl> surface_ptr = + LayerImpl::Create(host_impl()->pending_tree(), 4); + LayerImpl* surface = surface_ptr.get(); + animated->test_properties()->AddChild(std::move(surface_ptr)); + std::unique_ptr<LayerImpl> descendant_of_animation_ptr = + LayerImpl::Create(host_impl()->pending_tree(), 5); + LayerImpl* descendant_of_animation = descendant_of_animation_ptr.get(); + surface->test_properties()->AddChild(std::move(descendant_of_animation_ptr)); - SetElementIdsForTesting(); + host_impl()->pending_tree()->SetElementIdsForTesting(); + root->SetDrawsContent(true); animated->SetDrawsContent(true); surface->SetDrawsContent(true); descendant_of_animation->SetDrawsContent(true); @@ -9025,7 +8343,11 @@ TEST_F(LayerTreeHostCommonTest, AddAnimatedTransformToElementWithPlayer( animated->element_id(), timeline_impl(), 10.0, start_transform_operations, end_transform_operations); - ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); + ExecuteCalculateDrawProperties(root); + // Since animated has singular transform, it is not be part of render + // surface layer list but should be rastered. + EXPECT_FALSE(animated->contributes_to_drawn_render_surface()); + EXPECT_TRUE(animated->raster_even_if_not_in_rsll()); // The animated layer has a singular transform and maps to a non-empty rect in // clipped target space, so is treated as fully visible. @@ -9040,7 +8362,7 @@ TEST_F(LayerTreeHostCommonTest, zero_matrix.Scale3d(0.f, 0.f, 0.f); root->layer_tree_impl()->SetTransformMutated(animated->element_id(), zero_matrix); - ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root); + ExecuteCalculateDrawProperties(root); // The animated layer will be treated as fully visible when we combine clips // in screen space. @@ -9051,6 +8373,22 @@ TEST_F(LayerTreeHostCommonTest, // |surface| and layers that draw into it as having empty visible rect. EXPECT_EQ(gfx::Rect(100, 100), surface->visible_layer_rect()); EXPECT_EQ(gfx::Rect(200, 200), descendant_of_animation->visible_layer_rect()); + + host_impl()->ActivateSyncTree(); + LayerImpl* active_root = host_impl()->active_tree()->LayerById(root->id()); + ExecuteCalculateDrawProperties(active_root); + + // Since the animated has singular transform, it is not be part of render + // surface layer list. + LayerImpl* active_animated = + host_impl()->active_tree()->LayerById(animated->id()); + EXPECT_TRUE(active_root->contributes_to_drawn_render_surface()); + EXPECT_FALSE(active_animated->contributes_to_drawn_render_surface()); + + // Since animated has singular transform, it is not be part of render + // surface layer list but should be rastered. + EXPECT_TRUE(animated->raster_even_if_not_in_rsll()); + EXPECT_EQ(gfx::Rect(120, 120), active_animated->visible_layer_rect()); } // Verify that having animated opacity but current opacity 1 still creates @@ -9072,9 +8410,14 @@ TEST_F(LayerTreeHostCommonTest, AnimatedOpacityCreatesRenderSurface) { ExecuteCalculateDrawProperties(root); EXPECT_EQ(1.f, child->Opacity()); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_TRUE(child->GetRenderSurface()); - EXPECT_FALSE(grandchild->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(child), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(grandchild), GetRenderSurface(child)); +} + +static bool FilterIsAnimating(LayerImpl* layer) { + return layer->GetMutatorHost()->IsAnimatingFilterProperty( + layer->element_id(), layer->GetElementTypeForAnimation()); } // Verify that having an animated filter (but no current filter, as these @@ -9093,16 +8436,16 @@ TEST_F(LayerTreeHostCommonTest, AnimatedFilterCreatesRenderSurface) { 10.0, 0.1f, 0.2f); ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_TRUE(child->GetRenderSurface()); - EXPECT_FALSE(grandchild->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(child), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(grandchild), GetRenderSurface(child)); - EXPECT_TRUE(root->GetRenderSurface()->Filters().IsEmpty()); - EXPECT_TRUE(child->GetRenderSurface()->Filters().IsEmpty()); + EXPECT_TRUE(GetRenderSurface(root)->Filters().IsEmpty()); + EXPECT_TRUE(GetRenderSurface(child)->Filters().IsEmpty()); - EXPECT_FALSE(root->FilterIsAnimating()); - EXPECT_TRUE(child->FilterIsAnimating()); - EXPECT_FALSE(grandchild->FilterIsAnimating()); + EXPECT_FALSE(FilterIsAnimating(root)); + EXPECT_TRUE(FilterIsAnimating(child)); + EXPECT_FALSE(FilterIsAnimating(grandchild)); } // Verify that having a filter animation with a delayed start time creates a @@ -9137,18 +8480,18 @@ TEST_F(LayerTreeHostCommonTest, DelayedFilterAnimationCreatesRenderSurface) { std::move(animation)); ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_TRUE(child->GetRenderSurface()); - EXPECT_FALSE(grandchild->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_NE(GetRenderSurface(child), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(grandchild), GetRenderSurface(child)); - EXPECT_TRUE(root->GetRenderSurface()->Filters().IsEmpty()); - EXPECT_TRUE(child->GetRenderSurface()->Filters().IsEmpty()); + EXPECT_TRUE(GetRenderSurface(root)->Filters().IsEmpty()); + EXPECT_TRUE(GetRenderSurface(child)->Filters().IsEmpty()); - EXPECT_FALSE(root->FilterIsAnimating()); + EXPECT_FALSE(FilterIsAnimating(root)); EXPECT_FALSE(root->HasPotentiallyRunningFilterAnimation()); - EXPECT_FALSE(child->FilterIsAnimating()); + EXPECT_FALSE(FilterIsAnimating(child)); EXPECT_TRUE(child->HasPotentiallyRunningFilterAnimation()); - EXPECT_FALSE(grandchild->FilterIsAnimating()); + EXPECT_FALSE(FilterIsAnimating(grandchild)); EXPECT_FALSE(grandchild->HasPotentiallyRunningFilterAnimation()); } @@ -9344,14 +8687,7 @@ TEST_F(LayerTreeHostCommonTest, UpdateScrollChildPosition) { static void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result) {} -TEST_F(LayerTreeHostCommonTest, NumCopyRequestsInTargetSubtree) { - // If the layer has a node in effect_tree, the return value of - // num_copy_requests_in_target_subtree() must be equal to the actual number - // of copy requests in the sub-layer_tree; Otherwise, the number is expected - // to be the value of its nearest ancestor that owns an effect node and - // greater than or equal to the actual number of copy requests in the - // sub-layer_tree. - +TEST_F(LayerTreeHostCommonTest, HasCopyRequestsInTargetSubtree) { scoped_refptr<Layer> root = Layer::Create(); scoped_refptr<Layer> child1 = Layer::Create(); scoped_refptr<Layer> child2 = Layer::Create(); @@ -9371,11 +8707,11 @@ TEST_F(LayerTreeHostCommonTest, NumCopyRequestsInTargetSubtree) { child2->SetOpacity(0.f); ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get()); - EXPECT_EQ(root->num_copy_requests_in_target_subtree(), 2); - EXPECT_EQ(child1->num_copy_requests_in_target_subtree(), 2); - EXPECT_EQ(child2->num_copy_requests_in_target_subtree(), 0); - EXPECT_EQ(grandchild->num_copy_requests_in_target_subtree(), 2); - EXPECT_EQ(greatgrandchild->num_copy_requests_in_target_subtree(), 1); + EXPECT_TRUE(root->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(child1->has_copy_requests_in_target_subtree()); + EXPECT_FALSE(child2->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(grandchild->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(greatgrandchild->has_copy_requests_in_target_subtree()); } TEST_F(LayerTreeHostCommonTest, SkippingSubtreeMain) { @@ -9781,10 +9117,10 @@ TEST_F(LayerTreeHostCommonTest, LayerTreeRebuildTest) { CopyOutputRequest::CreateRequest(base::Bind(&EmptyCopyOutputCallback))); ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get()); - EXPECT_GT(root->num_copy_requests_in_target_subtree(), 0); + EXPECT_TRUE(root->has_copy_requests_in_target_subtree()); ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get()); - EXPECT_GT(root->num_copy_requests_in_target_subtree(), 0); + EXPECT_TRUE(root->has_copy_requests_in_target_subtree()); } TEST_F(LayerTreeHostCommonTest, ResetPropertyTreeIndices) { @@ -9856,11 +9192,11 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceClipsSubtree) { transform_tree.Node(significant_transform->transform_tree_index()); EXPECT_EQ(transform_node->owning_layer_id, significant_transform->id()); - EXPECT_TRUE(root->GetRenderSurface()); - EXPECT_FALSE(significant_transform->GetRenderSurface()); - EXPECT_TRUE(layer_clips_subtree->GetRenderSurface()); - EXPECT_TRUE(render_surface->GetRenderSurface()); - EXPECT_FALSE(test_layer->GetRenderSurface()); + EXPECT_TRUE(GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(significant_transform), GetRenderSurface(root)); + EXPECT_TRUE(GetRenderSurface(layer_clips_subtree)); + EXPECT_NE(GetRenderSurface(render_surface), GetRenderSurface(root)); + EXPECT_EQ(GetRenderSurface(test_layer), GetRenderSurface(render_surface)); EXPECT_EQ(gfx::Rect(30, 20), test_layer->visible_layer_rect()); } @@ -10066,13 +9402,13 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceContentRectWithMultipleSurfaces) { ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::Rect(50, 50), - unclipped_surface->GetRenderSurface()->content_rect()); + GetRenderSurface(unclipped_surface)->content_rect()); EXPECT_EQ(gfx::Rect(50, 50), - unclipped_desc_surface->GetRenderSurface()->content_rect()); + GetRenderSurface(unclipped_desc_surface)->content_rect()); EXPECT_EQ(gfx::Rect(60, 60), - unclipped_desc_surface2->GetRenderSurface()->content_rect()); + GetRenderSurface(unclipped_desc_surface2)->content_rect()); EXPECT_EQ(gfx::Rect(50, 50), - clipped_surface->GetRenderSurface()->content_rect()); + GetRenderSurface(clipped_surface)->content_rect()); } TEST_F(LayerTreeHostCommonTest, ClipBetweenClipChildTargetAndClipParentTarget) { @@ -10114,7 +9450,7 @@ TEST_F(LayerTreeHostCommonTest, ClipBetweenClipChildTargetAndClipParentTarget) { ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::Rect(10, 10), - unclipped_desc_surface->GetRenderSurface()->content_rect()); + GetRenderSurface(unclipped_desc_surface)->content_rect()); } TEST_F(LayerTreeHostCommonTest, VisibleRectForDescendantOfScaledSurface) { @@ -10278,13 +9614,13 @@ TEST_F(LayerTreeHostCommonTest, SubtreeIsHiddenTest) { ExecuteCalculateDrawProperties(root); EXPECT_EQ(0.f, - test->GetRenderSurface()->OwningEffectNode()->screen_space_opacity); + GetRenderSurface(test)->OwningEffectNode()->screen_space_opacity); hidden->test_properties()->hide_layer_and_subtree = false; root->layer_tree_impl()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); EXPECT_EQ(1.f, - test->GetRenderSurface()->OwningEffectNode()->screen_space_opacity); + GetRenderSurface(test)->OwningEffectNode()->screen_space_opacity); } TEST_F(LayerTreeHostCommonTest, TwoUnclippedRenderSurfaces) { @@ -10338,9 +9674,10 @@ TEST_F(LayerTreeHostCommonTest, MaskLayerDrawProperties) { ExecuteCalculateDrawProperties(root); // The render surface created for the mask has no contributing content, so the - // mask isn't a drawn RSLL member. This means it has an empty visible rect, - // but its screen space transform can still be computed correctly on-demand. - EXPECT_FALSE(mask->is_drawn_render_surface_layer_list_member()); + // mask doesn't contribute to a drawn render surface. This means it has an + // empty visible rect, but its screen space transform can still be computed + // correctly on-demand. + EXPECT_FALSE(mask->contributes_to_drawn_render_surface()); EXPECT_EQ(gfx::Rect(), mask->visible_layer_rect()); EXPECT_TRANSFORMATION_MATRIX_EQ(transform, mask->ScreenSpaceTransform()); @@ -10348,7 +9685,7 @@ TEST_F(LayerTreeHostCommonTest, MaskLayerDrawProperties) { child->SetDrawsContent(true); root->layer_tree_impl()->property_trees()->needs_rebuild = true; ExecuteCalculateDrawProperties(root); - EXPECT_TRUE(mask->is_drawn_render_surface_layer_list_member()); + EXPECT_TRUE(mask->contributes_to_drawn_render_surface()); EXPECT_EQ(gfx::Rect(20, 20), mask->visible_layer_rect()); EXPECT_TRANSFORMATION_MATRIX_EQ(transform, mask->ScreenSpaceTransform()); @@ -10471,7 +9808,7 @@ TEST_F(LayerTreeHostCommonTest, LargeTransformTest) { ExecuteCalculateDrawProperties(root); EXPECT_EQ(gfx::RectF(), - render_surface1->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface1)->DrawableContentRect()); bool is_inf_or_nan = std::isinf(child->DrawTransform().matrix().get(0, 0)) || std::isnan(child->DrawTransform().matrix().get(0, 0)); @@ -10481,9 +9818,10 @@ TEST_F(LayerTreeHostCommonTest, LargeTransformTest) { std::isnan(child->DrawTransform().matrix().get(1, 1)); EXPECT_TRUE(is_inf_or_nan); - // The root layer should be in the RenderSurfaceLayerListImpl. - const auto* rsll = render_surface_layer_list_impl(); - EXPECT_NE(std::find(rsll->begin(), rsll->end(), root), rsll->end()); + // The root layer should be in the RenderSurfaceList. + const auto* rsl = render_surface_list_impl(); + EXPECT_NE(std::find(rsl->begin(), rsl->end(), GetRenderSurface(root)), + rsl->end()); } TEST_F(LayerTreeHostCommonTest, PropertyTreesRebuildWithOpacityChanges) { @@ -10717,7 +10055,11 @@ TEST_F(LayerTreeHostCommonTest, ScrollTreeBuilderTest) { parent5->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50)); parent5->SetBounds(gfx::Size(10, 10)); - host()->RegisterViewportLayers(nullptr, page_scale_layer, parent2, nullptr); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = page_scale_layer; + viewport_layers.inner_viewport_container = root1; + viewport_layers.inner_viewport_scroll = parent2; + host()->RegisterViewportLayers(viewport_layers); ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root1.get()); const int kRootPropertyTreeNodeId = 0; @@ -10860,10 +10202,10 @@ TEST_F(LayerTreeHostCommonTest, CanAdjustRasterScaleTest) { // Check surface draw properties. EXPECT_EQ(gfx::Rect(10, 10), - render_surface->GetRenderSurface()->content_rect()); - EXPECT_EQ(transform, render_surface->GetRenderSurface()->draw_transform()); + GetRenderSurface(render_surface)->content_rect()); + EXPECT_EQ(transform, GetRenderSurface(render_surface)->draw_transform()); EXPECT_EQ(gfx::RectF(50.0f, 50.0f), - render_surface->GetRenderSurface()->DrawableContentRect()); + GetRenderSurface(render_surface)->DrawableContentRect()); // Check child layer draw properties. EXPECT_EQ(gfx::Rect(10, 10), child->visible_layer_rect()); diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index 05e4e3add0a..359d56d6e43 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -16,7 +16,7 @@ #include "base/auto_reset.h" #include "base/bind.h" -#include "base/containers/small_map.h" +#include "base/containers/flat_map.h" #include "base/json/json_writer.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram.h" @@ -36,6 +36,7 @@ #include "cc/input/scroll_elasticity_helper.h" #include "cc/input/scroll_state.h" #include "cc/input/scrollbar_animation_controller.h" +#include "cc/input/scroller_size_metrics.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/effect_tree_layer_list_iterator.h" #include "cc/layers/heads_up_display_layer_impl.h" @@ -162,7 +163,7 @@ DEFINE_SCOPED_UMA_HISTOGRAM_TIMER(PendingTreeDurationHistogramTimer, "Scheduling.%s.PendingTreeDuration"); LayerTreeHostImpl::FrameData::FrameData() - : render_surface_layer_list(nullptr), + : render_surface_list(nullptr), has_no_damage(false), may_contain_video(false) {} @@ -206,8 +207,6 @@ LayerTreeHostImpl::LayerTreeHostImpl( did_lock_scrolling_layer_(false), wheel_scrolling_(false), scroll_affects_scroll_handler_(false), - scroll_layer_id_mouse_currently_over_(Layer::INVALID_ID), - scroll_layer_id_mouse_currently_captured_(Layer::INVALID_ID), tile_priorities_dirty_(false), settings_(settings), visible_(false), @@ -243,7 +242,8 @@ LayerTreeHostImpl::LayerTreeHostImpl( scroll_animating_latched_node_id_(ScrollTree::kInvalidNodeId), has_scrolled_by_wheel_(false), has_scrolled_by_touch_(false), - touchpad_and_wheel_scroll_latching_enabled_(false) { + touchpad_and_wheel_scroll_latching_enabled_(false), + impl_thread_phase_(ImplThreadPhase::IDLE) { DCHECK(mutator_host_); mutator_host_->SetMutatorHostClient(this); @@ -325,15 +325,6 @@ void LayerTreeHostImpl::BeginMainFrameAborted( void LayerTreeHostImpl::BeginCommit() { TRACE_EVENT0("cc", "LayerTreeHostImpl::BeginCommit"); - // Ensure all textures are returned so partial texture updates can happen - // during the commit. - // TODO(ericrk): We should not need to ForceReclaimResources when using - // Impl-side-painting as it doesn't upload during commits. However, - // Display::Draw currently relies on resource being reclaimed to block drawing - // between BeginCommit / Swap. See crbug.com/489515. - if (compositor_frame_sink_) - compositor_frame_sink_->ForceReclaimResources(); - if (!CommitToActiveTree()) CreatePendingTree(); } @@ -341,6 +332,11 @@ void LayerTreeHostImpl::BeginCommit() { void LayerTreeHostImpl::CommitComplete() { TRACE_EVENT0("cc", "LayerTreeHostImpl::CommitComplete"); + // In high latency mode commit cannot finish within the same frame. We need to + // flush input here to make sure they got picked up by |PrepareTiles()|. + if (input_handler_client_ && impl_thread_phase_ == ImplThreadPhase::IDLE) + input_handler_client_->DeliverInputForBeginFrame(); + UpdateSyncTreeAfterCommitOrImplSideInvalidation(); micro_benchmark_controller_.DidCompleteCommit(); } @@ -350,6 +346,8 @@ void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() { tile_manager_.TakeImagesToInvalidateOnSyncTree()); if (CommitToActiveTree()) { + active_tree_->HandleScrollbarShowRequestsFromMain(); + // We have to activate animations here or "IsActive()" is true on the layers // but the animations aren't activated yet so they get ignored by // UpdateDrawProperties. @@ -416,7 +414,7 @@ bool LayerTreeHostImpl::CanDraw() const { if (resourceless_software_draw_) return true; - if (DrawViewportSize().IsEmpty()) { + if (DeviceViewport().IsEmpty()) { TRACE_EVENT_INSTANT0("cc", "LayerTreeHostImpl::CanDraw empty viewport", TRACE_EVENT_SCOPE_THREAD); return false; @@ -577,21 +575,20 @@ bool LayerTreeHostImpl::IsCurrentlyScrollingLayerAt( bool scroll_on_main_thread = false; uint32_t main_thread_scrolling_reasons; - LayerImpl* test_layer_impl = FindScrollLayerForDeviceViewportPoint( + auto* test_scroll_node = FindScrollNodeForDeviceViewportPoint( device_viewport_point, type, layer_impl, &scroll_on_main_thread, &main_thread_scrolling_reasons); if (scroll_on_main_thread) return false; - int test_scroll_tree_index = test_layer_impl->scroll_tree_index(); - if (scrolling_node->id == test_scroll_tree_index) + if (scrolling_node == test_scroll_node) return true; // For active scrolling state treat the inner/outer viewports interchangeably. if (scrolling_node->scrolls_inner_viewport || scrolling_node->scrolls_outer_viewport) { - return test_layer_impl == viewport()->MainScrollLayer(); + return test_scroll_node == OuterViewportScrollNode(); } return false; @@ -707,27 +704,6 @@ void LayerTreeHostImpl::QueueSwapPromiseForMainThreadScrollUpdate( std::move(swap_promise)); } -void LayerTreeHostImpl::TrackDamageForAllSurfaces( - const LayerImplList& render_surface_layer_list) { - // For now, we use damage tracking to compute a global scissor. To do this, we - // must compute all damage tracking before drawing anything, so that we know - // the root damage rect. The root damage rect is then used to scissor each - // surface. - size_t render_surface_layer_list_size = render_surface_layer_list.size(); - for (size_t i = 0; i < render_surface_layer_list_size; ++i) { - size_t surface_index = render_surface_layer_list_size - 1 - i; - LayerImpl* render_surface_layer = render_surface_layer_list[surface_index]; - RenderSurfaceImpl* render_surface = - render_surface_layer->GetRenderSurface(); - DCHECK(render_surface); - render_surface->damage_tracker()->UpdateDamageTrackingState( - render_surface->layer_list(), render_surface, - render_surface->SurfacePropertyChangedOnlyFromDescendant(), - render_surface->content_rect(), render_surface->MaskLayer(), - render_surface->Filters()); - } -} - void LayerTreeHostImpl::FrameData::AsValueInto( base::trace_event::TracedValue* value) const { value->SetBoolean("has_no_damage", has_no_damage); @@ -758,11 +734,12 @@ DrawMode LayerTreeHostImpl::GetDrawMode() const { } } -static void AppendQuadsToFillScreen(const gfx::Rect& root_scroll_layer_rect, - RenderPass* target_render_pass, - RenderSurfaceImpl* root_render_surface, - SkColor screen_background_color, - const Region& fill_region) { +static void AppendQuadsToFillScreen( + const gfx::Rect& root_scroll_layer_rect, + RenderPass* target_render_pass, + const RenderSurfaceImpl* root_render_surface, + SkColor screen_background_color, + const Region& fill_region) { if (!root_render_surface || !SkColorGetA(screen_background_color)) return; if (fill_region.IsEmpty()) @@ -778,7 +755,7 @@ static void AppendQuadsToFillScreen(const gfx::Rect& root_scroll_layer_rect, int sorting_context_id = 0; SharedQuadState* shared_quad_state = target_render_pass->CreateAndAppendSharedQuadState(); - shared_quad_state->SetAll(gfx::Transform(), root_target_rect.size(), + shared_quad_state->SetAll(gfx::Transform(), root_target_rect, root_target_rect, root_target_rect, false, opacity, SkBlendMode::kSrcOver, sorting_context_id); @@ -807,19 +784,24 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { DCHECK(CanDraw()); DCHECK(!active_tree_->LayerListIsEmpty()); - TrackDamageForAllSurfaces(*frame->render_surface_layer_list); + // For now, we use damage tracking to compute a global scissor. To do this, we + // must compute all damage tracking before drawing anything, so that we know + // the root damage rect. The root damage rect is then used to scissor each + // surface. + DamageTracker::UpdateDamageTracking(active_tree_.get(), + active_tree_->GetRenderSurfaceList()); // If the root render surface has no visible damage, then don't generate a // frame at all. - RenderSurfaceImpl* root_surface = active_tree_->RootRenderSurface(); + const RenderSurfaceImpl* root_surface = active_tree_->RootRenderSurface(); bool root_surface_has_no_visible_damage = !root_surface->GetDamageRect().Intersects(root_surface->content_rect()); bool root_surface_has_contributing_layers = - !root_surface->layer_list().empty(); + !!root_surface->num_contributors(); bool hud_wants_to_draw_ = active_tree_->hud_layer() && active_tree_->hud_layer()->IsAnimatingHUDContents(); - bool resources_must_be_resent = - compositor_frame_sink_->capabilities().can_force_reclaim_resources; + bool must_always_swap = + compositor_frame_sink_->capabilities().must_always_swap; // When touch handle visibility changes there is no visible damage // because touch handles are composited in the browser. However we // still want the browser to be notified that the handles changed @@ -830,8 +812,7 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { if (root_surface_has_contributing_layers && root_surface_has_no_visible_damage && !active_tree_->property_trees()->effect_tree.HasCopyRequests() && - !resources_must_be_resent && !hud_wants_to_draw_ && - !handle_visibility_changed) { + !must_always_swap && !hud_wants_to_draw_ && !handle_visibility_changed) { TRACE_EVENT0("cc", "LayerTreeHostImpl::CalculateRenderPasses::EmptyDamageRect"); frame->has_no_damage = true; @@ -839,25 +820,22 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { return DRAW_SUCCESS; } - TRACE_EVENT_BEGIN2( - "cc", "LayerTreeHostImpl::CalculateRenderPasses", - "render_surface_layer_list.size()", - static_cast<uint64_t>(frame->render_surface_layer_list->size()), - "RequiresHighResToDraw", RequiresHighResToDraw()); + TRACE_EVENT_BEGIN2("cc", "LayerTreeHostImpl::CalculateRenderPasses", + "render_surface_list.size()", + static_cast<uint64_t>(frame->render_surface_list->size()), + "RequiresHighResToDraw", RequiresHighResToDraw()); // Create the render passes in dependency order. - size_t render_surface_layer_list_size = - frame->render_surface_layer_list->size(); - for (size_t i = 0; i < render_surface_layer_list_size; ++i) { - size_t surface_index = render_surface_layer_list_size - 1 - i; - LayerImpl* render_surface_layer = - (*frame->render_surface_layer_list)[surface_index]; + size_t render_surface_list_size = frame->render_surface_list->size(); + for (size_t i = 0; i < render_surface_list_size; ++i) { + size_t surface_index = render_surface_list_size - 1 - i; RenderSurfaceImpl* render_surface = - render_surface_layer->GetRenderSurface(); + (*frame->render_surface_list)[surface_index]; + bool is_root_surface = + render_surface->EffectTreeIndex() == EffectTree::kContentsRootNodeId; bool should_draw_into_render_pass = - active_tree_->IsRootLayer(render_surface_layer) || - render_surface->contributes_to_drawn_surface() || + is_root_surface || render_surface->contributes_to_drawn_surface() || render_surface->HasCopyRequest(); if (should_draw_into_render_pass) frame->render_passes.push_back(render_surface->CreateRenderPass()); @@ -967,15 +945,13 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { append_quads_data.checkerboarded_needs_raster_content_area; if (append_quads_data.num_missing_tiles > 0) { have_missing_animated_tiles |= - !layer->was_ever_ready_since_last_transform_animation() || layer->screen_space_transform_is_animating(); - } else { - layer->set_was_ever_ready_since_last_transform_animation(true); } } - frame->embedded_surfaces.insert(frame->embedded_surfaces.end(), - append_quads_data.embedded_surfaces.begin(), - append_quads_data.embedded_surfaces.end()); + frame->activation_dependencies.insert( + frame->activation_dependencies.end(), + append_quads_data.activation_dependencies.begin(), + append_quads_data.activation_dependencies.end()); } // If CommitToActiveTree() is true, then we wait to draw until @@ -1091,6 +1067,19 @@ void LayerTreeHostImpl::InvalidateContentOnImplSide() { if (!CommitToActiveTree()) CreatePendingTree(); + + // The state of PropertyTrees on the recycle tree can be stale if scrolling + // and animation updates were made on the active tree, since these are not + // replicated on the recycle tree. We use the function below to handle the + // similar case for updates from the main thread not being in sync with + // changes made on the active tree. + // Note that while in practice only the scroll state should need to be + // updated, since the animation state is updated both on the recycle and + // active tree. We use the same function as for main thread commits for + // consistency. + bool is_impl_side_update = true; + sync_tree()->UpdatePropertyTreeScrollingAndAnimationFromMainThread( + is_impl_side_update); UpdateSyncTreeAfterCommitOrImplSideInvalidation(); } @@ -1128,7 +1117,7 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { // they appear as part of the current frame being drawn. tile_manager_.Flush(); - frame->render_surface_layer_list = &active_tree_->RenderSurfaceLayerList(); + frame->render_surface_list = &active_tree_->GetRenderSurfaceList(); frame->render_passes.clear(); frame->will_draw_layers.clear(); frame->has_no_damage = false; @@ -1158,10 +1147,10 @@ void LayerTreeHostImpl::RemoveRenderPasses(FrameData* frame) { DCHECK_GE(frame->render_passes.size(), 1u); // A set of RenderPasses that we have seen. - std::set<int> pass_exists; + base::flat_set<int> pass_exists; // A set of RenderPassDrawQuads that we have seen (stored by the RenderPasses // they refer to). - base::SmallMap<std::unordered_map<int, int>> pass_references; + base::flat_map<int, int> pass_references; // Iterate RenderPasses in draw order, removing empty render passes (except // the root RenderPass). @@ -1296,6 +1285,14 @@ void LayerTreeHostImpl::UpdateTileManagerMemoryPolicy( // running work. if (image_decode_cache_) image_decode_cache_->SetShouldAggressivelyFreeResources(false); + } else { + // When the memory policy is set to zero, its important to release any + // decoded images cached by the tracker. But we can not re-checker any + // images that have been displayed since the resources, if held by the + // browser, may be re-used. Which is why its important to maintain the + // decode policy tracking. + bool can_clear_decode_policy_tracking = false; + tile_manager_.ClearCheckerImageTracking(can_clear_decode_policy_tracking); } DCHECK(resource_pool_); @@ -1352,11 +1349,23 @@ void LayerTreeHostImpl::SetIsLikelyToRequireADraw( } gfx::ColorSpace LayerTreeHostImpl::GetRasterColorSpace() const { + gfx::ColorSpace result; if (!settings_.enable_color_correct_rasterization) - return gfx::ColorSpace(); - if (!sync_tree()) - return gfx::ColorSpace::CreateSRGB(); - return sync_tree()->raster_color_space(); + return result; + + // The pending tree will have the most recently updated color space, so + // prefer that. + if (pending_tree_) + result = pending_tree_->raster_color_space(); + else if (active_tree_) + result = active_tree_->raster_color_space(); + + // Always specify a color space if color correct rasterization is requested + // (not specifying a color space indicates that no color conversion is + // required). + if (!result.IsValid()) + result = gfx::ColorSpace::CreateSRGB(); + return result; } void LayerTreeHostImpl::RequestImplSideInvalidation() { @@ -1674,8 +1683,8 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { if (debug_state_.ShowHudRects()) { debug_rect_history_->SaveDebugRectsForCurrentFrame( - active_tree(), active_tree_->hud_layer(), - *frame->render_surface_layer_list, debug_state_); + active_tree(), active_tree_->hud_layer(), *frame->render_surface_list, + debug_state_); } bool is_new_trace; @@ -1709,7 +1718,7 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { CompositorFrameMetadata metadata = MakeCompositorFrameMetadata(); metadata.may_contain_video = frame->may_contain_video; - metadata.embedded_surfaces = std::move(frame->embedded_surfaces); + metadata.activation_dependencies = std::move(frame->activation_dependencies); active_tree()->FinishSwapPromises(&metadata); for (auto& latency : metadata.latency_info) { TRACE_EVENT_WITH_FLOW1("input,benchmark", "LatencyInfo.Flow", @@ -1745,7 +1754,8 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { compositor_frame.render_pass_list = std::move(frame->render_passes); // TODO(fsamuel): Once all clients get their LocalSurfaceId from their parent, // the LocalSurfaceId should hang off CompositorFrameMetadata. - if (active_tree()->local_surface_id().is_valid()) { + if (settings_.enable_surface_synchronization && + active_tree()->local_surface_id().is_valid()) { compositor_frame_sink_->SetLocalSurfaceId( active_tree()->local_surface_id()); } @@ -1759,8 +1769,8 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { // are noted as they occur. // TODO(boliu): If we did a temporary software renderer frame, propogate the // damage forward to the next frame. - for (size_t i = 0; i < frame->render_surface_layer_list->size(); i++) { - auto* surface = (*frame->render_surface_layer_list)[i]->GetRenderSurface(); + for (size_t i = 0; i < frame->render_surface_list->size(); i++) { + auto* surface = (*frame->render_surface_list)[i]; surface->damage_tracker()->DidDrawDamagedArea(); } active_tree_->ResetAllChangeTracking(); @@ -1857,14 +1867,13 @@ bool LayerTreeHostImpl::UpdateGpuRasterizationStatus() { gpu_rasterization_status_ = GpuRasterizationStatus::OFF_DEVICE; } else if (!has_gpu_rasterization_trigger_) { gpu_rasterization_status_ = GpuRasterizationStatus::OFF_VIEWPORT; - } else if (content_is_suitable_for_gpu_rasterization_) { - use_gpu = true; - gpu_rasterization_status_ = GpuRasterizationStatus::ON; - } else if (using_msaa_for_complex_content) { + } else if (!content_is_suitable_for_gpu_rasterization_ && + using_msaa_for_complex_content) { use_gpu = use_msaa = true; gpu_rasterization_status_ = GpuRasterizationStatus::MSAA_CONTENT; } else { - gpu_rasterization_status_ = GpuRasterizationStatus::OFF_CONTENT; + use_gpu = true; + gpu_rasterization_status_ = GpuRasterizationStatus::ON; } if (use_gpu && !use_gpu_rasterization_) { @@ -1929,13 +1938,21 @@ void LayerTreeHostImpl::WillBeginImplFrame(const BeginFrameArgs& args) { for (auto* it : video_frame_controllers_) it->OnBeginFrame(args); + + impl_thread_phase_ = ImplThreadPhase::INSIDE_IMPL_FRAME; } void LayerTreeHostImpl::DidFinishImplFrame() { + impl_thread_phase_ = ImplThreadPhase::IDLE; current_begin_frame_tracker_.Finish(); decoded_image_tracker_.NotifyFrameFinished(); } +void LayerTreeHostImpl::DidNotProduceFrame(const BeginFrameAck& ack) { + if (compositor_frame_sink_) + compositor_frame_sink_->DidNotProduceFrame(ack); +} + void LayerTreeHostImpl::UpdateViewportContainerSizes() { LayerImpl* inner_container = active_tree_->InnerViewportContainerLayer(); LayerImpl* outer_container = active_tree_->OuterViewportContainerLayer(); @@ -1964,7 +1981,7 @@ void LayerTreeHostImpl::UpdateViewportContainerSizes() { // for changes in the size (e.g. browser controls) since the last resize from // Blink. gfx::Vector2dF amount_to_expand(0.f, delta_from_top_controls); - inner_container->SetBoundsDelta(amount_to_expand); + inner_container->SetViewportBoundsDelta(amount_to_expand); if (outer_container && !outer_container->BoundsForScrolling().IsEmpty()) { // Adjust the outer viewport container as well, since adjusting only the @@ -1972,8 +1989,8 @@ void LayerTreeHostImpl::UpdateViewportContainerSizes() { // clamping. gfx::Vector2dF amount_to_expand_scaled = gfx::ScaleVector2d( amount_to_expand, 1.f / active_tree_->min_page_scale_factor()); - outer_container->SetBoundsDelta(amount_to_expand_scaled); - active_tree_->InnerViewportScrollLayer()->SetBoundsDelta( + outer_container->SetViewportBoundsDelta(amount_to_expand_scaled); + active_tree_->InnerViewportScrollLayer()->SetViewportBoundsDelta( amount_to_expand_scaled); anchor.ResetViewportToAnchoredPosition(); @@ -2049,9 +2066,38 @@ void LayerTreeHostImpl::CreatePendingTree() { pending_tree_duration_timer_.reset(new PendingTreeDurationHistogramTimer()); } +void LayerTreeHostImpl::PushScrollbarOpacitiesFromActiveToPending() { + if (!active_tree()) + return; + for (auto& pair : scrollbar_animation_controllers_) { + for (auto* scrollbar : pair.second->Scrollbars()) { + if (const EffectNode* source_effect_node = + active_tree() + ->property_trees() + ->effect_tree.FindNodeFromElementId( + scrollbar->element_id())) { + if (EffectNode* target_effect_node = + pending_tree() + ->property_trees() + ->effect_tree.FindNodeFromElementId( + scrollbar->element_id())) { + DCHECK(target_effect_node); + float source_opacity = source_effect_node->opacity; + float target_opacity = target_effect_node->opacity; + if (source_opacity == target_opacity) + continue; + target_effect_node->opacity = source_opacity; + pending_tree()->property_trees()->effect_tree.set_needs_update(true); + } + } + } + } +} + void LayerTreeHostImpl::ActivateSyncTree() { if (pending_tree_) { TRACE_EVENT_ASYNC_END0("cc", "PendingTree:waiting", pending_tree_.get()); + active_tree_->lifecycle().AdvanceTo(LayerTreeLifecycle::kBeginningSync); DCHECK(pending_tree_duration_timer_); // Reset will call the destructor and log the timer histogram. @@ -2067,25 +2113,21 @@ void LayerTreeHostImpl::ActivateSyncTree() { active_tree_.get()); } - // Property trees may store damage status. We preserve the active tree - // damage status by pushing the damage status from active tree property - // trees to pending tree property trees or by moving it onto the layers. - if (active_tree_->property_trees()->changed) { - if (pending_tree_->property_trees()->sequence_number == - active_tree_->property_trees()->sequence_number) - active_tree_->property_trees()->PushChangeTrackingTo( - pending_tree_->property_trees()); - else - active_tree_->MoveChangeTrackingToLayers(); - } + PushScrollbarOpacitiesFromActiveToPending(); + pending_tree_->PushPropertyTreesTo(active_tree_.get()); + active_tree_->lifecycle().AdvanceTo( + LayerTreeLifecycle::kSyncedPropertyTrees); + TreeSynchronizer::PushLayerProperties(pending_tree(), active_tree()); - active_tree_->property_trees()->PushOpacityIfNeeded( - pending_tree_->property_trees()); + active_tree_->lifecycle().AdvanceTo( + LayerTreeLifecycle::kSyncedLayerProperties); pending_tree_->PushPropertiesTo(active_tree_.get()); if (!pending_tree_->LayerListIsEmpty()) pending_tree_->property_trees()->ResetAllChangeTracking(); + active_tree_->lifecycle().AdvanceTo(LayerTreeLifecycle::kNotSyncing); + // Now that we've synced everything from the pending tree to the active // tree, rename the pending tree the recycle tree so we can reuse it on the // next sync. @@ -2264,7 +2306,8 @@ void LayerTreeHostImpl::CreateResourceAndRasterBufferProvider( *resource_pool = ResourcePool::Create(resource_provider_.get(), GetTaskRunner(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, - ResourcePool::kDefaultExpirationDelay); + ResourcePool::kDefaultExpirationDelay, + settings_.disallow_non_exact_resource_reuse); *raster_buffer_provider = BitmapRasterBufferProvider::Create(resource_provider_.get()); @@ -2279,7 +2322,8 @@ void LayerTreeHostImpl::CreateResourceAndRasterBufferProvider( *resource_pool = ResourcePool::Create( resource_provider_.get(), GetTaskRunner(), ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER, - ResourcePool::kDefaultExpirationDelay); + ResourcePool::kDefaultExpirationDelay, + settings_.disallow_non_exact_resource_reuse); int msaa_sample_count = use_msaa_ ? RequestedMSAASampleCount() : 0; @@ -2304,7 +2348,8 @@ void LayerTreeHostImpl::CreateResourceAndRasterBufferProvider( *resource_pool = ResourcePool::CreateForGpuMemoryBufferResources( resource_provider_.get(), GetTaskRunner(), gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, - ResourcePool::kDefaultExpirationDelay); + ResourcePool::kDefaultExpirationDelay, + settings_.disallow_non_exact_resource_reuse); *raster_buffer_provider = ZeroCopyRasterBufferProvider::Create( resource_provider_.get(), @@ -2315,7 +2360,8 @@ void LayerTreeHostImpl::CreateResourceAndRasterBufferProvider( *resource_pool = ResourcePool::Create(resource_provider_.get(), GetTaskRunner(), ResourceProvider::TEXTURE_HINT_IMMUTABLE, - ResourcePool::kDefaultExpirationDelay); + ResourcePool::kDefaultExpirationDelay, + settings_.disallow_non_exact_resource_reuse); const int max_copy_texture_chromium_size = compositor_context_provider->ContextCapabilities() @@ -2367,6 +2413,10 @@ LayerTreeHostImpl::TakeCompletedImageDecodeCallbacks() { } void LayerTreeHostImpl::ClearImageCacheOnNavigation() { + // It is safe to clear the decode policy tracking on navigations since it + // comes with an invalidation and the image ids are never re-used. + bool can_clear_decode_policy_tracking = true; + tile_manager_.ClearCheckerImageTracking(can_clear_decode_policy_tracking); if (image_decode_cache_) image_decode_cache_->ClearCache(); } @@ -2514,17 +2564,6 @@ void LayerTreeHostImpl::SetViewportSize(const gfx::Size& device_viewport_size) { active_tree_->set_needs_update_draw_properties(); } -const gfx::Rect LayerTreeHostImpl::ViewportRectForTilePriority() const { - if (viewport_rect_for_tile_priority_.IsEmpty()) - return DeviceViewport(); - - return viewport_rect_for_tile_priority_; -} - -gfx::Size LayerTreeHostImpl::DrawViewportSize() const { - return DeviceViewport().size(); -} - gfx::Rect LayerTreeHostImpl::DeviceViewport() const { if (external_viewport_.IsEmpty()) return gfx::Rect(device_viewport_size_); @@ -2532,6 +2571,13 @@ gfx::Rect LayerTreeHostImpl::DeviceViewport() const { return external_viewport_; } +const gfx::Rect LayerTreeHostImpl::ViewportRectForTilePriority() const { + if (viewport_rect_for_tile_priority_.IsEmpty()) + return DeviceViewport(); + + return viewport_rect_for_tile_priority_; +} + const gfx::Transform& LayerTreeHostImpl::DrawTransform() const { return external_transform_; } @@ -2656,7 +2702,7 @@ static bool IsMainThreadScrolling(const InputHandler::ScrollStatus& status, return false; } -LayerImpl* LayerTreeHostImpl::FindScrollLayerForDeviceViewportPoint( +ScrollNode* LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint( const gfx::PointF& device_viewport_point, InputHandler::ScrollInputType type, LayerImpl* layer_impl, @@ -2681,7 +2727,7 @@ LayerImpl* LayerTreeHostImpl::FindScrollLayerForDeviceViewportPoint( if (IsMainThreadScrolling(status, scroll_node)) { *scroll_on_main_thread = true; *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons; - return active_tree_->LayerById(scroll_node->owning_layer_id); + return scroll_node; } if (status.thread == InputHandler::SCROLL_ON_IMPL_THREAD && @@ -2711,11 +2757,7 @@ LayerImpl* LayerTreeHostImpl::FindScrollLayerForDeviceViewportPoint( } } - // TODO(pdr): Refactor this function to directly return |impl_scroll_node| - // instead of using ScrollNode's owning_layer_id to return a LayerImpl. - if (!impl_scroll_node) - return nullptr; - return active_tree_->LayerById(impl_scroll_node->owning_layer_id); + return impl_scroll_node; } InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBeginImpl( @@ -2800,13 +2842,25 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin( } } - auto* scrolling_layer = FindScrollLayerForDeviceViewportPoint( + scrolling_node = FindScrollNodeForDeviceViewportPoint( device_viewport_point, type, layer_impl, &scroll_on_main_thread, &scroll_status.main_thread_scrolling_reasons); - ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree; - scrolling_node = - scrolling_layer ? scroll_tree.Node(scrolling_layer->scroll_tree_index()) - : nullptr; + if (!scroll_on_main_thread && scrolling_node && + (settings_.is_layer_tree_for_subframe || + (!scrolling_node->scrolls_outer_viewport && + !scrolling_node->scrolls_inner_viewport))) { + if (IsWheelBasedScroll(type)) { + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Event.Scroll.ScrollerSize.OnScroll_Wheel", + scrolling_node->scroll_clip_layer_bounds.GetArea(), 1, + kScrollerSizeLargestBucket, kScrollerSizeBucketCount); + } else { + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Event.Scroll.ScrollerSize.OnScroll_Touch", + scrolling_node->scroll_clip_layer_bounds.GetArea(), 1, + kScrollerSizeLargestBucket, kScrollerSizeBucketCount); + } + } } if (scroll_on_main_thread) { @@ -2946,8 +3000,14 @@ bool LayerTreeHostImpl::ScrollAnimationCreate(ScrollNode* scroll_node, active_tree()->LayerById(scroll_node->owning_layer_id)->element_id()), scroll_node->element_id); + // Start the animation one full frame in. Without any offset, the animation + // doesn't start until next frame, increasing latency, and preventing our + // input latency tracking architecture from working. + base::TimeDelta animation_start_offset = CurrentBeginFrameArgs().interval; + mutator_host_->ImplOnlyScrollAnimationCreate( - scroll_node->element_id, target_offset, current_offset, delayed_by); + scroll_node->element_id, target_offset, current_offset, delayed_by, + animation_start_offset); SetNeedsOneBeginImplFrame(); @@ -2967,7 +3027,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated( if (scroll_node) { // Flash the overlay scrollbar even if the scroll dalta is 0. ScrollbarAnimationController* animation_controller = - ScrollbarAnimationControllerForId(scroll_node->owning_layer_id); + ScrollbarAnimationControllerForElementId(scroll_node->element_id); if (animation_controller) animation_controller->WillUpdateScroll(); @@ -3018,7 +3078,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated( if (scrolls_main_viewport_scroll_layer) { // Flash the overlay scrollbar even if the scroll dalta is 0. ScrollbarAnimationController* animation_controller = - ScrollbarAnimationControllerForId(scroll_node->owning_layer_id); + ScrollbarAnimationControllerForElementId(scroll_node->element_id); if (animation_controller) animation_controller->WillUpdateScroll(); @@ -3286,7 +3346,7 @@ InputHandlerScrollResult LayerTreeHostImpl::ScrollBy( return InputHandlerScrollResult(); ScrollbarAnimationController* animation_controller = - ScrollbarAnimationControllerForId(scroll_node->owning_layer_id); + ScrollbarAnimationControllerForElementId(scroll_node->element_id); if (animation_controller) animation_controller->WillUpdateScroll(); @@ -3302,8 +3362,9 @@ InputHandlerScrollResult LayerTreeHostImpl::ScrollBy( DistributeScrollDelta(scroll_state); - active_tree_->SetCurrentlyScrollingNode( - scroll_state->current_native_scrolling_node()); + ScrollNode* current_scrolling_node = + scroll_state->current_native_scrolling_node(); + active_tree_->SetCurrentlyScrollingNode(current_scrolling_node); did_lock_scrolling_layer_ = scroll_state->delta_consumed_for_scroll_sequence(); @@ -3311,6 +3372,8 @@ InputHandlerScrollResult LayerTreeHostImpl::ScrollBy( bool did_scroll_y = scroll_state->caused_scroll_y(); bool did_scroll_content = did_scroll_x || did_scroll_y; if (did_scroll_content) { + ShowScrollbarsForImplScroll(current_scrolling_node->element_id); + // If we are scrolling with an active scroll handler, forward latency // tracking information to the main thread so the delay introduced by the // handler is accounted for. @@ -3371,6 +3434,7 @@ void LayerTreeHostImpl::SetSynchronousInputHandlerRootScrollOffset( if (!changed) return; + ShowScrollbarsForImplScroll(OuterViewportScrollLayer()->element_id()); client_->SetNeedsCommitOnImplThread(); // After applying the synchronous input handler's scroll offset, tell it what // we ended up with. @@ -3409,38 +3473,24 @@ InputHandler::ScrollStatus LayerTreeHostImpl::FlingScrollBegin() { return scroll_status; } -float LayerTreeHostImpl::DeviceSpaceDistanceToLayer( - const gfx::PointF& device_viewport_point, - LayerImpl* layer_impl) { - if (!layer_impl) - return std::numeric_limits<float>::max(); - - gfx::Rect layer_impl_bounds(layer_impl->bounds()); - - gfx::RectF device_viewport_layer_impl_bounds = MathUtil::MapClippedRect( - layer_impl->ScreenSpaceTransform(), gfx::RectF(layer_impl_bounds)); - - return device_viewport_layer_impl_bounds.ManhattanDistanceToPoint( - device_viewport_point); -} - void LayerTreeHostImpl::MouseDown() { ScrollbarAnimationController* animation_controller = - ScrollbarAnimationControllerForId(scroll_layer_id_mouse_currently_over_); + ScrollbarAnimationControllerForElementId( + scroll_element_id_mouse_currently_over_); if (animation_controller) { animation_controller->DidMouseDown(); - scroll_layer_id_mouse_currently_captured_ = - scroll_layer_id_mouse_currently_over_; + scroll_element_id_mouse_currently_captured_ = + scroll_element_id_mouse_currently_over_; } } void LayerTreeHostImpl::MouseUp() { - if (scroll_layer_id_mouse_currently_captured_ != Layer::INVALID_ID) { + if (scroll_element_id_mouse_currently_captured_) { ScrollbarAnimationController* animation_controller = - ScrollbarAnimationControllerForId( - scroll_layer_id_mouse_currently_captured_); + ScrollbarAnimationControllerForElementId( + scroll_element_id_mouse_currently_captured_); - scroll_layer_id_mouse_currently_captured_ = Layer::INVALID_ID; + scroll_element_id_mouse_currently_captured_ = ElementId(); if (animation_controller) animation_controller->DidMouseUp(); @@ -3455,52 +3505,48 @@ void LayerTreeHostImpl::MouseMoveAt(const gfx::Point& viewport_point) { // Check if mouse is over a scrollbar or not. // TODO(sahel): get rid of this extera checking when - // FindScrollLayerForDeviceViewportPoint finds the proper layer for - // scrolling on main thread when mouse is over scrollbar as well. - int new_id = Layer::INVALID_ID; + // FindScrollNodeForDeviceViewportPoint finds the proper node for scrolling on + // the main thread when the mouse is over a scrollbar as well. + ElementId scroll_element_id; if (layer_impl && layer_impl->ToScrollbarLayer()) - new_id = layer_impl->ToScrollbarLayer()->ScrollLayerId(); - if (new_id == Layer::INVALID_ID) { + scroll_element_id = layer_impl->ToScrollbarLayer()->scroll_element_id(); + if (!scroll_element_id) { bool scroll_on_main_thread = false; uint32_t main_thread_scrolling_reasons; - LayerImpl* scroll_layer_impl = FindScrollLayerForDeviceViewportPoint( + auto* scroll_node = FindScrollNodeForDeviceViewportPoint( device_viewport_point, InputHandler::TOUCHSCREEN, layer_impl, &scroll_on_main_thread, &main_thread_scrolling_reasons); + if (scroll_node) + scroll_element_id = scroll_node->element_id; // Scrollbars for the viewport are registered with the outer viewport layer. - if (scroll_layer_impl == InnerViewportScrollLayer()) - scroll_layer_impl = OuterViewportScrollLayer(); - - new_id = scroll_layer_impl ? scroll_layer_impl->id() : Layer::INVALID_ID; + if (InnerViewportScrollLayer() && OuterViewportScrollLayer() && + scroll_element_id == InnerViewportScrollLayer()->element_id()) + scroll_element_id = OuterViewportScrollLayer()->element_id(); } - if (new_id != scroll_layer_id_mouse_currently_over_) { + if (scroll_element_id != scroll_element_id_mouse_currently_over_) { ScrollbarAnimationController* old_animation_controller = - ScrollbarAnimationControllerForId( - scroll_layer_id_mouse_currently_over_); + ScrollbarAnimationControllerForElementId( + scroll_element_id_mouse_currently_over_); if (old_animation_controller) { old_animation_controller->DidMouseLeave(); } - scroll_layer_id_mouse_currently_over_ = new_id; + scroll_element_id_mouse_currently_over_ = scroll_element_id; } ScrollbarAnimationController* new_animation_controller = - ScrollbarAnimationControllerForId(new_id); + ScrollbarAnimationControllerForElementId(scroll_element_id); if (!new_animation_controller) return; - for (ScrollbarLayerImplBase* scrollbar : ScrollbarsFor(new_id)) { - new_animation_controller->DidMouseMoveNear( - scrollbar->orientation(), - DeviceSpaceDistanceToLayer(device_viewport_point, scrollbar) / - active_tree_->device_scale_factor()); - } + new_animation_controller->DidMouseMove(device_viewport_point); } void LayerTreeHostImpl::MouseLeave() { for (auto& pair : scrollbar_animation_controllers_) pair.second->DidMouseLeave(); - scroll_layer_id_mouse_currently_over_ = Layer::INVALID_ID; + scroll_element_id_mouse_currently_over_ = ElementId(); } void LayerTreeHostImpl::PinchGestureBegin() { @@ -3569,8 +3615,9 @@ static void CollectScrollDeltas(ScrollAndScaleSet* scroll_info, static void CollectScrollbarUpdates( ScrollAndScaleSet* scroll_info, - std::unordered_map<int, std::unique_ptr<ScrollbarAnimationController>>* - controllers) { + std::unordered_map<ElementId, + std::unique_ptr<ScrollbarAnimationController>, + ElementIdHash>* controllers) { scroll_info->scrollbars.reserve(controllers->size()); for (auto& pair : *controllers) { scroll_info->scrollbars.push_back(LayerTreeHostCommon::ScrollbarsUpdateInfo( @@ -3600,7 +3647,7 @@ std::unique_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() { } void LayerTreeHostImpl::SetFullViewportDamage() { - SetViewportDamage(gfx::Rect(DrawViewportSize())); + SetViewportDamage(gfx::Rect(DeviceViewport().size())); } bool LayerTreeHostImpl::AnimatePageScale(base::TimeTicks monotonic_time) { @@ -3714,29 +3761,30 @@ std::string LayerTreeHostImpl::LayerTreeAsJson() const { } void LayerTreeHostImpl::RegisterScrollbarAnimationController( - int scroll_layer_id) { - if (settings().scrollbar_animator == LayerTreeSettings::NO_ANIMATOR) - return; - if (ScrollbarAnimationControllerForId(scroll_layer_id)) + ElementId scroll_element_id, + float scrollbar_opacity) { + if (ScrollbarAnimationControllerForElementId(scroll_element_id)) return; - scrollbar_animation_controllers_[scroll_layer_id] = - active_tree_->CreateScrollbarAnimationController(scroll_layer_id); + + scrollbar_animation_controllers_[scroll_element_id] = + active_tree_->CreateScrollbarAnimationController(scroll_element_id, + scrollbar_opacity); } void LayerTreeHostImpl::UnregisterScrollbarAnimationController( - int scroll_layer_id) { - scrollbar_animation_controllers_.erase(scroll_layer_id); + ElementId scroll_element_id) { + scrollbar_animation_controllers_.erase(scroll_element_id); } ScrollbarAnimationController* -LayerTreeHostImpl::ScrollbarAnimationControllerForId( - int scroll_layer_id) const { +LayerTreeHostImpl::ScrollbarAnimationControllerForElementId( + ElementId scroll_element_id) const { // The viewport layers have only one set of scrollbars and their controller // is registered with the outer viewport. if (InnerViewportScrollLayer() && OuterViewportScrollLayer() && - scroll_layer_id == InnerViewportScrollLayer()->id()) - scroll_layer_id = OuterViewportScrollLayer()->id(); - auto i = scrollbar_animation_controllers_.find(scroll_layer_id); + scroll_element_id == InnerViewportScrollLayer()->element_id()) + scroll_element_id = OuterViewportScrollLayer()->element_id(); + auto i = scrollbar_animation_controllers_.find(scroll_element_id); if (i == scrollbar_animation_controllers_.end()) return nullptr; return i->second.get(); @@ -3761,8 +3809,8 @@ void LayerTreeHostImpl::SetNeedsRedrawForScrollbarAnimation() { SetNeedsRedraw(); } -ScrollbarSet LayerTreeHostImpl::ScrollbarsFor(int scroll_layer_id) const { - return active_tree_->ScrollbarsFor(scroll_layer_id); +ScrollbarSet LayerTreeHostImpl::ScrollbarsFor(ElementId id) const { + return active_tree_->ScrollbarsFor(id); } void LayerTreeHostImpl::AddVideoFrameController( @@ -3920,9 +3968,7 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, gfx::ColorSpace::CreateSRGB()); if (!scaled) { - AutoLockUIResourceBitmap bitmap_lock(bitmap); - auto* pixels = bitmap_lock.GetPixels(); - resource_provider_->CopyToResource(id, pixels, source_size); + resource_provider_->CopyToResource(id, bitmap.GetPixels(), source_size); } else { // Only support auto-resizing for N32 textures (since this is primarily for // scrollbars). Users of other types need to ensure they are not too big. @@ -3939,10 +3985,9 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, source_size.width(), source_size.height(), kPremul_SkAlphaType); int row_bytes = source_size.width() * 4; - AutoLockUIResourceBitmap bitmap_lock(bitmap); SkBitmap source_bitmap; source_bitmap.setInfo(info, row_bytes); - source_bitmap.setPixels(const_cast<uint8_t*>(bitmap_lock.GetPixels())); + source_bitmap.setPixels(const_cast<uint8_t*>(bitmap.GetPixels())); // This applies the scale to draw the |bitmap| into |scaled_bitmap|. SkBitmap scaled_bitmap; @@ -3956,7 +4001,6 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, scaled_canvas.clear(SK_ColorTRANSPARENT); scaled_canvas.drawBitmap(source_bitmap, 0, 0); - SkAutoLockPixels scaled_bitmap_lock(scaled_bitmap); auto* pixels = static_cast<uint8_t*>(scaled_bitmap.getPixels()); resource_provider_->CopyToResource(id, pixels, upload_size); } @@ -4172,6 +4216,7 @@ void LayerTreeHostImpl::SetElementScrollOffsetMutated( const gfx::ScrollOffset& scroll_offset) { if (list_type == ElementListType::ACTIVE) { SetTreeLayerScrollOffsetMutated(element_id, active_tree(), scroll_offset); + ShowScrollbarsForImplScroll(element_id); } else { SetTreeLayerScrollOffsetMutated(element_id, pending_tree(), scroll_offset); SetTreeLayerScrollOffsetMutated(element_id, recycle_tree(), scroll_offset); @@ -4213,12 +4258,6 @@ void LayerTreeHostImpl::ElementIsAnimatingChanged( list_type); property_trees->transform_tree.set_needs_update(true); tree->set_needs_update_draw_properties(); - // TODO(crbug.com/702777): - // was_ever_ready_since_last_transform_animation should not live on - // layers. - if (LayerImpl* layer = tree->LayerByElementId(element_id)) { - layer->set_was_ever_ready_since_last_transform_animation(false); - } } } break; @@ -4324,4 +4363,12 @@ void LayerTreeHostImpl::UpdateScrollSourceInfo(bool is_wheel_scroll) { has_scrolled_by_touch_ = true; } +void LayerTreeHostImpl::ShowScrollbarsForImplScroll(ElementId element_id) { + if (!element_id) + return; + if (ScrollbarAnimationController* animation_controller = + ScrollbarAnimationControllerForElementId(element_id)) + animation_controller->DidScrollUpdate(); +} + } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index 0b72dc47333..b8553cb323b 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -16,6 +16,7 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/sequenced_task_runner.h" #include "base/time/time.h" #include "cc/base/synced_property.h" #include "cc/benchmarks/micro_benchmark_controller_impl.h" @@ -86,7 +87,11 @@ enum class GpuRasterizationStatus { OFF_DEVICE, OFF_VIEWPORT, MSAA_CONTENT, - OFF_CONTENT +}; + +enum class ImplThreadPhase { + IDLE, + INSIDE_IMPL_FRAME, }; // LayerTreeHost->Proxy callback interface. @@ -223,11 +228,11 @@ class CC_EXPORT LayerTreeHostImpl ~FrameData(); void AsValueInto(base::trace_event::TracedValue* value) const; - std::vector<SurfaceId> embedded_surfaces; + std::vector<SurfaceId> activation_dependencies; std::vector<gfx::Rect> occluding_screen_space_rects; std::vector<gfx::Rect> non_occluding_screen_space_rects; RenderPassList render_passes; - const LayerImplList* render_surface_layer_list; + const RenderSurfaceList* render_surface_list; LayerImplList will_draw_layers; bool has_no_damage; bool may_contain_video; @@ -322,20 +327,14 @@ class CC_EXPORT LayerTreeHostImpl size_t SourceAnimationFrameNumberForTesting() const; - void RegisterScrollbarAnimationController(int scroll_layer_id); - void UnregisterScrollbarAnimationController(int scroll_layer_id); - ScrollbarAnimationController* ScrollbarAnimationControllerForId( - int scroll_layer_id) const; + void RegisterScrollbarAnimationController(ElementId scroll_element_id, + float initial_opacity); + void UnregisterScrollbarAnimationController(ElementId scroll_element_id); + ScrollbarAnimationController* ScrollbarAnimationControllerForElementId( + ElementId scroll_element_id) const; DrawMode GetDrawMode() const; - // Viewport size in draw space: this size is in physical pixels and is used - // for draw properties, tilings, quads and render passes. - gfx::Size DrawViewportSize() const; - - // Viewport rect in view space used for tiling prioritization. - const gfx::Rect ViewportRectForTilePriority() const; - // TileManagerClient implementation. void NotifyReadyToActivate() override; void NotifyReadyToDraw() override; @@ -355,7 +354,7 @@ class CC_EXPORT LayerTreeHostImpl base::TimeDelta delay) override; void SetNeedsAnimateForScrollbarAnimation() override; void SetNeedsRedrawForScrollbarAnimation() override; - ScrollbarSet ScrollbarsFor(int scroll_layer_id) const override; + ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const override; void DidChangeScrollbarVisibility() override; // VideoBeginFrameSource implementation. @@ -416,6 +415,7 @@ class CC_EXPORT LayerTreeHostImpl virtual void WillBeginImplFrame(const BeginFrameArgs& args); virtual void DidFinishImplFrame(); + void DidNotProduceFrame(const BeginFrameAck& ack); void DidModifyTilePriorities(); LayerTreeImpl* active_tree() { return active_tree_.get(); } @@ -538,9 +538,13 @@ class CC_EXPORT LayerTreeHostImpl void ScheduleMicroBenchmark(std::unique_ptr<MicroBenchmarkImpl> benchmark); CompositorFrameMetadata MakeCompositorFrameMetadata() const; + // Viewport rectangle and clip in device space. These rects are used to // prioritize raster and determine what is submitted in a CompositorFrame. gfx::Rect DeviceViewport() const; + // Viewport rect to be used for tiling prioritization instead of the + // DeviceViewport(). + const gfx::Rect ViewportRectForTilePriority() const; // When a SwapPromiseMonitor is created on the impl thread, it calls // InsertSwapPromiseMonitor() to register itself with LayerTreeHostImpl. @@ -665,9 +669,6 @@ class CC_EXPORT LayerTreeHostImpl bool AnimateScrollbars(base::TimeTicks monotonic_time); bool AnimateBrowserControls(base::TimeTicks monotonic_time); - void TrackDamageForAllSurfaces( - const LayerImplList& render_surface_layer_list); - void UpdateTileManagerMemoryPolicy(const ManagedMemoryPolicy& policy); // This function should only be called from PrepareToDraw, as DidDrawAllLayers @@ -677,14 +678,12 @@ class CC_EXPORT LayerTreeHostImpl void ClearCurrentlyScrollingNode(); - LayerImpl* FindScrollLayerForDeviceViewportPoint( + ScrollNode* FindScrollNodeForDeviceViewportPoint( const gfx::PointF& device_viewport_point, InputHandler::ScrollInputType type, LayerImpl* layer_hit_by_point, bool* scroll_on_main_thread, uint32_t* main_thread_scrolling_reason) const; - float DeviceSpaceDistanceToLayer(const gfx::PointF& device_viewport_point, - LayerImpl* layer_impl); void StartScrollbarFadeRecursive(LayerImpl* layer); void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy); @@ -713,6 +712,11 @@ class CC_EXPORT LayerTreeHostImpl void UpdateScrollSourceInfo(bool is_wheel_scroll); bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor); + void ShowScrollbarsForImplScroll(ElementId element_id); + + // Copy any opacity values already in the active tree to the pending + // tree, because the active tree value always takes precedence for scrollbars. + void PushScrollbarOpacitiesFromActiveToPending(); using UIResourceMap = std::unordered_map<UIResourceId, UIResourceData>; UIResourceMap ui_resource_map_; @@ -763,8 +767,8 @@ class CC_EXPORT LayerTreeHostImpl bool did_lock_scrolling_layer_; bool wheel_scrolling_; bool scroll_affects_scroll_handler_; - int scroll_layer_id_mouse_currently_over_; - int scroll_layer_id_mouse_currently_captured_; + ElementId scroll_element_id_mouse_currently_over_; + ElementId scroll_element_id_mouse_currently_captured_; std::vector<std::unique_ptr<SwapPromise>> swap_promises_for_main_thread_scroll_update_; @@ -824,9 +828,11 @@ class CC_EXPORT LayerTreeHostImpl std::unique_ptr<MutatorHost> mutator_host_; std::set<VideoFrameController*> video_frame_controllers_; - // Map from scroll layer ID to scrollbar animation controller. + // Map from scroll element ID to scrollbar animation controller. // There is one animation controller per pair of overlay scrollbars. - std::unordered_map<int, std::unique_ptr<ScrollbarAnimationController>> + std::unordered_map<ElementId, + std::unique_ptr<ScrollbarAnimationController>, + ElementIdHash> scrollbar_animation_controllers_; RenderingStatsInstrumentation* rendering_stats_instrumentation_; @@ -872,6 +878,8 @@ class CC_EXPORT LayerTreeHostImpl bool touchpad_and_wheel_scroll_latching_enabled_; + ImplThreadPhase impl_thread_phase_; + DISALLOW_COPY_AND_ASSIGN(LayerTreeHostImpl); }; diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc index a52bdf7996c..e8eba4fd251 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -15,6 +15,7 @@ #include "base/location.h" #include "base/memory/ptr_util.h" #include "base/run_loop.h" +#include "base/test/histogram_tester.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" @@ -127,6 +128,7 @@ class LayerTreeHostImplTest : public testing::Test, LayerTreeSettings DefaultSettings() { LayerTreeSettings settings; + settings.enable_surface_synchronization = true; settings.minimum_occlusion_tracking_size = gfx::Size(); settings.renderer_settings.texture_id_allocation_chunk_size = 1; settings.renderer_settings.buffer_to_texture_target_map = @@ -305,6 +307,10 @@ class LayerTreeHostImplTest : public testing::Test, LayerImpl* CreateScrollAndContentsLayers(LayerTreeImpl* layer_tree_impl, const gfx::Size& content_size) { + // Clear any existing viewport layers that were setup so this function can + // be called multiple times. + layer_tree_impl->ClearViewportLayers(); + // Create both an inner viewport scroll layer and an outer viewport scroll // layer. The MaxScrollOffset of the outer viewport scroll layer will be // 0x0, so the scrolls will be applied directly to the inner viewport. @@ -376,9 +382,13 @@ class LayerTreeHostImplTest : public testing::Test, layer_tree_impl->SetRootLayerForTesting(std::move(root)); layer_tree_impl->BuildPropertyTreesForTesting(); - layer_tree_impl->SetViewportLayersFromIds( - Layer::INVALID_ID, kPageScaleLayerId, kInnerViewportScrollLayerId, - kOuterViewportScrollLayerId); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = kPageScaleLayerId; + viewport_ids.inner_viewport_container = kInnerViewportClipLayerId; + viewport_ids.outer_viewport_container = kOuterViewportClipLayerId; + viewport_ids.inner_viewport_scroll = kInnerViewportScrollLayerId; + viewport_ids.outer_viewport_scroll = kOuterViewportScrollLayerId; + layer_tree_impl->SetViewportLayersFromIds(viewport_ids); layer_tree_impl->DidBecomeActive(); return layer_tree_impl->InnerViewportScrollLayer(); @@ -754,6 +764,66 @@ TEST_F(LayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) { scroll_delta + scroll_delta2)); } +TEST_F(LayerTreeHostImplTest, ScrollerSizeOfCCScrollingHistogramRecordingTest) { + const gfx::Size content_size(800, 600); + const gfx::Size viewport_size(500, 500); + CreateBasicVirtualViewportLayers(viewport_size, content_size); + + LayerImpl* outer_viewport_scroll_layer = + host_impl_->active_tree()->OuterViewportScrollLayer(); + int id = outer_viewport_scroll_layer->id(); + std::unique_ptr<LayerImpl> child = + LayerImpl::Create(host_impl_->active_tree(), id + 2); + std::unique_ptr<LayerImpl> child_clip = + LayerImpl::Create(host_impl_->active_tree(), id + 3); + + child_clip->SetBounds(gfx::Size(100, 100)); + + child->SetScrollClipLayer(child_clip->id()); + child->SetElementId(LayerIdToElementIdForTesting(child->id())); + child->SetBounds(gfx::Size(100, 400)); + child->SetPosition(gfx::PointF()); + child->SetDrawsContent(true); + + child_clip->test_properties()->AddChild(std::move(child)); + outer_viewport_scroll_layer->test_properties()->AddChild( + std::move(child_clip)); + host_impl_->active_tree()->BuildPropertyTreesForTesting(); + + base::HistogramTester histogram_tester; + + // Test touch scroll. + InputHandler::ScrollStatus status = host_impl_->ScrollBegin( + BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN); + EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); + EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, + status.main_thread_scrolling_reasons); + host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); + host_impl_->ScrollEnd(EndState().get()); + + histogram_tester.ExpectBucketCount("Event.Scroll.ScrollerSize.OnScroll_Touch", + 10000, 1); + histogram_tester.ExpectTotalCount("Event.Scroll.ScrollerSize.OnScroll_Touch", + 1); + + // Scrolling root layer doesn't add to count. + host_impl_->ScrollBegin(BeginState(gfx::Point(450, 450)).get(), + InputHandler::TOUCHSCREEN); + host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); + host_impl_->ScrollEnd(EndState().get()); + histogram_tester.ExpectTotalCount("Event.Scroll.ScrollerSize.OnScroll_Touch", + 1); + + // Test wheel scroll. + host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL); + host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); + host_impl_->ScrollEnd(EndState().get()); + histogram_tester.ExpectBucketCount("Event.Scroll.ScrollerSize.OnScroll_Wheel", + 10000, 1); + histogram_tester.ExpectTotalCount("Event.Scroll.ScrollerSize.OnScroll_Wheel", + 1); +} + TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) { SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -1041,7 +1111,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithOverlappingNonScrollableLayer) { false, true); scrollbar->SetBounds(scrollbar_size); scrollbar->SetPosition(gfx::PointF(345, 0)); - scrollbar->SetScrollLayerId(scroll->id()); + scrollbar->SetScrollElementId(scroll->element_id()); scrollbar->SetDrawsContent(true); scrollbar->test_properties()->opacity = 1.f; @@ -1113,7 +1183,7 @@ TEST_F(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) { false, true); drawn_scrollbar->SetBounds(scrollbar_size); drawn_scrollbar->SetPosition(gfx::PointF(345, 0)); - drawn_scrollbar->SetScrollLayerId(scroll->id()); + drawn_scrollbar->SetScrollElementId(scroll->element_id()); drawn_scrollbar->SetDrawsContent(true); drawn_scrollbar->test_properties()->opacity = 1.f; @@ -1559,7 +1629,7 @@ TEST_F(LayerTreeHostImplTest, AnimationSchedulingCommitToActiveTree) { // Set up the property trees so that UpdateDrawProperties will work in // CommitComplete below. - LayerImplList list; + RenderSurfaceList list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, gfx::Size(50, 50), &list); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); @@ -1657,91 +1727,6 @@ class MissingTilesLayer : public LayerImpl { bool has_missing_tiles_; }; -TEST_F(LayerTreeHostImplTest, AnimationMarksLayerNotReady) { - host_impl_->SetViewportSize(gfx::Size(50, 50)); - - host_impl_->active_tree()->SetRootLayerForTesting( - LayerImpl::Create(host_impl_->active_tree(), 1)); - LayerImpl* root = *host_impl_->active_tree()->begin(); - root->SetBounds(gfx::Size(50, 50)); - - root->test_properties()->AddChild(std::unique_ptr<MissingTilesLayer>( - new MissingTilesLayer(host_impl_->active_tree(), 2))); - MissingTilesLayer* child = - static_cast<MissingTilesLayer*>(root->test_properties()->children[0]); - child->SetBounds(gfx::Size(10, 10)); - child->draw_properties().visible_layer_rect = gfx::Rect(10, 10); - child->SetDrawsContent(true); - - host_impl_->active_tree()->SetElementIdsForTesting(); - - EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation()); - - // Add a translate from 6,7 to 8,9. - TransformOperations start; - start.AppendTranslate(6.f, 7.f, 0.f); - TransformOperations end; - end.AppendTranslate(8.f, 9.f, 0.f); - int animation_id = AddAnimatedTransformToElementWithPlayer( - child->element_id(), timeline(), 4.0, start, end); - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - - base::TimeTicks now = base::TimeTicks::Now(); - host_impl_->WillBeginImplFrame( - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, now)); - - host_impl_->ActivateAnimations(); - host_impl_->Animate(); - - EXPECT_FALSE(child->was_ever_ready_since_last_transform_animation()); - - host_impl_->ResetRequiresHighResToDraw(); - - // Child layer has an animating transform but missing tiles. - TestFrameData frame; - DrawResult result = host_impl_->PrepareToDraw(&frame); - EXPECT_EQ(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS, result); - host_impl_->DidDrawAllLayers(frame); - - child->set_has_missing_tiles(false); - - // Child layer has an animating and no missing tiles. - result = host_impl_->PrepareToDraw(&frame); - EXPECT_EQ(DRAW_SUCCESS, result); - EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation()); - host_impl_->DidDrawAllLayers(frame); - - // Remove the animation. - child->set_has_missing_tiles(true); - RemoveAnimationFromElementWithExistingPlayer(child->element_id(), timeline(), - animation_id); - child->draw_properties().screen_space_transform_is_animating = false; - - // Child layer doesn't have an animation, but was never ready since the last - // time it animated (and has missing tiles). - result = host_impl_->PrepareToDraw(&frame); - EXPECT_EQ(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS, result); - EXPECT_FALSE(child->was_ever_ready_since_last_transform_animation()); - host_impl_->DidDrawAllLayers(frame); - - child->set_has_missing_tiles(false); - - // Child layer doesn't have an animation and all tiles are ready. - result = host_impl_->PrepareToDraw(&frame); - EXPECT_EQ(DRAW_SUCCESS, result); - EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation()); - host_impl_->DidDrawAllLayers(frame); - - child->set_has_missing_tiles(true); - - // Child layer doesn't have an animation, and was ready at least once since - // the last time it animated. - result = host_impl_->PrepareToDraw(&frame); - EXPECT_EQ(DRAW_SUCCESS, result); - EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation()); - host_impl_->DidDrawAllLayers(frame); -} - TEST_F(LayerTreeHostImplTest, ImplPinchZoom) { LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -2769,7 +2754,7 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationCompletedNotification) { host_impl_->DidFinishImplFrame(); } -TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByBoundsDelta) { +TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByViewportBoundsDelta) { SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->SetViewportSize(gfx::Size(50, 50)); host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.5f, 4.f); @@ -2782,17 +2767,17 @@ TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByBoundsDelta) { DCHECK(inner_container); EXPECT_EQ(gfx::ScrollOffset(50, 50), inner_scroll->MaxScrollOffset()); - inner_container->SetBoundsDelta(gfx::Vector2dF(15.f, 15.f)); - inner_scroll->SetBoundsDelta(gfx::Vector2dF(7.f, 7.f)); + inner_container->SetViewportBoundsDelta(gfx::Vector2dF(15.f, 15.f)); + inner_scroll->SetViewportBoundsDelta(gfx::Vector2dF(7.f, 7.f)); EXPECT_EQ(gfx::ScrollOffset(42, 42), inner_scroll->MaxScrollOffset()); - inner_container->SetBoundsDelta(gfx::Vector2dF()); - inner_scroll->SetBoundsDelta(gfx::Vector2dF()); + inner_container->SetViewportBoundsDelta(gfx::Vector2dF()); + inner_scroll->SetViewportBoundsDelta(gfx::Vector2dF()); inner_scroll->SetBounds(gfx::Size()); host_impl_->active_tree()->BuildPropertyTreesForTesting(); DrawFrame(); - inner_scroll->SetBoundsDelta(gfx::Vector2dF(60.f, 60.f)); + inner_scroll->SetViewportBoundsDelta(gfx::Vector2dF(60.f, 60.f)); EXPECT_EQ(gfx::ScrollOffset(10, 10), inner_scroll->MaxScrollOffset()); } @@ -2856,13 +2841,14 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { LayerImpl* scroll = host_impl_->active_tree()->OuterViewportScrollLayer(); LayerImpl* root = host_impl_->active_tree()->InnerViewportContainerLayer(); - scrollbar->SetScrollLayerId(scroll->id()); + scrollbar->SetScrollElementId(scroll->element_id()); root->test_properties()->AddChild(std::move(scrollbar)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->DidBecomeActive(); + host_impl_->active_tree()->HandleScrollbarShowRequestsFromMain(); DrawFrame(); - // SetScrollLayerId will initialize the scrollbar which will cause it to + // SetScrollElementId will initialize the scrollbar which will cause it to // show and request a redraw. did_request_redraw_ = false; } @@ -2871,8 +2857,6 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { LayerTreeSettings settings = DefaultSettings(); settings.scrollbar_animator = animator; settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(20); - settings.scrollbar_fade_out_resize_delay = - base::TimeDelta::FromMilliseconds(20); settings.scrollbar_fade_duration = base::TimeDelta::FromMilliseconds(20); // If no animator is set, scrollbar won't show and no animation is expected. @@ -2882,7 +2866,8 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { base::TimeTicks fake_now = base::TimeTicks::Now(); - if (expecting_animations) { + // Android Overlay Scrollbar does not have a initial show and fade out. + if (animator == LayerTreeSettings::AURA_OVERLAY) { // A task will be posted to fade the initial scrollbar. EXPECT_FALSE(did_request_next_frame_); EXPECT_FALSE(did_request_redraw_); @@ -2995,8 +2980,8 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { host_impl_->DidFinishImplFrame(); } - // Setting the scroll offset outside a scroll should also cause the - // scrollbar to appear and to schedule a scrollbar animation. + // Setting the scroll offset outside a scroll should not cause the + // scrollbar to appear or schedule a scrollbar animation. if (host_impl_->active_tree() ->property_trees() ->scroll_tree.UpdateScrollOffsetBaseForTesting( @@ -3006,57 +2991,9 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { host_impl_->InnerViewportScrollLayer()->id()); EXPECT_FALSE(did_request_next_frame_); EXPECT_FALSE(did_request_redraw_); - if (expecting_animations) { - EXPECT_EQ(base::TimeDelta::FromMilliseconds(20), - requested_animation_delay_); - EXPECT_FALSE(animation_task_.Equals(base::Closure())); - requested_animation_delay_ = base::TimeDelta(); - animation_task_ = base::Closure(); - } else { - EXPECT_EQ(base::TimeDelta(), requested_animation_delay_); - EXPECT_TRUE(animation_task_.Equals(base::Closure())); - } - - if (expecting_animations) { - // Scrolling should have stopped the animation, so we should not be - // getting redraws. - begin_frame_args = - CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 5, fake_now); - host_impl_->WillBeginImplFrame(begin_frame_args); - host_impl_->Animate(); - EXPECT_FALSE(did_request_next_frame_); - did_request_next_frame_ = false; - EXPECT_FALSE(did_request_redraw_); - did_request_redraw_ = false; - host_impl_->DidFinishImplFrame(); - } - - // For Andrdoid, scrollbar animation is not triggered unnecessarily. - // For Aura Overlay Scrollbar, scrollbar appears even if scroll offset did - // not change. - host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL); - host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2dF(5, 0)).get()); - EXPECT_FALSE(did_request_next_frame_); - EXPECT_TRUE(did_request_redraw_); - did_request_redraw_ = false; EXPECT_EQ(base::TimeDelta(), requested_animation_delay_); EXPECT_TRUE(animation_task_.Equals(base::Closure())); - host_impl_->ScrollEnd(EndState().get()); - EXPECT_FALSE(did_request_next_frame_); - EXPECT_FALSE(did_request_redraw_); - if (animator == LayerTreeSettings::AURA_OVERLAY) { - EXPECT_EQ(base::TimeDelta::FromMilliseconds(20), - requested_animation_delay_); - EXPECT_FALSE(animation_task_.Equals(base::Closure())); - requested_animation_delay_ = base::TimeDelta(); - animation_task_ = base::Closure(); - } else { - EXPECT_EQ(base::TimeDelta(), requested_animation_delay_); - EXPECT_TRUE(animation_task_.Equals(base::Closure())); - } - // Changing page scale triggers scrollbar animation. host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 4.f); host_impl_->active_tree()->SetPageScaleOnActiveTree(1.1f); @@ -3109,7 +3046,9 @@ class LayerTreeHostImplTestScrollbarOpacity : public LayerTreeHostImplTest { LayerImpl* scroll = host_impl_->pending_tree()->OuterViewportScrollLayer(); LayerImpl* container = host_impl_->pending_tree()->InnerViewportContainerLayer(); - scrollbar->SetScrollLayerId(scroll->id()); + scrollbar->SetScrollElementId(scroll->element_id()); + scrollbar->SetBounds(gfx::Size(10, 100)); + scrollbar->SetPosition(gfx::PointF(90, 0)); container->test_properties()->AddChild(std::move(scrollbar)); host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f); host_impl_->pending_tree()->BuildPropertyTreesForTesting(); @@ -3125,11 +3064,11 @@ class LayerTreeHostImplTestScrollbarOpacity : public LayerTreeHostImplTest { active_tree_node->opacity); if (expecting_animations) { - host_impl_->ScrollbarAnimationControllerForId(scroll->id()) - ->DidMouseMoveNear(VERTICAL, 0); + host_impl_->ScrollbarAnimationControllerForElementId(scroll->element_id()) + ->DidMouseMove(gfx::PointF(0, 90)); } else { - EXPECT_EQ(nullptr, - host_impl_->ScrollbarAnimationControllerForId(scroll->id())); + EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId( + scroll->element_id())); } host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL); @@ -3149,9 +3088,6 @@ class LayerTreeHostImplTestScrollbarOpacity : public LayerTreeHostImplTest { EffectNode* pending_tree_node = host_impl_->pending_tree()->property_trees()->effect_tree.Node( pending_scrollbar_layer->effect_tree_index()); - host_impl_->pending_tree() - ->property_trees() - ->always_use_active_tree_opacity_effect_ids.push_back(400); if (expecting_animations) { EXPECT_FLOAT_EQ(1.f, active_tree_node->opacity); EXPECT_FLOAT_EQ(1.f, active_scrollbar_layer->Opacity()); @@ -3203,14 +3139,17 @@ TEST_F(LayerTreeHostImplTest, ScrollbarVisibilityChangeCausesRedrawAndCommit) { LayerImpl* scroll = host_impl_->pending_tree()->OuterViewportScrollLayer(); LayerImpl* container = host_impl_->pending_tree()->InnerViewportContainerLayer(); - scrollbar->SetScrollLayerId(scroll->id()); + scrollbar->SetScrollElementId(scroll->element_id()); + scrollbar->SetBounds(gfx::Size(10, 100)); + scrollbar->SetPosition(gfx::PointF(90, 0)); container->test_properties()->AddChild(std::move(scrollbar)); host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f); host_impl_->pending_tree()->BuildPropertyTreesForTesting(); host_impl_->ActivateSyncTree(); ScrollbarAnimationController* scrollbar_controller = - host_impl_->ScrollbarAnimationControllerForId(scroll->id()); + host_impl_->ScrollbarAnimationControllerForElementId( + scroll->element_id()); // Scrollbars will flash shown but we should have a fade out animation // queued. Run it and fade out the scrollbars. @@ -3228,10 +3167,10 @@ TEST_F(LayerTreeHostImplTest, ScrollbarVisibilityChangeCausesRedrawAndCommit) { } // Move the mouse over the scrollbar region. This should post a delayed fade - // in task. Execute it to show the scrollbars. + // in task. Execute it to fade in the scrollbars. { animation_task_ = base::Closure(); - scrollbar_controller->DidMouseMoveNear(VERTICAL, 0); + scrollbar_controller->DidMouseMove(gfx::PointF(90, 0)); ASSERT_FALSE(animation_task_.Equals(base::Closure())); ASSERT_FALSE(animation_task_.IsCancelled()); } @@ -3284,7 +3223,7 @@ TEST_F(LayerTreeHostImplTest, ScrollbarInnerLargerThanOuter) { LayerImpl::Create(host_impl_->active_tree(), child_clip_id); child->SetBounds(inner_viewport_size); - horiz_scrollbar->SetScrollLayerId(root_scroll->id()); + horiz_scrollbar->SetScrollElementId(root_scroll->element_id()); EXPECT_EQ(300, horiz_scrollbar->clip_layer_length()); } @@ -3307,22 +3246,36 @@ TEST_F(LayerTreeHostImplTest, ScrollbarRegistration) { const int child_scroll_id = 15; CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size); - host_impl_->active_tree()->InnerViewportContainerLayer()->SetBounds( - viewport_size); + LayerImpl* container = + host_impl_->active_tree()->InnerViewportContainerLayer(); + container->SetBounds(viewport_size); LayerImpl* root_scroll = host_impl_->active_tree()->OuterViewportScrollLayer(); - std::unique_ptr<SolidColorScrollbarLayerImpl> vert_1_scrollbar = - SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), vert_1_id, - VERTICAL, 5, 5, true, true); - std::unique_ptr<SolidColorScrollbarLayerImpl> horiz_1_scrollbar = - SolidColorScrollbarLayerImpl::Create( - host_impl_->active_tree(), horiz_1_id, HORIZONTAL, 5, 5, true, true); - std::unique_ptr<SolidColorScrollbarLayerImpl> vert_2_scrollbar = - SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), vert_2_id, - VERTICAL, 5, 5, true, true); - std::unique_ptr<SolidColorScrollbarLayerImpl> horiz_2_scrollbar = - SolidColorScrollbarLayerImpl::Create( - host_impl_->active_tree(), horiz_2_id, HORIZONTAL, 5, 5, true, true); + + container->test_properties()->AddChild(SolidColorScrollbarLayerImpl::Create( + host_impl_->active_tree(), vert_1_id, VERTICAL, 5, 5, true, true)); + SolidColorScrollbarLayerImpl* vert_1_scrollbar = + static_cast<SolidColorScrollbarLayerImpl*>( + container->test_properties()->children[1]); + + container->test_properties()->AddChild(SolidColorScrollbarLayerImpl::Create( + host_impl_->active_tree(), horiz_1_id, HORIZONTAL, 5, 5, true, true)); + SolidColorScrollbarLayerImpl* horiz_1_scrollbar = + static_cast<SolidColorScrollbarLayerImpl*>( + container->test_properties()->children[2]); + + container->test_properties()->AddChild(SolidColorScrollbarLayerImpl::Create( + host_impl_->active_tree(), vert_2_id, VERTICAL, 5, 5, true, true)); + SolidColorScrollbarLayerImpl* vert_2_scrollbar = + static_cast<SolidColorScrollbarLayerImpl*>( + container->test_properties()->children[3]); + + container->test_properties()->AddChild(SolidColorScrollbarLayerImpl::Create( + host_impl_->active_tree(), horiz_2_id, HORIZONTAL, 5, 5, true, true)); + SolidColorScrollbarLayerImpl* horiz_2_scrollbar = + static_cast<SolidColorScrollbarLayerImpl*>( + container->test_properties()->children[4]); + std::unique_ptr<LayerImpl> child = LayerImpl::Create(host_impl_->active_tree(), child_scroll_id); child->SetBounds(content_size); @@ -3332,75 +3285,75 @@ TEST_F(LayerTreeHostImplTest, ScrollbarRegistration) { LayerImpl* child_ptr = child.get(); LayerImpl* child_clip_ptr = child_clip.get(); + host_impl_->active_tree()->BuildPropertyTreesForTesting(); + // Check scrollbar registration on the viewport layers. - EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(root_scroll->id()).size()); - EXPECT_EQ(nullptr, - host_impl_->ScrollbarAnimationControllerForId(root_scroll->id())); - vert_1_scrollbar->SetScrollLayerId(root_scroll->id()); - EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(root_scroll->id()).size()); - EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(root_scroll->id())); - horiz_1_scrollbar->SetScrollLayerId(root_scroll->id()); - EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(root_scroll->id()).size()); - EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(root_scroll->id())); - - // Changing one of the viewport layers should result in a scrollbar animation - // update. + EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size()); + EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId( + root_scroll->element_id())); + vert_1_scrollbar->SetScrollElementId(root_scroll->element_id()); + EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size()); + EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId( + root_scroll->element_id())); + horiz_1_scrollbar->SetScrollElementId(root_scroll->element_id()); + EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size()); + EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId( + root_scroll->element_id())); + + // Scrolling the viewport should result in a scrollbar animation update. animation_task_ = base::Closure(); - host_impl_->active_tree()->InnerViewportContainerLayer()->SetBoundsDelta( - gfx::Vector2dF(10, 10)); - EXPECT_FALSE(animation_task_.Equals(base::Closure())); - animation_task_ = base::Closure(); - host_impl_->active_tree()->OuterViewportScrollLayer()->SetCurrentScrollOffset( - gfx::ScrollOffset(10, 10)); - EXPECT_FALSE(animation_task_.Equals(base::Closure())); - animation_task_ = base::Closure(); - host_impl_->active_tree()->InnerViewportScrollLayer()->SetCurrentScrollOffset( - gfx::ScrollOffset(10, 10)); + host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL); + host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(10, 10)).get()); + host_impl_->ScrollEnd(EndState().get()); EXPECT_FALSE(animation_task_.Equals(base::Closure())); animation_task_ = base::Closure(); // Check scrollbar registration on a sublayer. child->SetScrollClipLayer(child_clip->id()); child->SetElementId(LayerIdToElementIdForTesting(child->id())); + ElementId child_scroll_element_id = child->element_id(); child_clip->test_properties()->AddChild(std::move(child)); root_scroll->test_properties()->AddChild(std::move(child_clip)); - EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_id).size()); - EXPECT_EQ(nullptr, - host_impl_->ScrollbarAnimationControllerForId(child_scroll_id)); - vert_2_scrollbar->SetScrollLayerId(child_scroll_id); - EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(child_scroll_id).size()); - EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(child_scroll_id)); - horiz_2_scrollbar->SetScrollLayerId(child_scroll_id); - EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(child_scroll_id).size()); - EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(child_scroll_id)); + EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size()); + EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId( + child_scroll_element_id)); + vert_2_scrollbar->SetScrollElementId(child_scroll_element_id); + EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size()); + EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId( + child_scroll_element_id)); + horiz_2_scrollbar->SetScrollElementId(child_scroll_element_id); + EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size()); + EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId( + child_scroll_element_id)); // Changing one of the child layers should result in a scrollbar animation // update. animation_task_ = base::Closure(); child_clip_ptr->SetBounds(gfx::Size(200, 200)); - EXPECT_FALSE(animation_task_.Equals(base::Closure())); - animation_task_ = base::Closure(); - child_ptr->SetCurrentScrollOffset(gfx::ScrollOffset(10, 10)); + child_ptr->set_needs_show_scrollbars(true); + host_impl_->active_tree()->HandleScrollbarShowRequestsFromMain(); EXPECT_FALSE(animation_task_.Equals(base::Closure())); animation_task_ = base::Closure(); // Check scrollbar unregistration. - vert_1_scrollbar.reset(); - EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(root_scroll->id()).size()); - EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(root_scroll->id())); - horiz_1_scrollbar.reset(); - EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(root_scroll->id()).size()); - EXPECT_EQ(nullptr, - host_impl_->ScrollbarAnimationControllerForId(root_scroll->id())); - - EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(child_scroll_id).size()); - vert_2_scrollbar.reset(); - EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(child_scroll_id).size()); - EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForId(child_scroll_id)); - horiz_2_scrollbar.reset(); - EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_id).size()); - EXPECT_EQ(nullptr, - host_impl_->ScrollbarAnimationControllerForId(root_scroll->id())); + container->test_properties()->RemoveChild(vert_1_scrollbar); + EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size()); + EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId( + root_scroll->element_id())); + container->test_properties()->RemoveChild(horiz_1_scrollbar); + EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(root_scroll->element_id()).size()); + EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId( + root_scroll->element_id())); + + EXPECT_EQ(2ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size()); + container->test_properties()->RemoveChild(vert_2_scrollbar); + EXPECT_EQ(1ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size()); + EXPECT_TRUE(host_impl_->ScrollbarAnimationControllerForElementId( + child_scroll_element_id)); + container->test_properties()->RemoveChild(horiz_2_scrollbar); + EXPECT_EQ(0ul, host_impl_->ScrollbarsFor(child_scroll_element_id).size()); + EXPECT_EQ(nullptr, host_impl_->ScrollbarAnimationControllerForElementId( + root_scroll->element_id())); // Changing scroll offset should no longer trigger any animation. host_impl_->active_tree()->InnerViewportScrollLayer()->SetCurrentScrollOffset( @@ -3435,8 +3388,8 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale( // The scrollbar is on the left side. std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar = SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), 6, - VERTICAL, 5, 5, true, true); - scrollbar->SetScrollLayerId(root_scroll->id()); + VERTICAL, 15, 0, true, true); + scrollbar->SetScrollElementId(root_scroll->element_id()); scrollbar->SetDrawsContent(true); scrollbar->SetBounds(scrollbar_size); scrollbar->SetTouchEventHandlerRegion(gfx::Rect(scrollbar_size)); @@ -3451,32 +3404,46 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale( host_impl_->active_tree()->UpdateDrawProperties(false); ScrollbarAnimationController* scrollbar_animation_controller = - host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()); + host_impl_->ScrollbarAnimationControllerForElementId( + root_scroll->element_id()); + + const float kMouseMoveDistanceToTriggerFadeIn = + ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; - const float kMouseDistanceToTriggerAnimation = + const float kMouseMoveDistanceToTriggerExpand = SingleScrollbarAnimationControllerThinning:: - kDefaultMouseMoveDistanceToTriggerAnimation; + kMouseMoveDistanceToTriggerExpand; host_impl_->MouseMoveAt( - gfx::Point(15 + kMouseDistanceToTriggerAnimation * 2, 1)); + gfx::Point(15 + kMouseMoveDistanceToTriggerFadeIn, 1)); EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_FALSE( + scrollbar_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); host_impl_->MouseMoveAt( - gfx::Point(15 + kMouseDistanceToTriggerAnimation - 1, 50)); + gfx::Point(15 + kMouseMoveDistanceToTriggerExpand - 1, 10)); EXPECT_TRUE(scrollbar_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_TRUE( + scrollbar_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); host_impl_->MouseMoveAt( - gfx::Point(15 + kMouseDistanceToTriggerAnimation, 100)); + gfx::Point(15 + kMouseMoveDistanceToTriggerFadeIn, 100)); EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_FALSE( + scrollbar_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); did_request_redraw_ = false; - EXPECT_FALSE(scrollbar_animation_controller->MouseIsOverScrollbar(VERTICAL)); - host_impl_->MouseMoveAt(gfx::Point(10, 100)); - EXPECT_TRUE(scrollbar_animation_controller->MouseIsOverScrollbar(VERTICAL)); - host_impl_->MouseMoveAt(gfx::Point(10, 120)); - EXPECT_TRUE(scrollbar_animation_controller->MouseIsOverScrollbar(VERTICAL)); + EXPECT_FALSE( + scrollbar_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + host_impl_->MouseMoveAt(gfx::Point(10, 10)); + EXPECT_TRUE( + scrollbar_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + host_impl_->MouseMoveAt(gfx::Point(10, 0)); + EXPECT_TRUE( + scrollbar_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); host_impl_->MouseMoveAt(gfx::Point(150, 120)); - EXPECT_FALSE(scrollbar_animation_controller->MouseIsOverScrollbar(VERTICAL)); + EXPECT_FALSE( + scrollbar_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); } TEST_F(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf1) { @@ -3487,24 +3454,33 @@ TEST_F(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf2) { SetupMouseMoveAtWithDeviceScale(2.f); } -// This test verifies that only SurfaceLayers in the viewport are included -// in CompositorFrameMetadata's |embedded_surfaces|. -TEST_F(LayerTreeHostImplTest, EmbeddedSurfacesInMetadata) { +// This test verifies that only SurfaceLayers in the viewport and have fallbacks +// that are different are included in CompositorFrameMetadata's +// |activation_dependencies|. +TEST_F(LayerTreeHostImplTest, ActivationDependenciesInMetadata) { SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->SetViewportSize(gfx::Size(50, 50)); LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing(); - std::vector<SurfaceId> children = {MakeSurfaceId(FrameSinkId(1, 1), 1), - MakeSurfaceId(FrameSinkId(2, 2), 2), - MakeSurfaceId(FrameSinkId(3, 3), 3)}; - for (size_t i = 0; i < children.size(); ++i) { + std::vector<SurfaceId> primary_surfaces = { + MakeSurfaceId(FrameSinkId(1, 1), 1), MakeSurfaceId(FrameSinkId(2, 2), 2), + MakeSurfaceId(FrameSinkId(3, 3), 3)}; + + std::vector<SurfaceId> fallback_surfaces = { + MakeSurfaceId(FrameSinkId(4, 4), 1), MakeSurfaceId(FrameSinkId(4, 4), 2), + MakeSurfaceId(FrameSinkId(4, 4), 3)}; + + for (size_t i = 0; i < primary_surfaces.size(); ++i) { std::unique_ptr<SurfaceLayerImpl> child = SurfaceLayerImpl::Create(host_impl_->active_tree(), i + 6); child->SetPosition(gfx::PointF(25.f * i, 0.f)); child->SetBounds(gfx::Size(1, 1)); child->SetDrawsContent(true); child->SetPrimarySurfaceInfo( - SurfaceInfo(children[i], 1.f /* device_scale_factor */, + SurfaceInfo(primary_surfaces[i], 1.f /* device_scale_factor */, + gfx::Size(10, 10) /* size_in_pixels */)); + child->SetFallbackSurfaceInfo( + SurfaceInfo(fallback_surfaces[i], 1.f /* device_scale_factor */, gfx::Size(10, 10) /* size_in_pixels */)); root->test_properties()->AddChild(std::move(child)); } @@ -3517,11 +3493,13 @@ TEST_F(LayerTreeHostImplTest, EmbeddedSurfacesInMetadata) { host_impl_->compositor_frame_sink()); const CompositorFrameMetadata& metadata = fake_compositor_frame_sink->last_sent_frame()->metadata; - EXPECT_THAT(metadata.embedded_surfaces, - testing::UnorderedElementsAre(children[0], children[1])); + EXPECT_THAT( + metadata.activation_dependencies, + testing::UnorderedElementsAre(primary_surfaces[0], primary_surfaces[1])); EXPECT_THAT( metadata.referenced_surfaces, - testing::UnorderedElementsAre(children[0], children[1], children[2])); + testing::UnorderedElementsAre(fallback_surfaces[0], fallback_surfaces[1], + fallback_surfaces[2])); } TEST_F(LayerTreeHostImplTest, CompositorFrameMetadata) { @@ -3879,8 +3857,8 @@ TEST_F(LayerTreeHostImplTest, DidDrawCalledOnAllLayers) { EXPECT_TRUE(layer1->did_draw_called()); EXPECT_TRUE(layer2->did_draw_called()); - EXPECT_NE(root->GetRenderSurface(), layer1->GetRenderSurface()); - EXPECT_TRUE(layer1->GetRenderSurface()); + EXPECT_NE(GetRenderSurface(root), GetRenderSurface(layer1)); + EXPECT_TRUE(GetRenderSurface(layer1)); } class MissingTextureAnimatingLayer : public DidDrawCheckLayer { @@ -4251,6 +4229,8 @@ class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest { outer_scroll->test_properties()->is_container_for_fixed_position_layers = true; + int inner_viewport_container_layer_id = root_clip->id(); + int outer_viewport_container_layer_id = outer_clip->id(); int inner_viewport_scroll_layer_id = root->id(); int outer_viewport_scroll_layer_id = outer_scroll->id(); int page_scale_layer_id = page_scale->id(); @@ -4261,9 +4241,13 @@ class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest { root_clip->test_properties()->AddChild(std::move(page_scale)); tree_impl->SetRootLayerForTesting(std::move(root_clip)); - tree_impl->SetViewportLayersFromIds(Layer::INVALID_ID, page_scale_layer_id, - inner_viewport_scroll_layer_id, - outer_viewport_scroll_layer_id); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = page_scale_layer_id; + viewport_ids.inner_viewport_container = inner_viewport_container_layer_id; + viewport_ids.outer_viewport_container = outer_viewport_container_layer_id; + viewport_ids.inner_viewport_scroll = inner_viewport_scroll_layer_id; + viewport_ids.outer_viewport_scroll = outer_viewport_scroll_layer_id; + tree_impl->SetViewportLayersFromIds(viewport_ids); tree_impl->BuildPropertyTreesForTesting(); host_impl_->SetViewportSize(inner_viewport_size); @@ -4373,12 +4357,10 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta); host_impl_->browser_controls_manager()->ScrollEnd(); - LayerImpl* inner_viewport_scroll_layer = - host_impl_->active_tree()->InnerViewportScrollLayer(); - DCHECK(inner_viewport_scroll_layer); host_impl_->ScrollEnd(EndState().get()); + auto* property_trees = host_impl_->active_tree()->property_trees(); EXPECT_FLOAT_EQ(top_controls_scroll_delta.y(), - inner_viewport_scroll_layer->FixedContainerSizeDelta().y()); + property_trees->inner_viewport_container_bounds_delta().y()); } // In this test, the outer viewport is initially unscrollable. We test that a @@ -4479,9 +4461,6 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) { host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f); float page_scale = 1.5f; - LayerImpl* outer_viewport_scroll_layer = - host_impl_->active_tree()->OuterViewportScrollLayer(); - // Zoom in, since the fixed container is the outer viewport, the delta should // not be scaled. host_impl_->active_tree()->PushPageScaleFromMainThread(page_scale, 1.f, 2.f); @@ -4499,8 +4478,10 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) { host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta); EXPECT_FLOAT_EQ(top_controls_height_ - top_controls_scroll_delta.y(), host_impl_->browser_controls_manager()->ContentTopOffset()); + + auto* property_trees = host_impl_->active_tree()->property_trees(); EXPECT_FLOAT_EQ(top_controls_scroll_delta.y(), - outer_viewport_scroll_layer->FixedContainerSizeDelta().y()); + property_trees->outer_viewport_container_bounds_delta().y()); host_impl_->ScrollEnd(EndState().get()); // Scroll past the maximum extent. The delta shouldn't be greater than the @@ -4511,7 +4492,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) { host_impl_->browser_controls_manager()->ScrollBy(top_controls_scroll_delta); EXPECT_EQ(0.f, host_impl_->browser_controls_manager()->ContentTopOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, top_controls_height_), - outer_viewport_scroll_layer->FixedContainerSizeDelta()); + property_trees->outer_viewport_container_bounds_delta()); host_impl_->ScrollEnd(EndState().get()); // Scroll in the direction to make the browser controls show. @@ -4521,7 +4502,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) { host_impl_->browser_controls_manager()->ContentTopOffset()); EXPECT_VECTOR_EQ( gfx::Vector2dF(0, top_controls_height_ - top_controls_scroll_delta.y()), - outer_viewport_scroll_layer->FixedContainerSizeDelta()); + property_trees->outer_viewport_container_bounds_delta()); host_impl_->browser_controls_manager()->ScrollEnd(); } @@ -4755,7 +4736,8 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, // account for the difference between the layout height and the current // browser controls offset. EXPECT_EQ(viewport_size_, inner_clip_ptr->bounds()); - EXPECT_VECTOR_EQ(gfx::Vector2dF(0.f, 50.f), inner_clip_ptr->bounds_delta()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0.f, 50.f), + inner_clip_ptr->ViewportBoundsDelta()); host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(1.f); host_impl_->DidChangeBrowserControlsPosition(); @@ -4764,7 +4746,8 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, host_impl_->browser_controls_manager()->TopControlsShownRatio()); EXPECT_EQ(50.f, host_impl_->browser_controls_manager()->TopControlsHeight()); EXPECT_EQ(50.f, host_impl_->browser_controls_manager()->ContentTopOffset()); - EXPECT_VECTOR_EQ(gfx::Vector2dF(0.f, 0.f), inner_clip_ptr->bounds_delta()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0.f, 0.f), + inner_clip_ptr->ViewportBoundsDelta()); EXPECT_EQ(gfx::Size(viewport_size_.width(), viewport_size_.height() - 50.f), inner_clip_ptr->bounds()); } @@ -5608,7 +5591,8 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedLatchToChild) { host_impl_->Animate(); host_impl_->UpdateAnimationState(true); - EXPECT_EQ(gfx::ScrollOffset(0, 30), grand_child_layer->CurrentScrollOffset()); + // Should have started scrolling. + EXPECT_NE(gfx::ScrollOffset(0, 30), grand_child_layer->CurrentScrollOffset()); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = @@ -5656,13 +5640,16 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { // the scroll doesn't bubble up to the parent layer. gfx::Size surface_size(20, 20); gfx::Size viewport_size(10, 10); + const int kPageScaleLayerId = 1; + const int kViewportClipLayerId = 2; + const int kViewportScrollLayerId = 3; std::unique_ptr<LayerImpl> root_ptr = - LayerImpl::Create(host_impl_->active_tree(), 1); + LayerImpl::Create(host_impl_->active_tree(), kPageScaleLayerId); std::unique_ptr<LayerImpl> root_clip = - LayerImpl::Create(host_impl_->active_tree(), 2); + LayerImpl::Create(host_impl_->active_tree(), kViewportClipLayerId); root_clip->test_properties()->force_render_surface = true; - std::unique_ptr<LayerImpl> root_scrolling = - CreateScrollableLayer(3, surface_size, root_clip.get()); + std::unique_ptr<LayerImpl> root_scrolling = CreateScrollableLayer( + kViewportScrollLayerId, surface_size, root_clip.get()); root_scrolling->test_properties()->is_container_for_fixed_position_layers = true; @@ -5681,8 +5668,11 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { root_ptr->test_properties()->AddChild(std::move(root_clip)); host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_ptr)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 1, 3, - Layer::INVALID_ID); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = kPageScaleLayerId; + viewport_ids.inner_viewport_container = kViewportClipLayerId; + viewport_ids.inner_viewport_scroll = kViewportScrollLayerId; + host_impl_->active_tree()->SetViewportLayersFromIds(viewport_ids); host_impl_->active_tree()->DidBecomeActive(); host_impl_->SetViewportSize(viewport_size); @@ -5801,17 +5791,20 @@ TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) { // should be applied to one of its ancestors if possible. gfx::Size surface_size(10, 10); gfx::Size content_size(20, 20); + const int kPageScaleLayerId = 4; + const int kViewportClipLayerId = 1; + const int kViewportScrollLayerId = 2; std::unique_ptr<LayerImpl> root_ptr = - LayerImpl::Create(host_impl_->active_tree(), 4); + LayerImpl::Create(host_impl_->active_tree(), kPageScaleLayerId); std::unique_ptr<LayerImpl> root_clip = LayerImpl::Create(host_impl_->active_tree(), 3); root_clip->test_properties()->force_render_surface = true; - std::unique_ptr<LayerImpl> root_scroll = - CreateScrollableLayer(1, content_size, root_clip.get()); + std::unique_ptr<LayerImpl> root_scroll = CreateScrollableLayer( + kViewportClipLayerId, content_size, root_clip.get()); // Make 'root' the clip layer for child: since they have the same sizes the // child will have zero max_scroll_offset and scrolls will bubble. - std::unique_ptr<LayerImpl> child = - CreateScrollableLayer(2, content_size, root_scroll.get()); + std::unique_ptr<LayerImpl> child = CreateScrollableLayer( + kViewportScrollLayerId, content_size, root_scroll.get()); child->test_properties()->is_container_for_fixed_position_layers = true; root_scroll->SetBounds(content_size); @@ -5821,8 +5814,11 @@ TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) { root_ptr->test_properties()->AddChild(std::move(root_clip)); host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_ptr)); - host_impl_->active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 4, 2, - Layer::INVALID_ID); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = kPageScaleLayerId; + viewport_ids.inner_viewport_container = kViewportClipLayerId; + viewport_ids.inner_viewport_scroll = kViewportScrollLayerId; + host_impl_->active_tree()->SetViewportLayersFromIds(viewport_ids); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->DidBecomeActive(); @@ -5849,17 +5845,22 @@ TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) { } TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) { + const int kPageScaleLayerId = 1; + const int kInnerViewportClipLayerId = 2; + const int kOuterViewportClipLayerId = 7; + const int kInnerViewportScrollLayerId = 3; + const int kOuterViewportScrollLayerId = 8; gfx::Size surface_size(10, 10); std::unique_ptr<LayerImpl> root_ptr = - LayerImpl::Create(host_impl_->active_tree(), 1); + LayerImpl::Create(host_impl_->active_tree(), kPageScaleLayerId); std::unique_ptr<LayerImpl> inner_clip = - LayerImpl::Create(host_impl_->active_tree(), 2); - std::unique_ptr<LayerImpl> inner_scroll = - CreateScrollableLayer(3, surface_size, inner_clip.get()); + LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId); + std::unique_ptr<LayerImpl> inner_scroll = CreateScrollableLayer( + kInnerViewportScrollLayerId, surface_size, inner_clip.get()); std::unique_ptr<LayerImpl> outer_clip = - LayerImpl::Create(host_impl_->active_tree(), 7); - std::unique_ptr<LayerImpl> outer_scroll = - CreateScrollableLayer(8, surface_size, outer_clip.get()); + LayerImpl::Create(host_impl_->active_tree(), kOuterViewportClipLayerId); + std::unique_ptr<LayerImpl> outer_scroll = CreateScrollableLayer( + kOuterViewportScrollLayerId, surface_size, outer_clip.get()); inner_clip->test_properties()->force_render_surface = true; inner_scroll->test_properties()->is_container_for_fixed_position_layers = true; @@ -5870,8 +5871,13 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) { inner_clip->test_properties()->AddChild(std::move(inner_scroll)); root_ptr->test_properties()->AddChild(std::move(inner_clip)); host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_ptr)); - host_impl_->active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 1, 3, - 8); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = kPageScaleLayerId; + viewport_ids.inner_viewport_container = kInnerViewportClipLayerId; + viewport_ids.outer_viewport_container = kOuterViewportClipLayerId; + viewport_ids.inner_viewport_scroll = kInnerViewportScrollLayerId; + viewport_ids.outer_viewport_scroll = kOuterViewportScrollLayerId; + host_impl_->active_tree()->SetViewportLayersFromIds(viewport_ids); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->DidBecomeActive(); @@ -5881,17 +5887,22 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) { // synchronization. DrawFrame(); + const int kPageScaleLayerId2 = 4; + const int kInnerViewportClipLayerId2 = 5; + const int kOuterViewportClipLayerId2 = 9; + const int kInnerViewportScrollLayerId2 = 6; + const int kOuterViewportScrollLayerId2 = 10; host_impl_->active_tree()->DetachLayers(); std::unique_ptr<LayerImpl> root_ptr2 = LayerImpl::Create(host_impl_->active_tree(), 4); std::unique_ptr<LayerImpl> inner_clip2 = - LayerImpl::Create(host_impl_->active_tree(), 5); - std::unique_ptr<LayerImpl> inner_scroll2 = - CreateScrollableLayer(6, surface_size, inner_clip2.get()); + LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId2); + std::unique_ptr<LayerImpl> inner_scroll2 = CreateScrollableLayer( + kInnerViewportScrollLayerId2, surface_size, inner_clip2.get()); std::unique_ptr<LayerImpl> outer_clip2 = - LayerImpl::Create(host_impl_->active_tree(), 9); - std::unique_ptr<LayerImpl> outer_scroll2 = - CreateScrollableLayer(10, surface_size, outer_clip2.get()); + LayerImpl::Create(host_impl_->active_tree(), kOuterViewportClipLayerId2); + std::unique_ptr<LayerImpl> outer_scroll2 = CreateScrollableLayer( + kOuterViewportScrollLayerId2, surface_size, outer_clip2.get()); inner_scroll2->test_properties()->is_container_for_fixed_position_layers = true; outer_scroll2->test_properties()->is_container_for_fixed_position_layers = @@ -5903,8 +5914,13 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) { root_ptr2->test_properties()->AddChild(std::move(inner_clip2)); host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_ptr2)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 4, 6, - 10); + LayerTreeImpl::ViewportLayerIds viewport_ids2; + viewport_ids2.page_scale = kPageScaleLayerId2; + viewport_ids2.inner_viewport_container = kInnerViewportClipLayerId2; + viewport_ids2.outer_viewport_container = kOuterViewportClipLayerId2; + viewport_ids2.inner_viewport_scroll = kInnerViewportScrollLayerId2; + viewport_ids2.outer_viewport_scroll = kOuterViewportScrollLayerId2; + host_impl_->active_tree()->SetViewportLayersFromIds(viewport_ids2); host_impl_->active_tree()->DidBecomeActive(); // Scrolling should still work even though we did not draw yet. @@ -6471,12 +6487,14 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { // overscroll does not accumulate. InputHandlerScrollResult scroll_result; gfx::Size surface_size(10, 10); + const int kInnerViewportClipLayerId = 4; + const int kInnerViewportScrollLayerId = 1; std::unique_ptr<LayerImpl> root_clip = - LayerImpl::Create(host_impl_->active_tree(), 4); + LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId); root_clip->test_properties()->force_render_surface = true; - std::unique_ptr<LayerImpl> root = - CreateScrollableLayer(1, surface_size, root_clip.get()); + std::unique_ptr<LayerImpl> root = CreateScrollableLayer( + kInnerViewportScrollLayerId, surface_size, root_clip.get()); std::unique_ptr<LayerImpl> grand_child = CreateScrollableLayer(3, surface_size, root_clip.get()); @@ -6486,8 +6504,10 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { LayerImpl* grand_child_layer = grand_child.get(); child->test_properties()->AddChild(std::move(grand_child)); - host_impl_->active_tree()->SetViewportLayersFromIds( - Layer::INVALID_ID, Layer::INVALID_ID, root->id(), Layer::INVALID_ID); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.inner_viewport_container = kInnerViewportClipLayerId; + viewport_ids.inner_viewport_scroll = kInnerViewportScrollLayerId; + host_impl_->active_tree()->SetViewportLayersFromIds(viewport_ids); LayerImpl* child_layer = child.get(); root->test_properties()->AddChild(std::move(child)); @@ -6890,9 +6910,11 @@ TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) { clip->test_properties()->AddChild(std::move(scroll)); content_layer->test_properties()->AddChild(std::move(clip)); - layer_tree_impl->SetViewportLayersFromIds( - Layer::INVALID_ID, layer_tree_impl->PageScaleLayer()->id(), - inner_scroll_layer->id(), scroll_layer->id()); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = layer_tree_impl->PageScaleLayer()->id(); + viewport_ids.inner_viewport_scroll = inner_scroll_layer->id(); + viewport_ids.outer_viewport_scroll = scroll_layer->id(); + layer_tree_impl->SetViewportLayersFromIds(viewport_ids); layer_tree_impl->BuildPropertyTreesForTesting(); } @@ -7025,9 +7047,14 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) { clip2->test_properties()->AddChild(std::move(scroll2)); content_layer->test_properties()->AddChild(std::move(clip2)); - layer_tree_impl->SetViewportLayersFromIds( - Layer::INVALID_ID, layer_tree_impl->PageScaleLayer()->id(), - inner_scroll_layer->id(), outer_scroll_layer->id()); + LayerImpl* inner_container = + host_impl_->active_tree()->InnerViewportContainerLayer(); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = layer_tree_impl->PageScaleLayer()->id(); + viewport_ids.inner_viewport_container = inner_container->id(); + viewport_ids.inner_viewport_scroll = inner_scroll_layer->id(); + viewport_ids.outer_viewport_scroll = outer_scroll_layer->id(); + layer_tree_impl->SetViewportLayersFromIds(viewport_ids); layer_tree_impl->BuildPropertyTreesForTesting(); ASSERT_EQ(outer_scroll_layer, layer_tree_impl->OuterViewportScrollLayer()); @@ -7219,12 +7246,16 @@ class BlendStateCheckLayer : public LayerImpl { gfx::Size(1, 1), false, false); test_blending_draw_quad->visible_rect = quad_visible_rect_; EXPECT_EQ(blend_, test_blending_draw_quad->ShouldDrawWithBlending()); - EXPECT_EQ(has_render_surface_, !!GetRenderSurface()); + EXPECT_EQ(has_render_surface_, + GetRenderSurface(this) != GetRenderSurface(comparison_layer_)); } - void SetExpectation(bool blend, bool has_render_surface) { + void SetExpectation(bool blend, + bool has_render_surface, + LayerImpl* comparison_layer) { blend_ = blend; has_render_surface_ = has_render_surface; + comparison_layer_ = comparison_layer; quads_appended_ = false; } @@ -7243,6 +7274,7 @@ class BlendStateCheckLayer : public LayerImpl { : LayerImpl(tree_impl, id), blend_(false), has_render_surface_(false), + comparison_layer_(nullptr), quads_appended_(false), quad_rect_(5, 5, 5, 5), quad_visible_rect_(5, 5, 5, 5), @@ -7258,6 +7290,7 @@ class BlendStateCheckLayer : public LayerImpl { bool blend_; bool has_render_surface_; + LayerImpl* comparison_layer_; bool quads_appended_; gfx::Rect quad_rect_; gfx::Rect opaque_content_rect_; @@ -7286,7 +7319,7 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { // Opaque layer, drawn without blending. layer1->SetContentsOpaque(true); - layer1->SetExpectation(false, false); + layer1->SetExpectation(false, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -7296,7 +7329,7 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { // Layer with translucent content and painting, so drawn with blending. layer1->SetContentsOpaque(false); - layer1->SetExpectation(true, false); + layer1->SetExpectation(true, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->set_needs_update_draw_properties(); @@ -7309,7 +7342,7 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetContentsOpaque(true); layer1->test_properties()->opacity = 0.5f; layer1->NoteLayerPropertyChanged(); - layer1->SetExpectation(true, false); + layer1->SetExpectation(true, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -7321,7 +7354,7 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetContentsOpaque(true); layer1->test_properties()->opacity = 0.5f; layer1->NoteLayerPropertyChanged(); - layer1->SetExpectation(true, false); + layer1->SetExpectation(true, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -7339,12 +7372,12 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetContentsOpaque(true); layer1->test_properties()->opacity = 1.f; layer1->NoteLayerPropertyChanged(); - layer1->SetExpectation(false, false); + layer1->SetExpectation(false, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); layer2->SetContentsOpaque(true); layer2->test_properties()->opacity = 1.f; layer2->NoteLayerPropertyChanged(); - layer2->SetExpectation(false, false); + layer2->SetExpectation(false, false, root); layer2->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->set_needs_update_draw_properties(); @@ -7357,9 +7390,9 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { // Parent layer with translucent content, drawn with blending. // Child layer with opaque content, drawn without blending. layer1->SetContentsOpaque(false); - layer1->SetExpectation(true, false); + layer1->SetExpectation(true, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); - layer2->SetExpectation(false, false); + layer2->SetExpectation(false, false, root); layer2->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->set_needs_update_draw_properties(); @@ -7373,9 +7406,9 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { // blending. // Child layer with opaque content, drawn without blending. layer1->SetContentsOpaque(true); - layer1->SetExpectation(false, false); + layer1->SetExpectation(false, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); - layer2->SetExpectation(false, false); + layer2->SetExpectation(false, false, root); layer2->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -7393,9 +7426,9 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->test_properties()->opacity = 0.5f; layer1->NoteLayerPropertyChanged(); layer1->test_properties()->force_render_surface = true; - layer1->SetExpectation(false, true); + layer1->SetExpectation(false, true, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); - layer2->SetExpectation(false, false); + layer2->SetExpectation(false, false, layer1); layer2->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -7410,12 +7443,12 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetContentsOpaque(true); layer1->test_properties()->opacity = 1.f; layer1->NoteLayerPropertyChanged(); - layer1->SetExpectation(false, false); + layer1->SetExpectation(false, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); layer2->SetContentsOpaque(true); layer2->test_properties()->opacity = 0.5f; layer2->NoteLayerPropertyChanged(); - layer2->SetExpectation(true, false); + layer2->SetExpectation(true, false, layer1); layer2->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -7428,12 +7461,12 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetContentsOpaque(true); layer1->test_properties()->opacity = 1.f; layer1->NoteLayerPropertyChanged(); - layer1->SetExpectation(false, false); + layer1->SetExpectation(false, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); layer2->SetContentsOpaque(false); layer2->test_properties()->opacity = 1.f; layer2->NoteLayerPropertyChanged(); - layer2->SetExpectation(true, false); + layer2->SetExpectation(true, false, root); layer2->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -7447,12 +7480,12 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetContentsOpaque(true); layer1->test_properties()->opacity = 1.f; layer1->NoteLayerPropertyChanged(); - layer1->SetExpectation(false, false); + layer1->SetExpectation(false, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); layer2->SetContentsOpaque(true); layer2->test_properties()->opacity = 1.f; layer2->NoteLayerPropertyChanged(); - layer2->SetExpectation(false, false); + layer2->SetExpectation(false, false, root); layer2->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -7466,7 +7499,7 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetQuadRect(gfx::Rect(5, 5, 5, 5)); layer1->SetQuadVisibleRect(gfx::Rect(5, 5, 5, 5)); layer1->SetOpaqueContentRect(gfx::Rect(5, 5, 2, 5)); - layer1->SetExpectation(true, false); + layer1->SetExpectation(true, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->set_needs_update_draw_properties(); @@ -7480,7 +7513,7 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetQuadRect(gfx::Rect(5, 5, 5, 5)); layer1->SetQuadVisibleRect(gfx::Rect(5, 5, 5, 2)); layer1->SetOpaqueContentRect(gfx::Rect(5, 5, 2, 5)); - layer1->SetExpectation(true, false); + layer1->SetExpectation(true, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->set_needs_update_draw_properties(); @@ -7494,7 +7527,7 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetQuadRect(gfx::Rect(5, 5, 5, 5)); layer1->SetQuadVisibleRect(gfx::Rect(7, 5, 3, 5)); layer1->SetOpaqueContentRect(gfx::Rect(5, 5, 2, 5)); - layer1->SetExpectation(true, false); + layer1->SetExpectation(true, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->set_needs_update_draw_properties(); @@ -7509,7 +7542,7 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetQuadRect(gfx::Rect(5, 5, 5, 5)); layer1->SetQuadVisibleRect(gfx::Rect(5, 5, 2, 5)); layer1->SetOpaqueContentRect(gfx::Rect(5, 5, 2, 5)); - layer1->SetExpectation(false, false); + layer1->SetExpectation(false, false, root); layer1->SetUpdateRect(gfx::Rect(layer1->bounds())); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->set_needs_update_draw_properties(); @@ -7599,7 +7632,8 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { ->root_layer_for_testing() ->test_properties() ->children[0]); - child_->SetExpectation(false, false); + child_->SetExpectation(false, false, + host_impl_->active_tree()->root_layer_for_testing()); child_->SetContentsOpaque(true); } @@ -7982,7 +8016,7 @@ TEST_F(LayerTreeHostImplTest, RootLayerDoesntCreateExtraSurface) { TestFrameData frame; EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); - EXPECT_EQ(1u, frame.render_surface_layer_list->size()); + EXPECT_EQ(1u, frame.render_surface_list->size()); EXPECT_EQ(1u, frame.render_passes.size()); host_impl_->DidDrawAllLayers(frame); } @@ -8293,7 +8327,7 @@ TEST_F(LayerTreeHostImplTestDrawAndTestDamage, FrameIncludesDamageRect) { host_impl_->active_tree()->BuildPropertyTreesForTesting(); // Draw a frame. In the first frame, the entire viewport should be damaged. - gfx::Rect full_frame_damage(host_impl_->DrawViewportSize()); + gfx::Rect full_frame_damage(host_impl_->DeviceViewport().size()); DrawFrameAndTestDamage(full_frame_damage); // The second frame has damage that doesn't touch the child layer. Its quads @@ -8339,7 +8373,7 @@ TEST_F(LayerTreeHostImplTest, FarAwayQuadsDontNeedAA) { LayerImpl* scrolling_layer = scoped_scrolling_layer.get(); root->test_properties()->AddChild(std::move(scoped_scrolling_layer)); - gfx::Size content_layer_bounds(100000, 100); + gfx::Size content_layer_bounds(100001, 100); scoped_refptr<FakeRasterSource> raster_source( FakeRasterSource::CreateFilled(content_layer_bounds)); @@ -8367,7 +8401,7 @@ TEST_F(LayerTreeHostImplTest, FarAwayQuadsDontNeedAA) { bool update_lcd_text = false; host_impl_->active_tree()->UpdateDrawProperties(update_lcd_text); - ASSERT_EQ(1u, host_impl_->active_tree()->RenderSurfaceLayerList().size()); + ASSERT_EQ(1u, host_impl_->active_tree()->GetRenderSurfaceList().size()); TestFrameData frame; EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); @@ -8757,11 +8791,12 @@ TEST_F(LayerTreeHostImplTest, ShutdownReleasesContext) { TestContextProvider::Create(); FrameSinkClient test_client_(context_provider); + constexpr bool synchronous_composite = true; + constexpr bool disable_display_vsync = false; auto compositor_frame_sink = base::MakeUnique<TestCompositorFrameSink>( context_provider, TestContextProvider::CreateWorker(), nullptr, nullptr, RendererSettings(), base::ThreadTaskRunnerHandle::Get().get(), - true /* synchronous_composite */, - false /* force_disable_reclaim_resources */); + synchronous_composite, disable_display_vsync); compositor_frame_sink->SetClient(&test_client_); CreateHostImpl(DefaultSettings(), std::move(compositor_frame_sink)); @@ -8798,14 +8833,17 @@ TEST_F(LayerTreeHostImplTest, TouchFlingShouldNotBubble) { // bubble). gfx::Size surface_size(10, 10); gfx::Size content_size(20, 20); + const int kPageScaleLayerId = 4; + const int kInnerViewportClipLayerId = 3; + const int kInnerViewportScrollLayerId = 1; std::unique_ptr<LayerImpl> root_ptr = - LayerImpl::Create(host_impl_->active_tree(), 4); + LayerImpl::Create(host_impl_->active_tree(), kPageScaleLayerId); std::unique_ptr<LayerImpl> root_clip = - LayerImpl::Create(host_impl_->active_tree(), 3); + LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId); root_clip->test_properties()->force_render_surface = true; - std::unique_ptr<LayerImpl> root_scroll = - CreateScrollableLayer(1, content_size, root_clip.get()); + std::unique_ptr<LayerImpl> root_scroll = CreateScrollableLayer( + kInnerViewportScrollLayerId, content_size, root_clip.get()); root_scroll->test_properties()->is_container_for_fixed_position_layers = true; std::unique_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size, root_clip.get()); @@ -8817,8 +8855,11 @@ TEST_F(LayerTreeHostImplTest, TouchFlingShouldNotBubble) { host_impl_->SetViewportSize(surface_size); host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_ptr)); - host_impl_->active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 4, 1, - Layer::INVALID_ID); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = kPageScaleLayerId; + viewport_ids.inner_viewport_container = kInnerViewportClipLayerId; + viewport_ids.inner_viewport_scroll = kInnerViewportScrollLayerId; + host_impl_->active_tree()->SetViewportLayersFromIds(viewport_ids); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->DidBecomeActive(); DrawFrame(); @@ -9122,7 +9163,7 @@ TEST_F(LayerTreeHostImplTest, LatencyInfoPassedToCompositorFrameMetadata) { new LatencyInfoSwapPromise(latency_info)); host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise)); - gfx::Rect full_frame_damage(host_impl_->DrawViewportSize()); + gfx::Rect full_frame_damage(host_impl_->DeviceViewport().size()); TestFrameData frame; EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); EXPECT_TRUE(host_impl_->DrawLayers(&frame)); @@ -9166,7 +9207,7 @@ TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToCompositorFrameMetadata) { // Trigger a draw-swap sequence. host_impl_->SetNeedsRedraw(); - gfx::Rect full_frame_damage(host_impl_->DrawViewportSize()); + gfx::Rect full_frame_damage(host_impl_->DeviceViewport().size()); TestFrameData frame; EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); EXPECT_TRUE(host_impl_->DrawLayers(&frame)); @@ -9825,6 +9866,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer(); LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); LayerImpl* scroll_layer = nullptr; + LayerImpl* clip_layer = nullptr; // Initialization: Add a child scrolling layer to the outer scroll layer and // set its scroll layer as the outer viewport. This simulates setting a @@ -9841,12 +9883,18 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, scroll->SetDrawsContent(true); scroll_layer = scroll.get(); + clip_layer = clip.get(); clip->test_properties()->AddChild(std::move(scroll)); outer_scroll->test_properties()->AddChild(std::move(clip)); - layer_tree_impl->SetViewportLayersFromIds( - Layer::INVALID_ID, layer_tree_impl->PageScaleLayer()->id(), - inner_scroll->id(), scroll_layer->id()); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = layer_tree_impl->PageScaleLayer()->id(); + viewport_ids.inner_viewport_container = + layer_tree_impl->InnerViewportContainerLayer()->id(); + viewport_ids.outer_viewport_container = clip_layer->id(); + viewport_ids.inner_viewport_scroll = inner_scroll->id(); + viewport_ids.outer_viewport_scroll = scroll_layer->id(); + layer_tree_impl->SetViewportLayersFromIds(viewport_ids); layer_tree_impl->BuildPropertyTreesForTesting(); DrawFrame(); } @@ -9935,9 +9983,13 @@ class LayerTreeHostImplVirtualViewportTest : public LayerTreeHostImplTest { inner_clip->test_properties()->force_render_surface = true; layer_tree_impl->SetRootLayerForTesting(std::move(inner_clip)); - layer_tree_impl->SetViewportLayersFromIds( - Layer::INVALID_ID, kPageScaleLayerId, kInnerViewportScrollLayerId, - kOuterViewportScrollLayerId); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = kPageScaleLayerId; + viewport_ids.inner_viewport_container = kInnerViewportClipLayerId; + viewport_ids.outer_viewport_container = kOuterViewportClipLayerId; + viewport_ids.inner_viewport_scroll = kInnerViewportScrollLayerId; + viewport_ids.outer_viewport_scroll = kOuterViewportScrollLayerId; + layer_tree_impl->SetViewportLayersFromIds(viewport_ids); host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->DidBecomeActive(); @@ -10746,7 +10798,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimated) { host_impl_->Animate(); host_impl_->UpdateAnimationState(true); - EXPECT_EQ(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); + EXPECT_NE(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = @@ -10887,13 +10939,15 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedWithDelay) { begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); - EXPECT_EQ(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); + EXPECT_NE(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); host_impl_->DidFinishImplFrame(); - // Second tick after 50ms, animation should be half way done since - // the duration due to delay is 100ms. - begin_frame_args.frame_time = - start_time + base::TimeDelta::FromMilliseconds(50); + // Second tick after 50ms, animation should be half way done since the + // duration due to delay is 100ms. Subtract off the frame interval since we + // progress a full frame on the first tick. + base::TimeTicks half_way_time = start_time - begin_frame_args.interval + + base::TimeDelta::FromMilliseconds(50); + begin_frame_args.frame_time = half_way_time; begin_frame_args.sequence_number++; host_impl_->WillBeginImplFrame(begin_frame_args); host_impl_->UpdateAnimationState(true); @@ -10950,7 +11004,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedAborted) { EXPECT_TRUE(GetImplAnimationHost()->HasAnyAnimationTargetingProperty( scrolling_layer->element_id(), TargetProperty::SCROLL_OFFSET)); - EXPECT_EQ(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); + EXPECT_NE(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = @@ -11019,7 +11073,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimated) { host_impl_->Animate(); host_impl_->UpdateAnimationState(true); - EXPECT_EQ(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); + EXPECT_NE(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = @@ -11255,7 +11309,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedNotUserScrollable) { host_impl_->Animate(); host_impl_->UpdateAnimationState(true); - EXPECT_EQ(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); + EXPECT_NE(gfx::ScrollOffset(), scrolling_layer->CurrentScrollOffset()); host_impl_->DidFinishImplFrame(); begin_frame_args.frame_time = @@ -11706,13 +11760,22 @@ TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusTrigger) { // Tests that SetContentIsSuitableForGpuRasterization behaves as expected. TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusSuitability) { + std::unique_ptr<TestWebGraphicsContext3D> context_with_msaa = + TestWebGraphicsContext3D::Create(); + context_with_msaa->SetMaxSamples(4); + context_with_msaa->set_gpu_rasterization(true); + LayerTreeSettings msaaSettings = DefaultSettings(); + msaaSettings.gpu_rasterization_msaa_sample_count = 4; + EXPECT_TRUE(CreateHostImpl(msaaSettings, FakeCompositorFrameSink::Create3d( + std::move(context_with_msaa)))); + // Set initial state, before varying GPU rasterization suitability. host_impl_->SetHasGpuRasterizationTrigger(true); host_impl_->SetContentIsSuitableForGpuRasterization(false); host_impl_->CommitComplete(); - EXPECT_EQ(GpuRasterizationStatus::OFF_CONTENT, + EXPECT_EQ(GpuRasterizationStatus::MSAA_CONTENT, host_impl_->gpu_rasterization_status()); - EXPECT_FALSE(host_impl_->use_gpu_rasterization()); + EXPECT_TRUE(host_impl_->use_msaa()); // Toggle suitability on. host_impl_->SetContentIsSuitableForGpuRasterization(true); @@ -11724,10 +11787,10 @@ TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusSuitability) { // And off. host_impl_->SetContentIsSuitableForGpuRasterization(false); host_impl_->CommitComplete(); - EXPECT_EQ(GpuRasterizationStatus::OFF_CONTENT, + EXPECT_EQ(GpuRasterizationStatus::MSAA_CONTENT, host_impl_->gpu_rasterization_status()); - EXPECT_FALSE(host_impl_->use_gpu_rasterization()); - EXPECT_FALSE(host_impl_->use_msaa()); + EXPECT_TRUE(host_impl_->use_gpu_rasterization()); + EXPECT_TRUE(host_impl_->use_msaa()); } // Tests that SetDeviceScaleFactor correctly impacts GPU rasterization. @@ -11746,9 +11809,8 @@ TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusDeviceScaleFactor) { host_impl_->SetHasGpuRasterizationTrigger(true); host_impl_->SetContentIsSuitableForGpuRasterization(false); host_impl_->CommitComplete(); - EXPECT_EQ(GpuRasterizationStatus::OFF_CONTENT, - host_impl_->gpu_rasterization_status()); - EXPECT_FALSE(host_impl_->use_gpu_rasterization()); + EXPECT_EQ(GpuRasterizationStatus::ON, host_impl_->gpu_rasterization_status()); + EXPECT_TRUE(host_impl_->use_gpu_rasterization()); // Set device scale factor to 2, which lowers the required MSAA samples from // 8 to 4. @@ -11762,9 +11824,8 @@ TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusDeviceScaleFactor) { // Set device scale factor back to 1. host_impl_->active_tree()->SetDeviceScaleFactor(1.0f); host_impl_->CommitComplete(); - EXPECT_EQ(GpuRasterizationStatus::OFF_CONTENT, - host_impl_->gpu_rasterization_status()); - EXPECT_FALSE(host_impl_->use_gpu_rasterization()); + EXPECT_EQ(GpuRasterizationStatus::ON, host_impl_->gpu_rasterization_status()); + EXPECT_TRUE(host_impl_->use_gpu_rasterization()); EXPECT_FALSE(host_impl_->use_msaa()); } @@ -11850,40 +11911,14 @@ TEST_F(MsaaIsSlowLayerTreeHostImplTest, GpuRasterizationStatusMsaaIsSlow) { EXPECT_TRUE(host_impl_->use_gpu_rasterization()); // Ensure that with the msaa_is_slow cap we don't raster unsuitable content - // with msaa. + // with msaa (we'll still use GPU raster, though). CreateHostImplWithMsaaIsSlow(true); host_impl_->SetHasGpuRasterizationTrigger(true); host_impl_->SetContentIsSuitableForGpuRasterization(false); host_impl_->CommitComplete(); - EXPECT_EQ(GpuRasterizationStatus::OFF_CONTENT, - host_impl_->gpu_rasterization_status()); - EXPECT_FALSE(host_impl_->use_gpu_rasterization()); -} - -// A mock output surface which lets us detect calls to ForceReclaimResources. -class MockReclaimResourcesCompositorFrameSink : public FakeCompositorFrameSink { - public: - MockReclaimResourcesCompositorFrameSink() - : FakeCompositorFrameSink(TestContextProvider::Create(), - TestContextProvider::CreateWorker()) {} - - MOCK_METHOD0(ForceReclaimResources, void()); -}; - -// Display::Draw (and the planned Display Scheduler) currently rely on resources -// being reclaimed to block drawing between BeginCommit / Swap. This test -// ensures that BeginCommit triggers ForceReclaimResources. See -// crbug.com/489515. -TEST_F(LayerTreeHostImplTest, BeginCommitReclaimsResources) { - auto compositor_frame_sink = - base::MakeUnique<MockReclaimResourcesCompositorFrameSink>(); - // Hold an unowned pointer to the output surface to use for mock expectations. - MockReclaimResourcesCompositorFrameSink* mock_compositor_frame_sink = - compositor_frame_sink.get(); - - CreateHostImpl(DefaultSettings(), std::move(compositor_frame_sink)); - EXPECT_CALL(*mock_compositor_frame_sink, ForceReclaimResources()).Times(1); - host_impl_->BeginCommit(); + EXPECT_EQ(GpuRasterizationStatus::ON, host_impl_->gpu_rasterization_status()); + EXPECT_TRUE(host_impl_->use_gpu_rasterization()); + EXPECT_FALSE(host_impl_->use_msaa()); } TEST_F(LayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) { @@ -12093,12 +12128,14 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( // scrollbar_1 on root scroll. std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar_1 = SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), - scrollbar_1_id, VERTICAL, 5, 5, true, - true); - scrollbar_1->SetScrollLayerId(root_scroll->id()); + scrollbar_1_id, VERTICAL, 15, 0, + true, true); + scrollbar_1->SetScrollElementId(root_scroll->element_id()); scrollbar_1->SetDrawsContent(true); scrollbar_1->SetBounds(scrollbar_size_1); scrollbar_1->SetTouchEventHandlerRegion(gfx::Rect(scrollbar_size_1)); + scrollbar_1->SetCurrentPos(0); + scrollbar_1->SetPosition(gfx::PointF(0, 0)); host_impl_->active_tree() ->InnerViewportContainerLayer() ->test_properties() @@ -12111,46 +12148,81 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( host_impl_->active_tree()->UpdateDrawProperties(false); ScrollbarAnimationController* scrollbar_1_animation_controller = - host_impl_->ScrollbarAnimationControllerForId(root_scroll->id()); + host_impl_->ScrollbarAnimationControllerForElementId( + root_scroll->element_id()); EXPECT_TRUE(scrollbar_1_animation_controller); - const float kMouseDistanceToTriggerAnimation = + const float kMouseMoveDistanceToTriggerFadeIn = + ScrollbarAnimationController::kMouseMoveDistanceToTriggerFadeIn; + + const float kMouseMoveDistanceToTriggerExpand = SingleScrollbarAnimationControllerThinning:: - kDefaultMouseMoveDistanceToTriggerAnimation; + kMouseMoveDistanceToTriggerExpand; // Mouse moves close to the scrollbar, goes over the scrollbar, and // moves back to where it was. host_impl_->MouseMoveAt( - gfx::Point(15 + kMouseDistanceToTriggerAnimation, 150)); + gfx::Point(15 + kMouseMoveDistanceToTriggerFadeIn, 0)); EXPECT_FALSE( scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); EXPECT_FALSE( - scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_FALSE( + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + + host_impl_->MouseMoveAt( + gfx::Point(15 + kMouseMoveDistanceToTriggerExpand, 0)); + EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_FALSE( + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_FALSE( + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + host_impl_->MouseMoveAt( - gfx::Point(14 + kMouseDistanceToTriggerAnimation, 150)); + gfx::Point(14 + kMouseMoveDistanceToTriggerExpand, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_TRUE( + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); EXPECT_FALSE( - scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); - host_impl_->MouseMoveAt(gfx::Point(10, 150)); + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + + host_impl_->MouseMoveAt(gfx::Point(10, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); - EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); + EXPECT_TRUE( + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_TRUE( + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + host_impl_->MouseMoveAt( - gfx::Point(14 + kMouseDistanceToTriggerAnimation, 150)); + gfx::Point(14 + kMouseMoveDistanceToTriggerExpand, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_TRUE( + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); EXPECT_FALSE( - scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + host_impl_->MouseMoveAt( - gfx::Point(15 + kMouseDistanceToTriggerAnimation, 150)); + gfx::Point(15 + kMouseMoveDistanceToTriggerExpand, 0)); + EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_FALSE( + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_FALSE( + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + + host_impl_->MouseMoveAt( + gfx::Point(15 + kMouseMoveDistanceToTriggerFadeIn, 0)); EXPECT_FALSE( scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); EXPECT_FALSE( - scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_FALSE( + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); // scrollbar_2 on child. std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar_2 = SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), - scrollbar_2_id, VERTICAL, 5, 5, true, - true); + scrollbar_2_id, VERTICAL, 15, 0, + true, true); std::unique_ptr<LayerImpl> child_clip = LayerImpl::Create(host_impl_->active_tree(), child_clip_id); std::unique_ptr<LayerImpl> child = @@ -12160,15 +12232,18 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( child->SetDrawsContent(true); child->SetScrollClipLayer(child_clip_id); child->SetElementId(LayerIdToElementIdForTesting(child->id())); + ElementId child_element_id = child->element_id(); if (main_thread_scrolling) { child->set_main_thread_scrolling_reasons( MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); } - scrollbar_2->SetScrollLayerId(child_scroll_id); + scrollbar_2->SetScrollElementId(child_element_id); scrollbar_2->SetDrawsContent(true); scrollbar_2->SetBounds(scrollbar_size_2); + scrollbar_2->SetCurrentPos(0); + scrollbar_2->SetPosition(gfx::PointF(0, 0)); child->test_properties()->AddChild(std::move(scrollbar_2)); child_clip->test_properties()->AddChild(std::move(child)); @@ -12178,50 +12253,70 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( host_impl_->active_tree()->DidBecomeActive(); ScrollbarAnimationController* scrollbar_2_animation_controller = - host_impl_->ScrollbarAnimationControllerForId(child_scroll_id); + host_impl_->ScrollbarAnimationControllerForElementId(child_element_id); EXPECT_TRUE(scrollbar_2_animation_controller); // Mouse goes over scrollbar_2, moves close to scrollbar_2, moves close to // scrollbar_1, goes over scrollbar_1. - host_impl_->MouseMoveAt(gfx::Point(60, 150)); + host_impl_->MouseMoveAt(gfx::Point(60, 60)); EXPECT_FALSE( scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); EXPECT_FALSE( - scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_FALSE( + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); EXPECT_TRUE(scrollbar_2_animation_controller->MouseIsNearScrollbar(VERTICAL)); - EXPECT_TRUE(scrollbar_2_animation_controller->MouseIsOverScrollbar(VERTICAL)); + EXPECT_TRUE( + scrollbar_2_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_TRUE( + scrollbar_2_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + host_impl_->MouseMoveAt( - gfx::Point(64 + kMouseDistanceToTriggerAnimation, 150)); + gfx::Point(64 + kMouseMoveDistanceToTriggerExpand, 50)); EXPECT_FALSE( scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); EXPECT_FALSE( - scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_FALSE( + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); EXPECT_TRUE(scrollbar_2_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_TRUE( + scrollbar_2_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); EXPECT_FALSE( - scrollbar_2_animation_controller->MouseIsOverScrollbar(VERTICAL)); + scrollbar_2_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); host_impl_->MouseMoveAt( - gfx::Point(14 + kMouseDistanceToTriggerAnimation, 150)); + gfx::Point(14 + kMouseMoveDistanceToTriggerExpand, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); + EXPECT_TRUE( + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); EXPECT_FALSE( - scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); EXPECT_FALSE( scrollbar_2_animation_controller->MouseIsNearScrollbar(VERTICAL)); EXPECT_FALSE( - scrollbar_2_animation_controller->MouseIsOverScrollbar(VERTICAL)); - host_impl_->MouseMoveAt(gfx::Point(10, 150)); + scrollbar_2_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_FALSE( + scrollbar_2_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); + host_impl_->MouseMoveAt(gfx::Point(10, 0)); EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(VERTICAL)); - EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsOverScrollbar(VERTICAL)); + EXPECT_TRUE( + scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_TRUE( + scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); EXPECT_FALSE( scrollbar_2_animation_controller->MouseIsNearScrollbar(VERTICAL)); EXPECT_FALSE( - scrollbar_2_animation_controller->MouseIsOverScrollbar(VERTICAL)); + scrollbar_2_animation_controller->MouseIsNearScrollbarThumb(VERTICAL)); + EXPECT_FALSE( + scrollbar_2_animation_controller->MouseIsOverScrollbarThumb(VERTICAL)); // Capture scrollbar_1, then move mouse to scrollbar_2's layer, should post an // event to fade out scrollbar_1. + scrollbar_1_animation_controller->DidScrollUpdate(); animation_task_ = base::Closure(); host_impl_->MouseDown(); - host_impl_->MouseMoveAt(gfx::Point(100, 150)); + host_impl_->MouseMoveAt(gfx::Point(60, 50)); host_impl_->MouseUp(); EXPECT_FALSE(animation_task_.Equals(base::Closure())); @@ -12239,17 +12334,17 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates( // scrollbar_2_animation_controller, then mouse up should not cause crash. host_impl_->MouseMoveAt(gfx::Point(40, 150)); host_impl_->MouseDown(); - host_impl_->UnregisterScrollbarAnimationController(root_scroll->id()); + host_impl_->UnregisterScrollbarAnimationController(root_scroll->element_id()); host_impl_->MouseUp(); } TEST_F(LayerTreeHostImplTest, - LayerTreeHostImplTestScrollbarStatesInMainThreadScorlling) { + LayerTreeHostImplTestScrollbarStatesInMainThreadScrolling) { SetupMouseMoveAtTestScrollbarStates(true); } TEST_F(LayerTreeHostImplTest, - LayerTreeHostImplTestScrollbarStatesInNotMainThreadScorlling) { + LayerTreeHostImplTestScrollbarStatesInNotMainThreadScrolling) { SetupMouseMoveAtTestScrollbarStates(false); } @@ -12263,9 +12358,8 @@ TEST_F(LayerTreeHostImplTest, CheckerImagingTileInvalidation) { std::unique_ptr<FakeRecordingSource> recording_source = FakeRecordingSource::CreateFilledRecordingSource(layer_size); - recording_source->SetGenerateDiscardableImagesMetadata(true); - sk_sp<SkImage> checkerable_image = - CreateDiscardableImage(gfx::Size(500, 500)); + PaintImage checkerable_image = PaintImage( + PaintImage::GetNextId(), CreateDiscardableImage(gfx::Size(500, 500))); recording_source->add_draw_image(checkerable_image, gfx::Point(0, 0)); SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67); @@ -12325,9 +12419,22 @@ TEST_F(LayerTreeHostImplTest, CheckerImagingTileInvalidation) { EXPECT_FALSE(tile->HasRasterTask()); } Region expected_invalidation( - raster_source->GetRectForImage(checkerable_image->uniqueID())); + raster_source->GetRectForImage(checkerable_image.stable_id())); EXPECT_EQ(expected_invalidation, *(root->GetPendingInvalidation())); } +TEST_F(LayerTreeHostImplTest, RasterColorSpaceNoColorCorrection) { + LayerTreeSettings settings = DefaultSettings(); + CreateHostImpl(settings, CreateCompositorFrameSink()); + EXPECT_FALSE(host_impl_->GetRasterColorSpace().IsValid()); +} + +TEST_F(LayerTreeHostImplTest, RasterColorSpace) { + LayerTreeSettings settings = DefaultSettings(); + settings.enable_color_correct_rasterization = true; + CreateHostImpl(settings, CreateCompositorFrameSink()); + EXPECT_EQ(host_impl_->GetRasterColorSpace(), gfx::ColorSpace::CreateSRGB()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_perftest.cc b/chromium/cc/trees/layer_tree_host_perftest.cc index ff17af70317..7649a527fa2 100644 --- a/chromium/cc/trees/layer_tree_host_perftest.cc +++ b/chromium/cc/trees/layer_tree_host_perftest.cc @@ -23,6 +23,7 @@ #include "cc/test/layer_tree_json_parser.h" #include "cc/test/layer_tree_test.h" #include "cc/test/paths.h" +#include "cc/test/test_compositor_frame_sink.h" #include "cc/trees/layer_tree_impl.h" #include "testing/perf/perf_test.h" @@ -45,12 +46,18 @@ class LayerTreeHostPerfTest : public LayerTreeTest { measure_commit_cost_(false) { } - void InitializeSettings(LayerTreeSettings* settings) override { - // LayerTreeTests give the Display's BeginFrameSource directly to the - // LayerTreeHost like we do in the Browser process via - // TestDelegatingOutputSurface, so setting disable_display_vsync here - // unthrottles both the DisplayScheduler and the Scheduler. - settings->renderer_settings.disable_display_vsync = true; + std::unique_ptr<TestCompositorFrameSink> CreateCompositorFrameSink( + scoped_refptr<ContextProvider> compositor_context_provider, + scoped_refptr<ContextProvider> worker_context_provider) override { + constexpr bool disable_display_vsync = true; + bool synchronous_composite = + !HasImplThread() && + !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; + return base::MakeUnique<TestCompositorFrameSink>( + compositor_context_provider, std::move(worker_context_provider), + shared_bitmap_manager(), gpu_memory_buffer_manager(), + layer_tree_host()->GetSettings().renderer_settings, + ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync); } void BeginTest() override { @@ -149,13 +156,25 @@ class LayerTreeHostPerfTestJsonReader : public LayerTreeHostPerfTest { }; // Simulates a tab switcher scene with two stacks of 10 tabs each. -TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread) { +// Timed out on Android: http://crbug.com/723821 +#if defined(OS_ANDROID) +#define MAYBE_TenTenSingleThread DISABLED_TenTenSingleThread +#else +#define MAYBE_TenTenSingleThread TenTenSingleThread +#endif +TEST_F(LayerTreeHostPerfTestJsonReader, MAYBE_TenTenSingleThread) { SetTestName("10_10_single_thread"); ReadTestFile("10_10_layer_tree"); RunTest(CompositorMode::SINGLE_THREADED); } -TEST_F(LayerTreeHostPerfTestJsonReader, TenTenThreaded) { +// Timed out on Android: http://crbug.com/723821 +#if defined(OS_ANDROID) +#define MAYBE_TenTenThreaded DISABLED_TenTenThreaded +#else +#define MAYBE_TenTenThreaded TenTenThreaded +#endif +TEST_F(LayerTreeHostPerfTestJsonReader, MAYBE_TenTenThreaded) { SetTestName("10_10_threaded_impl_side"); ReadTestFile("10_10_layer_tree"); RunTest(CompositorMode::THREADED); @@ -211,7 +230,8 @@ TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenSingleThread) { RunTest(CompositorMode::SINGLE_THREADED); } -TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenThreaded) { +// Timed out on Android: http://crbug.com/723821 +TEST_F(LayerTreeHostPerfTestLeafInvalidates, MAYBE_TenTenThreaded) { SetTestName("10_10_threaded_impl_side_leaf_invalidates"); ReadTestFile("10_10_layer_tree"); RunTest(CompositorMode::THREADED); @@ -242,13 +262,26 @@ class ScrollingLayerTreePerfTest : public LayerTreeHostPerfTestJsonReader { scoped_refptr<Layer> scrollable_; }; -TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageSingleThread) { +// Timed out on Android: http://crbug.com/723821 +#if defined(OS_ANDROID) +#define MAYBE_LongScrollablePageSingleThread \ + DISABLED_LongScrollablePageSingleThread +#else +#define MAYBE_LongScrollablePageSingleThread LongScrollablePageSingleThread +#endif +TEST_F(ScrollingLayerTreePerfTest, MAYBE_LongScrollablePageSingleThread) { SetTestName("long_scrollable_page"); ReadTestFile("long_scrollable_page"); RunTest(CompositorMode::SINGLE_THREADED); } -TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageThreaded) { +// Timed out on Android: http://crbug.com/723821 +#if defined(OS_ANDROID) +#define MAYBE_LongScrollablePageThreaded DISABLED_LongScrollablePageThreaded +#else +#define MAYBE_LongScrollablePageThreaded LongScrollablePageThreaded +#endif +TEST_F(ScrollingLayerTreePerfTest, MAYBE_LongScrollablePageThreaded) { SetTestName("long_scrollable_page_threaded_impl_side"); ReadTestFile("long_scrollable_page"); RunTest(CompositorMode::THREADED); @@ -343,7 +376,13 @@ TEST_F(BrowserCompositorInvalidateLayerTreePerfTest, DenseBrowserUIThreaded) { } // Simulates a page with several large, transformed and animated layers. -TEST_F(LayerTreeHostPerfTestJsonReader, HeavyPageThreaded) { +// Timed out on Android: http://crbug.com/723821 +#if defined(OS_ANDROID) +#define MAYBE_HeavyPageThreaded DISABLED_HeavyPageThreaded +#else +#define MAYBE_HeavyPageThreaded HeavyPageThreaded +#endif +TEST_F(LayerTreeHostPerfTestJsonReader, MAYBE_HeavyPageThreaded) { begin_frame_driven_drawing_ = true; measure_commit_cost_ = true; SetTestName("heavy_page"); diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc index 05f94594ae4..656d2922e02 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc @@ -6,6 +6,7 @@ #include "cc/layers/picture_image_layer.h" #include "cc/layers/solid_color_layer.h" +#include "cc/paint/paint_image.h" #include "cc/test/layer_tree_pixel_resource_test.h" #include "cc/test/pixel_comparator.h" #include "third_party/skia/include/core/SkImage.h" @@ -145,7 +146,8 @@ class LayerTreeHostBlendingPixelTest : public LayerTreeHostPixelResourceTest { scoped_refptr<PictureImageLayer> layer = PictureImageLayer::Create(); layer->SetIsDrawable(true); layer->SetBounds(gfx::Size(width, height)); - layer->SetImage(backing_store->makeImageSnapshot()); + layer->SetImage(PaintImage(PaintImage::GetNextId(), + backing_store->makeImageSnapshot())); return layer; } @@ -167,7 +169,8 @@ class LayerTreeHostBlendingPixelTest : public LayerTreeHostPixelResourceTest { bounds.width() - kMaskOffset * 2, bounds.height() - kMaskOffset * 2), paint); - mask->SetImage(surface->makeImageSnapshot()); + mask->SetImage( + PaintImage(PaintImage::GetNextId(), surface->makeImageSnapshot())); layer->SetMaskLayer(mask.get()); } diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc index eed99f6a625..1d1d70b2cdd 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc @@ -11,7 +11,9 @@ #include "cc/layers/solid_color_layer.h" #include "cc/paint/drawing_display_item.h" #include "cc/paint/paint_flags.h" +#include "cc/paint/paint_image.h" #include "cc/paint/paint_recorder.h" +#include "cc/test/fake_picture_layer.h" #include "cc/test/layer_tree_pixel_resource_test.h" #include "cc/test/pixel_comparator.h" #include "cc/test/solid_color_content_layer_client.h" @@ -40,7 +42,7 @@ class MaskContentLayerClient : public ContentLayerClient { PaintingControlSetting picture_control) override { PaintRecorder recorder; PaintCanvas* canvas = - recorder.beginRecording(gfx::RectToSkRect(gfx::Rect(bounds_))); + recorder.beginRecording(gfx::RectToSkRect(PaintableRegion())); PaintFlags flags; flags.setStyle(PaintFlags::kStroke_Style); @@ -60,7 +62,8 @@ class MaskContentLayerClient : public ContentLayerClient { auto display_list = make_scoped_refptr(new DisplayItemList); display_list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - PaintableRegion(), recorder.finishRecordingAsPicture()); + PaintableRegion(), recorder.finishRecordingAsPicture(), + gfx::RectToSkRect(PaintableRegion())); display_list->Finalize(); return display_list; @@ -71,8 +74,8 @@ class MaskContentLayerClient : public ContentLayerClient { }; TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayer) { - scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( - gfx::Rect(100, 100), SK_ColorWHITE); + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder( gfx::Rect(25, 25, 50, 50), kCSSGreen, 1, SK_ColorBLACK); @@ -83,7 +86,7 @@ TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayer) { scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); mask->SetBounds(mask_bounds); mask->SetIsDrawable(true); - mask->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask->SetLayerMaskType(mask_type_); green->SetMaskLayer(mask.get()); RunPixelResourceTest(background, @@ -91,14 +94,14 @@ TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayer) { } TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) { - scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( - gfx::Rect(100, 100), SK_ColorWHITE); + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); gfx::Size mask_bounds(50, 50); scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create(); mask->SetIsDrawable(true); - mask->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask->SetLayerMaskType(mask_type_); mask->SetBounds(mask_bounds); sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(200, 200); @@ -108,8 +111,9 @@ TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) { scoped_refptr<DisplayItemList> mask_display_list = client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); - mask_display_list->Raster(canvas, nullptr); - mask->SetImage(surface->makeImageSnapshot()); + mask_display_list->Raster(canvas); + mask->SetImage( + PaintImage(PaintImage::GetNextId(), surface->makeImageSnapshot())); scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder( gfx::Rect(25, 25, 50, 50), kCSSGreen, 1, SK_ColorBLACK); @@ -121,8 +125,8 @@ TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) { } TEST_P(LayerTreeHostMasksPixelTest, MaskOfClippedLayer) { - scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( - gfx::Rect(100, 100), SK_ColorWHITE); + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); // Clip to the top half of the green layer. scoped_refptr<Layer> clip = Layer::Create(); @@ -140,7 +144,7 @@ TEST_P(LayerTreeHostMasksPixelTest, MaskOfClippedLayer) { scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); mask->SetBounds(mask_bounds); mask->SetIsDrawable(true); - mask->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask->SetLayerMaskType(mask_type_); green->SetMaskLayer(mask.get()); RunPixelResourceTest( @@ -148,6 +152,63 @@ TEST_P(LayerTreeHostMasksPixelTest, MaskOfClippedLayer) { base::FilePath(FILE_PATH_LITERAL("mask_of_clipped_layer.png"))); } +TEST_P(LayerTreeHostMasksPixelTest, MaskOfLargerLayer) { + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); + + scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder( + gfx::Rect(0, 0, 100, 100), kCSSGreen, 1, SK_ColorBLACK); + background->AddChild(green); + + gfx::Size mask_bounds(50, 50); + MaskContentLayerClient client(mask_bounds); + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); + mask->SetBounds(mask_bounds); + mask->SetIsDrawable(true); + mask->SetLayerMaskType(mask_type_); + green->SetMaskLayer(mask.get()); + + if (raster_buffer_provider_type_ == RASTER_BUFFER_PROVIDER_TYPE_BITMAP) { + // Bitmap produces a sharper (but equivalent sized) mask. + float percentage_pixels_large_error = 40.0f; + float percentage_pixels_small_error = 0.0f; + float average_error_allowed_in_bad_pixels = 65.0f; + int large_error_allowed = 120; + int small_error_allowed = 0; + pixel_comparator_.reset(new FuzzyPixelComparator( + true, // discard_alpha + percentage_pixels_large_error, percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, large_error_allowed, + small_error_allowed)); + } + + RunPixelResourceTest( + background, + base::FilePath(FILE_PATH_LITERAL("mask_of_larger_layer.png"))); +} + +TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayerNonExactTextureSize) { + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); + + scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder( + gfx::Rect(0, 0, 100, 100), kCSSGreen, 1, SK_ColorBLACK); + background->AddChild(green); + + gfx::Size mask_bounds(100, 100); + MaskContentLayerClient client(mask_bounds); + scoped_refptr<FakePictureLayer> mask = FakePictureLayer::Create(&client); + mask->SetBounds(mask_bounds); + mask->SetIsDrawable(true); + mask->SetLayerMaskType(mask_type_); + mask->set_fixed_tile_size(gfx::Size(173, 135)); + green->SetMaskLayer(mask.get()); + + RunPixelResourceTest(background, + base::FilePath(FILE_PATH_LITERAL( + "mask_with_non_exact_texture_size.png"))); +} + class CheckerContentLayerClient : public ContentLayerClient { public: CheckerContentLayerClient(const gfx::Size& bounds, @@ -162,7 +223,7 @@ class CheckerContentLayerClient : public ContentLayerClient { PaintingControlSetting picture_control) override { PaintRecorder recorder; PaintCanvas* canvas = - recorder.beginRecording(gfx::RectToSkRect(gfx::Rect(bounds_))); + recorder.beginRecording(gfx::RectToSkRect(PaintableRegion())); PaintFlags flags; flags.setStyle(PaintFlags::kStroke_Style); @@ -181,7 +242,8 @@ class CheckerContentLayerClient : public ContentLayerClient { auto display_list = make_scoped_refptr(new DisplayItemList); display_list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - PaintableRegion(), recorder.finishRecordingAsPicture()); + PaintableRegion(), recorder.finishRecordingAsPicture(), + gfx::RectToSkRect(PaintableRegion())); display_list->Finalize(); return display_list; @@ -205,7 +267,7 @@ class CircleContentLayerClient : public ContentLayerClient { PaintingControlSetting picture_control) override { PaintRecorder recorder; PaintCanvas* canvas = - recorder.beginRecording(gfx::RectToSkRect(gfx::Rect(bounds_))); + recorder.beginRecording(gfx::RectToSkRect(PaintableRegion())); PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); @@ -216,7 +278,8 @@ class CircleContentLayerClient : public ContentLayerClient { auto display_list = make_scoped_refptr(new DisplayItemList); display_list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - PaintableRegion(), recorder.finishRecordingAsPicture()); + PaintableRegion(), recorder.finishRecordingAsPicture(), + gfx::RectToSkRect(PaintableRegion())); display_list->Finalize(); return display_list; @@ -229,16 +292,20 @@ class CircleContentLayerClient : public ContentLayerClient { using LayerTreeHostMasksForBackgroundFiltersPixelTest = ParameterizedPixelResourceTest; -INSTANTIATE_TEST_CASE_P(PixelResourceTest, - LayerTreeHostMasksForBackgroundFiltersPixelTest, - ::testing::Values(SOFTWARE, - GL_GPU_RASTER_2D_DRAW, - GL_ONE_COPY_2D_STAGING_2D_DRAW, - GL_ONE_COPY_RECT_STAGING_2D_DRAW, - GL_ONE_COPY_EXTERNAL_STAGING_2D_DRAW, - GL_ZERO_COPY_2D_DRAW, - GL_ZERO_COPY_RECT_DRAW, - GL_ZERO_COPY_EXTERNAL_DRAW)); +INSTANTIATE_TEST_CASE_P( + PixelResourceTest, + LayerTreeHostMasksForBackgroundFiltersPixelTest, + ::testing::Combine( + ::testing::Values(SOFTWARE, + GL_GPU_RASTER_2D_DRAW, + GL_ONE_COPY_2D_STAGING_2D_DRAW, + GL_ONE_COPY_RECT_STAGING_2D_DRAW, + GL_ONE_COPY_EXTERNAL_STAGING_2D_DRAW, + GL_ZERO_COPY_2D_DRAW, + GL_ZERO_COPY_RECT_DRAW, + GL_ZERO_COPY_EXTERNAL_DRAW), + ::testing::Values(Layer::LayerMaskType::SINGLE_TEXTURE_MASK, + Layer::LayerMaskType::MULTI_TEXTURE_MASK))); TEST_P(LayerTreeHostMasksForBackgroundFiltersPixelTest, MaskOfLayerWithBackgroundFilter) { @@ -265,7 +332,7 @@ TEST_P(LayerTreeHostMasksForBackgroundFiltersPixelTest, scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client); mask->SetBounds(mask_bounds); mask->SetIsDrawable(true); - mask->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask->SetLayerMaskType(mask_type_); blur->SetMaskLayer(mask.get()); float percentage_pixels_large_error = 2.5f; // 2.5%, ~250px / (100*100) @@ -316,7 +383,7 @@ TEST_P(LayerTreeHostMasksForBackgroundFiltersPixelTest, scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client); mask->SetBounds(mask_bounds); mask->SetIsDrawable(true); - mask->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask->SetLayerMaskType(mask_type_); picture_horizontal->SetMaskLayer(mask.get()); float percentage_pixels_large_error = 0.04f; // 0.04%, ~6px / (128*128) diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc index 9e6c405beb3..93d29ad071b 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc @@ -88,7 +88,7 @@ TEST_F(LayerTreeHostScrollbarsPixelTest, NoScale) { auto scrollbar = base::MakeUnique<PaintedScrollbar>(); scoped_refptr<PaintedScrollbarLayer> layer = - PaintedScrollbarLayer::Create(std::move(scrollbar), Layer::INVALID_ID); + PaintedScrollbarLayer::Create(std::move(scrollbar)); layer->SetIsDrawable(true); layer->SetBounds(gfx::Size(200, 200)); background->AddChild(layer); @@ -107,7 +107,7 @@ TEST_F(LayerTreeHostScrollbarsPixelTest, DeviceScaleFactor) { auto scrollbar = base::MakeUnique<PaintedScrollbar>(); scoped_refptr<PaintedScrollbarLayer> layer = - PaintedScrollbarLayer::Create(std::move(scrollbar), Layer::INVALID_ID); + PaintedScrollbarLayer::Create(std::move(scrollbar)); layer->SetIsDrawable(true); layer->SetBounds(gfx::Size(100, 100)); background->AddChild(layer); @@ -122,7 +122,7 @@ TEST_F(LayerTreeHostScrollbarsPixelTest, TransformScale) { auto scrollbar = base::MakeUnique<PaintedScrollbar>(); scoped_refptr<PaintedScrollbarLayer> layer = - PaintedScrollbarLayer::Create(std::move(scrollbar), Layer::INVALID_ID); + PaintedScrollbarLayer::Create(std::move(scrollbar)); layer->SetIsDrawable(true); layer->SetBounds(gfx::Size(100, 100)); background->AddChild(layer); @@ -144,7 +144,7 @@ TEST_F(LayerTreeHostScrollbarsPixelTest, HugeTransformScale) { auto scrollbar = base::MakeUnique<PaintedScrollbar>(); scrollbar->set_paint_scale(1); scoped_refptr<PaintedScrollbarLayer> layer = - PaintedScrollbarLayer::Create(std::move(scrollbar), Layer::INVALID_ID); + PaintedScrollbarLayer::Create(std::move(scrollbar)); layer->SetIsDrawable(true); layer->SetBounds(gfx::Size(10, 400)); background->AddChild(layer); @@ -239,8 +239,7 @@ TEST_F(LayerTreeHostOverlayScrollbarsPixelTest, NinePatchScrollbarScaledUp) { auto scrollbar = base::MakeUnique<PaintedOverlayScrollbar>(); scoped_refptr<PaintedOverlayScrollbarLayer> layer = - PaintedOverlayScrollbarLayer::Create(std::move(scrollbar), - Layer::INVALID_ID); + PaintedOverlayScrollbarLayer::Create(std::move(scrollbar)); scrollbar_layer_id_ = layer->id(); thickness_scale_ = 5.f; @@ -264,8 +263,7 @@ TEST_F(LayerTreeHostOverlayScrollbarsPixelTest, NinePatchScrollbarScaledDown) { auto scrollbar = base::MakeUnique<PaintedOverlayScrollbar>(); scoped_refptr<PaintedOverlayScrollbarLayer> layer = - PaintedOverlayScrollbarLayer::Create(std::move(scrollbar), - Layer::INVALID_ID); + PaintedOverlayScrollbarLayer::Create(std::move(scrollbar)); scrollbar_layer_id_ = layer->id(); thickness_scale_ = 0.4f; diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc index d6145d577c8..e2fc6d64670 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc @@ -113,7 +113,7 @@ class BlueYellowClient : public ContentLayerClient { PaintRecorder recorder; PaintCanvas* canvas = - recorder.beginRecording(gfx::RectToSkRect(gfx::Rect(size_))); + recorder.beginRecording(gfx::RectToSkRect(PaintableRegion())); gfx::Rect top(0, 0, size_.width(), size_.height() / 2); gfx::Rect bottom(0, size_.height() / 2, size_.width(), size_.height() / 2); @@ -129,7 +129,8 @@ class BlueYellowClient : public ContentLayerClient { canvas->drawRect(gfx::RectToSkRect(yellow_rect), flags); display_list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - PaintableRegion(), recorder.finishRecordingAsPicture()); + PaintableRegion(), recorder.finishRecordingAsPicture(), + gfx::RectToSkRect(PaintableRegion())); display_list->Finalize(); return display_list; } @@ -157,11 +158,17 @@ class LayerTreeHostTilesTestPartialInvalidation void DidCommitAndDrawFrame() override { switch (layer_tree_host()->SourceFrameNumber()) { case 1: - // We have done one frame, so the layer's content has been rastered. - // Now we change the picture behind it to record something completely - // different, but we give a smaller invalidation rect. The layer should - // only re-raster the stuff in the rect. If it doesn't do partial raster - // it would re-raster the whole thing instead. + // We have done one frame, but the resource may not be available for + // partial raster yet. Force a second frame. + picture_layer_->SetNeedsDisplayRect(gfx::Rect(50, 50, 100, 100)); + break; + case 2: + // We have done two frames, so the layer's content has been rastered + // twice and the first frame's resource is available for partial + // raster. Now we change the picture behind it to record something + // completely different, but we give a smaller invalidation rect. The + // layer should only re-raster the stuff in the rect. If it doesn't do + // partial raster it would re-raster the whole thing instead. client_.set_blue_top(false); Finish(); picture_layer_->SetNeedsDisplayRect(gfx::Rect(50, 50, 100, 100)); @@ -203,30 +210,15 @@ TEST_F(LayerTreeHostTilesTestPartialInvalidation, base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped.png"))); } -// crbug.com/707711 -#if defined(OS_LINUX) -#define MAYBE_PartialRaster_MultiThread_OneCopy \ - DISABLED_PartialRaster_MultiThread_OneCopy -#else -#define MAYBE_PartialRaster_MultiThread_OneCopy \ - PartialRaster_MultiThread_OneCopy -#endif TEST_F(LayerTreeHostTilesTestPartialInvalidation, - MAYBE_PartialRaster_MultiThread_OneCopy) { + PartialRaster_MultiThread_OneCopy) { RunRasterPixelTest( true, PARTIAL_ONE_COPY, picture_layer_, base::FilePath(FILE_PATH_LITERAL("blue_yellow_partial_flipped.png"))); } -// crbug.com/707711 -#if defined(OS_LINUX) -#define MAYBE_FullRaster_MultiThread_OneCopy \ - DISABLED_FullRaster_MultiThread_OneCopy -#else -#define MAYBE_FullRaster_MultiThread_OneCopy FullRaster_MultiThread_OneCopy -#endif TEST_F(LayerTreeHostTilesTestPartialInvalidation, - MAYBE_FullRaster_MultiThread_OneCopy) { + FullRaster_MultiThread_OneCopy) { RunRasterPixelTest( true, FULL_ONE_COPY, picture_layer_, base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped.png"))); diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index 2e7457a49aa..9a657f521e4 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -46,6 +46,7 @@ #include "cc/test/fake_scoped_ui_resource.h" #include "cc/test/fake_video_frame_provider.h" #include "cc/test/geometry_test_utils.h" +#include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" #include "cc/test/push_properties_counting_layer.h" #include "cc/test/push_properties_counting_layer_impl.h" @@ -1043,29 +1044,29 @@ class LayerTreeHostTestSurfaceDamage : public LayerTreeHostTest { LayerImpl* child_impl = impl->active_tree()->LayerById(child_->id()); switch (impl->active_tree()->source_frame_number()) { case 0: - EXPECT_TRUE(root_impl->GetRenderSurface()->AncestorPropertyChanged()); - EXPECT_TRUE(child_impl->GetRenderSurface()->AncestorPropertyChanged()); + EXPECT_TRUE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); + EXPECT_TRUE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); PostSetNeedsCommitToMainThread(); break; case 1: - EXPECT_FALSE(root_impl->GetRenderSurface()->AncestorPropertyChanged()); - EXPECT_FALSE(child_impl->GetRenderSurface()->AncestorPropertyChanged()); + EXPECT_FALSE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); + EXPECT_FALSE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); PostSetNeedsCommitToMainThread(); break; case 2: - EXPECT_TRUE(root_impl->GetRenderSurface()->AncestorPropertyChanged()); - EXPECT_TRUE(child_impl->GetRenderSurface()->AncestorPropertyChanged()); + EXPECT_TRUE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); + EXPECT_TRUE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); PostSetNeedsCommitToMainThread(); break; case 3: - EXPECT_FALSE(root_impl->GetRenderSurface()->AncestorPropertyChanged()); - EXPECT_TRUE(child_impl->GetRenderSurface()->AncestorPropertyChanged()); + EXPECT_FALSE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); + EXPECT_TRUE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); EndTest(); PostSetNeedsCommitToMainThread(); break; case 4: - EXPECT_FALSE(root_impl->GetRenderSurface()->AncestorPropertyChanged()); - EXPECT_FALSE(child_impl->GetRenderSurface()->AncestorPropertyChanged()); + EXPECT_FALSE(GetRenderSurface(root_impl)->AncestorPropertyChanged()); + EXPECT_FALSE(GetRenderSurface(child_impl)->AncestorPropertyChanged()); EndTest(); break; } @@ -1179,6 +1180,227 @@ class LayerTreeHostTestPropertyTreesChangedSync : public LayerTreeHostTest { SINGLE_THREAD_TEST_F(LayerTreeHostTestPropertyTreesChangedSync); +// Simple base class for tests that just need to mutate the layer tree +// host and observe results without any impl thread, multi-layer, or +// multi-frame antics. +class LayerTreeHostTestLayerListsTest : public LayerTreeHostTest { + public: + explicit LayerTreeHostTestLayerListsTest(bool use_layer_lists) + : use_layer_lists_(use_layer_lists) {} + + protected: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->use_layer_lists = use_layer_lists_; + } + + void SetupTree() override { + root_ = Layer::Create(); + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + void AfterTest() override {} + + scoped_refptr<Layer> root_; + + private: + bool use_layer_lists_; +}; + +class LayerTreeHostTestAnimationOpacityMutatedNotUsingLayerLists + : public LayerTreeHostTestLayerListsTest { + public: + LayerTreeHostTestAnimationOpacityMutatedNotUsingLayerLists() + : LayerTreeHostTestLayerListsTest(false) {} + + protected: + void BeginTest() override { + EXPECT_EQ(1.0f, root_->opacity()); + layer_tree_host()->SetElementOpacityMutated(root_->element_id(), + ElementListType::ACTIVE, 0.3f); + // When not using layer lists, opacity is stored on the layer. + EXPECT_EQ(0.3f, root_->opacity()); + EndTest(); + } +}; + +SINGLE_THREAD_TEST_F( + LayerTreeHostTestAnimationOpacityMutatedNotUsingLayerLists); + +class LayerTreeHostTestAnimationOpacityMutatedUsingLayerLists + : public LayerTreeHostTestLayerListsTest { + public: + LayerTreeHostTestAnimationOpacityMutatedUsingLayerLists() + : LayerTreeHostTestLayerListsTest(true) {} + + protected: + void BeginTest() override { + // Insert a dummy effect node to observe its mutation. This would + // normally have been created by PaintArtifactCompositor. + int effect_node_id = + layer_tree_host()->property_trees()->effect_tree.Insert( + EffectNode(), EffectTree::kInvalidNodeId); + layer_tree_host() + ->property_trees() + ->element_id_to_effect_node_index[root_->element_id()] = effect_node_id; + + EXPECT_EQ(1.0f, root_->opacity()); + EXPECT_EQ(1.0f, + layer_tree_host() + ->property_trees() + ->effect_tree.FindNodeFromElementId(root_->element_id()) + ->opacity); + + layer_tree_host()->SetElementOpacityMutated(root_->element_id(), + ElementListType::ACTIVE, 0.3f); + + // When using layer lists, we don't have to store the opacity on the layer. + EXPECT_EQ(1.0f, root_->opacity()); + // The opacity should have been set directly on the effect node instead. + EXPECT_EQ(0.3f, + layer_tree_host() + ->property_trees() + ->effect_tree.FindNodeFromElementId(root_->element_id()) + ->opacity); + EndTest(); + } +}; + +SINGLE_THREAD_TEST_F(LayerTreeHostTestAnimationOpacityMutatedUsingLayerLists); + +class LayerTreeHostTestAnimationTransformMutatedNotUsingLayerLists + : public LayerTreeHostTestLayerListsTest { + public: + LayerTreeHostTestAnimationTransformMutatedNotUsingLayerLists() + : LayerTreeHostTestLayerListsTest(false) {} + + protected: + void BeginTest() override { + EXPECT_EQ(gfx::Transform(), root_->transform()); + gfx::Transform expected_transform; + expected_transform.Translate(42, 42); + layer_tree_host()->SetElementTransformMutated( + root_->element_id(), ElementListType::ACTIVE, expected_transform); + // When not using layer lists, transform is stored on the layer. + EXPECT_EQ(expected_transform, root_->transform()); + EndTest(); + } +}; + +SINGLE_THREAD_TEST_F( + LayerTreeHostTestAnimationTransformMutatedNotUsingLayerLists); + +class LayerTreeHostTestAnimationTransformMutatedUsingLayerLists + : public LayerTreeHostTestLayerListsTest { + public: + LayerTreeHostTestAnimationTransformMutatedUsingLayerLists() + : LayerTreeHostTestLayerListsTest(true) {} + + protected: + void BeginTest() override { + // Insert a dummy transform node to observe its mutation. This would + // normally have been created by PaintArtifactCompositor. + int transform_node_id = + layer_tree_host()->property_trees()->transform_tree.Insert( + TransformNode(), TransformTree::kInvalidNodeId); + layer_tree_host() + ->property_trees() + ->element_id_to_transform_node_index[root_->element_id()] = + transform_node_id; + + EXPECT_EQ(gfx::Transform(), root_->transform()); + EXPECT_EQ(gfx::Transform(), + layer_tree_host() + ->property_trees() + ->transform_tree.FindNodeFromElementId(root_->element_id()) + ->local); + + gfx::Transform expected_transform; + expected_transform.Translate(42, 42); + layer_tree_host()->SetElementTransformMutated( + root_->element_id(), ElementListType::ACTIVE, expected_transform); + + // When using layer lists, we don't have to store the transform on the + // layer. + EXPECT_EQ(gfx::Transform(), root_->transform()); + // The transform should have been set directly on the transform node + // instead. + EXPECT_EQ(expected_transform, + layer_tree_host() + ->property_trees() + ->transform_tree.FindNodeFromElementId(root_->element_id()) + ->local); + EndTest(); + } +}; + +SINGLE_THREAD_TEST_F(LayerTreeHostTestAnimationTransformMutatedUsingLayerLists); + +class LayerTreeHostTestAnimationFilterMutatedNotUsingLayerLists + : public LayerTreeHostTestLayerListsTest { + public: + LayerTreeHostTestAnimationFilterMutatedNotUsingLayerLists() + : LayerTreeHostTestLayerListsTest(false) {} + + protected: + void BeginTest() override { + FilterOperations filters; + EXPECT_EQ(FilterOperations(), root_->filters()); + filters.Append(FilterOperation::CreateOpacityFilter(0.5f)); + layer_tree_host()->SetElementFilterMutated( + root_->element_id(), ElementListType::ACTIVE, filters); + // When not using layer lists, filters are just stored directly on the + // layer. + EXPECT_EQ(filters, root_->filters()); + EndTest(); + } +}; + +SINGLE_THREAD_TEST_F(LayerTreeHostTestAnimationFilterMutatedNotUsingLayerLists); + +class LayerTreeHostTestAnimationFilterMutatedUsingLayerLists + : public LayerTreeHostTestLayerListsTest { + public: + LayerTreeHostTestAnimationFilterMutatedUsingLayerLists() + : LayerTreeHostTestLayerListsTest(true) {} + + protected: + void BeginTest() override { + // Insert a dummy effect node to observe its mutation. This would + // normally have been created by PaintArtifactCompositor. + int effect_node_id = + layer_tree_host()->property_trees()->effect_tree.Insert( + EffectNode(), EffectTree::kInvalidNodeId); + layer_tree_host() + ->property_trees() + ->element_id_to_effect_node_index[root_->element_id()] = effect_node_id; + + EXPECT_EQ(FilterOperations(), root_->filters()); + EXPECT_EQ(FilterOperations(), + layer_tree_host() + ->property_trees() + ->effect_tree.FindNodeFromElementId(root_->element_id()) + ->filters); + + FilterOperations filters; + filters.Append(FilterOperation::CreateOpacityFilter(0.5f)); + layer_tree_host()->SetElementFilterMutated( + root_->element_id(), ElementListType::ACTIVE, filters); + + // When using layer lists, we don't have to store the filters on the layer. + EXPECT_EQ(FilterOperations(), root_->filters()); + // The filter should have been set directly on the effect node instead. + EXPECT_EQ(filters, + layer_tree_host() + ->property_trees() + ->effect_tree.FindNodeFromElementId(root_->element_id()) + ->filters); + EndTest(); + } +}; + +SINGLE_THREAD_TEST_F(LayerTreeHostTestAnimationFilterMutatedUsingLayerLists); + class LayerTreeHostTestEffectTreeSync : public LayerTreeHostTest { protected: void SetupTree() override { @@ -1376,8 +1598,8 @@ class LayerTreeHostTestTransformTreeDamageIsUpdated : public LayerTreeHostTest { root_->SetBounds(gfx::Size(50, 50)); - // Make sure child is registerd for animation. - child_->SetElementId(ElementId(2, 0)); + // Make sure child is registered for animation. + child_->SetElementId(ElementId(2)); // Make sure child and grand_child have transform nodes. gfx::Transform rotation; @@ -1442,6 +1664,21 @@ class LayerTreeHostTestTransformTreeDamageIsUpdated : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestTransformTreeDamageIsUpdated); +class UpdateCountingLayer : public Layer { + public: + bool Update() override { + update_count_++; + return false; + } + + int update_count() const { return update_count_; } + + private: + ~UpdateCountingLayer() override = default; + + int update_count_ = 0; +}; + // Test that when mask layers switches layers, this gets pushed onto impl. // Also test that mask layer is in the layer update list even if its owning // layer isn't. @@ -1450,11 +1687,11 @@ class LayerTreeHostTestSwitchMaskLayer : public LayerTreeHostTest { void SetupTree() override { scoped_refptr<Layer> root = Layer::Create(); root->SetBounds(gfx::Size(10, 10)); - scoped_refptr<Layer> child = Layer::Create(); - mask_layer = Layer::Create(); - mask_layer->SetBounds(gfx::Size(10, 10)); - child->SetMaskLayer(mask_layer.get()); - root->AddChild(std::move(child)); + child_layer_ = make_scoped_refptr(new UpdateCountingLayer); + mask_layer_ = make_scoped_refptr(new UpdateCountingLayer); + mask_layer_->SetBounds(gfx::Size(10, 10)); + child_layer_->SetMaskLayer(mask_layer_.get()); + root->AddChild(child_layer_); layer_tree_host()->SetRootLayer(root); LayerTreeHostTest::SetupTree(); } @@ -1470,19 +1707,11 @@ class LayerTreeHostTestSwitchMaskLayer : public LayerTreeHostTest { // Root and mask layer should have the same source frame number as they // will be in the layer update list but the child is not as it has empty // bounds. - EXPECT_EQ(mask_layer->paint_properties().source_frame_number, - layer_tree_host() - ->root_layer() - ->paint_properties() - .source_frame_number); - EXPECT_NE(mask_layer->paint_properties().source_frame_number, - layer_tree_host() - ->root_layer() - ->child_at(0) - ->paint_properties() - .source_frame_number); + EXPECT_EQ(mask_layer_->update_count(), 1); + EXPECT_EQ(child_layer_->update_count(), 0); + layer_tree_host()->root_layer()->RemoveAllChildren(); - layer_tree_host()->root_layer()->SetMaskLayer(mask_layer.get()); + layer_tree_host()->root_layer()->SetMaskLayer(mask_layer_.get()); break; } } @@ -1491,16 +1720,14 @@ class LayerTreeHostTestSwitchMaskLayer : public LayerTreeHostTest { switch (index_) { case 0: index_++; - EXPECT_FALSE(impl->sync_tree() - ->root_layer_for_testing() - ->GetRenderSurface() - ->MaskLayer()); + EXPECT_FALSE( + GetRenderSurface(impl->sync_tree()->root_layer_for_testing()) + ->MaskLayer()); break; case 1: - EXPECT_TRUE(impl->sync_tree() - ->root_layer_for_testing() - ->GetRenderSurface() - ->MaskLayer()); + EXPECT_TRUE( + GetRenderSurface(impl->sync_tree()->root_layer_for_testing()) + ->MaskLayer()); EndTest(); break; } @@ -1508,7 +1735,8 @@ class LayerTreeHostTestSwitchMaskLayer : public LayerTreeHostTest { void AfterTest() override {} - scoped_refptr<Layer> mask_layer; + scoped_refptr<UpdateCountingLayer> mask_layer_; + scoped_refptr<UpdateCountingLayer> child_layer_; int index_; }; @@ -1750,7 +1978,7 @@ class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate bool paint_scrollbar = true; bool has_thumb = false; scrollbar_ = FakePaintedScrollbarLayer::Create(paint_scrollbar, has_thumb, - root_layer_->id()); + root_layer_->element_id()); scrollbar_->SetPosition(gfx::PointF(0.f, 10.f)); scrollbar_->SetBounds(gfx::Size(10, 10)); @@ -2298,7 +2526,7 @@ class LayerTreeHostTestCommit : public LayerTreeHostTest { } void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { - EXPECT_EQ(gfx::Size(20, 20), impl->DrawViewportSize()); + EXPECT_EQ(gfx::Rect(20, 20), impl->DeviceViewport()); EXPECT_EQ(SK_ColorGRAY, impl->active_tree()->background_color()); EXPECT_EQ(EventListenerProperties::kPassive, impl->active_tree()->event_listener_properties( @@ -2473,15 +2701,16 @@ class LayerTreeHostTestStartPageScaleAnimation : public LayerTreeHostTest { break; case 1: EXPECT_EQ(1.f, impl->active_tree()->current_page_scale_factor()); + // Once the animation starts, an ImplFrame will be requested. However, + // main frames may be happening in the mean-time due to high-latency + // mode. If one happens before the next impl frame, then the source + // frame number may increment twice instead of just once. break; case 2: + case 3: EXPECT_EQ(1.25f, impl->active_tree()->current_page_scale_factor()); EndTest(); break; - case 3: - break; - default: - NOTREACHED(); } } @@ -2570,7 +2799,7 @@ class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers EXPECT_NEAR(impl->active_tree()->device_scale_factor(), 1.5f, 0.00001f); // Device viewport is scaled. - EXPECT_EQ(gfx::Size(60, 60), impl->DrawViewportSize()); + EXPECT_EQ(gfx::Rect(60, 60), impl->DeviceViewport()); FakePictureLayerImpl* root = static_cast<FakePictureLayerImpl*>( impl->active_tree()->root_layer_for_testing()); @@ -2586,18 +2815,16 @@ class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers impl->PrepareToDraw(&frame_data); impl->DidDrawAllLayers(frame_data); - const LayerImplList& render_surface_layer_list = - *frame_data.render_surface_layer_list; + const RenderSurfaceList& render_surface_list = + *frame_data.render_surface_list; // Both layers should be drawing into the root render surface. - ASSERT_EQ(1u, render_surface_layer_list.size()); - ASSERT_EQ(root->GetRenderSurface(), - render_surface_layer_list[0]->GetRenderSurface()); - ASSERT_EQ(2u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, render_surface_list.size()); + ASSERT_EQ(GetRenderSurface(root), render_surface_list[0]); + ASSERT_EQ(2, GetRenderSurface(root)->num_contributors()); // The root render surface is the size of the viewport. - EXPECT_EQ(gfx::Rect(0, 0, 60, 60), - root->GetRenderSurface()->content_rect()); + EXPECT_EQ(gfx::Rect(0, 0, 60, 60), GetRenderSurface(root)->content_rect()); // The max tiling scale of the child should be scaled. EXPECT_FLOAT_EQ(1.5f, child->MaximumTilingContentsScale()); @@ -3085,7 +3312,6 @@ class OnDrawCompositorFrameSink : public TestCompositorFrameSink { const RendererSettings& renderer_settings, base::SingleThreadTaskRunner* task_runner, bool synchronous_composite, - bool force_disable_reclaim_resources, base::Closure invalidate_callback) : TestCompositorFrameSink(std::move(compositor_context_provider), std::move(worker_context_provider), @@ -3094,7 +3320,7 @@ class OnDrawCompositorFrameSink : public TestCompositorFrameSink { renderer_settings, task_runner, synchronous_composite, - force_disable_reclaim_resources), + false /* disable_display_vsync */), invalidate_callback_(std::move(invalidate_callback)) {} // TestCompositorFrameSink overrides. @@ -3130,7 +3356,6 @@ class LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor shared_bitmap_manager(), gpu_memory_buffer_manager(), layer_tree_host()->GetSettings().renderer_settings, ImplThreadTaskRunner(), false /* synchronous_composite */, - false /* force_disable_reclaim_resources */, std::move(on_draw_callback)); compositor_frame_sink_ = frame_sink.get(); return std::move(frame_sink); @@ -3737,7 +3962,7 @@ class LayerTreeHostTestPropertyChangesDuringUpdateArePushed bool paint_scrollbar = true; bool has_thumb = false; scrollbar_layer_ = FakePaintedScrollbarLayer::Create( - paint_scrollbar, has_thumb, root_->id()); + paint_scrollbar, has_thumb, root_->element_id()); root_->AddChild(scrollbar_layer_); @@ -4496,9 +4721,12 @@ class LayerTreeHostTestElasticOverscroll : public LayerTreeHostTest { inner_viewport_scroll_layer->AddChild(content_layer); layer_tree_host()->SetRootLayer(root_layer_); - layer_tree_host()->RegisterViewportLayers( - overscroll_elasticity_layer, page_scale_layer, - inner_viewport_scroll_layer, nullptr); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.overscroll_elasticity = overscroll_elasticity_layer; + viewport_layers.page_scale = page_scale_layer; + viewport_layers.inner_viewport_container = inner_viewport_container_layer; + viewport_layers.inner_viewport_scroll = inner_viewport_scroll_layer; + layer_tree_host()->RegisterViewportLayers(viewport_layers); LayerTreeHostTest::SetupTree(); client_.set_bounds(content_layer->bounds()); } @@ -5300,15 +5528,15 @@ class LayerTreeHostTestGpuRasterizationEnabled // Ensure the suitability bit sticks. EXPECT_FALSE(layer_->IsSuitableForGpuRasterization()); - EXPECT_FALSE(host_impl->pending_tree()->use_gpu_rasterization()); - EXPECT_FALSE(host_impl->use_gpu_rasterization()); + EXPECT_TRUE(host_impl->pending_tree()->use_gpu_rasterization()); + EXPECT_TRUE(host_impl->use_gpu_rasterization()); } void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { EXPECT_FALSE(layer_->IsSuitableForGpuRasterization()); - EXPECT_FALSE(host_impl->active_tree()->use_gpu_rasterization()); - EXPECT_FALSE(host_impl->use_gpu_rasterization()); + EXPECT_TRUE(host_impl->active_tree()->use_gpu_rasterization()); + EXPECT_TRUE(host_impl->use_gpu_rasterization()); EndTest(); } @@ -5324,6 +5552,23 @@ MULTI_THREAD_TEST_F(LayerTreeHostTestGpuRasterizationEnabled); class LayerTreeHostTestGpuRasterizationReenabled : public LayerTreeHostWithGpuRasterizationTest { protected: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->gpu_rasterization_msaa_sample_count = 4; + } + + std::unique_ptr<TestCompositorFrameSink> CreateCompositorFrameSink( + scoped_refptr<ContextProvider> compositor_context_provider, + scoped_refptr<ContextProvider> worker_context_provider) override { + std::unique_ptr<TestWebGraphicsContext3D> context = + TestWebGraphicsContext3D::Create(); + context->SetMaxSamples(4); + context->set_gpu_rasterization(true); + compositor_context_provider = + TestContextProvider::Create(std::move(context)); + return LayerTreeTest::CreateCompositorFrameSink(compositor_context_provider, + worker_context_provider); + } + void SetupTree() override { LayerTreeHostTest::SetupTree(); @@ -5362,10 +5607,10 @@ class LayerTreeHostTestGpuRasterizationReenabled void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { SCOPED_TRACE(base::StringPrintf("commit %d", num_commits_)); - if (expected_gpu_enabled_) { - EXPECT_TRUE(host_impl->use_gpu_rasterization()); + if (expected_use_msaa_) { + EXPECT_TRUE(host_impl->use_msaa()); } else { - EXPECT_FALSE(host_impl->use_gpu_rasterization()); + EXPECT_FALSE(host_impl->use_msaa()); } ++num_commits_; @@ -5380,7 +5625,7 @@ class LayerTreeHostTestGpuRasterizationReenabled layer_->set_force_unsuitable_for_gpu_rasterization(false); break; case 90: - expected_gpu_enabled_ = true; + expected_use_msaa_ = false; break; } PostSetNeedsCommitToMainThread(); @@ -5394,7 +5639,7 @@ class LayerTreeHostTestGpuRasterizationReenabled FakePictureLayer* layer_; FakeRecordingSource* recording_source_; int num_commits_ = 0; - bool expected_gpu_enabled_ = false; + bool expected_use_msaa_ = true; }; MULTI_THREAD_TEST_F(LayerTreeHostTestGpuRasterizationReenabled); @@ -5666,7 +5911,7 @@ class LayerTreeHostTestRenderSurfaceEffectTreeIndex : public LayerTreeHostTest { LayerImpl* grand_child_impl = host_impl->sync_tree()->LayerById(grand_child_->id()); EXPECT_EQ(grand_child_impl->effect_tree_index(), - grand_child_impl->GetRenderSurface()->EffectTreeIndex()); + GetRenderSurface(grand_child_impl)->EffectTreeIndex()); } } @@ -5680,12 +5925,12 @@ class LayerTreeHostTestRenderSurfaceEffectTreeIndex : public LayerTreeHostTest { case 1: case 2: EXPECT_EQ(grand_child_impl->effect_tree_index(), - grand_child_impl->GetRenderSurface()->EffectTreeIndex()); + GetRenderSurface(grand_child_impl)->EffectTreeIndex()); PostSetNeedsCommitToMainThread(); break; case 3: EXPECT_EQ(grand_child_impl->effect_tree_index(), - grand_child_impl->GetRenderSurface()->EffectTreeIndex()); + GetRenderSurface(grand_child_impl)->EffectTreeIndex()); EndTest(); } } @@ -5727,17 +5972,15 @@ class LayerTreeHostTestSynchronousCompositeSwapPromise std::unique_ptr<TestCompositorFrameSink> CreateCompositorFrameSink( scoped_refptr<ContextProvider> compositor_context_provider, scoped_refptr<ContextProvider> worker_context_provider) override { + constexpr bool disable_display_vsync = false; bool synchronous_composite = !HasImplThread() && !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; - // Relaiming resources is parameterized for this test. - bool force_disable_reclaim_resources = !reclaim_resources_; return base::MakeUnique<TestCompositorFrameSink>( compositor_context_provider, std::move(worker_context_provider), shared_bitmap_manager(), gpu_memory_buffer_manager(), layer_tree_host()->GetSettings().renderer_settings, - ImplThreadTaskRunner(), synchronous_composite, - force_disable_reclaim_resources); + ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync); } void BeginTest() override { @@ -5784,19 +6027,13 @@ class LayerTreeHostTestSynchronousCompositeSwapPromise EXPECT_TRUE(swap_promise_result_[0].dtor_called); } - // Second swap promise fails to swap if not reclaiming resources from the - // Display. + // Second swap promise fails to swap. { base::AutoLock lock(swap_promise_result_[1].lock); EXPECT_TRUE(swap_promise_result_[1].did_activate_called); - if (!reclaim_resources_) { - EXPECT_FALSE(swap_promise_result_[1].did_swap_called); - EXPECT_TRUE(swap_promise_result_[1].did_not_swap_called); - EXPECT_EQ(SwapPromise::SWAP_FAILS, swap_promise_result_[1].reason); - } else { - EXPECT_TRUE(swap_promise_result_[1].did_swap_called); - EXPECT_FALSE(swap_promise_result_[1].did_not_swap_called); - } + EXPECT_FALSE(swap_promise_result_[1].did_swap_called); + EXPECT_TRUE(swap_promise_result_[1].did_not_swap_called); + EXPECT_EQ(SwapPromise::SWAP_FAILS, swap_promise_result_[1].reason); EXPECT_TRUE(swap_promise_result_[1].dtor_called); } @@ -5811,20 +6048,12 @@ class LayerTreeHostTestSynchronousCompositeSwapPromise } } - bool reclaim_resources_; int commit_count_ = 0; TestSwapPromiseResult swap_promise_result_[3]; }; -TEST_F(LayerTreeHostTestSynchronousCompositeSwapPromise, NoReclaim) { - reclaim_resources_ = false; - RunTest(CompositorMode::SINGLE_THREADED); -} - -TEST_F(LayerTreeHostTestSynchronousCompositeSwapPromise, Reclaim) { - reclaim_resources_ = true; - RunTest(CompositorMode::SINGLE_THREADED); -} +// Synchronous composite is a single-threaded only feature. +SINGLE_THREAD_TEST_F(LayerTreeHostTestSynchronousCompositeSwapPromise); // Make sure page scale and top control deltas are applied to the client even // when the LayerTreeHost doesn't have a root layer. @@ -5901,8 +6130,11 @@ class LayerTreeHostTestCrispUpAfterPinchEnds : public LayerTreeHostTest { // pinch. pinch->AddChild(layer); - layer_tree_host()->RegisterViewportLayers(NULL, page_scale_layer, pinch, - nullptr); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = page_scale_layer; + viewport_layers.inner_viewport_container = root_clip; + viewport_layers.inner_viewport_scroll = pinch; + layer_tree_host()->RegisterViewportLayers(viewport_layers); layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 1.f, 4.f); layer_tree_host()->SetRootLayer(root_clip); LayerTreeHostTest::SetupTree(); @@ -6205,8 +6437,11 @@ class LayerTreeHostTestContinuousDrawWhenCreatingVisibleTiles // pinch. pinch->AddChild(layer); - layer_tree_host()->RegisterViewportLayers(NULL, page_scale_layer, pinch, - nullptr); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = page_scale_layer; + viewport_layers.inner_viewport_container = root_clip; + viewport_layers.inner_viewport_scroll = pinch; + layer_tree_host()->RegisterViewportLayers(viewport_layers); layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 1.f, 4.f); layer_tree_host()->SetRootLayer(root_clip); LayerTreeHostTest::SetupTree(); @@ -6508,7 +6743,7 @@ class LayerTreeHostTestUpdateCopyRequests : public LayerTreeHostTest { void WillCommit() override { switch (layer_tree_host()->SourceFrameNumber()) { case 1: - EXPECT_GT(root->num_copy_requests_in_target_subtree(), 0); + EXPECT_TRUE(root->has_copy_requests_in_target_subtree()); break; } } @@ -6531,7 +6766,7 @@ class LayerTreeHostTestUpdateCopyRequests : public LayerTreeHostTest { child->SetTransform(transform); break; case 3: - EXPECT_EQ(root->num_copy_requests_in_target_subtree(), 0); + EXPECT_FALSE(root->has_copy_requests_in_target_subtree()); EndTest(); break; } @@ -6641,8 +6876,27 @@ class LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin FakeContentLayerClient client_; }; +class LayerTreeTestSingleTextureMaskLayerForSurfaceWithContentRectNotAtOrigin + : public LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_mask_tiling = false; + } +}; + SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin); + LayerTreeTestSingleTextureMaskLayerForSurfaceWithContentRectNotAtOrigin); + +class LayerTreeTestMultiTextureMaskLayerForSurfaceWithContentRectNotAtOrigin + : public LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_mask_tiling = true; + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeTestMultiTextureMaskLayerForSurfaceWithContentRectNotAtOrigin); class LayerTreeTestMaskLayerForSurfaceWithClippedLayer : public LayerTreeTest { protected: @@ -6754,15 +7008,30 @@ class LayerTreeTestMaskLayerForSurfaceWithClippedLayer : public LayerTreeTest { FakeContentLayerClient client_; }; +class LayerTreeTestSingleTextureMaskLayerForSurfaceWithClippedLayer + : public LayerTreeTestMaskLayerForSurfaceWithClippedLayer { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_mask_tiling = false; + } +}; + SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskLayerForSurfaceWithClippedLayer); + LayerTreeTestSingleTextureMaskLayerForSurfaceWithClippedLayer); -class LayerTreeTestMaskLayerWithScaling : public LayerTreeTest { - protected: +class LayerTreeTestMultiTextureMaskLayerForSurfaceWithClippedLayer + : public LayerTreeTestMaskLayerForSurfaceWithClippedLayer { + public: void InitializeSettings(LayerTreeSettings* settings) override { - settings->layer_transforms_should_scale_layer_contents = true; + settings->enable_mask_tiling = true; } +}; +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeTestMultiTextureMaskLayerForSurfaceWithClippedLayer); + +class LayerTreeTestMaskLayerWithScaling : public LayerTreeTest { + protected: void SetupTree() override { // Root // | @@ -6878,7 +7147,27 @@ class LayerTreeTestMaskLayerWithScaling : public LayerTreeTest { FakeContentLayerClient client_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMaskLayerWithScaling); +class LayerTreeTestSingleTextureMaskLayerWithScaling + : public LayerTreeTestMaskLayerWithScaling { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_mask_tiling = false; + settings->layer_transforms_should_scale_layer_contents = true; + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestSingleTextureMaskLayerWithScaling); + +class LayerTreeTestMultiTextureMaskLayerWithScaling + : public LayerTreeTestMaskLayerWithScaling { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_mask_tiling = true; + settings->layer_transforms_should_scale_layer_contents = true; + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMultiTextureMaskLayerWithScaling); class LayerTreeTestMaskLayerWithDifferentBounds : public LayerTreeTest { protected: @@ -6983,7 +7272,103 @@ class LayerTreeTestMaskLayerWithDifferentBounds : public LayerTreeTest { FakeContentLayerClient client_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMaskLayerWithDifferentBounds); +class LayerTreeTestSingleTextureMaskLayerWithDifferentBounds + : public LayerTreeTestMaskLayerWithDifferentBounds { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_mask_tiling = false; + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeTestSingleTextureMaskLayerWithDifferentBounds); + +class LayerTreeTestMultiTextureMaskLayerWithDifferentBounds + : public LayerTreeTestMaskLayerWithDifferentBounds { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_mask_tiling = true; + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeTestMultiTextureMaskLayerWithDifferentBounds); + +class LayerTreeTestMaskWithNonExactTextureSize : public LayerTreeTest { + protected: + void SetupTree() override { + // The masked layer has bounds 100x100, but is allocated a 120x150 texture. + + scoped_refptr<Layer> root = Layer::Create(); + + scoped_refptr<FakePictureLayer> content_layer = + FakePictureLayer::Create(&client_); + root->AddChild(content_layer); + + std::unique_ptr<RecordingSource> recording_source = + FakeRecordingSource::CreateFilledRecordingSource(gfx::Size(100, 100)); + PaintFlags paint1, paint2; + static_cast<FakeRecordingSource*>(recording_source.get()) + ->add_draw_rect_with_flags(gfx::Rect(0, 0, 100, 90), paint1); + static_cast<FakeRecordingSource*>(recording_source.get()) + ->add_draw_rect_with_flags(gfx::Rect(0, 90, 100, 10), paint2); + client_.set_fill_with_nonsolid_color(true); + static_cast<FakeRecordingSource*>(recording_source.get())->Rerecord(); + + scoped_refptr<FakePictureLayer> mask_layer = + FakePictureLayer::CreateWithRecordingSource( + &client_, std::move(recording_source)); + content_layer->SetMaskLayer(mask_layer.get()); + + gfx::Size root_size(100, 100); + root->SetBounds(root_size); + + gfx::Size layer_size(100, 100); + content_layer->SetBounds(layer_size); + + gfx::Size mask_size(100, 100); + gfx::Size mask_texture_size(120, 150); + mask_layer->SetBounds(mask_size); + mask_layer->SetLayerMaskType(Layer::LayerMaskType::SINGLE_TEXTURE_MASK); + mask_layer->set_fixed_tile_size(mask_texture_size); + + layer_tree_host()->SetRootLayer(root); + LayerTreeTest::SetupTree(); + client_.set_bounds(root->bounds()); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + DrawResult draw_result) override { + EXPECT_EQ(2u, frame_data->render_passes.size()); + RenderPass* root_pass = frame_data->render_passes.back().get(); + EXPECT_EQ(2u, root_pass->quad_list.size()); + + // There's a solid color quad under everything. + EXPECT_EQ(DrawQuad::SOLID_COLOR, root_pass->quad_list.back()->material); + + // The surface is 100x100 + EXPECT_EQ(DrawQuad::RENDER_PASS, root_pass->quad_list.front()->material); + const RenderPassDrawQuad* render_pass_quad = + RenderPassDrawQuad::MaterialCast(root_pass->quad_list.front()); + EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), + render_pass_quad->rect.ToString()); + // The mask layer is 100x100, but is backed by a 120x150 image. + EXPECT_EQ(gfx::RectF(0.0f, 0.0f, 100.f / 120.0f, 100.f / 150.0f).ToString(), + render_pass_quad->mask_uv_rect.ToString()); + EndTest(); + return draw_result; + } + + void AfterTest() override {} + + int mask_layer_id_; + FakeContentLayerClient client_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMaskWithNonExactTextureSize); class LayerTreeTestPageScaleFlags : public LayerTreeTest { protected: @@ -7015,12 +7400,9 @@ class LayerTreeTestPageScaleFlags : public LayerTreeTest { layer_tree_host()->SetRootLayer(root); LayerTreeTest::SetupTree(); - scoped_refptr<Layer> overscroll_elasticity_layer = nullptr; - scoped_refptr<Layer> inner_viewport_scroll_layer = nullptr; - scoped_refptr<Layer> outer_viewport_scroll_layer = nullptr; - layer_tree_host()->RegisterViewportLayers( - overscroll_elasticity_layer, page_scale, inner_viewport_scroll_layer, - outer_viewport_scroll_layer); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = page_scale; + layer_tree_host()->RegisterViewportLayers(viewport_layers); affected_by_page_scale_.push_back(page_scale->id()); affected_by_page_scale_.push_back(page_scale_child1->id()); @@ -7104,6 +7486,10 @@ SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPaintedDeviceScaleFactor); // Makes sure that LocalSurfaceId is propagated to the CompositorFrameSink. class LayerTreeHostTestLocalSurfaceId : public LayerTreeHostTest { protected: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_surface_synchronization = true; + } + void BeginTest() override { expected_local_surface_id_ = allocator_.GenerateId(); PostSetLocalSurfaceIdToMainThread(expected_local_surface_id_); @@ -7162,7 +7548,6 @@ class GpuRasterizationSucceedsWithLargeImage : public LayerTreeHostTest { recording->add_draw_image(CreateDiscardableImage(large_image_size_), gfx::Point(0, 0)); - recording->SetGenerateDiscardableImagesMetadata(true); recording->Rerecord(); scoped_refptr<FakePictureLayer> root = @@ -7502,5 +7887,86 @@ class LayerTreeHostTestHudLayerWithLayerLists : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestHudLayerWithLayerLists); +// Verifies that LayerTreeHostClient does not receive frame acks from a released +// CompositorFrameSink. +class LayerTreeHostTestDiscardAckAfterRelease : public LayerTreeHostTest { + protected: + void SetupTree() override { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(10, 10)); + layer_tree_host()->SetRootLayer(std::move(root)); + LayerTreeHostTest::SetupTree(); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void WillReceiveCompositorFrameAckOnThread( + LayerTreeHostImpl* host_impl) override { + // This method is called before ack is posted to main thread. This ensures + // that WillReceiveCompositorFrameAck which we PostTask below will be called + // before DidReceiveCompositorFrameAck. + MainThreadTaskRunner()->PostTask( + FROM_HERE, base::Bind(&LayerTreeHostTestDiscardAckAfterRelease:: + WillReceiveCompositorFrameAck, + base::Unretained(this))); + } + + void WillReceiveCompositorFrameAck() { + switch (layer_tree_host()->SourceFrameNumber()) { + case 1: + // For the first commit, don't release the CompositorFrameSink. We must + // receive the ack later on. + break; + case 2: + // Release the CompositorFrameSink for the second commit. We'll later + // check that the ack is discarded. + layer_tree_host()->SetVisible(false); + layer_tree_host()->ReleaseCompositorFrameSink(); + break; + default: + NOTREACHED(); + } + } + + void DidReceiveCompositorFrameAckOnThread( + LayerTreeHostImpl* host_impl) override { + // Since this method is called after ack is posted to main thread, we can be + // sure that if the ack is not discarded, it will be definitely received + // before we are in CheckFrameAck. + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&LayerTreeHostTestDiscardAckAfterRelease::CheckFrameAck, + base::Unretained(this))); + } + + void DidReceiveCompositorFrameAck() override { received_ack_ = true; } + + void CheckFrameAck() { + switch (layer_tree_host()->SourceFrameNumber()) { + case 1: + // CompositorFrameSink was not released. We must receive the ack. + EXPECT_TRUE(received_ack_); + // Cause damage so that we draw and swap. + layer_tree_host()->root_layer()->SetBackgroundColor(SK_ColorGREEN); + break; + case 2: + // CompositorFrameSink was released. The ack must be discarded. + EXPECT_FALSE(received_ack_); + EndTest(); + break; + default: + NOTREACHED(); + } + received_ack_ = false; + } + + void AfterTest() override {} + + private: + bool received_ack_ = false; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDiscardAckAfterRelease); + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index f25dc93e40f..b9fa010f632 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -5,6 +5,7 @@ #include "cc/trees/layer_tree_host.h" #include <stdint.h> +#include <climits> #include "cc/animation/animation_curve.h" #include "cc/animation/animation_host.h" @@ -489,7 +490,7 @@ class LayerTreeHostAnimationTestLayerAddedWithAnimation AttachPlayersToTimeline(); scoped_refptr<Layer> layer = Layer::Create(); - layer->SetElementId(ElementId(42, 0)); + layer->SetElementId(ElementId(42)); player_->AttachElement(layer->element_id()); player_->set_animation_delegate(this); @@ -810,7 +811,7 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationTakeover if (host_impl->sync_tree()->source_frame_number() == 0) { GetImplAnimationHost(host_impl)->ImplOnlyScrollAnimationCreate( scroll_layer_->element_id(), gfx::ScrollOffset(650.f, 750.f), - gfx::ScrollOffset(10, 20), base::TimeDelta()); + gfx::ScrollOffset(10, 20), base::TimeDelta(), base::TimeDelta()); } } @@ -905,7 +906,7 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted if (host_impl->sync_tree()->source_frame_number() == 0) { GetImplAnimationHost(host_impl)->ImplOnlyScrollAnimationCreate( scroll_layer_->element_id(), gfx::ScrollOffset(650.f, 750.f), - gfx::ScrollOffset(10, 20), base::TimeDelta()); + gfx::ScrollOffset(10, 20), base::TimeDelta(), base::TimeDelta()); } } @@ -1403,7 +1404,11 @@ class LayerTreeHostAnimationTestRemoveAnimation player_child_->AttachElement(layer_->element_id()); } - void BeginTest() override { PostSetNeedsCommitToMainThread(); } + void BeginTest() override { + animation_stopped_ = false; + last_frame_number_ = INT_MAX; + PostSetNeedsCommitToMainThread(); + } void DidCommit() override { switch (layer_tree_host()->SourceFrameNumber()) { @@ -1433,6 +1438,7 @@ class LayerTreeHostAnimationTestRemoveAnimation } void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + GetImplTimelineAndPlayerByID(*host_impl); LayerImpl* child = host_impl->active_tree()->LayerById(layer_->id()); switch (host_impl->active_tree()->source_frame_number()) { case 0: @@ -1443,18 +1449,37 @@ class LayerTreeHostAnimationTestRemoveAnimation EXPECT_TRUE(child->screen_space_transform_is_animating()); break; case 2: { - // The animation is removed, the transform that was set afterward is + // The animation is stopped, the transform that was set afterward is // applied. gfx::Transform expected_transform; expected_transform.Translate(10.f, 10.f); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, child->DrawTransform()); EXPECT_FALSE(child->screen_space_transform_is_animating()); - EndTest(); + animation_stopped_ = true; + PostSetNeedsCommitToMainThread(); break; } - default: - NOTREACHED(); + } + } + + void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { + if (host_impl->sync_tree()->source_frame_number() >= last_frame_number_) { + // Check that eventually the animation is removed. + EXPECT_FALSE(player_child_impl_->has_any_animation()); + EndTest(); + } + } + + void UpdateAnimationState(LayerTreeHostImpl* host_impl, + bool has_unfinished_animation) override { + // Non impl only animations are removed during commit. After the animation + // is fully stopped on compositor thread, make sure another commit happens. + if (animation_stopped_ && !has_unfinished_animation) { + last_frame_number_ = + std::min(last_frame_number_, + host_impl->active_tree()->source_frame_number() + 1); + PostSetNeedsCommitToMainThread(); } } @@ -1463,6 +1488,9 @@ class LayerTreeHostAnimationTestRemoveAnimation private: scoped_refptr<Layer> layer_; FakeContentLayerClient client_; + + int last_frame_number_; + bool animation_stopped_; }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestRemoveAnimation); @@ -1620,6 +1648,84 @@ class LayerTreeHostAnimationTestAnimationFinishesDuringCommit // compositor thread. MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestAnimationFinishesDuringCommit); +class LayerTreeHostAnimationTestImplSideInvalidation + : public LayerTreeHostAnimationTest { + public: + void SetupTree() override { + LayerTreeHostAnimationTest::SetupTree(); + layer_ = FakePictureLayer::Create(&client_); + layer_->SetBounds(gfx::Size(4, 4)); + client_.set_bounds(layer_->bounds()); + layer_tree_host()->root_layer()->AddChild(layer_); + + AttachPlayersToTimeline(); + + player_->AttachElement(layer_tree_host()->root_layer()->element_id()); + player_child_->AttachElement(layer_->element_id()); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void DidCommit() override { + if (layer_tree_host()->SourceFrameNumber() == 1) + AddAnimatedTransformToPlayer(player_child_.get(), 0.04, 5, 5); + } + + void WillCommit() override { + if (layer_tree_host()->SourceFrameNumber() == 2) { + // Block until the animation finishes on the compositor thread. Since + // animations have already been ticked on the main thread, when the commit + // happens the state on the main thread will be consistent with having a + // running animation but the state on the compositor thread will be + // consistent with having only a finished animation. + completion_.Wait(); + } + } + + void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override { + DCHECK(did_request_impl_side_invalidation_); + completion_.Signal(); + } + + void UpdateAnimationState(LayerTreeHostImpl* host_impl, + bool has_unfinished_animation) override { + if (host_impl->active_tree()->source_frame_number() == 1 && + !has_unfinished_animation && !did_request_impl_side_invalidation_) { + // The animation on the active tree has finished, now request an impl-side + // invalidation and make sure it finishes before the main thread is + // released. + did_request_impl_side_invalidation_ = true; + host_impl->RequestImplSideInvalidation(); + } + } + + void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { + switch (host_impl->sync_tree()->source_frame_number()) { + case 1: + PostSetNeedsCommitToMainThread(); + break; + case 2: + gfx::Transform expected_transform; + expected_transform.Translate(5.f, 5.f); + LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, + layer_impl->DrawTransform()); + EndTest(); + break; + } + } + + void AfterTest() override {} + + private: + scoped_refptr<Layer> layer_; + FakeContentLayerClient client_; + CompletionEvent completion_; + bool did_request_impl_side_invalidation_ = false; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestImplSideInvalidation); + class LayerTreeHostAnimationTestNotifyAnimationFinished : public LayerTreeHostAnimationTest { public: diff --git a/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc b/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc index 37b3e1b006f..0828b107c44 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc @@ -21,7 +21,6 @@ class LayerTreeHostCheckerImagingTest : public LayerTreeTest { void AfterTest() override {} void InitializeSettings(LayerTreeSettings* settings) override { - settings->image_decode_tasks_enabled = true; settings->enable_checker_imaging = true; } diff --git a/chromium/cc/trees/layer_tree_host_unittest_context.cc b/chromium/cc/trees/layer_tree_host_unittest_context.cc index 3c7351f3ff4..d1337cb9904 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_context.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_context.cc @@ -973,7 +973,7 @@ class LayerTreeHostContextTestDontUseLostResources scoped_refptr<PaintedScrollbarLayer> scrollbar = PaintedScrollbarLayer::Create( - std::unique_ptr<Scrollbar>(new FakeScrollbar), layer->id()); + std::unique_ptr<Scrollbar>(new FakeScrollbar), layer->element_id()); scrollbar->SetBounds(gfx::Size(10, 10)); scrollbar->SetIsDrawable(true); root->AddChild(scrollbar); @@ -1088,8 +1088,8 @@ class ScrollbarLayerLostContext : public LayerTreeHostContextTest { void BeginTest() override { scoped_refptr<Layer> scroll_layer = Layer::Create(); - scrollbar_layer_ = - FakePaintedScrollbarLayer::Create(false, true, scroll_layer->id()); + scrollbar_layer_ = FakePaintedScrollbarLayer::Create( + false, true, scroll_layer->element_id()); scrollbar_layer_->SetBounds(gfx::Size(10, 100)); layer_tree_host()->root_layer()->AddChild(scrollbar_layer_); layer_tree_host()->root_layer()->AddChild(scroll_layer); diff --git a/chromium/cc/trees/layer_tree_host_unittest_damage.cc b/chromium/cc/trees/layer_tree_host_unittest_damage.cc index 73d71a2d6dd..578324f785e 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_damage.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_damage.cc @@ -12,6 +12,7 @@ #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_painted_scrollbar_layer.h" #include "cc/test/fake_picture_layer.h" +#include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/damage_tracker.h" #include "cc/trees/layer_tree_impl.h" @@ -55,7 +56,7 @@ class LayerTreeHostDamageTestSetNeedsRedraw EXPECT_EQ(DRAW_SUCCESS, draw_result); RenderSurfaceImpl* root_surface = - impl->active_tree()->root_layer_for_testing()->GetRenderSurface(); + GetRenderSurface(impl->active_tree()->root_layer_for_testing()); gfx::Rect root_damage; EXPECT_TRUE( root_surface->damage_tracker()->GetDamageRectIfValid(&root_damage)); @@ -118,7 +119,7 @@ class LayerTreeHostDamageTestSetViewportSize EXPECT_EQ(DRAW_SUCCESS, draw_result); RenderSurfaceImpl* root_surface = - impl->active_tree()->root_layer_for_testing()->GetRenderSurface(); + GetRenderSurface(impl->active_tree()->root_layer_for_testing()); gfx::Rect root_damage; EXPECT_TRUE( root_surface->damage_tracker()->GetDamageRectIfValid(&root_damage)); @@ -260,7 +261,7 @@ class LayerTreeHostDamageTestForcedFullDamage : public LayerTreeHostDamageTest { EXPECT_EQ(DRAW_SUCCESS, draw_result); RenderSurfaceImpl* root_surface = - host_impl->active_tree()->root_layer_for_testing()->GetRenderSurface(); + GetRenderSurface(host_impl->active_tree()->root_layer_for_testing()); gfx::Rect root_damage; EXPECT_TRUE( root_surface->damage_tracker()->GetDamageRectIfValid(&root_damage)); @@ -340,6 +341,8 @@ class LayerTreeHostScrollbarDamageTest : public LayerTreeHostDamageTest { scoped_refptr<Layer> scroll_clip_layer = Layer::Create(); content_layer_ = FakePictureLayer::Create(&client_); + content_layer_->SetElementId( + LayerIdToElementIdForTesting(content_layer_->id())); content_layer_->SetScrollClipLayerId(scroll_clip_layer->id()); content_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20)); content_layer_->SetBounds(gfx::Size(100, 200)); @@ -350,8 +353,8 @@ class LayerTreeHostScrollbarDamageTest : public LayerTreeHostDamageTest { scroll_clip_layer->AddChild(content_layer_); root_layer->AddChild(scroll_clip_layer); - scoped_refptr<Layer> scrollbar_layer = - FakePaintedScrollbarLayer::Create(false, true, content_layer_->id()); + scoped_refptr<Layer> scrollbar_layer = FakePaintedScrollbarLayer::Create( + false, true, content_layer_->element_id()); scrollbar_layer->SetPosition(gfx::PointF(300.f, 300.f)); scrollbar_layer->SetBounds(gfx::Size(10, 100)); root_layer->AddChild(scrollbar_layer); @@ -385,7 +388,7 @@ class LayerTreeHostDamageTestScrollbarDoesDamage DrawResult draw_result) override { EXPECT_EQ(DRAW_SUCCESS, draw_result); RenderSurfaceImpl* root_surface = - host_impl->active_tree()->root_layer_for_testing()->GetRenderSurface(); + GetRenderSurface(host_impl->active_tree()->root_layer_for_testing()); gfx::Rect root_damage; EXPECT_TRUE( root_surface->damage_tracker()->GetDamageRectIfValid(&root_damage)); @@ -471,7 +474,7 @@ class LayerTreeHostDamageTestScrollbarCommitDoesNoDamage DrawResult draw_result) override { EXPECT_EQ(DRAW_SUCCESS, draw_result); RenderSurfaceImpl* root_surface = - host_impl->active_tree()->root_layer_for_testing()->GetRenderSurface(); + GetRenderSurface(host_impl->active_tree()->root_layer_for_testing()); gfx::Rect root_damage; EXPECT_TRUE( root_surface->damage_tracker()->GetDamageRectIfValid(&root_damage)); diff --git a/chromium/cc/trees/layer_tree_host_unittest_occlusion.cc b/chromium/cc/trees/layer_tree_host_unittest_occlusion.cc index 3202bb93f1a..d979e0b46ea 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_occlusion.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_occlusion.cc @@ -7,6 +7,7 @@ #include "cc/layers/layer.h" #include "cc/layers/picture_layer.h" #include "cc/test/fake_content_layer_client.h" +#include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/layer_tree_impl.h" @@ -52,8 +53,8 @@ class LayerTreeHostOcclusionTestDrawPropertiesOnLayer LayerImpl* child = impl->active_tree()->LayerById(child_->id()); // Verify the draw properties are valid. - EXPECT_TRUE(root->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(child->is_drawn_render_surface_layer_list_member()); + EXPECT_TRUE(root->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child->contributes_to_drawn_render_surface()); EXPECT_OCCLUSION_EQ( Occlusion(child->DrawTransform(), SimpleEnclosedRegion(), @@ -106,13 +107,13 @@ class LayerTreeHostOcclusionTestDrawPropertiesOnSurface void DrawLayersOnThread(LayerTreeHostImpl* impl) override { LayerImpl* root = impl->active_tree()->root_layer_for_testing(); LayerImpl* child = impl->active_tree()->LayerById(child_->id()); - RenderSurfaceImpl* surface = child->GetRenderSurface(); + RenderSurfaceImpl* surface = GetRenderSurface(child); // Verify the draw properties are valid. - EXPECT_TRUE(root->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(child->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(child->GetRenderSurface()); - EXPECT_EQ(child->GetRenderSurface(), child->render_target()); + EXPECT_TRUE(root->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child->contributes_to_drawn_render_surface()); + EXPECT_TRUE(GetRenderSurface(child)); + EXPECT_EQ(GetRenderSurface(child), child->render_target()); EXPECT_OCCLUSION_EQ( Occlusion(surface->draw_transform(), SimpleEnclosedRegion(), @@ -173,14 +174,14 @@ class LayerTreeHostOcclusionTestDrawPropertiesOnMask void DrawLayersOnThread(LayerTreeHostImpl* impl) override { LayerImpl* root = impl->active_tree()->root_layer_for_testing(); LayerImpl* child = impl->active_tree()->LayerById(child_->id()); - RenderSurfaceImpl* surface = child->GetRenderSurface(); + RenderSurfaceImpl* surface = GetRenderSurface(child); LayerImpl* mask = surface->MaskLayer(); // Verify the draw properties are valid. - EXPECT_TRUE(root->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(child->is_drawn_render_surface_layer_list_member()); - EXPECT_TRUE(child->GetRenderSurface()); - EXPECT_EQ(child->GetRenderSurface(), child->render_target()); + EXPECT_TRUE(root->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child->contributes_to_drawn_render_surface()); + EXPECT_TRUE(GetRenderSurface(child)); + EXPECT_EQ(GetRenderSurface(child), child->render_target()); gfx::Transform transform = surface->draw_transform(); transform.PreconcatTransform(child->DrawTransform()); @@ -245,7 +246,7 @@ class LayerTreeHostOcclusionTestDrawPropertiesOnScaledMask void DrawLayersOnThread(LayerTreeHostImpl* impl) override { LayerImpl* child = impl->active_tree()->LayerById(child_->id()); - LayerImpl* mask = child->GetRenderSurface()->MaskLayer(); + LayerImpl* mask = GetRenderSurface(child)->MaskLayer(); gfx::Transform scale; scale.Scale(2, 2); diff --git a/chromium/cc/trees/layer_tree_host_unittest_picture.cc b/chromium/cc/trees/layer_tree_host_unittest_picture.cc index 55eabcb1eb7..e4fefef207c 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_picture.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_picture.cc @@ -423,8 +423,11 @@ class LayerTreeHostPictureTestRSLLMembershipWithScale picture_->SetBounds(gfx::Size(100, 100)); pinch_->AddChild(picture_); - layer_tree_host()->RegisterViewportLayers(NULL, page_scale_layer, pinch_, - nullptr); + LayerTreeHost::ViewportLayers viewport_layers; + viewport_layers.page_scale = page_scale_layer; + viewport_layers.inner_viewport_container = root_clip; + viewport_layers.inner_viewport_scroll = pinch_; + layer_tree_host()->RegisterViewportLayers(viewport_layers); layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 1.f, 4.f); layer_tree_host()->SetRootLayer(root_clip); LayerTreeHostPictureTest::SetupTree(); diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index b547fefcc45..e9a1e5d6b2e 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -1417,7 +1417,8 @@ class LayerTreeHostScrollTestLayerStructureChange virtual void DidScroll(Layer* layer) { if (scroll_destroy_whole_tree_) { - layer_tree_host()->RegisterViewportLayers(NULL, NULL, NULL, NULL); + layer_tree_host()->RegisterViewportLayers( + LayerTreeHost::ViewportLayers()); layer_tree_host()->SetRootLayer(NULL); EndTest(); return; @@ -2132,6 +2133,25 @@ class LayerTreeHostScrollTestImplSideInvalidation SignalCompletionIfPossible(); } + void WillNotifyReadyToActivateOnThread( + LayerTreeHostImpl* host_impl) override { + // Ensure that the scroll-offsets on the TransformTree are consistent with + // the synced scroll offsets, for the pending tree. + if (!host_impl->pending_tree()) + return; + + LayerImpl* scroll_layer = + host_impl->pending_tree()->OuterViewportScrollLayer(); + gfx::ScrollOffset scroll_offset = scroll_layer->CurrentScrollOffset(); + int transform_index = scroll_layer->transform_tree_index(); + gfx::ScrollOffset transform_tree_scroll_offset = + host_impl->pending_tree() + ->property_trees() + ->transform_tree.Node(transform_index) + ->scroll_offset; + EXPECT_EQ(scroll_offset, transform_tree_scroll_offset); + } + void SignalCompletionIfPossible() { if (!invalidated_on_impl_thread_ || !impl_side_invalidation_event_) return; diff --git a/chromium/cc/trees/layer_tree_host_unittest_video.cc b/chromium/cc/trees/layer_tree_host_unittest_video.cc index ae48abaa7b3..1c659af77e1 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_video.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_video.cc @@ -8,6 +8,7 @@ #include "cc/layers/video_layer.h" #include "cc/layers/video_layer_impl.h" #include "cc/test/fake_video_frame_provider.h" +#include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/damage_tracker.h" #include "cc/trees/layer_tree_impl.h" @@ -48,7 +49,7 @@ class LayerTreeHostVideoTestSetNeedsDisplay LayerTreeHostImpl::FrameData* frame, DrawResult draw_result) override { LayerImpl* root_layer = host_impl->active_tree()->root_layer_for_testing(); - RenderSurfaceImpl* root_surface = root_layer->GetRenderSurface(); + RenderSurfaceImpl* root_surface = GetRenderSurface(root_layer); gfx::Rect damage_rect; EXPECT_TRUE( root_surface->damage_tracker()->GetDamageRectIfValid(&damage_rect)); diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index 12ebf3fa413..2c57e5a1f0e 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -51,6 +51,21 @@ namespace cc { +void LayerTreeLifecycle::AdvanceTo(LifecycleState next_state) { + switch (next_state) { + case (kNotSyncing): + DCHECK_EQ(state_, kLastSyncState); + break; + case (kBeginningSync): + case (kSyncedPropertyTrees): + case (kSyncedLayerProperties): + // Only allow tree synchronization states to be transitioned in order. + DCHECK_EQ(state_ + 1, next_state); + break; + } + state_ = next_state; +} + LayerTreeImpl::LayerTreeImpl( LayerTreeHostImpl* layer_tree_host_impl, scoped_refptr<SyncedProperty<ScaleGroup>> page_scale_factor, @@ -64,10 +79,6 @@ LayerTreeImpl::LayerTreeImpl( background_color_(0), has_transparent_background_(false), last_scrolled_scroll_node_index_(ScrollTree::kInvalidNodeId), - overscroll_elasticity_layer_id_(Layer::INVALID_ID), - page_scale_layer_id_(Layer::INVALID_ID), - inner_viewport_scroll_layer_id_(Layer::INVALID_ID), - outer_viewport_scroll_layer_id_(Layer::INVALID_ID), page_scale_factor_(page_scale_factor), min_page_scale_factor_(0), max_page_scale_factor_(0), @@ -139,21 +150,26 @@ void LayerTreeImpl::RecreateTileResources() { } bool LayerTreeImpl::IsViewportLayerId(int id) const { - if (id == inner_viewport_scroll_layer_id_ || - id == outer_viewport_scroll_layer_id_) - return true; - if (InnerViewportContainerLayer() && - id == InnerViewportContainerLayer()->id()) - return true; - if (OuterViewportContainerLayer() && - id == OuterViewportContainerLayer()->id()) - return true; - +#if DCHECK_IS_ON() + // Ensure the LayerImpl viewport layer types correspond to the LayerTreeImpl's + // viewport layers. + if (id == viewport_layer_ids_.inner_viewport_container) + DCHECK(LayerById(id)->viewport_layer_type() == INNER_VIEWPORT_CONTAINER); + if (id == viewport_layer_ids_.outer_viewport_container) + DCHECK(LayerById(id)->viewport_layer_type() == OUTER_VIEWPORT_CONTAINER); + if (id == viewport_layer_ids_.inner_viewport_scroll) + DCHECK(LayerById(id)->viewport_layer_type() == INNER_VIEWPORT_SCROLL); + if (id == viewport_layer_ids_.outer_viewport_scroll) + DCHECK(LayerById(id)->viewport_layer_type() == OUTER_VIEWPORT_SCROLL); +#endif + if (auto* layer = LayerById(id)) + return layer->viewport_layer_type() != NOT_VIEWPORT_LAYER; return false; } void LayerTreeImpl::DidUpdateScrollOffset(int layer_id) { DidUpdateScrollState(layer_id); + DCHECK(lifecycle().AllowsPropertyTreeAccess()); TransformTree& transform_tree = property_trees()->transform_tree; ScrollTree& scroll_tree = property_trees()->scroll_tree; int transform_id = TransformTree::kInvalidNodeId; @@ -191,6 +207,12 @@ void LayerTreeImpl::DidUpdateScrollState(int layer_id) { if (!IsActiveTree()) return; + DCHECK(lifecycle().AllowsPropertyTreeAccess()); + + // The scroll_clip_layer Layer properties should be up-to-date. + // TODO(pdr): This DCHECK fails on existing tests but should be enabled. + // DCHECK(lifecycle().AllowsLayerPropertyAccess()); + if (layer_id == Layer::INVALID_ID) return; @@ -202,8 +224,8 @@ void LayerTreeImpl::DidUpdateScrollState(int layer_id) { // For scrollbar purposes, a change to any of the four viewport layers // should affect the scrollbars tied to the outermost layers, which express // the sum of the entire viewport. - scroll_layer_id = outer_viewport_scroll_layer_id_; - clip_layer_id = InnerViewportContainerLayer()->id(); + scroll_layer_id = viewport_layer_ids_.outer_viewport_scroll; + clip_layer_id = viewport_layer_ids_.inner_viewport_container; } else { // If the clip layer id was passed in, then look up the scroll layer, or // vice versa. @@ -242,55 +264,28 @@ void LayerTreeImpl::UpdateScrollbars(int scroll_layer_id, int clip_layer_id) { clip_size.Scale(1 / current_page_scale_factor()); } - bool scrollbar_needs_animation = false; - bool clip_layer_size_did_change = false; - bool scroll_layer_size_did_change = false; bool y_offset_did_change = false; - for (ScrollbarLayerImplBase* scrollbar : ScrollbarsFor(scroll_layer_id)) { + for (auto* scrollbar : ScrollbarsFor(scroll_layer->element_id())) { if (scrollbar->orientation() == HORIZONTAL) { - scrollbar_needs_animation |= scrollbar->SetCurrentPos(current_offset.x()); - clip_layer_size_did_change |= - scrollbar->SetClipLayerLength(clip_size.width()); - scroll_layer_size_did_change |= - scrollbar->SetScrollLayerLength(scroll_size.width()); + scrollbar->SetCurrentPos(current_offset.x()); + scrollbar->SetClipLayerLength(clip_size.width()); + scrollbar->SetScrollLayerLength(scroll_size.width()); } else { - scrollbar_needs_animation |= y_offset_did_change |= - scrollbar->SetCurrentPos(current_offset.y()); - clip_layer_size_did_change |= - scrollbar->SetClipLayerLength(clip_size.height()); - scroll_layer_size_did_change |= - scrollbar->SetScrollLayerLength(scroll_size.height()); + y_offset_did_change = scrollbar->SetCurrentPos(current_offset.y()); + scrollbar->SetClipLayerLength(clip_size.height()); + scrollbar->SetScrollLayerLength(scroll_size.height()); } - scrollbar_needs_animation |= - scrollbar->SetVerticalAdjust(clip_layer->bounds_delta().y()); + scrollbar->SetVerticalAdjust(clip_layer->ViewportBoundsDelta().y()); } - scrollbar_needs_animation |= - (clip_layer_size_did_change || scroll_layer_size_did_change); - if (y_offset_did_change && IsViewportLayerId(scroll_layer_id)) TRACE_COUNTER_ID1("cc", "scroll_offset_y", scroll_layer->id(), current_offset.y()); - - if (scrollbar_needs_animation) { - ScrollbarAnimationController* controller = - layer_tree_host_impl_->ScrollbarAnimationControllerForId( - scroll_layer_id); - if (!controller) - return; - - // TODO(chaopeng) clip_layer_size_did_change should call DidResize after - // crbug.com/701810 got fixed. - if (scroll_layer_size_did_change) { - controller->DidResize(); - } else { - controller->DidScrollUpdate(); - } - } } -RenderSurfaceImpl* LayerTreeImpl::RootRenderSurface() const { - return layer_list_.empty() ? nullptr : layer_list_[0]->GetRenderSurface(); +const RenderSurfaceImpl* LayerTreeImpl::RootRenderSurface() const { + return property_trees_.effect_tree.GetRenderSurface( + EffectTree::kContentsRootNodeId); } bool LayerTreeImpl::LayerListIsEmpty() const { @@ -330,7 +325,7 @@ void LayerTreeImpl::BuildLayerListForTesting() { } void LayerTreeImpl::InvalidateRegionForImages( - const ImageIdFlatSet& images_to_invalidate) { + const PaintImageIdFlatSet& images_to_invalidate) { DCHECK(IsSyncTree()); if (images_to_invalidate.empty()) @@ -344,14 +339,6 @@ bool LayerTreeImpl::IsRootLayer(const LayerImpl* layer) const { return layer_list_.empty() ? false : layer_list_[0] == layer; } -LayerImpl* LayerTreeImpl::InnerViewportScrollLayer() const { - return LayerById(inner_viewport_scroll_layer_id_); -} - -LayerImpl* LayerTreeImpl::OuterViewportScrollLayer() const { - return LayerById(outer_viewport_scroll_layer_id_); -} - gfx::ScrollOffset LayerTreeImpl::TotalScrollOffset() const { gfx::ScrollOffset offset; @@ -379,28 +366,13 @@ gfx::ScrollOffset LayerTreeImpl::TotalMaxScrollOffset() const { std::unique_ptr<OwnedLayerImplList> LayerTreeImpl::DetachLayers() { root_layer_for_testing_ = nullptr; layer_list_.clear(); - render_surface_layer_list_.clear(); + render_surface_list_.clear(); set_needs_update_draw_properties(); std::unique_ptr<OwnedLayerImplList> ret = std::move(layers_); layers_.reset(new OwnedLayerImplList); return ret; } -static void UpdateClipTreeForBoundsDeltaOnLayer(LayerImpl* layer, - ClipTree* clip_tree) { - if (layer && layer->masks_to_bounds()) { - ClipNode* clip_node = clip_tree->Node(layer->clip_tree_index()); - if (clip_node) { - DCHECK_EQ(layer->id(), clip_node->owning_layer_id); - gfx::SizeF bounds = gfx::SizeF(layer->bounds()); - if (clip_node->clip.size() != bounds) { - clip_node->clip.set_size(bounds); - clip_tree->set_needs_update(true); - } - } - } -} - void LayerTreeImpl::SetPropertyTrees(PropertyTrees* property_trees) { std::vector<std::unique_ptr<RenderSurfaceImpl>> old_render_surfaces; property_trees_.effect_tree.TakeRenderSurfaces(&old_render_surfaces); @@ -421,33 +393,17 @@ void LayerTreeImpl::SetPropertyTrees(PropertyTrees* property_trees) { property_trees_.effect_tree.set_needs_update(true); } -void LayerTreeImpl::UpdatePropertyTreesForBoundsDelta() { - DCHECK(IsActiveTree()); - LayerImpl* inner_container = InnerViewportContainerLayer(); - LayerImpl* outer_container = OuterViewportContainerLayer(); - LayerImpl* inner_scroll = InnerViewportScrollLayer(); - - UpdateClipTreeForBoundsDeltaOnLayer(inner_container, - &property_trees_.clip_tree); - UpdateClipTreeForBoundsDeltaOnLayer(InnerViewportScrollLayer(), - &property_trees_.clip_tree); - UpdateClipTreeForBoundsDeltaOnLayer(outer_container, - &property_trees_.clip_tree); - - if (inner_container) - property_trees_.SetInnerViewportContainerBoundsDelta( - inner_container->bounds_delta()); - if (outer_container) - property_trees_.SetOuterViewportContainerBoundsDelta( - outer_container->bounds_delta()); - if (inner_scroll) - property_trees_.SetInnerViewportScrollBoundsDelta( - inner_scroll->bounds_delta()); -} - -void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { - // The request queue should have been processed and does not require a push. - DCHECK_EQ(ui_resource_request_queue_.size(), 0u); +void LayerTreeImpl::PushPropertyTreesTo(LayerTreeImpl* target_tree) { + // Property trees may store damage status. We preserve the active tree + // damage status by pushing the damage status from active tree property + // trees to pending tree property trees or by moving it onto the layers. + if (target_tree->property_trees()->changed) { + if (property_trees()->sequence_number == + target_tree->property_trees()->sequence_number) + target_tree->property_trees()->PushChangeTrackingTo(property_trees()); + else + target_tree->MoveChangeTrackingToLayers(); + } // To maintain the current scrolling node we need to use element ids which // are stable across the property tree update in SetPropertyTrees. @@ -459,24 +415,19 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { ScrollNode* scrolling_node = nullptr; if (scrolling_element_id) { - auto& scroll_node_index_map = - target_tree->property_trees()->element_id_to_scroll_node_index; - auto scrolling_node_it = scroll_node_index_map.find(scrolling_element_id); - if (scrolling_node_it != scroll_node_index_map.end()) { - int index = scrolling_node_it->second; - scrolling_node = target_tree->property_trees()->scroll_tree.Node(index); - } + auto& scroll_tree = target_tree->property_trees()->scroll_tree; + scrolling_node = scroll_tree.FindNodeFromElementId(scrolling_element_id); } target_tree->SetCurrentlyScrollingNode(scrolling_node); +} + +void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { + // The request queue should have been processed and does not require a push. + DCHECK_EQ(ui_resource_request_queue_.size(), 0u); target_tree->property_trees()->scroll_tree.PushScrollUpdatesFromPendingTree( &property_trees_, target_tree); - // This needs to be called early so that we don't clamp with incorrect max - // offsets when UpdateViewportContainerSizes is called from e.g. - // PushBrowserControls - target_tree->UpdatePropertyTreesForBoundsDelta(); - if (next_activation_forces_redraw_) { target_tree->ForceRedrawNextActivation(); next_activation_forces_redraw_ = false; @@ -491,6 +442,10 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { target_tree->set_bottom_controls_height(bottom_controls_height_); target_tree->PushBrowserControls(nullptr); + // The page scale factor update can affect scrolling which requires that + // these ids are set, so this must be before PushPageScaleFactorAndLimits. + target_tree->SetViewportLayersFromIds(viewport_layer_ids_); + // Active tree already shares the page_scale_factor object with pending // tree so only the limits need to be provided. target_tree->PushPageScaleFactorAndLimits(nullptr, min_page_scale_factor(), @@ -507,10 +462,6 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { target_tree->pending_page_scale_animation_ = std::move(pending_page_scale_animation_); - target_tree->SetViewportLayersFromIds( - overscroll_elasticity_layer_id_, page_scale_layer_id_, - inner_viewport_scroll_layer_id_, outer_viewport_scroll_layer_id_); - target_tree->RegisterSelection(selection_); // This should match the property synchronization in @@ -543,16 +494,17 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { target_tree->has_ever_been_drawn_ = false; // Note: this needs to happen after SetPropertyTrees. - target_tree->ShowScrollbars(); + target_tree->HandleScrollbarShowRequestsFromMain(); } -void LayerTreeImpl::ShowScrollbars() { +void LayerTreeImpl::HandleScrollbarShowRequestsFromMain() { LayerTreeHostCommon::CallFunctionForEveryLayer(this, [this]( LayerImpl* layer) { if (!layer->needs_show_scrollbars()) return; ScrollbarAnimationController* controller = - layer_tree_host_impl_->ScrollbarAnimationControllerForId(layer->id()); + layer_tree_host_impl_->ScrollbarAnimationControllerForElementId( + layer->element_id()); if (controller) { controller->DidRequestShowFromMainThread(); layer->set_needs_show_scrollbars(false); @@ -654,24 +606,14 @@ void LayerTreeImpl::RemoveFromElementMap(LayerImpl* layer) { element_layers_map_.erase(layer->element_id()); } -void LayerTreeImpl::AddToOpacityAnimationsMap(int id, float opacity) { - if (LayerImpl* layer = LayerById(id)) - element_id_to_opacity_animations_[layer->element_id()] = opacity; -} - void LayerTreeImpl::SetTransformMutated(ElementId element_id, const gfx::Transform& transform) { DCHECK_EQ(1u, property_trees()->element_id_to_transform_node_index.count( element_id)); element_id_to_transform_animations_[element_id] = transform; - if (!property_trees()->transform_tree.OnTransformAnimated(element_id, - transform)) - return; - - if (LayerImpl* layer = LayerByElementId(element_id)) - layer->set_was_ever_ready_since_last_transform_animation(false); - - set_needs_update_draw_properties(); + if (property_trees()->transform_tree.OnTransformAnimated(element_id, + transform)) + set_needs_update_draw_properties(); } void LayerTreeImpl::SetOpacityMutated(ElementId element_id, float opacity) { @@ -691,18 +633,6 @@ void LayerTreeImpl::SetFilterMutated(ElementId element_id, set_needs_update_draw_properties(); } -LayerImpl* LayerTreeImpl::InnerViewportContainerLayer() const { - return InnerViewportScrollLayer() - ? InnerViewportScrollLayer()->scroll_clip_layer() - : NULL; -} - -LayerImpl* LayerTreeImpl::OuterViewportContainerLayer() const { - return OuterViewportScrollLayer() - ? OuterViewportScrollLayer()->scroll_clip_layer() - : NULL; -} - ScrollNode* LayerTreeImpl::CurrentlyScrollingNode() { DCHECK(IsActiveTree()); return property_trees_.scroll_tree.CurrentlyScrollingNode(); @@ -736,14 +666,12 @@ void LayerTreeImpl::SetCurrentlyScrollingNode(ScrollNode* node) { if (old_element_id == new_element_id) return; - // TODO(pdr): Make the scrollbar animation controller lookup based on - // element ids instead of layer ids. ScrollbarAnimationController* old_animation_controller = - layer_tree_host_impl_->ScrollbarAnimationControllerForId( - LayerIdByElementId(old_element_id)); + layer_tree_host_impl_->ScrollbarAnimationControllerForElementId( + old_element_id); ScrollbarAnimationController* new_animation_controller = - layer_tree_host_impl_->ScrollbarAnimationControllerForId( - LayerIdByElementId(new_element_id)); + layer_tree_host_impl_->ScrollbarAnimationControllerForElementId( + new_element_id); if (old_animation_controller) old_animation_controller->DidScrollEnd(); @@ -766,7 +694,8 @@ float LayerTreeImpl::ClampPageScaleFactorToLimits( return page_scale_factor; } -void LayerTreeImpl::UpdatePropertyTreeScrollingAndAnimationFromMainThread() { +void LayerTreeImpl::UpdatePropertyTreeScrollingAndAnimationFromMainThread( + bool is_impl_side_update) { // TODO(enne): This should get replaced by pulling out scrolling and // animations into their own trees. Then scrolls and animations would have // their own ways of synchronizing across commits. This occurs to push @@ -775,13 +704,19 @@ void LayerTreeImpl::UpdatePropertyTreeScrollingAndAnimationFromMainThread() { // frame to a newly-committed property tree. if (layer_list_.empty()) return; + + // Entries from |element_id_to_*_animations_| should be deleted only after + // they have been synchronized with the main thread, which will not be the + // case if this is an impl-side invalidation. + const bool can_delete_animations = !is_impl_side_update; auto element_id_to_opacity = element_id_to_opacity_animations_.begin(); while (element_id_to_opacity != element_id_to_opacity_animations_.end()) { const ElementId id = element_id_to_opacity->first; if (EffectNode* node = property_trees_.effect_tree.FindNodeFromElementId(id)) { - if (!node->is_currently_animating_opacity || - node->opacity == element_id_to_opacity->second) { + if ((!node->is_currently_animating_opacity || + node->opacity == element_id_to_opacity->second) && + can_delete_animations) { element_id_to_opacity_animations_.erase(element_id_to_opacity++); continue; } @@ -796,8 +731,9 @@ void LayerTreeImpl::UpdatePropertyTreeScrollingAndAnimationFromMainThread() { const ElementId id = element_id_to_filter->first; if (EffectNode* node = property_trees_.effect_tree.FindNodeFromElementId(id)) { - if (!node->is_currently_animating_filter || - node->filters == element_id_to_filter->second) { + if ((!node->is_currently_animating_filter || + node->filters == element_id_to_filter->second) && + can_delete_animations) { element_id_to_filter_animations_.erase(element_id_to_filter++); continue; } @@ -812,8 +748,9 @@ void LayerTreeImpl::UpdatePropertyTreeScrollingAndAnimationFromMainThread() { const ElementId id = element_id_to_transform->first; if (TransformNode* node = property_trees_.transform_tree.FindNodeFromElementId(id)) { - if (!node->is_currently_animating || - node->local == element_id_to_transform->second) { + if ((!node->is_currently_animating || + node->local == element_id_to_transform->second) && + can_delete_animations) { element_id_to_transform_animations_.erase(element_id_to_transform++); continue; } @@ -831,6 +768,7 @@ void LayerTreeImpl::UpdatePropertyTreeScrollingAndAnimationFromMainThread() { void LayerTreeImpl::SetPageScaleOnActiveTree(float active_page_scale) { DCHECK(IsActiveTree()); + DCHECK(lifecycle().AllowsPropertyTreeAccess()); if (page_scale_factor()->SetCurrent( ClampPageScaleFactorToLimits(active_page_scale))) { DidUpdatePageScale(); @@ -881,6 +819,7 @@ void LayerTreeImpl::PushPageScaleFactorAndLimits(const float* page_scale_factor, if (changed_page_scale) DidUpdatePageScale(); + DCHECK(lifecycle().AllowsPropertyTreeAccess()); if (page_scale_factor) { if (PageScaleLayer()) { draw_property_utils::UpdatePageScaleFactor( @@ -970,7 +909,14 @@ void LayerTreeImpl::DidUpdatePageScale() { ClampPageScaleFactorToLimits(current_page_scale_factor())); set_needs_update_draw_properties(); - DidUpdateScrollState(inner_viewport_scroll_layer_id_); + DidUpdateScrollState(viewport_layer_ids_.inner_viewport_scroll); + + if (IsActiveTree() && layer_tree_host_impl_->ViewportMainScrollLayer()) { + if (ScrollbarAnimationController* controller = + layer_tree_host_impl_->ScrollbarAnimationControllerForElementId( + OuterViewportScrollLayer()->element_id())) + controller->DidScrollUpdate(); + } } void LayerTreeImpl::SetDeviceScaleFactor(float device_scale_factor) { @@ -1031,22 +977,22 @@ void LayerTreeImpl::ApplySentScrollAndScaleDeltasFromAbortedCommit() { property_trees()->scroll_tree.ApplySentScrollDeltasFromAbortedCommit(); } -void LayerTreeImpl::SetViewportLayersFromIds( - int overscroll_elasticity_layer_id, - int page_scale_layer_id, - int inner_viewport_scroll_layer_id, - int outer_viewport_scroll_layer_id) { - overscroll_elasticity_layer_id_ = overscroll_elasticity_layer_id; - page_scale_layer_id_ = page_scale_layer_id; - inner_viewport_scroll_layer_id_ = inner_viewport_scroll_layer_id; - outer_viewport_scroll_layer_id_ = outer_viewport_scroll_layer_id; +void LayerTreeImpl::SetViewportLayersFromIds(const ViewportLayerIds& ids) { + viewport_layer_ids_ = ids; + + // Set the viewport layer types. + if (auto* inner_container = InnerViewportContainerLayer()) + inner_container->SetViewportLayerType(INNER_VIEWPORT_CONTAINER); + if (auto* inner_scroll = InnerViewportScrollLayer()) + inner_scroll->SetViewportLayerType(INNER_VIEWPORT_SCROLL); + if (auto* outer_container = OuterViewportContainerLayer()) + outer_container->SetViewportLayerType(OUTER_VIEWPORT_CONTAINER); + if (auto* outer_scroll = OuterViewportScrollLayer()) + outer_scroll->SetViewportLayerType(OUTER_VIEWPORT_SCROLL); } void LayerTreeImpl::ClearViewportLayers() { - overscroll_elasticity_layer_id_ = Layer::INVALID_ID; - page_scale_layer_id_ = Layer::INVALID_ID; - inner_viewport_scroll_layer_id_ = Layer::INVALID_ID; - outer_viewport_scroll_layer_id_ = Layer::INVALID_ID; + SetViewportLayersFromIds(ViewportLayerIds()); } // For unit tests, we use the layer's id as its element id. @@ -1077,7 +1023,7 @@ bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) { // Clear this after the renderer early out, as it should still be // possible to hit test even without a renderer. - render_surface_layer_list_.clear(); + render_surface_list_.clear(); if (layer_list_.empty()) return false; @@ -1087,24 +1033,18 @@ bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) { TRACE_EVENT2( "cc", "LayerTreeImpl::UpdateDrawProperties::CalculateDrawProperties", "IsActive", IsActiveTree(), "SourceFrameNumber", source_frame_number_); - // TODO(crbug.com/692780): Remove this option entirely once this get to - // stable and proves it works. - bool can_render_to_separate_surface = true; - // We verify visible rect calculations whenever we verify clip tree // calculations except when this function is explicitly passed a flag asking // us to skip it. LayerTreeHostCommon::CalcDrawPropsImplInputs inputs( - layer_list_[0], DrawViewportSize(), + layer_list_[0], DeviceViewport().size(), layer_tree_host_impl_->DrawTransform(), device_scale_factor(), current_page_scale_factor(), PageScaleLayer(), InnerViewportScrollLayer(), OuterViewportScrollLayer(), elastic_overscroll()->Current(IsActiveTree()), OverscrollElasticityLayer(), resource_provider()->max_texture_size(), - can_render_to_separate_surface, settings().layer_transforms_should_scale_layer_contents, - settings().use_layer_lists, &render_surface_layer_list_, - &property_trees_); + &render_surface_list_, &property_trees_); LayerTreeHostCommon::CalculateDrawProperties(&inputs); if (const char* client_name = GetClientNameForMetrics()) { UMA_HISTOGRAM_COUNTS( @@ -1114,7 +1054,7 @@ bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) { timer.Elapsed().InMicroseconds()); UMA_HISTOGRAM_COUNTS_100( base::StringPrintf("Compositing.%s.NumRenderSurfaces", client_name), - base::saturated_cast<int>(render_surface_layer_list_.size())); + base::saturated_cast<int>(render_surface_list_.size())); } } @@ -1122,8 +1062,7 @@ bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) { TRACE_EVENT2("cc", "LayerTreeImpl::UpdateDrawProperties::Occlusion", "IsActive", IsActiveTree(), "SourceFrameNumber", source_frame_number_); - OcclusionTracker occlusion_tracker( - layer_list_[0]->GetRenderSurface()->content_rect()); + OcclusionTracker occlusion_tracker(RootRenderSurface()->content_rect()); occlusion_tracker.set_minimum_tracking_size( settings().minimum_occlusion_tracking_size); @@ -1205,7 +1144,7 @@ bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) { size_t layers_updated_count = 0; bool tile_priorities_updated = false; for (PictureLayerImpl* layer : picture_layers_) { - if (!layer->is_drawn_render_surface_layer_list_member()) + if (!layer->contributes_to_drawn_render_surface()) continue; ++layers_updated_count; tile_priorities_updated |= layer->UpdateTiles(); @@ -1237,36 +1176,40 @@ void LayerTreeImpl::BuildPropertyTreesForTesting() { OuterViewportScrollLayer(), OverscrollElasticityLayer(), elastic_overscroll()->Current(IsActiveTree()), current_page_scale_factor(), device_scale_factor(), - gfx::Rect(DrawViewportSize()), layer_tree_host_impl_->DrawTransform(), - &property_trees_); + gfx::Rect(DeviceViewport().size()), + layer_tree_host_impl_->DrawTransform(), &property_trees_); property_trees_.transform_tree.set_source_to_parent_updates_allowed(false); } -const LayerImplList& LayerTreeImpl::RenderSurfaceLayerList() const { +const RenderSurfaceList& LayerTreeImpl::GetRenderSurfaceList() const { // If this assert triggers, then the list is dirty. DCHECK(!needs_update_draw_properties_); - return render_surface_layer_list_; + return render_surface_list_; } const Region& LayerTreeImpl::UnoccludedScreenSpaceRegion() const { - // If this assert triggers, then the render_surface_layer_list_ is dirty, so - // the unoccluded_screen_space_region_ is not valid anymore. + // If this assert triggers, then the render_surface_list_ is dirty, so the + // unoccluded_screen_space_region_ is not valid anymore. DCHECK(!needs_update_draw_properties_); return unoccluded_screen_space_region_; } gfx::SizeF LayerTreeImpl::ScrollableSize() const { - LayerImpl* root_scroll_layer = OuterViewportScrollLayer() - ? OuterViewportScrollLayer() - : InnerViewportScrollLayer(); - if (!root_scroll_layer) + LayerImpl* root_scroll_layer = nullptr; + LayerImpl* root_container_layer = nullptr; + if (OuterViewportScrollLayer()) { + root_scroll_layer = OuterViewportScrollLayer(); + root_container_layer = OuterViewportContainerLayer(); + } else if (InnerViewportScrollLayer()) { + root_scroll_layer = InnerViewportScrollLayer(); + root_container_layer = InnerViewportContainerLayer(); + } + + if (!root_scroll_layer || !root_container_layer) return gfx::SizeF(); gfx::SizeF content_size = root_scroll_layer->BoundsForScrolling(); - gfx::SizeF viewport_size = - root_scroll_layer->scroll_clip_layer()->BoundsForScrolling(); - - content_size.SetToMax(viewport_size); + content_size.SetToMax(root_container_layer->BoundsForScrolling()); return content_size; } @@ -1460,36 +1403,31 @@ gfx::Rect LayerTreeImpl::DeviceViewport() const { return layer_tree_host_impl_->DeviceViewport(); } -gfx::Size LayerTreeImpl::DrawViewportSize() const { - return layer_tree_host_impl_->DrawViewportSize(); -} - const gfx::Rect LayerTreeImpl::ViewportRectForTilePriority() const { return layer_tree_host_impl_->ViewportRectForTilePriority(); } std::unique_ptr<ScrollbarAnimationController> -LayerTreeImpl::CreateScrollbarAnimationController(int scroll_layer_id) { +LayerTreeImpl::CreateScrollbarAnimationController(ElementId scroll_element_id, + float initial_opacity) { DCHECK(!settings().scrollbar_fade_delay.is_zero()); DCHECK(!settings().scrollbar_fade_duration.is_zero()); base::TimeDelta fade_delay = settings().scrollbar_fade_delay; - base::TimeDelta fade_out_resize_delay = - settings().scrollbar_fade_out_resize_delay; base::TimeDelta fade_duration = settings().scrollbar_fade_duration; switch (settings().scrollbar_animator) { case LayerTreeSettings::ANDROID_OVERLAY: { return ScrollbarAnimationController:: CreateScrollbarAnimationControllerAndroid( - scroll_layer_id, layer_tree_host_impl_, fade_delay, - fade_out_resize_delay, fade_duration); + scroll_element_id, layer_tree_host_impl_, fade_delay, + fade_duration, initial_opacity); } case LayerTreeSettings::AURA_OVERLAY: { base::TimeDelta thinning_duration = settings().scrollbar_thinning_duration; return ScrollbarAnimationController:: CreateScrollbarAnimationControllerAuraOverlay( - scroll_layer_id, layer_tree_host_impl_, fade_delay, - fade_out_resize_delay, fade_duration, thinning_duration); + scroll_element_id, layer_tree_host_impl_, fade_delay, + fade_duration, thinning_duration, initial_opacity); } case LayerTreeSettings::NO_ANIMATOR: NOTREACHED(); @@ -1522,7 +1460,7 @@ void LayerTreeImpl::GetAllPrioritizedTilesForTracing( std::vector<PrioritizedTile>* prioritized_tiles) const { for (auto it = layer_list_.rbegin(); it != layer_list_.rend(); ++it) { LayerImpl* layer_impl = *it; - if (!layer_impl->is_drawn_render_surface_layer_list_member()) + if (!layer_impl->contributes_to_drawn_render_surface()) continue; layer_impl->GetAllPrioritizedTilesForTracing(prioritized_tiles); } @@ -1534,7 +1472,7 @@ void LayerTreeImpl::AsValueInto(base::trace_event::TracedValue* state) const { state->BeginArray("render_surface_layer_list"); for (auto it = layer_list_.rbegin(); it != layer_list_.rend(); ++it) { - if (!(*it)->is_drawn_render_surface_layer_list_member()) + if (!(*it)->contributes_to_drawn_render_surface()) continue; TracedValue::AppendIDRef(*it, state); } @@ -1719,41 +1657,65 @@ void LayerTreeImpl::UnregisterPictureLayerImpl(PictureLayerImpl* layer) { } void LayerTreeImpl::RegisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer) { - if (scrollbar_layer->ScrollLayerId() == Layer::INVALID_ID) + ElementId scroll_element_id = scrollbar_layer->scroll_element_id(); + if (!scroll_element_id) return; - scrollbar_map_.insert(std::pair<int, int>(scrollbar_layer->ScrollLayerId(), - scrollbar_layer->id())); - if (IsActiveTree() && scrollbar_layer->is_overlay_scrollbar()) + auto& scrollbar_ids = element_id_to_scrollbar_layer_ids_[scroll_element_id]; + if (scrollbar_layer->orientation() == HORIZONTAL) { + DCHECK_EQ(scrollbar_ids.horizontal, Layer::INVALID_ID) + << "Existing scrollbar should have been unregistered."; + scrollbar_ids.horizontal = scrollbar_layer->id(); + } else { + DCHECK_EQ(scrollbar_ids.vertical, Layer::INVALID_ID) + << "Existing scrollbar should have been unregistered."; + scrollbar_ids.vertical = scrollbar_layer->id(); + } + + if (IsActiveTree() && scrollbar_layer->is_overlay_scrollbar() && + scrollbar_layer->GetScrollbarAnimator() != + LayerTreeSettings::NO_ANIMATOR) { layer_tree_host_impl_->RegisterScrollbarAnimationController( - scrollbar_layer->ScrollLayerId()); + scroll_element_id, scrollbar_layer->Opacity()); + } - DidUpdateScrollState(scrollbar_layer->ScrollLayerId()); + // TODO(pdr): Refactor DidUpdateScrollState to use ElementIds instead of + // layer ids and remove this use of LayerIdByElementId. + DidUpdateScrollState(LayerIdByElementId(scroll_element_id)); } void LayerTreeImpl::UnregisterScrollbar( ScrollbarLayerImplBase* scrollbar_layer) { - int scroll_layer_id = scrollbar_layer->ScrollLayerId(); - if (scroll_layer_id == Layer::INVALID_ID) + ElementId scroll_element_id = scrollbar_layer->scroll_element_id(); + if (!scroll_element_id) return; - auto scrollbar_range = scrollbar_map_.equal_range(scroll_layer_id); - for (auto i = scrollbar_range.first; i != scrollbar_range.second; ++i) - if (i->second == scrollbar_layer->id()) { - scrollbar_map_.erase(i); - break; + auto& scrollbar_ids = element_id_to_scrollbar_layer_ids_[scroll_element_id]; + if (scrollbar_layer->orientation() == HORIZONTAL) + scrollbar_ids.horizontal = Layer::INVALID_ID; + else + scrollbar_ids.vertical = Layer::INVALID_ID; + + if (scrollbar_ids.horizontal == Layer::INVALID_ID && + scrollbar_ids.vertical == Layer::INVALID_ID) { + element_id_to_scrollbar_layer_ids_.erase(scroll_element_id); + if (IsActiveTree()) { + layer_tree_host_impl_->UnregisterScrollbarAnimationController( + scroll_element_id); } - - if (IsActiveTree() && scrollbar_map_.count(scroll_layer_id) == 0) - layer_tree_host_impl_->UnregisterScrollbarAnimationController( - scroll_layer_id); + } } -ScrollbarSet LayerTreeImpl::ScrollbarsFor(int scroll_layer_id) const { +ScrollbarSet LayerTreeImpl::ScrollbarsFor(ElementId scroll_element_id) const { ScrollbarSet scrollbars; - auto scrollbar_range = scrollbar_map_.equal_range(scroll_layer_id); - for (auto i = scrollbar_range.first; i != scrollbar_range.second; ++i) - scrollbars.insert(LayerById(i->second)->ToScrollbarLayer()); + auto it = element_id_to_scrollbar_layer_ids_.find(scroll_element_id); + if (it != element_id_to_scrollbar_layer_ids_.end()) { + const ScrollbarLayerIds& layer_ids = it->second; + if (layer_ids.horizontal != Layer::INVALID_ID) + scrollbars.insert(LayerById(layer_ids.horizontal)->ToScrollbarLayer()); + if (layer_ids.vertical != Layer::INVALID_ID) + scrollbars.insert(LayerById(layer_ids.vertical)->ToScrollbarLayer()); + } return scrollbars; } @@ -1765,6 +1727,9 @@ void LayerTreeImpl::RegisterScrollLayer(LayerImpl* layer) { std::pair<int, int>(layer->scroll_clip_layer_id(), layer->id())); DidUpdateScrollState(layer->id()); + + if (settings().scrollbar_animator == LayerTreeSettings::AURA_OVERLAY) + layer->set_needs_show_scrollbars(true); } void LayerTreeImpl::UnregisterScrollLayer(LayerImpl* layer) { @@ -1854,20 +1819,6 @@ static bool PointHitsRegion(const gfx::PointF& screen_space_point, gfx::ToRoundedPoint(hit_test_point_in_layer_space)); } -static const gfx::Transform SurfaceScreenSpaceTransform( - const LayerImpl* layer) { - const PropertyTrees* property_trees = - layer->layer_tree_impl()->property_trees(); - RenderSurfaceImpl* render_surface = layer->GetRenderSurface(); - DCHECK(render_surface); - return layer->is_drawn_render_surface_layer_list_member() - ? render_surface->screen_space_transform() - : property_trees - ->ToScreenSpaceTransformWithoutSurfaceContentsScale( - render_surface->TransformTreeIndex(), - render_surface->EffectTreeIndex()); -} - static bool PointIsClippedByAncestorClipNode( const gfx::PointF& screen_space_point, const LayerImpl* layer) { @@ -1901,15 +1852,6 @@ static bool PointIsClippedByAncestorClipNode( return true; } } - const LayerImpl* clip_node_owner = - layer->layer_tree_impl()->LayerById(clip_node->owning_layer_id); - RenderSurfaceImpl* render_surface = clip_node_owner->GetRenderSurface(); - if (render_surface && - !PointHitsRect(screen_space_point, - SurfaceScreenSpaceTransform(clip_node_owner), - render_surface->content_rect(), NULL)) { - return true; - } } return false; } @@ -2013,7 +1955,7 @@ LayerTreeImpl::FindFirstScrollingLayerOrDrawnScrollbarThatIsHitByPoint( struct HitTestVisibleScrollableOrTouchableFunctor { bool operator()(LayerImpl* layer) const { return layer->scrollable() || - layer->is_drawn_render_surface_layer_list_member() || + layer->contributes_to_drawn_render_surface() || !layer->touch_event_handler_region().IsEmpty(); } }; diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index 1d785f86e4e..ccff3c2447b 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -54,6 +54,35 @@ typedef std::vector<UIResourceRequest> UIResourceRequestQueue; typedef SyncedProperty<AdditionGroup<float>> SyncedBrowserControls; typedef SyncedProperty<AdditionGroup<gfx::Vector2dF>> SyncedElasticOverscroll; +class LayerTreeLifecycle { + public: + enum LifecycleState { + kNotSyncing, + + // The following states are the steps performed when syncing properties to + // this tree (see: LayerTreeHost::FinishCommitOnImplThread or + // LayerTreeHostImpl::ActivateSyncTree). + kBeginningSync, + kSyncedPropertyTrees, + kSyncedLayerProperties, + kLastSyncState = kSyncedLayerProperties, + + // TODO(pdr): Add states to cover more than just the synchronization steps. + }; + + void AdvanceTo(LifecycleState); + + bool AllowsPropertyTreeAccess() const { + return state_ == kNotSyncing || state_ >= kSyncedPropertyTrees; + } + bool AllowsLayerPropertyAccess() const { + return state_ == kNotSyncing || state_ >= kSyncedLayerProperties; + } + + private: + LifecycleState state_ = kNotSyncing; +}; + class CC_EXPORT LayerTreeImpl { public: // This is the number of times a fixed point has to be hit continuously by a @@ -92,10 +121,10 @@ class CC_EXPORT LayerTreeImpl { BeginFrameArgs CurrentBeginFrameArgs() const; base::TimeDelta CurrentBeginFrameInterval() const; gfx::Rect DeviceViewport() const; - gfx::Size DrawViewportSize() const; const gfx::Rect ViewportRectForTilePriority() const; std::unique_ptr<ScrollbarAnimationController> - CreateScrollbarAnimationController(int scroll_layer_id); + CreateScrollbarAnimationController(ElementId scroll_element_id, + float initial_opacity); void DidAnimateScrollOffset(); bool use_gpu_rasterization() const; GpuRasterizationStatus GetGpuRasterizationStatus() const; @@ -122,7 +151,7 @@ class CC_EXPORT LayerTreeImpl { LayerImpl* root_layer_for_testing() { return layer_list_.empty() ? nullptr : layer_list_[0]; } - RenderSurfaceImpl* RootRenderSurface() const; + const RenderSurfaceImpl* RootRenderSurface() const; bool LayerListIsEmpty() const; void SetRootLayerForTesting(std::unique_ptr<LayerImpl>); void OnCanDrawStateChangedForTree(); @@ -130,10 +159,14 @@ class CC_EXPORT LayerTreeImpl { std::unique_ptr<OwnedLayerImplList> DetachLayers(); void SetPropertyTrees(PropertyTrees* property_trees); - PropertyTrees* property_trees() { return &property_trees_; } - - void UpdatePropertyTreesForBoundsDelta(); + PropertyTrees* property_trees() { + // TODO(pdr): We should enable this DCHECK because it will catch uses of + // stale property trees, but it currently fails too many existing tests. + // DCHECK(lifecycle().AllowsPropertyTreeAccess()); + return &property_trees_; + } + void PushPropertyTreesTo(LayerTreeImpl* tree_impl); void PushPropertiesTo(LayerTreeImpl* tree_impl); void MoveChangeTrackingToLayers(); @@ -145,10 +178,6 @@ class CC_EXPORT LayerTreeImpl { LayerImplList::reverse_iterator rbegin(); LayerImplList::reverse_iterator rend(); - // TODO(crbug.com/702832): This won't be needed if overlay scrollbars have - // element ids. - void AddToOpacityAnimationsMap(int id, float opacity); - void SetTransformMutated(ElementId element_id, const gfx::Transform& transform); void SetOpacityMutated(ElementId element_id, float opacity); @@ -173,29 +202,44 @@ class CC_EXPORT LayerTreeImpl { hud_layer_ = layer_impl; } - LayerImpl* InnerViewportScrollLayer() const; - // This function may return NULL, it is the caller's responsibility to check. - LayerImpl* OuterViewportScrollLayer() const; gfx::ScrollOffset TotalScrollOffset() const; gfx::ScrollOffset TotalMaxScrollOffset() const; - LayerImpl* InnerViewportContainerLayer() const; - LayerImpl* OuterViewportContainerLayer() const; ScrollNode* CurrentlyScrollingNode(); const ScrollNode* CurrentlyScrollingNode() const; int LastScrolledScrollNodeIndex() const; void SetCurrentlyScrollingNode(ScrollNode* node); void ClearCurrentlyScrollingNode(); - void SetViewportLayersFromIds(int overscroll_elasticity_layer, - int page_scale_layer_id, - int inner_viewport_scroll_layer_id, - int outer_viewport_scroll_layer_id); + struct ViewportLayerIds { + int overscroll_elasticity = Layer::INVALID_ID; + int page_scale = Layer::INVALID_ID; + int inner_viewport_container = Layer::INVALID_ID; + int outer_viewport_container = Layer::INVALID_ID; + int inner_viewport_scroll = Layer::INVALID_ID; + int outer_viewport_scroll = Layer::INVALID_ID; + }; + void SetViewportLayersFromIds(const ViewportLayerIds& viewport_layer_ids); void ClearViewportLayers(); - LayerImpl* OverscrollElasticityLayer() { - return LayerById(overscroll_elasticity_layer_id_); + LayerImpl* OverscrollElasticityLayer() const { + return LayerById(viewport_layer_ids_.overscroll_elasticity); + } + LayerImpl* PageScaleLayer() const { + return LayerById(viewport_layer_ids_.page_scale); + } + LayerImpl* InnerViewportContainerLayer() const { + return LayerById(viewport_layer_ids_.inner_viewport_container); + } + LayerImpl* OuterViewportContainerLayer() const { + return LayerById(viewport_layer_ids_.outer_viewport_container); + } + LayerImpl* InnerViewportScrollLayer() const { + return LayerById(viewport_layer_ids_.inner_viewport_scroll); } - LayerImpl* PageScaleLayer() { return LayerById(page_scale_layer_id_); } + LayerImpl* OuterViewportScrollLayer() const { + return LayerById(viewport_layer_ids_.outer_viewport_scroll); + } + void ApplySentScrollAndScaleDeltasFromAbortedCommit(); SkColor background_color() const { return background_color_; } @@ -208,7 +252,8 @@ class CC_EXPORT LayerTreeImpl { has_transparent_background_ = transparent; } - void UpdatePropertyTreeScrollingAndAnimationFromMainThread(); + void UpdatePropertyTreeScrollingAndAnimationFromMainThread( + bool is_impl_side_update); void SetPageScaleOnActiveTree(float active_page_scale); void PushPageScaleFromMainThread(float page_scale_factor, float min_page_scale_factor, @@ -294,7 +339,7 @@ class CC_EXPORT LayerTreeImpl { void set_ui_resource_request_queue(UIResourceRequestQueue queue); - const LayerImplList& RenderSurfaceLayerList() const; + const RenderSurfaceList& GetRenderSurfaceList() const; const Region& UnoccludedScreenSpaceRegion() const; // These return the size of the root scrollable area and the size of @@ -395,7 +440,7 @@ class CC_EXPORT LayerTreeImpl { void RegisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer); void UnregisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer); - ScrollbarSet ScrollbarsFor(int scroll_layer_id) const; + ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const; void RegisterScrollLayer(LayerImpl* layer); void UnregisterScrollLayer(LayerImpl* layer); @@ -465,8 +510,12 @@ class CC_EXPORT LayerTreeImpl { void ClearLayerList(); void BuildLayerListForTesting(); + void HandleScrollbarShowRequestsFromMain(); + + void InvalidateRegionForImages( + const PaintImageIdFlatSet& images_to_invalidate); - void InvalidateRegionForImages(const ImageIdFlatSet& images_to_invalidate); + LayerTreeLifecycle& lifecycle() { return lifecycle_; } protected: float ClampPageScaleFactorToLimits(float page_scale_factor) const; @@ -477,7 +526,6 @@ class CC_EXPORT LayerTreeImpl { float max_page_scale_factor); bool IsViewportLayerId(int id) const; void UpdateScrollbars(int scroll_layer_id, int clip_layer_id); - void ShowScrollbars(); void DidUpdatePageScale(); void PushBrowserControls(const float* top_controls_shown_ratio); bool ClampBrowserControlsShownRatio(); @@ -492,10 +540,8 @@ class CC_EXPORT LayerTreeImpl { bool has_transparent_background_; int last_scrolled_scroll_node_index_; - int overscroll_elasticity_layer_id_; - int page_scale_layer_id_; - int inner_viewport_scroll_layer_id_; - int outer_viewport_scroll_layer_id_; + + ViewportLayerIds viewport_layer_ids_; LayerSelection selection_; @@ -532,18 +578,21 @@ class CC_EXPORT LayerTreeImpl { // derived from LayerImpl::scroll_clip_layer_ and exists to avoid O(n) walks.) std::unordered_map<int, int> clip_scroll_map_; - // Maps scroll layer ids to scrollbar layer ids. For each scroll layer, there - // may be 1 or 2 scrollbar layers (for vertical and horizontal). (This is - // derived from ScrollbarLayerImplBase::scroll_layer_id_ and exists to avoid - // O(n) walks.) - std::multimap<int, int> scrollbar_map_; + struct ScrollbarLayerIds { + int horizontal = Layer::INVALID_ID; + int vertical = Layer::INVALID_ID; + }; + // Each scroll layer can have up to two scrollbar layers (vertical and + // horizontal). This mapping is maintained as part of scrollbar registration. + base::flat_map<ElementId, ScrollbarLayerIds> + element_id_to_scrollbar_layer_ids_; std::vector<PictureLayerImpl*> picture_layers_; LayerImplList surface_layers_; - // List of visible layers for the most recently prepared frame. - LayerImplList render_surface_layer_list_; - // After drawing the |render_surface_layer_list_| the areas in this region + // List of render surfaces for the most recently prepared frame. + RenderSurfaceList render_surface_list_; + // After drawing the |render_surface_list_| the areas in this region // would not be fully covered by opaque content. Region unoccluded_screen_space_region_; @@ -581,6 +630,10 @@ class CC_EXPORT LayerTreeImpl { std::unique_ptr<PendingPageScaleAnimation> pending_page_scale_animation_; + // Tracks the lifecycle which is used for enforcing dependencies between + // lifecycle states. See: |LayerTreeLifecycle|. + LayerTreeLifecycle lifecycle_; + private: DISALLOW_COPY_AND_ASSIGN(LayerTreeImpl); }; diff --git a/chromium/cc/trees/layer_tree_impl_unittest.cc b/chromium/cc/trees/layer_tree_impl_unittest.cc index 3b0cbeec663..836ba530118 100644 --- a/chromium/cc/trees/layer_tree_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_impl_unittest.cc @@ -36,8 +36,8 @@ class LayerTreeImplTest : public testing::Test { LayerImpl* root_layer() { return impl_test_.root_layer_for_testing(); } - const LayerImplList& RenderSurfaceLayerList() const { - return host_impl().active_tree()->RenderSurfaceLayerList(); + const RenderSurfaceList& GetRenderSurfaceList() const { + return host_impl().active_tree()->GetRenderSurfaceList(); } void ExecuteCalculateDrawProperties(LayerImpl* root_layer) { @@ -45,9 +45,9 @@ class LayerTreeImplTest : public testing::Test { // empty. DCHECK(!root_layer->bounds().IsEmpty()); - render_surface_layer_list_impl_.clear(); + render_surface_list_impl_.clear(); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root_layer, root_layer->bounds(), &render_surface_layer_list_impl_); + root_layer, root_layer->bounds(), &render_surface_list_impl_); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); } @@ -105,7 +105,7 @@ class LayerTreeImplTest : public testing::Test { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayerForTesting(std::move(root)); host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); - CHECK_EQ(1u, RenderSurfaceLayerList().size()); + CHECK_EQ(1u, GetRenderSurfaceList().size()); gfx::PointF test_point = gfx::PointF(1.f, 1.f); LayerImpl* result_layer = @@ -117,7 +117,7 @@ class LayerTreeImplTest : public testing::Test { private: LayerTestCommon::LayerImplTest impl_test_; - std::vector<LayerImpl*> render_surface_layer_list_impl_; + RenderSurfaceList render_surface_list_impl_; }; TEST_F(LayerTreeImplTest, HitTestingForSingleLayer) { @@ -130,8 +130,8 @@ TEST_F(LayerTreeImplTest, HitTestingForSingleLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root_layer()->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root_layer())->num_contributors()); // Hit testing for a point outside the layer should return a null pointer. gfx::PointF test_point(101.f, 101.f); @@ -199,8 +199,8 @@ TEST_F(LayerTreeImplTest, HitTestingForSingleLayerAndHud) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(2u, root_layer()->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(2, GetRenderSurface(root_layer())->num_contributors()); // Hit testing for a point inside HUD, but outside root should return null gfx::PointF test_point(101.f, 101.f); @@ -244,8 +244,8 @@ TEST_F(LayerTreeImplTest, HitTestingForUninvertibleTransform) { host_impl().SetViewportSize(root->bounds()); host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root_layer()->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root_layer())->num_contributors()); ASSERT_FALSE(root_layer()->ScreenSpaceTransform().IsInvertible()); // Hit testing any point should not hit the layer. If the invertible matrix is @@ -299,8 +299,8 @@ TEST_F(LayerTreeImplTest, HitTestingForSinglePositionedLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root_layer()->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root_layer())->num_contributors()); // Hit testing for a point outside the layer should return a null pointer. gfx::PointF test_point(49.f, 49.f); @@ -344,8 +344,8 @@ TEST_F(LayerTreeImplTest, HitTestingForSingleRotatedLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root_layer()->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root_layer())->num_contributors()); // Hit testing for points outside the layer. // These corners would have been inside the un-transformed layer, but they @@ -461,37 +461,6 @@ TEST_F(LayerTreeImplTest, HitTestingSiblings) { EXPECT_EQ(3, result_layer->id()); } -TEST_F(LayerTreeImplTest, HitTestingPointOutsideMaxTextureSize) { - gfx::Transform identity_matrix; - int max_texture_size = - host_impl().active_tree()->resource_provider()->max_texture_size(); - gfx::Size bounds(max_texture_size + 100, max_texture_size + 100); - - LayerImpl* root = root_layer(); - root->SetBounds(bounds); - - std::unique_ptr<LayerImpl> surface = - LayerImpl::Create(host_impl().active_tree(), 2); - surface->SetBounds(bounds); - surface->SetMasksToBounds(true); - surface->SetDrawsContent(true); - surface->test_properties()->force_render_surface = true; - - root->test_properties()->AddChild(std::move(surface)); - host_impl().SetViewportSize(root->bounds()); - host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); - - gfx::PointF test_point(max_texture_size - 50, max_texture_size - 50); - LayerImpl* result_layer = - host_impl().active_tree()->FindLayerThatIsHitByPoint(test_point); - EXPECT_TRUE(result_layer); - - test_point = gfx::PointF(max_texture_size + 50, max_texture_size + 50); - result_layer = - host_impl().active_tree()->FindLayerThatIsHitByPoint(test_point); - EXPECT_FALSE(result_layer); -} - TEST_F(LayerTreeImplTest, HitTestingForSinglePerspectiveLayer) { // perspective_projection_about_center * translation_by_z is designed so that // the 100 x 100 layer becomes 50 x 50, and remains centered at (50, 50). @@ -512,8 +481,8 @@ TEST_F(LayerTreeImplTest, HitTestingForSinglePerspectiveLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root_layer()->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root_layer())->num_contributors()); // Hit testing for points outside the layer. // These corners would have been inside the un-transformed layer, but they @@ -570,9 +539,10 @@ TEST_F(LayerTreeImplTest, HitTestingForSimpleClippedLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root_layer()->GetRenderSurface()->layer_list().size()); - ASSERT_EQ(456, root_layer()->GetRenderSurface()->layer_list().at(0)->id()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root_layer())->num_contributors()); + LayerImpl* child_layer = host_impl().active_tree()->LayerById(456); + EXPECT_TRUE(child_layer->contributes_to_drawn_render_surface()); // Hit testing for a point outside the layer should return a null pointer. // Despite the child layer being very large, it should be clipped to the root @@ -747,9 +717,10 @@ TEST_F(LayerTreeImplTest, HitTestingForNonClippingIntermediateLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root_layer()->GetRenderSurface()->layer_list().size()); - ASSERT_EQ(456, root_layer()->GetRenderSurface()->layer_list().at(0)->id()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root_layer())->num_contributors()); + LayerImpl* child_layer = host_impl().active_tree()->LayerById(456); + EXPECT_TRUE(child_layer->contributes_to_drawn_render_surface()); // Hit testing for a point outside the layer should return a null pointer. gfx::PointF test_point(69.f, 69.f); @@ -827,14 +798,14 @@ TEST_F(LayerTreeImplTest, HitTestingForMultipleLayers) { ASSERT_TRUE(child1); ASSERT_TRUE(child2); ASSERT_TRUE(grand_child1); - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); - RenderSurfaceImpl* root_render_surface = root->GetRenderSurface(); - ASSERT_EQ(4u, root_render_surface->layer_list().size()); - ASSERT_EQ(1, root_render_surface->layer_list().at(0)->id()); // root layer - ASSERT_EQ(2, root_render_surface->layer_list().at(1)->id()); // child1 - ASSERT_EQ(4, root_render_surface->layer_list().at(2)->id()); // grand_child1 - ASSERT_EQ(3, root_render_surface->layer_list().at(3)->id()); // child2 + RenderSurfaceImpl* root_render_surface = GetRenderSurface(root); + ASSERT_EQ(4, root_render_surface->num_contributors()); + EXPECT_TRUE(root_layer()->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child1->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child2->contributes_to_drawn_render_surface()); + EXPECT_TRUE(grand_child1->contributes_to_drawn_render_surface()); // Nothing overlaps the root at (1, 1), so hit testing there should find // the root layer. @@ -979,7 +950,7 @@ TEST_F(LayerTreeImplTest, HitTestingForMultipleLayersAtVaryingDepths) { ASSERT_TRUE(child1); ASSERT_TRUE(child2); ASSERT_TRUE(grand_child1); - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); // Nothing overlaps the root_layer at (1, 1), so hit testing there should find // the root layer. @@ -1176,21 +1147,21 @@ TEST_F(LayerTreeImplTest, HitTestingForMultipleLayerLists) { ASSERT_TRUE(child1); ASSERT_TRUE(child2); ASSERT_TRUE(grand_child1); - ASSERT_TRUE(child1->GetRenderSurface()); - ASSERT_TRUE(child2->GetRenderSurface()); - ASSERT_TRUE(grand_child1->GetRenderSurface()); - ASSERT_EQ(4u, RenderSurfaceLayerList().size()); + ASSERT_TRUE(GetRenderSurface(child1)); + ASSERT_TRUE(GetRenderSurface(child2)); + ASSERT_TRUE(GetRenderSurface(grand_child1)); + ASSERT_EQ(4u, GetRenderSurfaceList().size()); // The root surface has the root layer, and child1's and child2's render // surfaces. - ASSERT_EQ(3u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(3, GetRenderSurface(root)->num_contributors()); // The child1 surface has the child1 layer and grand_child1's render surface. - ASSERT_EQ(2u, child1->GetRenderSurface()->layer_list().size()); - ASSERT_EQ(1u, child2->GetRenderSurface()->layer_list().size()); - ASSERT_EQ(1u, grand_child1->GetRenderSurface()->layer_list().size()); - ASSERT_EQ(1, RenderSurfaceLayerList().at(0)->id()); // root layer - ASSERT_EQ(2, RenderSurfaceLayerList()[1]->id()); // child1 - ASSERT_EQ(4, RenderSurfaceLayerList().at(2)->id()); // grand_child1 - ASSERT_EQ(3, RenderSurfaceLayerList()[3]->id()); // child2 + ASSERT_EQ(2, GetRenderSurface(child1)->num_contributors()); + ASSERT_EQ(1, GetRenderSurface(child2)->num_contributors()); + ASSERT_EQ(1, GetRenderSurface(grand_child1)->num_contributors()); + EXPECT_TRUE(root_layer()->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child1->contributes_to_drawn_render_surface()); + EXPECT_TRUE(grand_child1->contributes_to_drawn_render_surface()); + EXPECT_TRUE(child2->contributes_to_drawn_render_surface()); // Nothing overlaps the root at (1, 1), so hit testing there should find // the root layer. @@ -1251,8 +1222,8 @@ TEST_F(LayerTreeImplTest, HitCheckingTouchHandlerRegionsForSingleLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root)->num_contributors()); // Hit checking for any point should return a null pointer for a layer without // any touch event handler regions. @@ -1328,8 +1299,8 @@ TEST_F(LayerTreeImplTest, host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root)->num_contributors()); ASSERT_FALSE(root->ScreenSpaceTransform().IsInvertible()); // Hit checking any point should not hit the touch handler region on the @@ -1395,8 +1366,8 @@ TEST_F(LayerTreeImplTest, host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root)->num_contributors()); // Hit checking for a point outside the layer should return a null pointer. gfx::PointF test_point(49.f, 49.f); @@ -1468,8 +1439,10 @@ TEST_F(LayerTreeImplTest, host_impl().SetViewportSize(scaled_bounds_for_root); host_impl().active_tree()->SetDeviceScaleFactor(device_scale_factor); - host_impl().active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 1, 1, - Layer::INVALID_ID); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = 1; + viewport_ids.inner_viewport_scroll = 1; + host_impl().active_tree()->SetViewportLayersFromIds(viewport_ids); host_impl().active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl().active_tree()->PushPageScaleFromMainThread( page_scale_factor, page_scale_factor, max_page_scale_factor); @@ -1480,8 +1453,8 @@ TEST_F(LayerTreeImplTest, // The visible content rect for test_layer is actually 100x100, even though // its layout size is 50x50, positioned at 25x25. LayerImpl* test_layer = root->test_properties()->children[0]; - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root)->num_contributors()); // Check whether the child layer fits into the root after scaled. EXPECT_EQ(gfx::Rect(test_layer->bounds()), test_layer->visible_layer_rect()); @@ -1607,9 +1580,10 @@ TEST_F(LayerTreeImplTest, HitCheckingTouchHandlerRegionsForSimpleClippedLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root->GetRenderSurface()->layer_list().size()); - ASSERT_EQ(456, root->GetRenderSurface()->layer_list().at(0)->id()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root)->num_contributors()); + LayerImpl* child_layer = host_impl().active_tree()->LayerById(456); + EXPECT_TRUE(child_layer->contributes_to_drawn_render_surface()); // Hit checking for a point outside the layer should return a null pointer. // Despite the child layer being very large, it should be clipped to the root @@ -1694,8 +1668,10 @@ TEST_F(LayerTreeImplTest, host_impl().SetViewportSize(scaled_bounds_for_root); host_impl().active_tree()->SetDeviceScaleFactor(device_scale_factor); - host_impl().active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 1, 1, - Layer::INVALID_ID); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = 1; + viewport_ids.inner_viewport_scroll = 1; + host_impl().active_tree()->SetViewportLayersFromIds(viewport_ids); host_impl().active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl().active_tree()->PushPageScaleFromMainThread( page_scale_factor, page_scale_factor, max_page_scale_factor); @@ -1703,7 +1679,7 @@ TEST_F(LayerTreeImplTest, host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(2u, RenderSurfaceLayerList().size()); + ASSERT_EQ(2u, GetRenderSurfaceList().size()); // Hit checking for a point outside the layer should return a null pointer. // Despite the child layer being very large, it should be clipped to the root @@ -1760,10 +1736,12 @@ TEST_F(LayerTreeImplTest, HitCheckingTouchHandlerOverlappingRegions) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(2u, root->GetRenderSurface()->layer_list().size()); - ASSERT_EQ(123, root->GetRenderSurface()->layer_list().at(0)->id()); - ASSERT_EQ(1234, root->GetRenderSurface()->layer_list().at(1)->id()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(2, GetRenderSurface(root)->num_contributors()); + LayerImpl* touch_layer = host_impl().active_tree()->LayerById(123); + LayerImpl* notouch_layer = host_impl().active_tree()->LayerById(1234); + EXPECT_TRUE(touch_layer->contributes_to_drawn_render_surface()); + EXPECT_TRUE(notouch_layer->contributes_to_drawn_render_surface()); gfx::PointF test_point(35.f, 35.f); LayerImpl* result_layer = @@ -1816,10 +1794,10 @@ TEST_F(LayerTreeImplTest, HitTestingTouchHandlerRegionsForLayerThatIsNotDrawn) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); LayerImpl* test_layer = root->test_properties()->children[0]; - // As test_layer doesn't draw content, the layer list of root's render surface - // should contain only the root layer. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root->GetRenderSurface()->layer_list().size()); + // As test_layer doesn't draw content, it shouldn't contribute content to the + // root surface. + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + EXPECT_FALSE(test_layer->contributes_to_drawn_render_surface()); // Hit testing for a point outside the test layer should return null pointer. // We also implicitly check that the updated screen space transform of a layer @@ -1837,7 +1815,7 @@ TEST_F(LayerTreeImplTest, HitTestingTouchHandlerRegionsForLayerThatIsNotDrawn) { host_impl().active_tree()->FindLayerThatIsHitByPointInTouchHandlerRegion( test_point); EXPECT_FALSE(result_layer); - EXPECT_FALSE(test_layer->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(test_layer->contributes_to_drawn_render_surface()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_screen_space_transform, draw_property_utils::ScreenSpaceTransform( @@ -1859,7 +1837,7 @@ TEST_F(LayerTreeImplTest, HitTestingTouchHandlerRegionsForLayerThatIsNotDrawn) { test_point); ASSERT_TRUE(result_layer); ASSERT_EQ(test_layer, result_layer); - EXPECT_FALSE(result_layer->is_drawn_render_surface_layer_list_member()); + EXPECT_FALSE(result_layer->contributes_to_drawn_render_surface()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_screen_space_transform, draw_property_utils::ScreenSpaceTransform( @@ -1876,8 +1854,8 @@ TEST_F(LayerTreeImplTest, SelectionBoundsForSingleLayer) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); - ASSERT_EQ(1u, root->GetRenderSurface()->layer_list().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); + ASSERT_EQ(1, GetRenderSurface(root)->num_contributors()); LayerSelection input; @@ -1957,7 +1935,7 @@ TEST_F(LayerTreeImplTest, SelectionBoundsForPartialOccludedLayers) { host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); LayerSelection input; input.start.type = gfx::SelectionBound::LEFT; @@ -2084,18 +2062,19 @@ TEST_F(LayerTreeImplTest, SelectionBoundsForScaledLayers) { root->bounds(), device_scale_factor * page_scale_factor); host_impl().SetViewportSize(scaled_bounds_for_root); - host_impl().active_tree()->SetViewportLayersFromIds(0, root->id(), 0, 0); + LayerTreeImpl::ViewportLayerIds viewport_ids; + viewport_ids.page_scale = root->id(); + host_impl().active_tree()->SetViewportLayersFromIds(viewport_ids); host_impl().active_tree()->SetDeviceScaleFactor(device_scale_factor); host_impl().active_tree()->SetPageScaleOnActiveTree(page_scale_factor); - host_impl().active_tree()->SetViewportLayersFromIds(Layer::INVALID_ID, 1, 1, - Layer::INVALID_ID); + host_impl().active_tree()->PushPageScaleFromMainThread( page_scale_factor, page_scale_factor, page_scale_factor); host_impl().active_tree()->SetPageScaleOnActiveTree(page_scale_factor); host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. - ASSERT_EQ(1u, RenderSurfaceLayerList().size()); + ASSERT_EQ(1u, GetRenderSurfaceList().size()); LayerSelection input; input.start.type = gfx::SelectionBound::LEFT; @@ -2263,7 +2242,7 @@ TEST_F(LayerTreeImplTest, HitTestingCorrectLayerWheelListener) { host_impl().SetViewportSize(root->bounds()); host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); - CHECK_EQ(1u, RenderSurfaceLayerList().size()); + CHECK_EQ(1u, GetRenderSurfaceList().size()); gfx::PointF test_point = gfx::PointF(1.f, 1.f); LayerImpl* result_layer = diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h index 90348788b5e..5191cdc20e3 100644 --- a/chromium/cc/trees/layer_tree_settings.h +++ b/chromium/cc/trees/layer_tree_settings.h @@ -50,7 +50,6 @@ class CC_EXPORT LayerTreeSettings { }; ScrollbarAnimator scrollbar_animator = NO_ANIMATOR; base::TimeDelta scrollbar_fade_delay; - base::TimeDelta scrollbar_fade_out_resize_delay; base::TimeDelta scrollbar_fade_duration; base::TimeDelta scrollbar_thinning_duration; SkColor solid_color_scrollbar_color = SK_ColorWHITE; @@ -79,11 +78,6 @@ class CC_EXPORT LayerTreeSettings { bool ignore_root_layer_flings = false; size_t scheduled_raster_task_limit = 32; bool use_occlusion_for_tile_prioritization = false; - - // TODO(khushalsagar): Enable for all client and remove this flag if possible. - // See crbug/com/696864. - bool image_decode_tasks_enabled = false; - bool use_layer_lists = false; int max_staging_buffer_usage_in_bytes = 32 * 1024 * 1024; ManagedMemoryPolicy gpu_memory_policy; @@ -112,6 +106,11 @@ class CC_EXPORT LayerTreeSettings { // Indicates the case when a sub-frame gets its own LayerTree because it's // rendered in a different process from its ancestor frames. bool is_layer_tree_for_subframe = false; + + // Determines whether we disallow non-exact matches when finding resources + // in ResourcePool. Only used for layout or pixel tests, as non-deterministic + // resource sizes can lead to floating point error and noise in these tests. + bool disallow_non_exact_resource_reuse = false; }; } // namespace cc diff --git a/chromium/cc/trees/mutator_host.h b/chromium/cc/trees/mutator_host.h index d454539c874..0c9825107da 100644 --- a/chromium/cc/trees/mutator_host.h +++ b/chromium/cc/trees/mutator_host.h @@ -116,7 +116,8 @@ class MutatorHost { ElementId element_id, const gfx::ScrollOffset& target_offset, const gfx::ScrollOffset& current_offset, - base::TimeDelta delayed_by) = 0; + base::TimeDelta delayed_by, + base::TimeDelta animation_start_offset) = 0; virtual bool ImplOnlyScrollAnimationUpdateTarget( ElementId element_id, const gfx::Vector2dF& scroll_delta, diff --git a/chromium/cc/trees/occlusion_tracker.cc b/chromium/cc/trees/occlusion_tracker.cc index 523b01f830f..8aa513bfe17 100644 --- a/chromium/cc/trees/occlusion_tracker.cc +++ b/chromium/cc/trees/occlusion_tracker.cc @@ -221,16 +221,17 @@ static void ReduceOcclusionBelowSurface( if (affected_area_in_target.IsEmpty()) return; + // The filter's bounds for asymmetric filters (ex: drop shadow) are + // relative to the target surface, which moves the pixels from outside of the + // clip to the filtered surface. As a result, |affected_area| needs to expand. + // Since we are concerned with the target surface, we need to swap the outsets + // before applying them to the filtered surface bounds. int outset_top, outset_right, outset_bottom, outset_left; contributing_surface->BackgroundFilters().GetOutsets( - &outset_top, &outset_right, &outset_bottom, &outset_left); - - // The filter can move pixels from outside of the clip, so allow affected_area - // to expand outside the clip. Notably the content we're concerned with here - // is not the affected area, but rather stuff slightly outside it. Thus the - // directions of the outsets are reversed from normal. - affected_area_in_target.Inset(-outset_right, -outset_bottom, -outset_left, - -outset_top); + &outset_bottom, &outset_left, &outset_top, &outset_right); + + affected_area_in_target.Inset(-outset_left, -outset_top, -outset_right, + -outset_bottom); SimpleEnclosedRegion affected_occlusion = *occlusion_from_inside_target; affected_occlusion.Intersect(affected_area_in_target); diff --git a/chromium/cc/trees/occlusion_tracker_unittest.cc b/chromium/cc/trees/occlusion_tracker_unittest.cc index 8f635a30ccd..a0218f64497 100644 --- a/chromium/cc/trees/occlusion_tracker_unittest.cc +++ b/chromium/cc/trees/occlusion_tracker_unittest.cc @@ -19,6 +19,7 @@ #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" +#include "cc/test/layer_test_common.h" #include "cc/test/test_occlusion_tracker.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host_common.h" @@ -77,7 +78,8 @@ class TestOcclusionTrackerWithClip : public TestOcclusionTracker { gfx::Rect UnoccludedSurfaceContentRect(const LayerImpl* layer, const gfx::Rect& content_rect) const { - RenderSurfaceImpl* surface = layer->GetRenderSurface(); + const RenderSurfaceImpl* surface = + GetRenderSurface(const_cast<LayerImpl*>(layer)); return this->GetCurrentOcclusionForContributingSurface( surface->draw_transform()) .GetUnoccludedContentRect(content_rect); @@ -188,7 +190,7 @@ class OcclusionTrackerTest : public testing::Test { void DestroyLayers() { host_->host_impl()->active_tree()->SetRootLayerForTesting(nullptr); - render_surface_layer_list_impl_.clear(); + render_surface_list_impl_.clear(); mask_layers_.clear(); layer_iterator_.reset(); } @@ -217,7 +219,7 @@ class OcclusionTrackerTest : public testing::Test { root->layer_tree_impl()->property_trees()->needs_rebuild = true; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( - root, root->bounds(), &render_surface_layer_list_impl_); + root, root->bounds(), &render_surface_list_impl_); inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); @@ -247,7 +249,7 @@ class OcclusionTrackerTest : public testing::Test { void EnterContributingSurface(LayerImpl* layer, OcclusionTracker* occlusion) { ASSERT_EQ(layer_iterator_->target_render_surface(), - layer->GetRenderSurface()); + GetRenderSurface(layer)); ASSERT_TRUE(layer_iterator_->state() == EffectTreeLayerListIterator::State::TARGET_SURFACE); occlusion->EnterLayer(*layer_iterator_); @@ -260,7 +262,7 @@ class OcclusionTrackerTest : public testing::Test { void LeaveContributingSurface(LayerImpl* layer, OcclusionTracker* occlusion) { ASSERT_EQ(layer_iterator_->current_render_surface(), - layer->GetRenderSurface()); + GetRenderSurface(layer)); ASSERT_TRUE(layer_iterator_->state() == EffectTreeLayerListIterator::State::CONTRIBUTING_SURFACE); occlusion->LeaveLayer(*layer_iterator_); @@ -305,7 +307,7 @@ class OcclusionTrackerTest : public testing::Test { std::unique_ptr<AnimationHost> animation_host_; std::unique_ptr<FakeLayerTreeHost> host_; // These hold ownership of the layers for the duration of the test. - LayerImplList render_surface_layer_list_impl_; + RenderSurfaceList render_surface_list_impl_; std::unique_ptr<EffectTreeLayerListIterator> layer_iterator_; LayerList mask_layers_; int next_layer_impl_id_; diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc index 4d1ca75e387..f450baa7067 100644 --- a/chromium/cc/trees/property_tree.cc +++ b/chromium/cc/trees/property_tree.cc @@ -352,24 +352,25 @@ gfx::Vector2dF StickyPositionOffset(TransformTree* tree, TransformNode* node) { return gfx::Vector2dF(); StickyPositionNodeData* sticky_data = tree->StickyPositionData(node->id); const LayerStickyPositionConstraint& constraint = sticky_data->constraints; + auto& property_trees = *tree->property_trees(); ScrollNode* scroll_node = - tree->property_trees()->scroll_tree.Node(sticky_data->scroll_ancestor); - gfx::ScrollOffset scroll_offset = - tree->property_trees()->scroll_tree.current_scroll_offset( - scroll_node->owning_layer_id); + property_trees.scroll_tree.Node(sticky_data->scroll_ancestor); + TransformNode* transform_node = + property_trees.transform_tree.Node(scroll_node->transform_id); + const auto& scroll_offset = transform_node->scroll_offset; + DCHECK(property_trees.scroll_tree.current_scroll_offset( + scroll_node->owning_layer_id) == scroll_offset); gfx::PointF scroll_position(scroll_offset.x(), scroll_offset.y()); - TransformNode* scroll_ancestor_transform_node = - tree->Node(scroll_node->transform_id); - if (scroll_ancestor_transform_node->scrolls) { + if (transform_node->scrolls) { // The scroll position does not include snapping which shifts the scroll // offset to align to a pixel boundary, we need to manually include it here. // In this case, snapping is caused by a scroll. - scroll_position -= scroll_ancestor_transform_node->snap_amount; + scroll_position -= transform_node->snap_amount; } gfx::RectF clip( scroll_position, - gfx::SizeF(tree->property_trees()->scroll_tree.scroll_clip_layer_bounds( + gfx::SizeF(property_trees.scroll_tree.scroll_clip_layer_bounds( scroll_node->id))); gfx::Vector2dF layer_offset(sticky_data->main_thread_offset); @@ -824,7 +825,8 @@ void EffectTree::UpdateBackfaceVisibility(EffectNode* node, if (parent_node && parent_node->hidden_by_backface_visibility) { node->hidden_by_backface_visibility = true; return; - } else if (node->double_sided) { + } + if (node->double_sided) { node->hidden_by_backface_visibility = false; return; } @@ -870,11 +872,6 @@ EffectNode* EffectTree::FindNodeFromElementId(ElementId id) { bool EffectTree::OnOpacityAnimated(ElementId id, float opacity) { EffectNode* node = FindNodeFromElementId(id); DCHECK(node); - // TODO(crbug.com/706766): Avoid crash. Need more investigation for what is - // calling this without setting element id. - if (!node) - return false; - if (node->opacity == opacity) return false; node->opacity = opacity; @@ -888,11 +885,6 @@ bool EffectTree::OnFilterAnimated(ElementId id, const FilterOperations& filters) { EffectNode* node = FindNodeFromElementId(id); DCHECK(node); - // TODO(crbug.com/706766): Avoid crash. Need more investigation for what is - // calling this without setting element id. - if (!node) - return false; - if (node->filters == filters) return false; node->filters = filters; @@ -986,8 +978,9 @@ bool EffectTree::HasCopyRequests() const { void EffectTree::ClearCopyRequests() { for (auto& node : nodes()) { - node.num_copy_requests_in_subtree = 0; + node.subtree_has_copy_request = false; node.has_copy_request = false; + node.closest_ancestor_with_copy_request_id = EffectTree::kInvalidNodeId; } // Any copy requests that are still left will be aborted (sending an empty @@ -996,33 +989,29 @@ void EffectTree::ClearCopyRequests() { set_needs_update(true); } -int EffectTree::ClosestAncestorWithCopyRequest(int id) const { - DCHECK_GE(id, EffectTree::kRootNodeId); - const EffectNode* node = Node(id); - while (node->id > EffectTree::kContentsRootNodeId) { - if (node->has_copy_request) - return node->id; - - node = parent(node); +int EffectTree::LowestCommonAncestorWithRenderSurface(int id_1, + int id_2) const { + DCHECK(GetRenderSurface(id_1)); + DCHECK(GetRenderSurface(id_2)); + while (id_1 != id_2) { + if (id_1 < id_2) + id_2 = Node(id_2)->target_id; + else + id_1 = Node(id_1)->target_id; } - if (node->has_copy_request) - return node->id; - else - return EffectTree::kInvalidNodeId; + return id_1; } void EffectTree::AddMaskLayerId(int id) { mask_layer_ids_.push_back(id); } -void EffectTree::UpdateRenderSurfaces(LayerTreeImpl* layer_tree_impl, - bool non_root_surfaces_enabled) { +void EffectTree::UpdateRenderSurfaces(LayerTreeImpl* layer_tree_impl) { for (int id = kContentsRootNodeId; id < static_cast<int>(size()); ++id) { EffectNode* effect_node = Node(id); bool needs_render_surface = - id == kContentsRootNodeId || - (non_root_surfaces_enabled && effect_node->has_render_surface); + id == kContentsRootNodeId || effect_node->has_render_surface; if (needs_render_surface == !!render_surfaces_[id]) continue; @@ -1211,6 +1200,14 @@ void ScrollTree::CopyCompleteTreeState(const ScrollTree& other) { } #endif +ScrollNode* ScrollTree::FindNodeFromElementId(ElementId id) { + auto iterator = property_trees()->element_id_to_scroll_node_index.find(id); + if (iterator == property_trees()->element_id_to_scroll_node_index.end()) + return nullptr; + + return Node(iterator->second); +} + void ScrollTree::clear() { PropertyTree<ScrollNode>::clear(); @@ -1349,11 +1346,9 @@ SyncedScrollOffset* ScrollTree::GetOrCreateSyncedScrollOffset(int layer_id) { const SyncedScrollOffset* ScrollTree::GetSyncedScrollOffset( int layer_id) const { DCHECK(!property_trees()->is_main_thread); - if (layer_id_to_synced_scroll_offset_map_.find(layer_id) == - layer_id_to_synced_scroll_offset_map_.end()) { - return nullptr; - } - return layer_id_to_synced_scroll_offset_map_.at(layer_id).get(); + auto it = layer_id_to_synced_scroll_offset_map_.find(layer_id); + return it != layer_id_to_synced_scroll_offset_map_.end() ? it->second.get() + : nullptr; } const gfx::ScrollOffset ScrollTree::current_scroll_offset(int layer_id) const { @@ -1606,7 +1601,6 @@ PropertyTreesCachedData::~PropertyTreesCachedData() {} PropertyTrees::PropertyTrees() : needs_rebuild(true), - non_root_surfaces_enabled(true), can_adjust_raster_scales(true), changed(false), full_tree_damaged(false), @@ -1631,13 +1625,10 @@ bool PropertyTrees::operator==(const PropertyTrees& other) const { other.element_id_to_scroll_node_index && element_id_to_transform_node_index == other.element_id_to_transform_node_index && - always_use_active_tree_opacity_effect_ids == - other.always_use_active_tree_opacity_effect_ids && needs_rebuild == other.needs_rebuild && changed == other.changed && full_tree_damaged == other.full_tree_damaged && is_main_thread == other.is_main_thread && is_active == other.is_active && - non_root_surfaces_enabled == other.non_root_surfaces_enabled && can_adjust_raster_scales == other.can_adjust_raster_scales && sequence_number == other.sequence_number; } @@ -1647,15 +1638,12 @@ PropertyTrees& PropertyTrees::operator=(const PropertyTrees& from) { effect_tree = from.effect_tree; clip_tree = from.clip_tree; scroll_tree = from.scroll_tree; - always_use_active_tree_opacity_effect_ids = - from.always_use_active_tree_opacity_effect_ids; element_id_to_effect_node_index = from.element_id_to_effect_node_index; element_id_to_scroll_node_index = from.element_id_to_scroll_node_index; element_id_to_transform_node_index = from.element_id_to_transform_node_index; needs_rebuild = from.needs_rebuild; changed = from.changed; full_tree_damaged = from.full_tree_damaged; - non_root_surfaces_enabled = from.non_root_surfaces_enabled; can_adjust_raster_scales = from.can_adjust_raster_scales; sequence_number = from.sequence_number; is_main_thread = from.is_main_thread; @@ -1682,12 +1670,10 @@ void PropertyTrees::clear() { element_id_to_effect_node_index.clear(); element_id_to_scroll_node_index.clear(); element_id_to_transform_node_index.clear(); - always_use_active_tree_opacity_effect_ids.clear(); needs_rebuild = true; full_tree_damaged = false; changed = false; - non_root_surfaces_enabled = true; can_adjust_raster_scales = true; sequence_number++; @@ -1729,22 +1715,6 @@ void PropertyTrees::SetInnerViewportScrollBoundsDelta( inner_viewport_scroll_bounds_delta_ = bounds_delta; } -void PropertyTrees::PushOpacityIfNeeded(PropertyTrees* target_tree) { - for (int id : target_tree->always_use_active_tree_opacity_effect_ids) { - if (const EffectNode* source_effect_node = - effect_tree.FindNodeFromOwningLayerId(id)) { - EffectNode* target_effect_node = - target_tree->effect_tree.UpdateNodeFromOwningLayerId(id); - float source_opacity = source_effect_node->opacity; - float target_opacity = target_effect_node->opacity; - if (source_opacity == target_opacity) - continue; - target_effect_node->opacity = source_opacity; - target_tree->effect_tree.set_needs_update(true); - } - } -} - void PropertyTrees::RemoveIdFromIdToIndexMaps(int id) { transform_tree.SetOwningLayerIdForNode(nullptr, id); clip_tree.SetOwningLayerIdForNode(nullptr, id); diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h index de5cc2823de..3ea602ad1c0 100644 --- a/chromium/cc/trees/property_tree.h +++ b/chromium/cc/trees/property_tree.h @@ -378,7 +378,10 @@ class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> { bool HasCopyRequests() const; void ClearCopyRequests(); - int ClosestAncestorWithCopyRequest(int id) const; + // Given the ids of two effect nodes that have render surfaces, returns the + // id of the lowest common ancestor effect node that also has a render + // surface. + int LowestCommonAncestorWithRenderSurface(int id_1, int id_2) const; void AddMaskLayerId(int id); const std::vector<int>& mask_layer_ids() const { return mask_layer_ids_; } @@ -391,8 +394,7 @@ class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> { return render_surfaces_[id].get(); } - void UpdateRenderSurfaces(LayerTreeImpl* layer_tree_impl, - bool non_root_surfaces_enabled); + void UpdateRenderSurfaces(LayerTreeImpl* layer_tree_impl); bool ContributesToDrawnSurface(int id); @@ -499,10 +501,12 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { void CopyCompleteTreeState(const ScrollTree& other); #endif + ScrollNode* FindNodeFromElementId(ElementId id); + private: - using ScrollOffsetMap = std::unordered_map<int, gfx::ScrollOffset>; + using ScrollOffsetMap = base::flat_map<int, gfx::ScrollOffset>; using SyncedScrollOffsetMap = - std::unordered_map<int, scoped_refptr<SyncedScrollOffset>>; + base::flat_map<int, scoped_refptr<SyncedScrollOffset>>; int currently_scrolling_node_id_; @@ -639,20 +643,15 @@ class CC_EXPORT PropertyTrees final { // from layer id to the respective property node. Completing that work is // pending the launch of Slimming Paint v2 and reworking UI compositor logic // to produce cc property trees and these maps. - std::unordered_map<ElementId, int, ElementIdHash> - element_id_to_effect_node_index; - std::unordered_map<ElementId, int, ElementIdHash> - element_id_to_scroll_node_index; - std::unordered_map<ElementId, int, ElementIdHash> - element_id_to_transform_node_index; - - std::vector<int> always_use_active_tree_opacity_effect_ids; + base::flat_map<ElementId, int> element_id_to_effect_node_index; + base::flat_map<ElementId, int> element_id_to_scroll_node_index; + base::flat_map<ElementId, int> element_id_to_transform_node_index; + TransformTree transform_tree; EffectTree effect_tree; ClipTree clip_tree; ScrollTree scroll_tree; bool needs_rebuild; - bool non_root_surfaces_enabled; bool can_adjust_raster_scales; // Change tracking done on property trees needs to be preserved across commits // (when they are not rebuild). We cache a global bool which stores whether @@ -674,7 +673,6 @@ class CC_EXPORT PropertyTrees final { void SetInnerViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta); void SetOuterViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta); void SetInnerViewportScrollBoundsDelta(gfx::Vector2dF bounds_delta); - void PushOpacityIfNeeded(PropertyTrees* target_tree); void RemoveIdFromIdToIndexMaps(int id); void UpdateChangeTracking(); void PushChangeTrackingTo(PropertyTrees* tree); diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc index 02fb9bd306c..a7efa0a7155 100644 --- a/chromium/cc/trees/property_tree_builder.cc +++ b/chromium/cc/trees/property_tree_builder.cc @@ -35,10 +35,10 @@ struct DataForRecursion { PropertyTrees* property_trees; LayerType* transform_tree_parent; LayerType* transform_fixed_parent; - int render_target; int clip_tree_parent; int effect_tree_parent; int scroll_tree_parent; + int closest_ancestor_with_copy_request; const LayerType* page_scale_layer; const LayerType* inner_viewport_scroll_layer; const LayerType* outer_viewport_scroll_layer; @@ -54,21 +54,11 @@ struct DataForRecursion { bool scroll_tree_parent_created_by_uninheritable_criteria; const gfx::Transform* device_transform; gfx::Transform compound_transform_since_render_target; - bool axis_align_since_render_target; + bool animation_axis_aligned_since_render_target; + bool not_axis_aligned_since_last_clip; SkColor safe_opaque_background_color; }; -template <typename LayerType> -struct DataForRecursionFromChild { - int num_copy_requests_in_subtree; - - DataForRecursionFromChild() : num_copy_requests_in_subtree(0) {} - - void Merge(const DataForRecursionFromChild& data) { - num_copy_requests_in_subtree += data.num_copy_requests_in_subtree; - } -}; - static LayerPositionConstraint PositionConstraint(Layer* layer) { return layer->position_constraint(); } @@ -366,14 +356,6 @@ bool AddTransformNodeIfNeeded( const bool has_surface = created_render_surface; - // A transform node is needed to change the render target for subtree when - // a scroll child's render target is different from the scroll parent's render - // target. - const bool scroll_child_has_different_target = - ScrollParent(layer) && - Parent(layer)->effect_tree_index() != - ScrollParent(layer)->effect_tree_index(); - const bool is_at_boundary_of_3d_rendering_context = IsAtBoundaryOf3dRenderingContext(layer); @@ -381,8 +363,7 @@ bool AddTransformNodeIfNeeded( bool requires_node = is_root || is_snapped || has_significant_transform || has_any_transform_animation || has_surface || is_fixed || is_page_scale_layer || is_overscroll_elasticity_layer || - has_proxied_transform_related_property || - scroll_child_has_different_target || is_sticky || + has_proxied_transform_related_property || is_sticky || is_at_boundary_of_3d_rendering_context; int parent_index = TransformTree::kRootNodeId; @@ -718,14 +699,6 @@ static inline bool HideLayerAndSubtree(LayerImpl* layer) { return layer->test_properties()->hide_layer_and_subtree; } -static inline bool AlwaysUseActiveTreeOpacity(Layer* layer) { - return layer->AlwaysUseActiveTreeOpacity(); -} - -static inline bool AlwaysUseActiveTreeOpacity(LayerImpl* layer) { - return false; -} - static inline bool HasCopyRequest(Layer* layer) { return layer->HasCopyRequest(); } @@ -745,10 +718,9 @@ static inline bool PropertyChanged(LayerImpl* layer) { template <typename LayerType> bool ShouldCreateRenderSurface(LayerType* layer, gfx::Transform current_transform, - bool axis_aligned) { + bool animation_axis_aligned) { const bool preserves_2d_axis_alignment = - (current_transform * Transform(layer)).Preserves2dAxisAlignment() && - axis_aligned && AnimationsPreserveAxisAlignment(layer); + current_transform.Preserves2dAxisAlignment() && animation_axis_aligned; const bool is_root = !Parent(layer); if (is_root) return true; @@ -858,6 +830,38 @@ static void TakeCopyRequests( layer->test_properties()->copy_requests.clear(); } +static void SetSubtreeHasCopyRequest(Layer* layer, + bool subtree_has_copy_request) { + layer->SetSubtreeHasCopyRequest(subtree_has_copy_request); +} + +static void SetSubtreeHasCopyRequest(LayerImpl* layer, + bool subtree_has_copy_request) { + layer->test_properties()->subtree_has_copy_request = subtree_has_copy_request; +} + +static bool SubtreeHasCopyRequest(Layer* layer) { + return layer->SubtreeHasCopyRequest(); +} + +static bool SubtreeHasCopyRequest(LayerImpl* layer) { + return layer->test_properties()->subtree_has_copy_request; +} + +template <typename LayerType> +bool UpdateSubtreeHasCopyRequestRecursive(LayerType* layer) { + bool subtree_has_copy_request = false; + if (HasCopyRequest(layer)) + subtree_has_copy_request = true; + for (size_t i = 0; i < Children(layer).size(); ++i) { + LayerType* current_child = ChildAt(layer, i); + subtree_has_copy_request |= + UpdateSubtreeHasCopyRequestRecursive(current_child); + } + SetSubtreeHasCopyRequest(layer, subtree_has_copy_request); + return subtree_has_copy_request; +} + template <typename LayerType> bool AddEffectNodeIfNeeded( const DataForRecursion<LayerType>& data_from_ancestor, @@ -871,14 +875,27 @@ bool AddEffectNodeIfNeeded( HasPotentiallyRunningFilterAnimation(layer); const bool has_proxied_opacity = !!(layer->mutable_properties() & MutableProperty::kOpacity); - const bool should_create_render_surface = ShouldCreateRenderSurface( - layer, data_from_ancestor.compound_transform_since_render_target, - data_from_ancestor.axis_align_since_render_target); - data_for_children->axis_align_since_render_target &= + + data_for_children->animation_axis_aligned_since_render_target &= AnimationsPreserveAxisAlignment(layer); + data_for_children->compound_transform_since_render_target *= Transform(layer); + const bool should_create_render_surface = ShouldCreateRenderSurface( + layer, data_for_children->compound_transform_since_render_target, + data_for_children->animation_axis_aligned_since_render_target); + + bool not_axis_aligned_since_last_clip = + data_from_ancestor.not_axis_aligned_since_last_clip + ? true + : !AnimationsPreserveAxisAlignment(layer) || + !Transform(layer).Preserves2dAxisAlignment(); + // A non-axis aligned clip may need a render surface. So, we create an effect + // node. + bool has_non_axis_aligned_clip = + not_axis_aligned_since_last_clip && LayerClipsSubtree(layer); bool requires_node = is_root || has_transparency || has_potential_opacity_animation || has_proxied_opacity || + has_non_axis_aligned_clip || should_create_render_surface; int parent_id = data_from_ancestor.effect_tree_parent; @@ -886,38 +903,38 @@ bool AddEffectNodeIfNeeded( if (!requires_node) { layer->SetEffectTreeIndex(parent_id); data_for_children->effect_tree_parent = parent_id; - data_for_children->compound_transform_since_render_target *= - Transform(layer); return false; } - EffectNode node; - node.owning_layer_id = layer->id(); - if (AlwaysUseActiveTreeOpacity(layer)) { - data_for_children->property_trees->always_use_active_tree_opacity_effect_ids - .push_back(node.owning_layer_id); - } + EffectTree& effect_tree = data_for_children->property_trees->effect_tree; + int node_id = effect_tree.Insert(EffectNode(), parent_id); + EffectNode* node = effect_tree.back(); - node.opacity = Opacity(layer); - node.blend_mode = BlendMode(layer); - node.unscaled_mask_target_size = layer->bounds(); - node.has_render_surface = should_create_render_surface; - node.has_copy_request = HasCopyRequest(layer); - node.filters = Filters(layer); - node.background_filters = BackgroundFilters(layer); - node.filters_origin = FiltersOrigin(layer); - node.has_potential_opacity_animation = has_potential_opacity_animation; - node.has_potential_filter_animation = has_potential_filter_animation; - node.double_sided = DoubleSided(layer); - node.subtree_hidden = HideLayerAndSubtree(layer); - node.is_currently_animating_opacity = OpacityIsAnimating(layer); - node.is_currently_animating_filter = FilterIsAnimating(layer); - node.effect_changed = PropertyChanged(layer); + node->owning_layer_id = layer->id(); + node->opacity = Opacity(layer); + node->blend_mode = BlendMode(layer); + node->unscaled_mask_target_size = layer->bounds(); + node->has_render_surface = should_create_render_surface; + node->has_copy_request = HasCopyRequest(layer); + node->filters = Filters(layer); + node->background_filters = BackgroundFilters(layer); + node->filters_origin = FiltersOrigin(layer); + node->has_potential_opacity_animation = has_potential_opacity_animation; + node->has_potential_filter_animation = has_potential_filter_animation; + node->double_sided = DoubleSided(layer); + node->subtree_hidden = HideLayerAndSubtree(layer); + node->is_currently_animating_opacity = OpacityIsAnimating(layer); + node->is_currently_animating_filter = FilterIsAnimating(layer); + node->effect_changed = PropertyChanged(layer); + node->subtree_has_copy_request = SubtreeHasCopyRequest(layer); + node->closest_ancestor_with_copy_request_id = + HasCopyRequest(layer) + ? node_id + : data_from_ancestor.closest_ancestor_with_copy_request; - EffectTree& effect_tree = data_for_children->property_trees->effect_tree; if (MaskLayer(layer)) { - node.mask_layer_id = MaskLayer(layer)->id(); - effect_tree.AddMaskLayerId(node.mask_layer_id); + node->mask_layer_id = MaskLayer(layer)->id(); + effect_tree.AddMaskLayerId(node->mask_layer_id); } if (!is_root) { @@ -928,19 +945,23 @@ bool AddEffectNodeIfNeeded( // In this case, we will create a transform node, so it's safe to use the // next available id from the transform tree as this effect node's // transform id. - node.transform_id = + node->transform_id = data_from_ancestor.property_trees->transform_tree.next_available_id(); } - node.clip_id = data_from_ancestor.clip_tree_parent; + node->clip_id = data_from_ancestor.clip_tree_parent; } else { - // Root render surface acts the unbounded and untransformed to draw content - // into. Transform node created from root layer (includes device scale - // factor) and clip node created from root layer (include viewports) applies - // to root render surface's content, but not root render surface itself. - node.transform_id = TransformTree::kRootNodeId; - node.clip_id = ClipTree::kViewportNodeId; + // The root render surface acts as the unbounded and untransformed + // surface into which content is drawn. The transform node created + // from the root layer (which includes device scale factor) and + // the clip node created from the root layer (which includes + // viewports) apply to the root render surface's content, but not + // to the root render surface itself. + node->transform_id = TransformTree::kRootNodeId; + node->clip_id = ClipTree::kViewportNodeId; } - int node_id = effect_tree.Insert(node, parent_id); + + data_for_children->closest_ancestor_with_copy_request = + node->closest_ancestor_with_copy_request_id; data_for_children->effect_tree_parent = node_id; layer->SetEffectTreeIndex(node_id); data_for_children->property_trees->effect_tree.SetOwningLayerIdForNode( @@ -963,7 +984,7 @@ bool AddEffectNodeIfNeeded( if (should_create_render_surface) { data_for_children->compound_transform_since_render_target = gfx::Transform(); - data_for_children->axis_align_since_render_target = true; + data_for_children->animation_axis_aligned_since_render_target = true; } return should_create_render_surface; } @@ -1082,7 +1103,7 @@ void SetBackfaceVisibilityTransform(LayerType* layer, (Is3dSorted(layer) && is_at_boundary_of_3d_rendering_context); layer->SetUseLocalTransformForBackfaceVisibility(use_local_transform); - // A double-sided layer's backface can been shown when its visibile. + // A double-sided layer's backface can been shown when its visible. if (DoubleSided(layer)) layer->SetShouldCheckBackfaceVisibility(false); // The backface of a layer that uses local transform for backface visibility @@ -1120,8 +1141,7 @@ static void SetLayerPropertyChangedForChild(LayerImpl* parent, template <typename LayerType> void BuildPropertyTreesInternal( LayerType* layer, - const DataForRecursion<LayerType>& data_from_parent, - DataForRecursionFromChild<LayerType>* data_to_parent) { + const DataForRecursion<LayerType>& data_from_parent) { layer->set_property_tree_sequence_number( data_from_parent.property_trees->sequence_number); @@ -1130,8 +1150,6 @@ void BuildPropertyTreesInternal( bool created_render_surface = AddEffectNodeIfNeeded(data_from_parent, layer, &data_for_children); - if (created_render_surface) - data_for_children.render_target = data_for_children.effect_tree_parent; bool created_transform_node = AddTransformNodeIfNeeded( data_from_parent, layer, created_render_surface, &data_for_children); @@ -1143,14 +1161,21 @@ void BuildPropertyTreesInternal( SetBackfaceVisibilityTransform(layer, created_transform_node); SetSafeOpaqueBackgroundColor(data_from_parent, layer, &data_for_children); + bool not_axis_aligned_since_last_clip = + data_from_parent.not_axis_aligned_since_last_clip + ? true + : !AnimationsPreserveAxisAlignment(layer) || + !Transform(layer).Preserves2dAxisAlignment(); + bool has_non_axis_aligned_clip = + not_axis_aligned_since_last_clip && LayerClipsSubtree(layer); + data_for_children.not_axis_aligned_since_last_clip = + !has_non_axis_aligned_clip; + for (size_t i = 0; i < Children(layer).size(); ++i) { LayerType* current_child = ChildAt(layer, i); SetLayerPropertyChangedForChild(layer, current_child); if (!ScrollParent(current_child)) { - DataForRecursionFromChild<LayerType> data_from_child; - BuildPropertyTreesInternal(current_child, data_for_children, - &data_from_child); - data_to_parent->Merge(data_from_child); + BuildPropertyTreesInternal(current_child, data_for_children); } else { // The child should be included in its scroll parent's list of scroll // children. @@ -1161,15 +1186,10 @@ void BuildPropertyTreesInternal( if (ScrollChildren(layer)) { for (LayerType* scroll_child : *ScrollChildren(layer)) { DCHECK_EQ(ScrollParent(scroll_child), layer); - DataForRecursionFromChild<LayerType> data_from_child; DCHECK(Parent(scroll_child)); data_for_children.effect_tree_parent = Parent(scroll_child)->effect_tree_index(); - data_for_children.render_target = - Parent(scroll_child)->effect_tree_index(); - BuildPropertyTreesInternal(scroll_child, data_for_children, - &data_from_child); - data_to_parent->Merge(data_from_child); + BuildPropertyTreesInternal(scroll_child, data_for_children); } } @@ -1183,16 +1203,6 @@ void BuildPropertyTreesInternal( MaskLayer(layer)->SetEffectTreeIndex(layer->effect_tree_index()); MaskLayer(layer)->SetScrollTreeIndex(layer->scroll_tree_index()); } - - EffectNode* effect_node = data_for_children.property_trees->effect_tree.Node( - data_for_children.effect_tree_parent); - - if (effect_node->owning_layer_id == layer->id()) { - if (effect_node->has_copy_request) - data_to_parent->num_copy_requests_in_subtree++; - effect_node->num_copy_requests_in_subtree = - data_to_parent->num_copy_requests_in_subtree; - } } } // namespace @@ -1246,10 +1256,11 @@ void BuildPropertyTreesTopLevelInternal( data_for_recursion.property_trees = property_trees; data_for_recursion.transform_tree_parent = nullptr; data_for_recursion.transform_fixed_parent = nullptr; - data_for_recursion.render_target = EffectTree::kRootNodeId; data_for_recursion.clip_tree_parent = ClipTree::kRootNodeId; data_for_recursion.effect_tree_parent = EffectTree::kInvalidNodeId; data_for_recursion.scroll_tree_parent = ScrollTree::kRootNodeId; + data_for_recursion.closest_ancestor_with_copy_request = + EffectTree::kInvalidNodeId; data_for_recursion.page_scale_layer = page_scale_layer; data_for_recursion.inner_viewport_scroll_layer = inner_viewport_scroll_layer; data_for_recursion.outer_viewport_scroll_layer = outer_viewport_scroll_layer; @@ -1269,7 +1280,8 @@ void BuildPropertyTreesTopLevelInternal( data_for_recursion.property_trees->clear(); data_for_recursion.compound_transform_since_render_target = gfx::Transform(); - data_for_recursion.axis_align_since_render_target = true; + data_for_recursion.animation_axis_aligned_since_render_target = true; + data_for_recursion.not_axis_aligned_since_last_clip = false; data_for_recursion.property_trees->transform_tree.set_device_scale_factor( device_scale_factor); data_for_recursion.safe_opaque_background_color = color; @@ -1282,8 +1294,7 @@ void BuildPropertyTreesTopLevelInternal( data_for_recursion.property_trees->clip_tree.Insert( root_clip, ClipTree::kRootNodeId); - DataForRecursionFromChild<LayerType> data_from_child; - BuildPropertyTreesInternal(root_layer, data_for_recursion, &data_from_child); + BuildPropertyTreesInternal(root_layer, data_for_recursion); property_trees->needs_rebuild = false; // The transform tree is kept up to date as it is built, but the @@ -1333,6 +1344,8 @@ void PropertyTreeBuilder::BuildPropertyTrees( SkColor color = root_layer->layer_tree_host()->background_color(); if (SkColorGetA(color) != 255) color = SkColorSetA(color, 255); + if (root_layer->layer_tree_host()->has_copy_request()) + UpdateSubtreeHasCopyRequestRecursive(root_layer); BuildPropertyTreesTopLevelInternal( root_layer, page_scale_layer, inner_viewport_scroll_layer, outer_viewport_scroll_layer, overscroll_elasticity_layer, @@ -1343,6 +1356,11 @@ void PropertyTreeBuilder::BuildPropertyTrees( CheckScrollAndClipPointersForLayer(layer); #endif property_trees->ResetCachedData(); + // During building property trees, all copy requests are moved from layers to + // effect tree, which are then pushed at commit to compositor thread and + // handled there. LayerTreeHost::has_copy_request is only required to + // decide if we want to create a effect node. So, it can be reset now. + root_layer->layer_tree_host()->SetHasCopyRequest(false); } void PropertyTreeBuilder::BuildPropertyTrees( @@ -1365,6 +1383,7 @@ void PropertyTreeBuilder::BuildPropertyTrees( SkColor color = root_layer->layer_tree_impl()->background_color(); if (SkColorGetA(color) != 255) color = SkColorSetA(color, 255); + UpdateSubtreeHasCopyRequestRecursive(root_layer); BuildPropertyTreesTopLevelInternal( root_layer, page_scale_layer, inner_viewport_scroll_layer, outer_viewport_scroll_layer, overscroll_elasticity_layer, diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index afced7e5c42..01a96109c89 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -126,10 +126,13 @@ void ProxyImpl::UpdateBrowserControlsStateOnImpl( } void ProxyImpl::InitializeCompositorFrameSinkOnImpl( - CompositorFrameSink* compositor_frame_sink) { + CompositorFrameSink* compositor_frame_sink, + base::WeakPtr<ProxyMain> proxy_main_frame_sink_bound_weak_ptr) { TRACE_EVENT0("cc", "ProxyImpl::InitializeCompositorFrameSinkOnImplThread"); DCHECK(IsImplThread()); + proxy_main_frame_sink_bound_weak_ptr_ = proxy_main_frame_sink_bound_weak_ptr; + LayerTreeHostImpl* host_impl = layer_tree_host_impl_.get(); bool success = host_impl->InitializeRenderer(compositor_frame_sink); MainThreadTaskRunner()->PostTask( @@ -232,19 +235,26 @@ NOINLINE void ProxyImpl::DumpForBeginMainFrameHang() { DCHECK(IsImplThread()); DCHECK(scheduler_); - char stack_string[20000] = ""; - base::debug::Alias(&stack_string); + auto state = base::MakeUnique<base::trace_event::TracedValue>(); - std::unique_ptr<base::trace_event::ConvertableToTraceFormat> scheduler_state = - scheduler_->AsValue(); - strncat(stack_string, scheduler_state->ToString().c_str(), - arraysize(stack_string) - strlen(stack_string) - 1); + state->SetBoolean("commit_completion_waits_for_activation", + commit_completion_waits_for_activation_); + state->SetBoolean("commit_completion_event", !!commit_completion_event_); + state->SetBoolean("activation_completion_event", + !!activation_completion_event_); - std::unique_ptr<base::trace_event::ConvertableToTraceFormat> - tile_manager_state = - layer_tree_host_impl_->tile_manager()->ActivationStateAsValue(); - strncat(stack_string, tile_manager_state->ToString().c_str(), - arraysize(stack_string) - strlen(stack_string) - 1); + state->BeginDictionary("scheduler_state"); + scheduler_->AsValueInto(state.get()); + state->EndDictionary(); + + state->BeginDictionary("tile_manager_state"); + layer_tree_host_impl_->tile_manager()->ActivationStateAsValueInto( + state.get()); + state->EndDictionary(); + + char stack_string[50000] = ""; + base::debug::Alias(&stack_string); + strncpy(stack_string, state->ToString().c_str(), arraysize(stack_string) - 1); base::debug::DumpWithoutCrashing(); } @@ -307,7 +317,7 @@ void ProxyImpl::DidReceiveCompositorFrameAckOnImplThread() { scheduler_->DidReceiveCompositorFrameAck(); MainThreadTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&ProxyMain::DidReceiveCompositorFrameAck, - proxy_main_weak_ptr_)); + proxy_main_frame_sink_bound_weak_ptr_)); } void ProxyImpl::OnCanDrawStateChanged(bool can_draw) { @@ -473,6 +483,11 @@ void ProxyImpl::DidFinishImplFrame() { layer_tree_host_impl_->DidFinishImplFrame(); } +void ProxyImpl::DidNotProduceFrame(const BeginFrameAck& ack) { + DCHECK(IsImplThread()); + layer_tree_host_impl_->DidNotProduceFrame(ack); +} + void ProxyImpl::ScheduledActionSendBeginMainFrame(const BeginFrameArgs& args) { DCHECK(IsImplThread()); unsigned int begin_frame_id = nextBeginFrameId++; @@ -602,6 +617,14 @@ void ProxyImpl::SendBeginMainFrameNotExpectedSoon() { proxy_main_weak_ptr_)); } +void ProxyImpl::ScheduledActionBeginMainFrameNotExpectedUntil( + base::TimeTicks time) { + DCHECK(IsImplThread()); + MainThreadTaskRunner()->PostTask( + FROM_HERE, base::Bind(&ProxyMain::BeginMainFrameNotExpectedUntil, + proxy_main_weak_ptr_, time)); +} + DrawResult ProxyImpl::DrawInternal(bool forced_draw) { TRACE_EVENT_SYNTHETIC_DELAY("cc.Draw"); diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h index 68edc334515..0953ae7a0e1 100644 --- a/chromium/cc/trees/proxy_impl.h +++ b/chromium/cc/trees/proxy_impl.h @@ -34,7 +34,8 @@ class CC_EXPORT ProxyImpl : public NON_EXPORTED_BASE(LayerTreeHostImplClient), BrowserControlsState current, bool animate); void InitializeCompositorFrameSinkOnImpl( - CompositorFrameSink* compositor_frame_sink); + CompositorFrameSink* compositor_frame_sink, + base::WeakPtr<ProxyMain> proxy_main_frame_sink_bound_weak_ptr); void InitializeMutatorOnImpl(std::unique_ptr<LayerTreeMutator> mutator); void MainThreadHasStoppedFlingingOnImpl(); void SetInputThrottledUntilCommitOnImpl(bool is_throttled); @@ -97,6 +98,7 @@ class CC_EXPORT ProxyImpl : public NON_EXPORTED_BASE(LayerTreeHostImplClient), // SchedulerClient implementation void WillBeginImplFrame(const BeginFrameArgs& args) override; void DidFinishImplFrame() override; + void DidNotProduceFrame(const BeginFrameAck& ack) override; void ScheduledActionSendBeginMainFrame(const BeginFrameArgs& args) override; DrawResult ScheduledActionDrawIfPossible() override; DrawResult ScheduledActionDrawForced() override; @@ -107,6 +109,8 @@ class CC_EXPORT ProxyImpl : public NON_EXPORTED_BASE(LayerTreeHostImplClient), void ScheduledActionInvalidateCompositorFrameSink() override; void ScheduledActionPerformImplSideInvalidation() override; void SendBeginMainFrameNotExpectedSoon() override; + void ScheduledActionBeginMainFrameNotExpectedUntil( + base::TimeTicks time) override; DrawResult DrawInternal(bool forced_draw); @@ -149,6 +153,10 @@ class CC_EXPORT ProxyImpl : public NON_EXPORTED_BASE(LayerTreeHostImplClient), // Used to post tasks to ProxyMain on the main thread. base::WeakPtr<ProxyMain> proxy_main_weak_ptr_; + // A weak pointer to ProxyMain that is invalidated when CompositorFrameSink is + // released. + base::WeakPtr<ProxyMain> proxy_main_frame_sink_bound_weak_ptr_; + DISALLOW_COPY_AND_ASSIGN(ProxyImpl); }; diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 6b06f0ee80f..8632105c293 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -36,6 +36,7 @@ ProxyMain::ProxyMain(LayerTreeHost* layer_tree_host, commit_waits_for_activation_(false), started_(false), defer_commits_(false), + frame_sink_bound_weak_factory_(this), weak_factory_(this) { TRACE_EVENT0("cc", "ProxyMain::ProxyMain"); DCHECK(task_runner_provider_); @@ -74,6 +75,12 @@ void ProxyMain::BeginMainFrameNotExpectedSoon() { layer_tree_host_->BeginMainFrameNotExpectedSoon(); } +void ProxyMain::BeginMainFrameNotExpectedUntil(base::TimeTicks time) { + TRACE_EVENT0("cc", "ProxyMain::BeginMainFrameNotExpectedUntil"); + DCHECK(IsMainThread()); + layer_tree_host_->BeginMainFrameNotExpectedUntil(time); +} + void ProxyMain::DidCommitAndDrawFrame() { DCHECK(IsMainThread()); layer_tree_host_->DidCommitAndDrawFrame(); @@ -288,9 +295,10 @@ bool ProxyMain::CommitToActiveTree() const { void ProxyMain::SetCompositorFrameSink( CompositorFrameSink* compositor_frame_sink) { ImplThreadTaskRunner()->PostTask( - FROM_HERE, base::BindOnce(&ProxyImpl::InitializeCompositorFrameSinkOnImpl, - base::Unretained(proxy_impl_.get()), - compositor_frame_sink)); + FROM_HERE, + base::BindOnce(&ProxyImpl::InitializeCompositorFrameSinkOnImpl, + base::Unretained(proxy_impl_.get()), compositor_frame_sink, + frame_sink_bound_weak_factory_.GetWeakPtr())); } void ProxyMain::SetVisible(bool visible) { @@ -469,6 +477,7 @@ bool ProxyMain::MainFrameWillHappenForTesting() { void ProxyMain::ReleaseCompositorFrameSink() { DCHECK(IsMainThread()); + frame_sink_bound_weak_factory_.InvalidateWeakPtrs(); DebugScopedSetMainThreadBlocked main_thread_blocked(task_runner_provider_); CompletionEvent completion; ImplThreadTaskRunner()->PostTask( diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h index 3d97276193a..e4ab354345e 100644 --- a/chromium/cc/trees/proxy_main.h +++ b/chromium/cc/trees/proxy_main.h @@ -42,6 +42,7 @@ class CC_EXPORT ProxyMain : public Proxy { void DidReceiveCompositorFrameAck(); void BeginMainFrameNotExpectedSoon(); + void BeginMainFrameNotExpectedUntil(base::TimeTicks time); void DidCommitAndDrawFrame(); void SetAnimationEvents(std::unique_ptr<MutatorEvents> events); void DidLoseCompositorFrameSink(); @@ -129,6 +130,10 @@ class CC_EXPORT ProxyMain : public Proxy { // run before we destroy it on the impl thread. std::unique_ptr<ProxyImpl> proxy_impl_; + // WeakPtrs generated by this factory will be invalidated when + // CompositorFrameSink is released. + base::WeakPtrFactory<ProxyMain> frame_sink_bound_weak_factory_; + base::WeakPtrFactory<ProxyMain> weak_factory_; DISALLOW_COPY_AND_ASSIGN(ProxyMain); diff --git a/chromium/cc/trees/scroll_node.cc b/chromium/cc/trees/scroll_node.cc index 050dbf00294..3c8bcf78d02 100644 --- a/chromium/cc/trees/scroll_node.cc +++ b/chromium/cc/trees/scroll_node.cc @@ -16,9 +16,9 @@ ScrollNode::ScrollNode() : id(ScrollTree::kInvalidNodeId), parent_id(ScrollTree::kInvalidNodeId), owning_layer_id(Layer::INVALID_ID), - scrollable(false), main_thread_scrolling_reasons( MainThreadScrollingReason::kNotScrollingOnMain), + scrollable(false), max_scroll_offset_affected_by_page_scale(false), scrolls_inner_viewport(false), scrolls_outer_viewport(false), diff --git a/chromium/cc/trees/scroll_node.h b/chromium/cc/trees/scroll_node.h index 4eefe42aff1..43841bf2f15 100644 --- a/chromium/cc/trees/scroll_node.h +++ b/chromium/cc/trees/scroll_node.h @@ -32,11 +32,6 @@ struct CC_EXPORT ScrollNode { // composited layer list. int owning_layer_id; - // This is used for subtrees that should not be scrolled independently. For - // example, when there is a layer that is not scrollable itself but is inside - // a scrolling layer. - bool scrollable; - uint32_t main_thread_scrolling_reasons; Region non_fast_scrollable_region; @@ -48,17 +43,21 @@ struct CC_EXPORT ScrollNode { // Bounds of the overflow scrolling area. gfx::Size bounds; - bool max_scroll_offset_affected_by_page_scale; - bool scrolls_inner_viewport; - bool scrolls_outer_viewport; + // This is used for subtrees that should not be scrolled independently. For + // example, when there is a layer that is not scrollable itself but is inside + // a scrolling layer. + bool scrollable : 1; + bool max_scroll_offset_affected_by_page_scale : 1; + bool scrolls_inner_viewport : 1; + bool scrolls_outer_viewport : 1; + bool should_flatten : 1; + bool user_scrollable_horizontal : 1; + bool user_scrollable_vertical : 1; // This offset is used when |scrollable| is false and there isn't a transform // node already present that covers this offset. gfx::Vector2dF offset_to_transform_parent; - bool should_flatten; - bool user_scrollable_horizontal; - bool user_scrollable_vertical; ElementId element_id; int transform_id; diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index 07b5afa607a..1769c19fe60 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -52,6 +52,7 @@ SingleThreadProxy::SingleThreadProxy(LayerTreeHost* layer_tree_host, inside_synchronous_composite_(false), compositor_frame_sink_creation_requested_(false), compositor_frame_sink_lost_(true), + frame_sink_bound_weak_factory_(this), weak_factory_(this) { TRACE_EVENT0("cc", "SingleThreadProxy::SingleThreadProxy"); DCHECK(task_runner_provider_); @@ -123,6 +124,7 @@ void SingleThreadProxy::RequestNewCompositorFrameSink() { void SingleThreadProxy::ReleaseCompositorFrameSink() { compositor_frame_sink_lost_ = true; + frame_sink_bound_weak_factory_.InvalidateWeakPtrs(); if (scheduler_on_impl_thread_) scheduler_on_impl_thread_->DidLoseCompositorFrameSink(); return layer_tree_host_impl_->ReleaseCompositorFrameSink(); @@ -141,6 +143,7 @@ void SingleThreadProxy::SetCompositorFrameSink( } if (success) { + frame_sink_bound_weak_ptr_ = frame_sink_bound_weak_factory_.GetWeakPtr(); layer_tree_host_->DidInitializeCompositorFrameSink(); if (scheduler_on_impl_thread_) scheduler_on_impl_thread_->DidCreateAndInitializeCompositorFrameSink(); @@ -425,7 +428,12 @@ void SingleThreadProxy::DidReceiveCompositorFrameAckOnImplThread() { "SingleThreadProxy::DidReceiveCompositorFrameAckOnImplThread"); if (scheduler_on_impl_thread_) scheduler_on_impl_thread_->DidReceiveCompositorFrameAck(); - layer_tree_host_->DidReceiveCompositorFrameAck(); + // We do a PostTask here because freeing resources in some cases (such as in + // TextureLayer) is PostTasked and we want to make sure ack is received after + // resources are returned. + task_runner_provider_->MainThreadTaskRunner()->PostTask( + FROM_HERE, base::Bind(&SingleThreadProxy::DidReceiveCompositorFrameAck, + frame_sink_bound_weak_ptr_)); } void SingleThreadProxy::OnDrawForCompositorFrameSink( @@ -624,6 +632,11 @@ void SingleThreadProxy::SendBeginMainFrameNotExpectedSoon() { layer_tree_host_->BeginMainFrameNotExpectedSoon(); } +void SingleThreadProxy::ScheduledActionBeginMainFrameNotExpectedUntil( + base::TimeTicks time) { + layer_tree_host_->BeginMainFrameNotExpectedUntil(time); +} + void SingleThreadProxy::BeginMainFrame(const BeginFrameArgs& begin_frame_args) { if (scheduler_on_impl_thread_) { scheduler_on_impl_thread_->NotifyBeginMainFrameStarted( @@ -794,4 +807,13 @@ void SingleThreadProxy::DidFinishImplFrame() { #endif } +void SingleThreadProxy::DidNotProduceFrame(const BeginFrameAck& ack) { + DebugScopedSetImplThread impl(task_runner_provider_); + layer_tree_host_impl_->DidNotProduceFrame(ack); +} + +void SingleThreadProxy::DidReceiveCompositorFrameAck() { + layer_tree_host_->DidReceiveCompositorFrameAck(); +} + } // namespace cc diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h index 4a091950a27..54842c41bf2 100644 --- a/chromium/cc/trees/single_thread_proxy.h +++ b/chromium/cc/trees/single_thread_proxy.h @@ -62,6 +62,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, // SchedulerClient implementation void WillBeginImplFrame(const BeginFrameArgs& args) override; void DidFinishImplFrame() override; + void DidNotProduceFrame(const BeginFrameAck& ack) override; void ScheduledActionSendBeginMainFrame(const BeginFrameArgs& args) override; DrawResult ScheduledActionDrawIfPossible() override; DrawResult ScheduledActionDrawForced() override; @@ -72,6 +73,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void ScheduledActionInvalidateCompositorFrameSink() override; void ScheduledActionPerformImplSideInvalidation() override; void SendBeginMainFrameNotExpectedSoon() override; + void ScheduledActionBeginMainFrameNotExpectedUntil( + base::TimeTicks time) override; // LayerTreeHostImplClient implementation void DidLoseCompositorFrameSinkOnImplThread() override; @@ -122,6 +125,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy, bool ShouldComposite() const; void ScheduleRequestNewCompositorFrameSink(); + void DidReceiveCompositorFrameAck(); + // Accessed on main thread only. LayerTreeHost* layer_tree_host_; LayerTreeHostSingleThreadClient* single_thread_client_; @@ -158,6 +163,12 @@ class CC_EXPORT SingleThreadProxy : public Proxy, // This is the callback for the scheduled RequestNewCompositorFrameSink. base::CancelableClosure compositor_frame_sink_creation_callback_; + base::WeakPtr<SingleThreadProxy> frame_sink_bound_weak_ptr_; + + // WeakPtrs generated by this factory will be invalidated when + // CompositorFrameSink is released. + base::WeakPtrFactory<SingleThreadProxy> frame_sink_bound_weak_factory_; + base::WeakPtrFactory<SingleThreadProxy> weak_factory_; DISALLOW_COPY_AND_ASSIGN(SingleThreadProxy); diff --git a/chromium/cc/trees/transform_node.h b/chromium/cc/trees/transform_node.h index 76f2a7c2ffc..2abfb1cc004 100644 --- a/chromium/cc/trees/transform_node.h +++ b/chromium/cc/trees/transform_node.h @@ -69,14 +69,27 @@ struct CC_EXPORT TransformNode { // TODO(vollick): will be moved when accelerated effects are implemented. bool needs_local_transform_update : 1; + // Whether this node or any ancestor has a potentially running + // (i.e., irrespective of exact timeline) transform animation or an + // invertible transform. bool node_and_ancestors_are_animated_or_invertible : 1; bool is_invertible : 1; + // Whether the transform from this node to the screen is + // invertible. bool ancestors_are_invertible : 1; + // Whether this node has a potentially running (i.e., irrespective + // of exact timeline) transform animation. bool has_potential_animation : 1; + // Whether this node has a currently running transform animation. bool is_currently_animating : 1; + // Whether this node *or an ancestor* has a potentially running + // (i.e., irrespective of exact timeline) transform + // animation. bool to_screen_is_potentially_animated : 1; + // Whether all animations on this transform node are simple + // translations. bool has_only_translation_animations : 1; // Flattening, when needed, is only applied to a node's inherited transform, diff --git a/chromium/cc/trees/tree_synchronizer.cc b/chromium/cc/trees/tree_synchronizer.cc index 52f1bb384c7..87efee06cab 100644 --- a/chromium/cc/trees/tree_synchronizer.cc +++ b/chromium/cc/trees/tree_synchronizer.cc @@ -74,6 +74,25 @@ std::unique_ptr<LayerImpl> ReuseOrCreateLayerImpl(OwnedLayerImplMap* old_layers, return layer_impl; } +#if DCHECK_IS_ON() +template <typename LayerType> +static void AssertValidPropertyTreeIndices(LayerType* layer) { + DCHECK(layer); + DCHECK_NE(layer->transform_tree_index(), TransformTree::kInvalidNodeId); + DCHECK_NE(layer->effect_tree_index(), EffectTree::kInvalidNodeId); + DCHECK_NE(layer->clip_tree_index(), ClipTree::kInvalidNodeId); + DCHECK_NE(layer->scroll_tree_index(), ScrollTree::kInvalidNodeId); +} + +static bool LayerHasValidPropertyTreeIndices(LayerImpl* layer) { + DCHECK(layer); + return layer->transform_tree_index() != TransformTree::kInvalidNodeId && + layer->effect_tree_index() != EffectTree::kInvalidNodeId && + layer->clip_tree_index() != ClipTree::kInvalidNodeId && + layer->scroll_tree_index() != ScrollTree::kInvalidNodeId; +} +#endif + template <typename LayerTreeType> void PushLayerList(OwnedLayerImplMap* old_layers, LayerTreeType* host, @@ -83,6 +102,15 @@ void PushLayerList(OwnedLayerImplMap* old_layers, std::unique_ptr<LayerImpl> layer_impl( ReuseOrCreateLayerImpl(old_layers, layer, tree_impl)); +#if DCHECK_IS_ON() + // Every layer should have valid property tree indices + AssertValidPropertyTreeIndices(layer); + // Every layer_impl should either have valid property tree indices already + // or the corresponding layer should push them onto layer_impl. + DCHECK(LayerHasValidPropertyTreeIndices(layer_impl.get()) || + host->LayerNeedsPushPropertiesForTesting(layer)); +#endif + tree_impl->AddToLayerList(layer_impl.get()); tree_impl->AddLayer(std::move(layer_impl)); } diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc index cbae4e4aea1..1def71a2b67 100644 --- a/chromium/cc/trees/tree_synchronizer_unittest.cc +++ b/chromium/cc/trees/tree_synchronizer_unittest.cc @@ -165,6 +165,7 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeFromEmpty) { layer_tree_root->AddChild(Layer::Create()); host_->SetRootLayer(layer_tree_root); + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->active_tree()); @@ -186,6 +187,7 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeReusingLayers) { int second_layer_impl_id = layer_tree_root->children()[1]->id(); host_->SetRootLayer(layer_tree_root); + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->active_tree()); @@ -206,6 +208,7 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeReusingLayers) { // Synchronize again. After the sync the trees should be equivalent and we // should have created and destroyed one LayerImpl. + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->active_tree()); layer_impl_tree_root = host_->active_tree()->root_layer_for_testing(); @@ -236,6 +239,7 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeAndTrackStackingOrderChange) { host_->SetRootLayer(layer_tree_root); + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->active_tree()); LayerImpl* layer_impl_tree_root = @@ -252,12 +256,15 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeAndTrackStackingOrderChange) { // re-insert the layer and sync again. child2->RemoveFromParent(); layer_tree_root->AddChild(child2); + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->active_tree()); layer_impl_tree_root = host_->active_tree()->root_layer_for_testing(); ExpectTreesAreIdentical(layer_tree_root.get(), layer_impl_tree_root, host_->active_tree()); + host_->active_tree()->SetPropertyTrees( + layer_tree_root->layer_tree_host()->property_trees()); TreeSynchronizer::PushLayerProperties(layer_tree_root->layer_tree_host(), host_->active_tree()); @@ -284,9 +291,9 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeAndProperties) { gfx::Size second_child_bounds = gfx::Size(25, 53); layer_tree_root->children()[1]->SetBounds(second_child_bounds); - layer_tree_root->children()[1]->SavePaintProperties(); int second_child_id = layer_tree_root->children()[1]->id(); + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->active_tree()); LayerImpl* layer_impl_tree_root = @@ -335,6 +342,7 @@ TEST_F(TreeSynchronizerTest, ReuseLayerImplsAfterStructuralChange) { scoped_refptr<Layer> layer_d = layer_b->children()[1]; host_->SetRootLayer(layer_tree_root); + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->active_tree()); @@ -363,6 +371,7 @@ TEST_F(TreeSynchronizerTest, ReuseLayerImplsAfterStructuralChange) { // After another synchronize our trees should match and we should not have // destroyed any LayerImpls + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->active_tree()); layer_impl_tree_root = host_->active_tree()->root_layer_for_testing(); @@ -392,6 +401,7 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeThenDestroy) { int old_tree_first_child_layer_id = old_layer_tree_root->children()[0]->id(); int old_tree_second_child_layer_id = old_layer_tree_root->children()[1]->id(); + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(old_layer_tree_root.get(), host_->active_tree()); LayerImpl* layer_impl_tree_root = @@ -411,6 +421,7 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeThenDestroy) { scoped_refptr<Layer> new_layer_tree_root = Layer::Create(); host_->SetRootLayer(new_layer_tree_root); + host_->BuildPropertyTreesForTesting(); TreeSynchronizer::SynchronizeTrees(new_layer_tree_root.get(), host_->active_tree()); layer_impl_tree_root = host_->active_tree()->root_layer_for_testing(); @@ -493,7 +504,7 @@ TEST_F(TreeSynchronizerTest, SynchronizeCurrentlyScrollingNode) { transient_scroll_layer->AddChild(scroll_clip_layer); scroll_clip_layer->AddChild(scroll_layer); - ElementId scroll_element_id = ElementId(5, 4); + ElementId scroll_element_id = ElementId(5); scroll_layer->SetElementId(scroll_element_id); transient_scroll_layer->SetScrollClipLayerId( @@ -540,7 +551,7 @@ TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) { scoped_refptr<Layer> transient_scroll_clip_layer = Layer::Create(); scoped_refptr<Layer> transient_scroll_layer = Layer::Create(); - ElementId scroll_element_id = ElementId(5, 4); + ElementId scroll_element_id = ElementId(5); scroll_layer->SetElementId(scroll_element_id); layer_tree_root->AddChild(transient_scroll_clip_layer); |