diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 15:28:34 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 13:54:51 +0000 |
commit | 2a19c63448c84c1805fb1a585c3651318bb86ca7 (patch) | |
tree | eb17888e8531aa6ee5e85721bd553b832a7e5156 /chromium/cc | |
parent | b014812705fc80bff0a5c120dfcef88f349816dc (diff) |
BASELINE: Update Chromium to 69.0.3497.70
Change-Id: I2b7b56e4e7a8b26656930def0d4575dc32b900a0
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/cc')
242 files changed, 8968 insertions, 8027 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index 4f6b936f03f..a70ef19a273 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -180,8 +180,6 @@ cc_component("cc") { "raster/zero_copy_raster_buffer_provider.h", "resources/cross_thread_shared_bitmap.cc", "resources/cross_thread_shared_bitmap.h", - "resources/layer_tree_resource_provider.cc", - "resources/layer_tree_resource_provider.h", "resources/memory_history.cc", "resources/memory_history.h", "resources/resource_pool.cc", @@ -197,8 +195,6 @@ cc_component("cc") { "resources/ui_resource_manager.h", "resources/ui_resource_request.cc", "resources/ui_resource_request.h", - "resources/video_resource_updater.cc", - "resources/video_resource_updater.h", "scheduler/begin_frame_tracker.cc", "scheduler/begin_frame_tracker.h", "scheduler/commit_earlyout_reason.h", @@ -261,6 +257,7 @@ cc_component("cc") { "tiles/tiling_set_raster_queue_all.h", "tiles/tiling_set_raster_queue_required.cc", "tiles/tiling_set_raster_queue_required.h", + "trees/animation_options.h", "trees/clip_expander.cc", "trees/clip_expander.h", "trees/clip_node.cc", @@ -278,8 +275,6 @@ cc_component("cc") { "trees/element_id.h", "trees/frame_rate_counter.cc", "trees/frame_rate_counter.h", - "trees/frame_token_allocator.cc", - "trees/frame_token_allocator.h", "trees/image_animation_controller.cc", "trees/image_animation_controller.h", "trees/latency_info_swap_promise.cc", @@ -369,17 +364,16 @@ cc_component("cc") { "//base", "//base/third_party/dynamic_annotations", "//components/ukm", - "//components/viz/common", + "//components/viz/client", "//gpu", "//gpu/command_buffer/client:gles2_interface", "//gpu/command_buffer/client:raster_interface", "//gpu/ipc:gl_in_process_context", "//gpu/skia_bindings:skia_bindings", "//gpu/vulkan:buildflags", - "//media", + "//media", # For VideoLayerImpl. "//mojo/public/cpp/bindings:struct_traits", "//services/metrics/public/cpp:ukm_builders", - "//third_party/libyuv", "//ui/events:events_base", "//ui/gfx", "//ui/gfx/geometry", @@ -435,7 +429,6 @@ cc_static_library("test_support") { "test/fake_recording_source.cc", "test/fake_recording_source.h", "test/fake_rendering_stats_instrumentation.h", - "test/fake_resource_provider.h", "test/fake_scoped_ui_resource.cc", "test/fake_scoped_ui_resource.h", "test/fake_scrollbar.cc", @@ -536,6 +529,7 @@ cc_static_library("test_support") { "//cc/paint", "//components/ukm", "//components/ukm:test_support", + "//components/viz/client", "//components/viz/common", "//components/viz/service", "//components/viz/test:test_support", @@ -543,8 +537,10 @@ cc_static_library("test_support") { "//gpu/command_buffer/client:raster", "//gpu/command_buffer/common", "//gpu/ipc:gl_in_process_context", + "//gpu/ipc/service", "//gpu/skia_bindings", "//media", + "//services/viz/privileged/interfaces", "//skia", "//testing/gtest", "//ui/gfx", @@ -618,6 +614,7 @@ cc_test("cc_unittests") { "layers/video_frame_provider_client_impl_unittest.cc", "layers/video_layer_impl_unittest.cc", "layers/viewport_unittest.cc", + "mojo_embedder/async_layer_tree_frame_sink_unittest.cc", "paint/discardable_image_map_unittest.cc", "paint/display_item_list_unittest.cc", "paint/filter_operations_unittest.cc", @@ -640,9 +637,7 @@ cc_test("cc_unittests") { "raster/synchronous_task_graph_runner_unittest.cc", "raster/task_graph_work_queue_unittest.cc", "raster/texture_compressor_etc1_unittest.cc", - "resources/layer_tree_resource_provider_unittest.cc", "resources/resource_pool_unittest.cc", - "resources/video_resource_updater_unittest.cc", "scheduler/compositor_timing_history_unittest.cc", "scheduler/scheduler_state_machine_unittest.cc", "scheduler/scheduler_unittest.cc", @@ -729,8 +724,10 @@ cc_test("cc_unittests") { ":cc", ":test_support", "//base/test:test_support", + "//cc/mojo_embedder", "//cc/paint", "//components/ukm:test_support", + "//components/viz/client", "//components/viz/common", "//components/viz/service", "//components/viz/test:test_support", @@ -743,7 +740,7 @@ cc_test("cc_unittests") { "//gpu/ipc:gl_in_process_context", "//gpu/skia_bindings", "//media", - "//mojo/edk", + "//mojo/core/embedder", "//mojo/public/cpp/bindings", "//testing/gmock", "//testing/gtest", @@ -773,6 +770,7 @@ cc_test("cc_perftests") { "test/cc_test_suite.cc", "test/cc_test_suite.h", "test/run_all_perftests.cc", + "tiles/gpu_image_decode_cache_perftest.cc", "tiles/software_image_decode_cache_perftest.cc", "tiles/tile_manager_perftest.cc", "trees/layer_tree_host_common_perftest.cc", @@ -785,15 +783,18 @@ cc_test("cc_perftests") { "//base", "//base/test:test_support", "//cc/paint", + "//components/viz/client", "//components/viz/common", "//components/viz/test:test_support", "//gpu", "//gpu:test_support", "//gpu/command_buffer/client:gles2_implementation", "//gpu/command_buffer/client:raster", + "//gpu/command_buffer/client:raster_interface", + "//gpu/ipc:gl_in_process_context", "//gpu/ipc/common:struct_traits", "//media", - "//mojo/edk", + "//mojo/core/embedder", "//mojo/public/cpp/bindings", "//services/viz/public/interfaces", "//skia", @@ -811,11 +812,10 @@ cc_test("cc_perftests") { data = [ "//components/viz/test/data/", + ] + data_deps = [ # Needed for isolate script to execute. - "//testing/scripts/common.py", - "//testing/xvfb.py", - "//testing/scripts/run_gtest_perf_test.py", - "//tools/perf/generate_legacy_perf_dashboard_json.py", + "//testing:run_perf_test", ] } diff --git a/chromium/cc/DEPS b/chromium/cc/DEPS index 2e684073776..7b0e07a0097 100644 --- a/chromium/cc/DEPS +++ b/chromium/cc/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/ukm/test_ukm_recorder.h", + "+components/viz/client", "+components/viz/common", "+gpu/GLES2", "+gpu/command_buffer/client/context_support.h", @@ -28,9 +29,13 @@ include_rules = [ "+third_party/libyuv", "+third_party/skia/include", "+third_party/skia/src/core/SkRemoteGlyphCache.h", - "+ui/latency/latency_info.h", + "+ui/latency", "+ui/gfx", "+ui/gl", + + # Do not use mojo bindings in cc/. This library should be agnostic about how + # to communicate with viz. + "-mojo/public/cpp/bindings", ] specific_include_rules = { diff --git a/chromium/cc/animation/DEPS b/chromium/cc/animation/DEPS index a59045da943..6afc308b2a6 100644 --- a/chromium/cc/animation/DEPS +++ b/chromium/cc/animation/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+cc/paint", "+cc/output/filter_operations.h", "+cc/test", + "+cc/trees/animation_options.h", "+cc/trees/element_id.h", "+cc/trees/mutator_host.h", "+cc/trees/mutator_host_client.h", diff --git a/chromium/cc/animation/animation.cc b/chromium/cc/animation/animation.cc index 57af2598821..8da03807dcf 100644 --- a/chromium/cc/animation/animation.cc +++ b/chromium/cc/animation/animation.cc @@ -13,6 +13,7 @@ #include "cc/animation/animation_events.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_timeline.h" +#include "cc/animation/keyframe_effect.h" #include "cc/animation/scroll_offset_animation_curve.h" #include "cc/animation/transform_operations.h" #include "cc/trees/property_animation_state.h" @@ -229,13 +230,19 @@ void Animation::AbortKeyframeModelForKeyframeEffect( ->AbortKeyframeModel(keyframe_model_id); } -void Animation::AbortKeyframeModels(TargetProperty::Type target_property, - bool needs_completion) { +void Animation::AbortKeyframeModelsWithProperty( + TargetProperty::Type target_property, + bool needs_completion) { for (auto& keyframe_effect : keyframe_effects_) - keyframe_effect->AbortKeyframeModels(target_property, needs_completion); + keyframe_effect->AbortKeyframeModelsWithProperty(target_property, + needs_completion); } void Animation::PushPropertiesTo(Animation* animation_impl) { + // In general when pushing proerties to impl thread we first push attached + // properties to impl followed by removing the detached ones. However, we + // never remove individual keyframe effect from an animation so there is no + // need to remove the detached ones. PushAttachedKeyframeEffectsToImplThread(animation_impl); PushPropertiesToImplThread(animation_impl); } diff --git a/chromium/cc/animation/animation.h b/chromium/cc/animation/animation.h index 6ac050dfabe..fe0a875ffe9 100644 --- a/chromium/cc/animation/animation.h +++ b/chromium/cc/animation/animation.h @@ -14,7 +14,6 @@ #include "cc/animation/animation_curve.h" #include "cc/animation/animation_export.h" #include "cc/animation/element_animations.h" -#include "cc/animation/keyframe_effect.h" #include "cc/animation/keyframe_model.h" #include "cc/trees/element_id.h" @@ -24,6 +23,7 @@ class AnimationDelegate; class AnimationEvents; class AnimationHost; class AnimationTimeline; +class KeyframeEffect; struct AnimationEvent; // An Animation is responsible for managing animating properties for a set of @@ -62,6 +62,10 @@ class CC_ANIMATION_EXPORT Animation : public base::RefCounted<Animation> { } virtual void SetAnimationTimeline(AnimationTimeline* timeline); + // TODO(smcgruer): If/once ScrollTimeline is supported on normal Animations, + // we will need to move the promotion logic from WorkletAnimation to here. + virtual void PromoteScrollTimelinePendingToActive() {} + bool has_element_animations() const; scoped_refptr<ElementAnimations> element_animations( KeyframeEffectId keyframe_effect_id) const; @@ -87,10 +91,10 @@ class CC_ANIMATION_EXPORT Animation : public base::RefCounted<Animation> { KeyframeEffectId keyframe_effect_id); void AbortKeyframeModelForKeyframeEffect(int keyframe_model_id, KeyframeEffectId keyframe_effect_id); - void AbortKeyframeModels(TargetProperty::Type target_property, - bool needs_completion); + void AbortKeyframeModelsWithProperty(TargetProperty::Type target_property, + bool needs_completion); - void PushPropertiesTo(Animation* animation_impl); + virtual void PushPropertiesTo(Animation* animation_impl); void UpdateState(bool start_ready_keyframe_models, AnimationEvents* events); virtual void Tick(base::TimeTicks monotonic_time); diff --git a/chromium/cc/animation/animation_host.cc b/chromium/cc/animation/animation_host.cc index cc1f51ad0f3..e3a4a2a25ce 100644 --- a/chromium/cc/animation/animation_host.cc +++ b/chromium/cc/animation/animation_host.cc @@ -30,13 +30,6 @@ namespace cc { -namespace { -WorkletAnimation* ToWorkletAnimation(Animation* animation) { - DCHECK(animation->IsWorkletAnimation()); - return static_cast<WorkletAnimation*>(animation); -} -} // namespace - std::unique_ptr<AnimationHost> AnimationHost::CreateMainInstance() { return base::WrapUnique(new AnimationHost(ThreadInstance::MAIN)); } @@ -282,21 +275,20 @@ bool AnimationHost::NeedsTickAnimations() const { return !ticking_animations_.empty(); } -bool AnimationHost::NeedsTickMutator(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree) const { +bool AnimationHost::TickMutator(base::TimeTicks monotonic_time, + const ScrollTree& scroll_tree, + bool is_active_tree) { if (!mutator_ || !mutator_->HasAnimators()) return false; - for (auto& animation : ticking_animations_) { - if (!animation->IsWorkletAnimation()) - continue; + std::unique_ptr<MutatorInputState> state = CollectWorkletAnimationsState( + monotonic_time, scroll_tree, is_active_tree); + if (state->IsEmpty()) + return false; - if (ToWorkletAnimation(animation.get()) - ->NeedsUpdate(monotonic_time, scroll_tree)) - return true; - } + mutator_->Mutate(std::move(state)); - return false; + return true; } bool AnimationHost::ActivateAnimations() { @@ -312,50 +304,47 @@ bool AnimationHost::ActivateAnimations() { } bool AnimationHost::TickAnimations(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree) { + const ScrollTree& scroll_tree, + bool is_active_tree) { TRACE_EVENT0("cc", "AnimationHost::TickAnimations"); - bool did_animate = false; + // Notes on ordering between a) ticking mutator b) ticking animations: Since + // (a) is currently synchronous by doing a, b in that order we ensure worklet + // animation output state is up-to-date before having that output actually + // take effect in (b). This gaurantees worklet animation output takes effect + // in the same impl frame that it was mutated. + // TODO(crbug.com/834452): We will need a different approach when we make (a) + // asynchronous but until then this simple ordering is sufficient. - if (NeedsTickAnimations()) { - TRACE_EVENT_INSTANT0("cc", "NeedsTickAnimations", TRACE_EVENT_SCOPE_THREAD); - AnimationsList ticking_animations_copy = ticking_animations_; - for (auto& it : ticking_animations_copy) - it->Tick(monotonic_time); + if (!NeedsTickAnimations()) + return false; - did_animate = true; - } - if (NeedsTickMutator(monotonic_time, scroll_tree)) { - // TODO(majidvp): At the moment we call this for both active and pending - // trees similar to other animations. However our final goal is to only call - // it once, ideally after activation, and only when the input - // to an active timeline has changed. http://crbug.com/767210 - mutator_->Mutate(CollectAnimatorsState(monotonic_time, scroll_tree)); - did_animate = true; - } + TRACE_EVENT_INSTANT0("cc", "NeedsTickAnimations", TRACE_EVENT_SCOPE_THREAD); - return did_animate; + // TODO(majidvp): At the moment we call this for both active and pending + // trees similar to other animations. However our final goal is to only call + // it once, ideally after activation, and only when the input + // to an active timeline has changed. http://crbug.com/767210 + TickMutator(monotonic_time, scroll_tree, is_active_tree); + + AnimationsList ticking_animations_copy = ticking_animations_; + for (auto& it : ticking_animations_copy) + it->Tick(monotonic_time); + + return true; } void AnimationHost::TickScrollAnimations(base::TimeTicks monotonic_time, const ScrollTree& scroll_tree) { - // TODO(majidvp) For now the logic simply generates an update when at least - // one animation needs updating but this is inefficient. We need a more - // fine-grained approach based on invalidating individual ScrollTimelines and - // then ticking the animations attached to those timelines. To make - // this happen we probably need to move "ticking" animations to timeline. - if (!NeedsTickMutator(monotonic_time, scroll_tree)) - return; - DCHECK(mutator_); - // TODO(majidvp): We need to return a boolean here so that LTHI knows // whether it needs to schedule another frame. - mutator_->Mutate(CollectAnimatorsState(monotonic_time, scroll_tree)); + TickMutator(monotonic_time, scroll_tree, true /* is_active_tree */); } -std::unique_ptr<MutatorInputState> AnimationHost::CollectAnimatorsState( +std::unique_ptr<MutatorInputState> AnimationHost::CollectWorkletAnimationsState( base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree) { - TRACE_EVENT0("cc", "AnimationHost::CollectAnimatorsState"); + const ScrollTree& scroll_tree, + bool is_active_tree) { + TRACE_EVENT0("cc", "AnimationHost::CollectWorkletAnimationsState"); std::unique_ptr<MutatorInputState> result = std::make_unique<MutatorInputState>(); @@ -363,12 +352,9 @@ std::unique_ptr<MutatorInputState> AnimationHost::CollectAnimatorsState( if (!animation->IsWorkletAnimation()) continue; - WorkletAnimation* worklet_animation = ToWorkletAnimation(animation.get()); - MutatorInputState::AnimationState state{ - worklet_animation->id(), worklet_animation->name(), - worklet_animation->CurrentTime(monotonic_time, scroll_tree)}; - - result->animations.push_back(std::move(state)); + ToWorkletAnimation(animation.get()) + ->UpdateInputState(result.get(), monotonic_time, scroll_tree, + is_active_tree); } return result; @@ -389,6 +375,12 @@ bool AnimationHost::UpdateAnimationState(bool start_ready_animations, return true; } +void AnimationHost::PromoteScrollTimelinesPendingToActive() { + for (auto& animation : ticking_animations_) { + animation->PromoteScrollTimelinePendingToActive(); + } +} + std::unique_ptr<MutatorEvents> AnimationHost::CreateEvents() { return std::make_unique<AnimationEvents>(); } @@ -628,19 +620,19 @@ void AnimationHost::SetMutationUpdate( TRACE_EVENT0("cc", "AnimationHost::SetMutationUpdate"); for (auto& animation_state : output_state->animations) { - int id = animation_state.animation_id; + WorkletAnimationId id = animation_state.worklet_animation_id; // TODO(majidvp): Use a map to make lookup O(1) - auto to_update = - std::find_if(ticking_animations_.begin(), ticking_animations_.end(), - [id](auto& it) { return it->id() == id; }); + auto to_update = std::find_if( + ticking_animations_.begin(), ticking_animations_.end(), [id](auto& it) { + return it->IsWorkletAnimation() && + ToWorkletAnimation(it.get())->worklet_animation_id() == id; + }); if (to_update == ticking_animations_.end()) continue; - DCHECK(to_update->get()->IsWorkletAnimation()); - ToWorkletAnimation(to_update->get()) - ->SetLocalTime(animation_state.local_time); + ToWorkletAnimation(to_update->get())->SetOutputState(animation_state); } } diff --git a/chromium/cc/animation/animation_host.h b/chromium/cc/animation/animation_host.h index beae9903713..fd582c4ee4b 100644 --- a/chromium/cc/animation/animation_host.h +++ b/chromium/cc/animation/animation_host.h @@ -102,11 +102,13 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, bool ActivateAnimations() override; bool TickAnimations(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree) override; + const ScrollTree& scroll_tree, + bool is_active_tree) override; void TickScrollAnimations(base::TimeTicks monotonic_time, const ScrollTree& scroll_tree) override; bool UpdateAnimationState(bool start_ready_animations, MutatorEvents* events) override; + void PromoteScrollTimelinesPendingToActive() override; std::unique_ptr<MutatorEvents> CreateEvents() override; void SetAnimationEvents(std::unique_ptr<MutatorEvents> events) override; @@ -198,13 +200,16 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, void EraseTimeline(scoped_refptr<AnimationTimeline> timeline); - bool NeedsTickMutator(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree) const; + // Return true if there are any animations that get mutated. + bool TickMutator(base::TimeTicks monotonic_time, + const ScrollTree& scroll_tree, + bool is_active_tree); - // Return the animator state representing all ticking worklet animations. - std::unique_ptr<MutatorInputState> CollectAnimatorsState( + // Return the state representing all ticking worklet animations. + std::unique_ptr<MutatorInputState> CollectWorkletAnimationsState( base::TimeTicks timeline_time, - const ScrollTree& scroll_tree); + const ScrollTree& scroll_tree, + bool is_active_tree); ElementToAnimationsMap element_to_animations_map_; AnimationsList ticking_animations_; diff --git a/chromium/cc/animation/animation_host_unittest.cc b/chromium/cc/animation/animation_host_unittest.cc index df816b46085..e78e5d8e289 100644 --- a/chromium/cc/animation/animation_host_unittest.cc +++ b/chromium/cc/animation/animation_host_unittest.cc @@ -4,13 +4,23 @@ #include "cc/animation/animation_host.h" +#include "base/memory/ptr_util.h" #include "cc/animation/animation_id_provider.h" #include "cc/animation/animation_timeline.h" +#include "cc/animation/scroll_timeline.h" +#include "cc/animation/worklet_animation.h" #include "cc/test/animation_test_common.h" #include "cc/test/animation_timelines_test_common.h" +#include "cc/test/mock_layer_tree_mutator.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using ::testing::InvokeWithoutArgs; +using ::testing::Mock; +using ::testing::NiceMock; +using ::testing::Return; +using ::testing::_; + namespace cc { namespace { @@ -18,6 +28,33 @@ class AnimationHostTest : public AnimationTimelinesTest { public: AnimationHostTest() = default; ~AnimationHostTest() override = default; + + void AttachWorkletAnimation() { + client_.RegisterElement(element_id_, ElementListType::ACTIVE); + client_impl_.RegisterElement(element_id_, ElementListType::PENDING); + client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE); + + worklet_animation_ = WorkletAnimation::Create( + worklet_animation_id_, "test_name", nullptr, nullptr); + int cc_id = worklet_animation_->id(); + worklet_animation_->AttachElement(element_id_); + host_->AddAnimationTimeline(timeline_); + timeline_->AttachAnimation(worklet_animation_); + + host_->PushPropertiesTo(host_impl_); + timeline_impl_ = host_impl_->GetTimelineById(timeline_id_); + worklet_animation_impl_ = + ToWorkletAnimation(timeline_impl_->GetAnimationById(cc_id)); + } + + void SetOutputState(base::TimeDelta local_time) { + worklet_animation_impl_->SetOutputState( + {worklet_animation_id_, local_time}); + } + + scoped_refptr<WorkletAnimation> worklet_animation_; + scoped_refptr<WorkletAnimation> worklet_animation_impl_; + WorkletAnimationId worklet_animation_id_{11, 12}; }; // See Animation tests on layer registration/unregistration in @@ -107,5 +144,44 @@ TEST_F(AnimationHostTest, ImplOnlyScrollAnimationUpdateTargetIfDetached) { element_id_, scroll_delta, max_scroll_offset, time, base::TimeDelta())); } +// Tests that verify interaction of AnimationHost with LayerTreeMutator. + +TEST_F(AnimationHostTest, LayerTreeMutatorUpdateTakesEffectInSameFrame) { + AttachWorkletAnimation(); + + const float start_opacity = .7f; + const float end_opacity = .3f; + const double duration = 1.; + + const float expected_opacity = + start_opacity + (end_opacity - start_opacity) / 2; + AddOpacityTransitionToAnimation(worklet_animation_.get(), duration, + start_opacity, end_opacity, true); + + base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2); + + MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>(); + host_impl_->SetLayerTreeMutator( + base::WrapUnique<LayerTreeMutator>(mock_mutator)); + ON_CALL(*mock_mutator, HasAnimators()).WillByDefault(Return(true)); + ON_CALL(*mock_mutator, MutateRef(_)) + .WillByDefault(InvokeWithoutArgs( + [this, local_time]() { this->SetOutputState(local_time); })); + + // Push the opacity animation to the impl thread. + host_->PushPropertiesTo(host_impl_); + host_impl_->ActivateAnimations(); + + // Ticking host should cause layer tree mutator to update output state which + // should take effect in the same animation frame. + TickAnimationsTransferEvents(base::TimeTicks(), 0u); + + TestLayer* layer = + client_.FindTestLayer(element_id_, ElementListType::ACTIVE); + EXPECT_FALSE(layer->is_property_mutated(TargetProperty::OPACITY)); + client_impl_.ExpectOpacityPropertyMutated( + element_id_, ElementListType::ACTIVE, expected_opacity); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/animation_unittest.cc b/chromium/cc/animation/animation_unittest.cc index 6bef965ac93..cb09ab0ea21 100644 --- a/chromium/cc/animation/animation_unittest.cc +++ b/chromium/cc/animation/animation_unittest.cc @@ -21,123 +21,11 @@ namespace { class AnimationTest : public AnimationTimelinesTest { public: - AnimationTest() - : animation_(Animation::Create(animation_id_)), - group_id_(100), - keyframe_model_id_(100) { + AnimationTest() : animation_(Animation::Create(animation_id_)) { keyframe_effect_id_ = animation_->NextKeyframeEffectId(); } ~AnimationTest() override = default; - int NextGroupId() { return ++group_id_; } - - int NextKeyframeModelId() { return ++keyframe_model_id_; } - - int AddOpacityTransition(Animation* target, - double duration, - float start_opacity, - float end_opacity, - bool use_timing_function, - KeyframeEffectId keyframe_effect_id) { - std::unique_ptr<KeyframedFloatAnimationCurve> curve( - KeyframedFloatAnimationCurve::Create()); - - std::unique_ptr<TimingFunction> func; - if (!use_timing_function) - func = CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE); - if (duration > 0.0) - curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), start_opacity, - std::move(func))); - curve->AddKeyframe(FloatKeyframe::Create( - base::TimeDelta::FromSecondsD(duration), end_opacity, nullptr)); - - int id = NextKeyframeModelId(); - - std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), id, NextGroupId(), TargetProperty::OPACITY)); - keyframe_model->set_needs_synchronized_start_time(true); - - target->AddKeyframeModelForKeyframeEffect(std::move(keyframe_model), - keyframe_effect_id); - return id; - } - - int AddAnimatedTransform(Animation* target, - double duration, - TransformOperations start_operations, - TransformOperations operations, - KeyframeEffectId keyframe_effect_id) { - std::unique_ptr<KeyframedTransformAnimationCurve> curve( - KeyframedTransformAnimationCurve::Create()); - - if (duration > 0.0) { - curve->AddKeyframe(TransformKeyframe::Create(base::TimeDelta(), - start_operations, nullptr)); - } - - curve->AddKeyframe(TransformKeyframe::Create( - base::TimeDelta::FromSecondsD(duration), operations, nullptr)); - - int id = NextKeyframeModelId(); - - std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), id, NextGroupId(), TargetProperty::TRANSFORM)); - keyframe_model->set_needs_synchronized_start_time(true); - - target->AddKeyframeModelForKeyframeEffect(std::move(keyframe_model), - keyframe_effect_id); - return id; - } - - int AddAnimatedTransform(Animation* target, - double duration, - int delta_x, - int delta_y, - KeyframeEffectId keyframe_effect_id) { - TransformOperations start_operations; - if (duration > 0.0) { - start_operations.AppendTranslate(0, 0, 0.0); - } - - TransformOperations operations; - operations.AppendTranslate(delta_x, delta_y, 0.0); - return AddAnimatedTransform(target, duration, start_operations, operations, - keyframe_effect_id); - } - - int AddAnimatedFilter(Animation* target, - double duration, - float start_brightness, - float end_brightness, - KeyframeEffectId keyframe_effect_id) { - std::unique_ptr<KeyframedFilterAnimationCurve> curve( - KeyframedFilterAnimationCurve::Create()); - - if (duration > 0.0) { - FilterOperations start_filters; - start_filters.Append( - FilterOperation::CreateBrightnessFilter(start_brightness)); - curve->AddKeyframe( - FilterKeyframe::Create(base::TimeDelta(), start_filters, nullptr)); - } - - FilterOperations filters; - filters.Append(FilterOperation::CreateBrightnessFilter(end_brightness)); - curve->AddKeyframe(FilterKeyframe::Create( - base::TimeDelta::FromSecondsD(duration), filters, nullptr)); - - int id = NextKeyframeModelId(); - - std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), id, NextGroupId(), TargetProperty::FILTER)); - keyframe_model->set_needs_synchronized_start_time(true); - - target->AddKeyframeModelForKeyframeEffect(std::move(keyframe_model), - keyframe_effect_id); - return id; - } - void CheckKeyframeEffectAndTimelineNeedsPushProperties( bool needs_push_properties, KeyframeEffectId keyframe_effect_id) const { @@ -150,9 +38,7 @@ class AnimationTest : public AnimationTimelinesTest { protected: scoped_refptr<Animation> animation_; scoped_refptr<Animation> animation_impl_; - int group_id_; KeyframeEffectId keyframe_effect_id_; - int keyframe_model_id_; }; // See element_animations_unittest.cc for active/pending observers tests. @@ -310,13 +196,13 @@ TEST_F(AnimationTest, PropertiesMutate) { const double duration = 1.; - AddOpacityTransition(animation_.get(), duration, start_opacity, end_opacity, - false, keyframe_effect_id_); + AddOpacityTransitionToAnimation(animation_.get(), duration, start_opacity, + end_opacity, false, keyframe_effect_id_); - AddAnimatedTransform(animation_.get(), duration, transform_x, transform_y, - keyframe_effect_id_); - AddAnimatedFilter(animation_.get(), duration, start_brightness, - end_brightness, keyframe_effect_id_); + AddAnimatedTransformToAnimation(animation_.get(), duration, transform_x, + transform_y, keyframe_effect_id_); + AddAnimatedFilterToAnimation(animation_.get(), duration, start_brightness, + end_brightness, keyframe_effect_id_); CheckKeyframeEffectAndTimelineNeedsPushProperties(true, keyframe_effect_id_); host_->PushPropertiesTo(host_impl_); @@ -412,10 +298,10 @@ TEST_F(AnimationTest, AttachTwoAnimationsToOneLayer) { const double duration = 1.; - AddOpacityTransition(animation1.get(), duration, start_opacity, end_opacity, - false, keyframe_effect_id1); - AddAnimatedTransform(animation2.get(), duration, transform_x, transform_y, - keyframe_effect_id2); + AddOpacityTransitionToAnimation(animation1.get(), duration, start_opacity, + end_opacity, false, keyframe_effect_id1); + AddAnimatedTransformToAnimation(animation2.get(), duration, transform_x, + transform_y, keyframe_effect_id2); host_->PushPropertiesTo(host_impl_); host_impl_->ActivateAnimations(); @@ -481,10 +367,10 @@ TEST_F(AnimationTest, AddRemoveAnimationToNonAttachedAnimation) { const float start_opacity = .7f; const float end_opacity = .3f; - const int filter_id = AddAnimatedFilter(animation_.get(), duration, 0.1f, - 0.9f, keyframe_effect_id_); - AddOpacityTransition(animation_.get(), duration, start_opacity, end_opacity, - false, keyframe_effect_id_); + const int filter_id = AddAnimatedFilterToAnimation( + animation_.get(), duration, 0.1f, 0.9f, keyframe_effect_id_); + AddOpacityTransitionToAnimation(animation_.get(), duration, start_opacity, + end_opacity, false, keyframe_effect_id_); EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id_) ->needs_push_properties()); @@ -560,7 +446,7 @@ TEST_F(AnimationTest, AddRemoveAnimationCausesSetNeedsCommit) { EXPECT_FALSE(client_.mutators_need_commit()); - const int keyframe_model_id = AddOpacityTransition( + const int keyframe_model_id = AddOpacityTransitionToAnimation( animation_.get(), 1., .7f, .3f, false, keyframe_effect_id_); EXPECT_TRUE(client_.mutators_need_commit()); @@ -973,10 +859,10 @@ TEST_F(AnimationTest, TickingAnimationsFromTwoKeyframeEffects) { const double duration = 1.; - AddOpacityTransition(animation_.get(), duration, start_opacity, end_opacity, - false, keyframe_effect_id1); - AddAnimatedTransform(animation_.get(), duration, transform_x, transform_y, - keyframe_effect_id2); + AddOpacityTransitionToAnimation(animation_.get(), duration, start_opacity, + end_opacity, false, keyframe_effect_id1); + AddAnimatedTransformToAnimation(animation_.get(), duration, transform_x, + transform_y, keyframe_effect_id2); host_->PushPropertiesTo(host_impl_); host_impl_->ActivateAnimations(); diff --git a/chromium/cc/animation/element_animations_unittest.cc b/chromium/cc/animation/element_animations_unittest.cc index 9cff257a6f4..beb43dbe276 100644 --- a/chromium/cc/animation/element_animations_unittest.cc +++ b/chromium/cc/animation/element_animations_unittest.cc @@ -294,7 +294,7 @@ TEST_F(ElementAnimationsTest, CubicBezierTimingFunction::EaseType::EASE_IN_OUT))); const int animation2_id = 2; std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( - std::move(curve), animation2_id, 0, TargetProperty::SCROLL_OFFSET)); + std::move(curve), animation2_id, 1, TargetProperty::SCROLL_OFFSET)); animation_->AddKeyframeModel(std::move(keyframe_model)); PushProperties(); EXPECT_VECTOR2DF_EQ(provider_initial_value, @@ -1417,7 +1417,7 @@ TEST_F(ElementAnimationsTest, Interrupt) { std::unique_ptr<KeyframeModel> to_add(CreateKeyframeModel( std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)), 2, TargetProperty::OPACITY)); - animation_->AbortKeyframeModels(TargetProperty::OPACITY, false); + animation_->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY, false); animation_->AddKeyframeModel(std::move(to_add)); // Since the previous animation was aborted, the new animation should start @@ -1830,9 +1830,9 @@ TEST_F(ElementAnimationsTest, InactiveObserverGetsTicked) { client_impl_.GetOpacity(element_id_, ElementListType::ACTIVE)); } -// Tests that AbortKeyframeModels aborts all animations targeting the -// specified property. -TEST_F(ElementAnimationsTest, AbortKeyframeModels) { +// Tests that AbortKeyframeModelsWithProperty aborts all animations targeting +// the specified property. +TEST_F(ElementAnimationsTest, AbortKeyframeModelsWithProperty) { CreateTestLayer(false, false); AttachTimelineAnimationLayer(); @@ -1875,7 +1875,7 @@ TEST_F(ElementAnimationsTest, AbortKeyframeModels) { KeyframeModel::RUNNING, animation_->keyframe_effect()->GetKeyframeModelById(5)->run_state()); - animation_->AbortKeyframeModels(TargetProperty::TRANSFORM, false); + animation_->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, false); // Only un-finished TRANSFORM animations should have been aborted. EXPECT_EQ( @@ -1913,7 +1913,7 @@ TEST_F(ElementAnimationsTest, MainThreadAbortedAnimationGetsDeleted) { keyframe_model_id)); EXPECT_FALSE(host_->needs_push_properties()); - animation_->AbortKeyframeModels(TargetProperty::OPACITY, false); + animation_->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY, false); EXPECT_EQ(KeyframeModel::ABORTED, animation_->GetKeyframeModel(TargetProperty::OPACITY)->run_state()); EXPECT_TRUE(host_->needs_push_properties()); @@ -1955,7 +1955,8 @@ TEST_F(ElementAnimationsTest, ImplThreadAbortedAnimationGetsDeleted) { EXPECT_TRUE(animation_impl_->keyframe_effect()->GetKeyframeModelById( keyframe_model_id)); - animation_impl_->AbortKeyframeModels(TargetProperty::OPACITY, false); + animation_impl_->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY, + false); EXPECT_EQ( KeyframeModel::ABORTED, animation_impl_->GetKeyframeModel(TargetProperty::OPACITY)->run_state()); @@ -2026,7 +2027,8 @@ TEST_F(ElementAnimationsTest, ImplThreadTakeoverAnimationGetsDeleted) { EXPECT_TRUE(animation_impl_->keyframe_effect()->GetKeyframeModelById( keyframe_model_id)); - animation_impl_->AbortKeyframeModels(TargetProperty::SCROLL_OFFSET, true); + animation_impl_->AbortKeyframeModelsWithProperty( + TargetProperty::SCROLL_OFFSET, true); EXPECT_TRUE(host_impl_->needs_push_properties()); EXPECT_EQ(KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION, animation_impl_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET) @@ -2152,7 +2154,8 @@ TEST_F(ElementAnimationsTest, FinishedAndAbortedEventsForGroup) { EXPECT_EQ(AnimationEvent::STARTED, events->events_[0].type); EXPECT_EQ(AnimationEvent::STARTED, events->events_[1].type); - animation_impl_->AbortKeyframeModels(TargetProperty::OPACITY, false); + animation_impl_->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY, + false); events = CreateEventsForTesting(); animation_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); @@ -2877,7 +2880,8 @@ TEST_F(ElementAnimationsTest, ObserverNotifiedWhenTransformAnimationChanges) { animation_->keyframe_effect()->NotifyKeyframeModelStarted(events->events_[0]); events->events_.clear(); - animation_impl_->AbortKeyframeModels(TargetProperty::TRANSFORM, false); + animation_impl_->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, + false); EXPECT_FALSE(client_impl_.GetHasPotentialTransformAnimation( element_id_, ElementListType::PENDING)); EXPECT_FALSE(client_impl_.GetTransformIsCurrentlyAnimating( @@ -3091,7 +3095,8 @@ TEST_F(ElementAnimationsTest, ObserverNotifiedWhenOpacityAnimationChanges) { animation_->keyframe_effect()->NotifyKeyframeModelStarted(events->events_[0]); events->events_.clear(); - animation_impl_->AbortKeyframeModels(TargetProperty::OPACITY, false); + animation_impl_->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY, + false); EXPECT_FALSE(client_impl_.GetHasPotentialOpacityAnimation( element_id_, ElementListType::PENDING)); EXPECT_FALSE(client_impl_.GetOpacityIsCurrentlyAnimating( @@ -3304,7 +3309,8 @@ TEST_F(ElementAnimationsTest, ObserverNotifiedWhenFilterAnimationChanges) { animation_->keyframe_effect()->NotifyKeyframeModelStarted(events->events_[0]); events->events_.clear(); - animation_impl_->AbortKeyframeModels(TargetProperty::FILTER, false); + animation_impl_->AbortKeyframeModelsWithProperty(TargetProperty::FILTER, + false); EXPECT_FALSE(client_impl_.GetHasPotentialFilterAnimation( element_id_, ElementListType::PENDING)); EXPECT_FALSE(client_impl_.GetFilterIsCurrentlyAnimating( @@ -3722,7 +3728,7 @@ TEST_F(ElementAnimationsTest, RemoveAndReAddAnimationToTicking) { // animations. Remove the animation using RemoveFromTicking(). animation_->AddKeyframeModel(CreateKeyframeModel( std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)), - 2, TargetProperty::OPACITY)); + 1, TargetProperty::OPACITY)); ASSERT_EQ(1u, host_->ticking_animations_for_testing().size()); animation_->keyframe_effect()->RemoveFromTicking(); ASSERT_EQ(0u, host_->ticking_animations_for_testing().size()); @@ -3755,5 +3761,39 @@ TEST_F(ElementAnimationsTest, TickingKeyframeModelsCount) { EXPECT_EQ(0u, host_->CompositedAnimationsCount()); } +// This test verifies that finished keyframe models don't get copied over to +// impl thread. +TEST_F(ElementAnimationsTest, FinishedKeyframeModelsNotCopiedToImpl) { + CreateTestLayer(false, false); + AttachTimelineAnimationLayer(); + CreateImplTimelineAndAnimation(); + + animation_->AddKeyframeModel(KeyframeModel::Create( + std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1.0)), 1, 1, + TargetProperty::TRANSFORM)); + animation_->AddKeyframeModel(KeyframeModel::Create( + std::unique_ptr<AnimationCurve>(new FakeFloatTransition(2.0, 0.f, 1.f)), + 2, 2, TargetProperty::OPACITY)); + + // Finish the first keyframe model. + animation_->Tick(kInitialTickTime); + animation_->UpdateState(true, nullptr); + animation_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); + animation_->UpdateState(true, nullptr); + + EXPECT_EQ( + KeyframeModel::FINISHED, + animation_->keyframe_effect()->GetKeyframeModelById(1)->run_state()); + EXPECT_EQ( + KeyframeModel::RUNNING, + animation_->keyframe_effect()->GetKeyframeModelById(2)->run_state()); + + PushProperties(); + + // Finished keyframe model doesn't get copied to impl thread. + EXPECT_FALSE(animation_impl_->keyframe_effect()->GetKeyframeModelById(1)); + EXPECT_TRUE(animation_impl_->keyframe_effect()->GetKeyframeModelById(2)); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/keyframe_effect.cc b/chromium/cc/animation/keyframe_effect.cc index 9b01eda5754..15ef5feccc4 100644 --- a/chromium/cc/animation/keyframe_effect.cc +++ b/chromium/cc/animation/keyframe_effect.cc @@ -221,11 +221,8 @@ void KeyframeEffect::UpdateTickingState(UpdateTickingType type) { } void KeyframeEffect::Pause(base::TimeDelta pause_offset) { - for (auto& keyframe_model : keyframe_models_) { - base::TimeTicks pause_time = keyframe_model->time_offset() + - keyframe_model->start_time() + pause_offset; - keyframe_model->SetRunState(KeyframeModel::PAUSED, pause_time); - } + for (auto& keyframe_model : keyframe_models_) + keyframe_model->Pause(pause_offset); if (has_bound_element_animations()) { animation_->SetNeedsCommit(); @@ -235,12 +232,20 @@ void KeyframeEffect::Pause(base::TimeDelta pause_offset) { void KeyframeEffect::AddKeyframeModel( std::unique_ptr<KeyframeModel> keyframe_model) { - AnimationHost* animation_host = animation_->animation_host(); DCHECK(keyframe_model->target_property_id() != TargetProperty::SCROLL_OFFSET || - (animation_host && animation_host->SupportsScrollAnimations())); + (animation_->animation_host()->SupportsScrollAnimations())); DCHECK(!keyframe_model->is_impl_only() || keyframe_model->target_property_id() == TargetProperty::SCROLL_OFFSET); + // This is to make sure that keyframe models in the same group, i.e., start + // together, don't animate the same property. + DCHECK(std::none_of( + keyframe_models_.begin(), keyframe_models_.end(), + [&](const auto& existing_keyframe_model) { + return keyframe_model->target_property_id() == + existing_keyframe_model->target_property_id() && + keyframe_model->group() == existing_keyframe_model->group(); + })); keyframe_models_.push_back(std::move(keyframe_model)); @@ -252,12 +257,11 @@ void KeyframeEffect::AddKeyframeModel( void KeyframeEffect::PauseKeyframeModel(int keyframe_model_id, double time_offset) { - const base::TimeDelta time_delta = base::TimeDelta::FromSecondsD(time_offset); + const base::TimeDelta pause_offset = + base::TimeDelta::FromSecondsD(time_offset); for (auto& keyframe_model : keyframe_models_) { if (keyframe_model->id() == keyframe_model_id) { - keyframe_model->SetRunState(KeyframeModel::PAUSED, - time_delta + keyframe_model->start_time() + - keyframe_model->time_offset()); + keyframe_model->Pause(pause_offset); } } @@ -315,8 +319,9 @@ void KeyframeEffect::AbortKeyframeModel(int keyframe_model_id) { } } -void KeyframeEffect::AbortKeyframeModels(TargetProperty::Type target_property, - bool needs_completion) { +void KeyframeEffect::AbortKeyframeModelsWithProperty( + TargetProperty::Type target_property, + bool needs_completion) { if (needs_completion) DCHECK(target_property == TargetProperty::SCROLL_OFFSET); @@ -681,6 +686,11 @@ void KeyframeEffect::PushNewKeyframeModelsToImplThread( // Any new KeyframeModels owned by the main thread's Animation are // cloned and added to the impl thread's Animation. for (const auto& keyframe_model : keyframe_models_) { + // If the keyframe_model is finished, do not copy it over to impl since the + // impl instance, if there was one, was just removed in + // |RemoveKeyframeModelsCompletedOnMainThread|. + if (keyframe_model->is_finished()) + continue; // If the keyframe_model is already running on the impl thread, there is no // need to copy it over. if (keyframe_effect_impl->GetKeyframeModelById(keyframe_model->id())) @@ -785,12 +795,8 @@ void KeyframeEffect::PushPropertiesTo(KeyframeEffect* keyframe_effect_impl) { // aborted KeyframeModels and pushing any new animations. MarkAbortedKeyframeModelsForDeletion(keyframe_effect_impl); PurgeKeyframeModelsMarkedForDeletion(/* impl_only */ false); - PushNewKeyframeModelsToImplThread(keyframe_effect_impl); - - // Remove finished impl side KeyframeModels only after pushing, - // and only after the KeyframeModels are deleted on the main thread - // this insures we will never push a keyframe model twice. RemoveKeyframeModelsCompletedOnMainThread(keyframe_effect_impl); + PushNewKeyframeModelsToImplThread(keyframe_effect_impl); // Now that the keyframe model lists are synchronized, push the properties for // the individual KeyframeModels. diff --git a/chromium/cc/animation/keyframe_effect.h b/chromium/cc/animation/keyframe_effect.h index 4ee5176e517..0c33cb7db6d 100644 --- a/chromium/cc/animation/keyframe_effect.h +++ b/chromium/cc/animation/keyframe_effect.h @@ -23,7 +23,6 @@ namespace cc { class Animation; -class KeyframeModel; struct PropertyAnimationState; typedef size_t KeyframeEffectId; @@ -42,7 +41,7 @@ typedef size_t KeyframeEffectId; class CC_ANIMATION_EXPORT KeyframeEffect { public: explicit KeyframeEffect(KeyframeEffectId id); - ~KeyframeEffect(); + virtual ~KeyframeEffect(); static std::unique_ptr<KeyframeEffect> Create(KeyframeEffectId id); std::unique_ptr<KeyframeEffect> CreateImplInstance() const; @@ -81,7 +80,7 @@ class CC_ANIMATION_EXPORT KeyframeEffect { void AttachElement(ElementId element_id); void DetachElement(); - void Tick(base::TimeTicks monotonic_time); + virtual void Tick(base::TimeTicks monotonic_time); static void TickKeyframeModel(base::TimeTicks monotonic_time, KeyframeModel* keyframe_model, AnimationTarget* target); @@ -97,8 +96,8 @@ class CC_ANIMATION_EXPORT KeyframeEffect { void PauseKeyframeModel(int keyframe_model_id, double time_offset); void RemoveKeyframeModel(int keyframe_model_id); void AbortKeyframeModel(int keyframe_model_id); - void AbortKeyframeModels(TargetProperty::Type target_property, - bool needs_completion); + void AbortKeyframeModelsWithProperty(TargetProperty::Type target_property, + bool needs_completion); void ActivateKeyframeEffects(); diff --git a/chromium/cc/animation/keyframe_model.cc b/chromium/cc/animation/keyframe_model.cc index d8495a62ff1..10556098fc8 100644 --- a/chromium/cc/animation/keyframe_model.cc +++ b/chromium/cc/animation/keyframe_model.cc @@ -66,7 +66,7 @@ std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance( to_return->iteration_start_ = iteration_start_; to_return->start_time_ = start_time_; to_return->pause_time_ = pause_time_; - to_return->total_paused_time_ = total_paused_time_; + to_return->total_paused_duration_ = total_paused_duration_; to_return->time_offset_ = time_offset_; to_return->direction_ = direction_; to_return->playback_rate_ = playback_rate_; @@ -121,7 +121,7 @@ void KeyframeModel::SetRunState(RunState run_state, const char* old_run_state_name = s_runStateNames[run_state_]; if (run_state == RUNNING && run_state_ == PAUSED) - total_paused_time_ += (monotonic_time - pause_time_); + total_paused_duration_ += (monotonic_time - pause_time_); else if (run_state == PAUSED) pause_time_ = monotonic_time; run_state_ = run_state; @@ -140,6 +140,18 @@ void KeyframeModel::SetRunState(RunState run_state, TRACE_STR_COPY(name_buffer), "State", TRACE_STR_COPY(state_buffer)); } +void KeyframeModel::Pause(base::TimeDelta pause_offset) { + // Convert pause offset to monotonic time. + + // TODO(crbug.com/840364): This conversion is incorrect. pause_offset is + // actually a local time so to convert it to monotonic time we should include + // total_paused_duration_ but exclude time_offset. The current calculation is + // is incorrect for animations that have start-delay or are paused and + // unpaused multiple times. + base::TimeTicks monotonic_time = pause_offset + start_time_ + time_offset_; + SetRunState(PAUSED, monotonic_time); +} + bool KeyframeModel::IsFinishedAt(base::TimeTicks monotonic_time) const { if (is_finished()) return true; @@ -152,39 +164,42 @@ bool KeyframeModel::IsFinishedAt(base::TimeTicks monotonic_time) const { return run_state_ == RUNNING && iterations_ >= 0 && (curve_->Duration() * (iterations_ / std::abs(playback_rate_))) <= - (monotonic_time + time_offset_ - start_time_ - total_paused_time_); + (ConvertMonotonicTimeToLocalTime(monotonic_time) + time_offset_); } bool KeyframeModel::InEffect(base::TimeTicks monotonic_time) const { - return ConvertToActiveTime(monotonic_time) >= base::TimeDelta() || + return ConvertMonotonicTimeToLocalTime(monotonic_time) + time_offset_ >= + base::TimeDelta() || (fill_mode_ == FillMode::BOTH || fill_mode_ == FillMode::BACKWARDS); } -base::TimeDelta KeyframeModel::ConvertToActiveTime( +base::TimeDelta KeyframeModel::ConvertMonotonicTimeToLocalTime( base::TimeTicks monotonic_time) const { - // If we're just starting or we're waiting on receiving a start time, - // time is 'stuck' at the initial state. + // When waiting on receiving a start time, then our global clock is 'stuck' at + // the initial state. if ((run_state_ == STARTING && !has_set_start_time()) || - needs_synchronized_start_time()) { - return time_offset_; - } - - // Compute active time. If we're paused, time is 'stuck' at the pause time. - base::TimeTicks active_time = - (run_state_ == PAUSED) ? pause_time_ : (monotonic_time + time_offset_); + needs_synchronized_start_time()) + return base::TimeDelta(); - // Returned time should always be relative to the start time and should - // subtract all time spent paused. - return active_time - start_time_ - total_paused_time_; + // If we're paused, time is 'stuck' at the pause time. + base::TimeTicks time = + (run_state_ == PAUSED) ? pause_time_ - time_offset_ : monotonic_time; + return time - start_time_ - total_paused_duration_; } base::TimeDelta KeyframeModel::TrimTimeToCurrentIteration( base::TimeTicks monotonic_time) const { + base::TimeDelta local_time = ConvertMonotonicTimeToLocalTime(monotonic_time); + return TrimLocalTimeToCurrentIteration(local_time); +} + +base::TimeDelta KeyframeModel::TrimLocalTimeToCurrentIteration( + base::TimeDelta local_time) const { // Check for valid parameters DCHECK(playback_rate_); DCHECK_GE(iteration_start_, 0); - base::TimeDelta active_time = ConvertToActiveTime(monotonic_time); + base::TimeDelta active_time = local_time + time_offset_; base::TimeDelta start_offset = curve_->Duration() * iteration_start_; // Return start offset if we are before the start of the keyframe model @@ -254,7 +269,7 @@ void KeyframeModel::PushPropertiesTo(KeyframeModel* other) const { other->run_state_ == KeyframeModel::PAUSED) { other->run_state_ = run_state_; other->pause_time_ = pause_time_; - other->total_paused_time_ = total_paused_time_; + other->total_paused_duration_ = total_paused_duration_; } } diff --git a/chromium/cc/animation/keyframe_model.h b/chromium/cc/animation/keyframe_model.h index 9d770995dd8..329a986253f 100644 --- a/chromium/cc/animation/keyframe_model.h +++ b/chromium/cc/animation/keyframe_model.h @@ -95,6 +95,9 @@ class CC_ANIMATION_EXPORT KeyframeModel { time_offset_ = monotonic_time; } + // Pause the keyframe effect at local time |pause_offset|. + void Pause(base::TimeDelta pause_offset); + Direction direction() { return direction_; } void set_direction(Direction direction) { direction_ = direction; } @@ -168,7 +171,35 @@ class CC_ANIMATION_EXPORT KeyframeModel { int group_id, int target_property_id); - base::TimeDelta ConvertToActiveTime(base::TimeTicks monotonic_time) const; + // Return local time for this keyframe model given the absolute monotonic + // time. + // + // Local time represents the time value that is used to tick this keyframe + // model and is relative to its start time. It is closely related to the local + // time concept in web animations [1]. It is: + // - for playing animation : wall time - start time - paused duration + // - for paused animation : paused time + // - otherwise : zero + // + // Here is small diagram that shows how active, local, and monotonic times + // relate to each other and to the run state. + // + // run state Starting (R)unning Paused (R) Paused (R) Finished + // ^ ^ + // | | + // monotonic time -------------------------------------------------> + // | | + // local time +-----------------+ +---+ +---------> + // | | + // active time + +------+ +---+ +------+ + // (-offset) + // + // [1] https://drafts.csswg.org/web-animations/#local-time-section + base::TimeDelta ConvertMonotonicTimeToLocalTime( + base::TimeTicks monotonic_time) const; + + base::TimeDelta TrimLocalTimeToCurrentIteration( + base::TimeDelta local_time) const; std::unique_ptr<AnimationCurve> curve_; @@ -199,12 +230,12 @@ class CC_ANIMATION_EXPORT KeyframeModel { bool needs_synchronized_start_time_; bool received_finished_event_; - // These are used in TrimTimeToCurrentIteration to account for time + // These are used when converting monotonic to local time to account for time // spent while paused. This is not included in AnimationState since it // there is absolutely no need for clients of this controller to know // about these values. base::TimeTicks pause_time_; - base::TimeDelta total_paused_time_; + base::TimeDelta total_paused_duration_; // KeyframeModels lead dual lives. An active keyframe model will be // conceptually owned by two controllers, one on the impl thread and one on diff --git a/chromium/cc/animation/scroll_offset_animations_impl.cc b/chromium/cc/animation/scroll_offset_animations_impl.cc index 9e0b9aa1025..2e1a274e925 100644 --- a/chromium/cc/animation/scroll_offset_animations_impl.cc +++ b/chromium/cc/animation/scroll_offset_animations_impl.cc @@ -4,6 +4,8 @@ #include "cc/animation/scroll_offset_animations_impl.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_argument.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" #include "cc/animation/animation_timeline.h" @@ -44,6 +46,8 @@ void ScrollOffsetAnimationsImpl::ScrollAnimationCreate( CubicBezierTimingFunction::EaseType::EASE_IN_OUT), ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA); curve->SetInitialValue(current_offset, delayed_by); + TRACE_EVENT_INSTANT1("cc", "ScrollAnimationCreate", TRACE_EVENT_SCOPE_THREAD, + "Duration", curve->Duration().InMillisecondsF()); std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create( std::move(curve), AnimationIdProvider::NextKeyframeModelId(), @@ -66,8 +70,11 @@ bool ScrollOffsetAnimationsImpl::ScrollAnimationUpdateTarget( base::TimeTicks frame_monotonic_time, base::TimeDelta delayed_by) { DCHECK(scroll_offset_animation_); - if (!scroll_offset_animation_->has_element_animations()) + if (!scroll_offset_animation_->has_element_animations()) { + TRACE_EVENT_INSTANT0("cc", "No element animation exists", + TRACE_EVENT_SCOPE_THREAD); return false; + } DCHECK_EQ(element_id, scroll_offset_animation_->element_id()); @@ -75,6 +82,8 @@ bool ScrollOffsetAnimationsImpl::ScrollAnimationUpdateTarget( scroll_offset_animation_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET); if (!keyframe_model) { scroll_offset_animation_->DetachElement(); + TRACE_EVENT_INSTANT0("cc", "No keyframe model exists", + TRACE_EVENT_SCOPE_THREAD); return false; } if (scroll_delta.IsZero()) @@ -102,6 +111,9 @@ bool ScrollOffsetAnimationsImpl::ScrollAnimationUpdateTarget( trimmed -= delayed_by; curve->UpdateTarget(trimmed.InSecondsF(), new_target); + TRACE_EVENT_INSTANT1("cc", "ScrollAnimationUpdateTarget", + TRACE_EVENT_SCOPE_THREAD, "UpdatedDuration", + curve->Duration().InMillisecondsF()); return true; } @@ -143,8 +155,8 @@ void ScrollOffsetAnimationsImpl::ScrollAnimationApplyAdjustment( void ScrollOffsetAnimationsImpl::ScrollAnimationAbort(bool needs_completion) { DCHECK(scroll_offset_animation_); - scroll_offset_animation_->AbortKeyframeModels(TargetProperty::SCROLL_OFFSET, - needs_completion); + scroll_offset_animation_->AbortKeyframeModelsWithProperty( + TargetProperty::SCROLL_OFFSET, needs_completion); } void ScrollOffsetAnimationsImpl::NotifyAnimationFinished( diff --git a/chromium/cc/animation/scroll_timeline.cc b/chromium/cc/animation/scroll_timeline.cc index a880877b605..ee02fb5cab4 100644 --- a/chromium/cc/animation/scroll_timeline.cc +++ b/chromium/cc/animation/scroll_timeline.cc @@ -11,31 +11,44 @@ namespace cc { -ScrollTimeline::ScrollTimeline(ElementId scroller_id, +ScrollTimeline::ScrollTimeline(base::Optional<ElementId> scroller_id, ScrollDirection orientation, double time_range) - : scroller_id_(scroller_id), + : active_id_(), + pending_id_(scroller_id), orientation_(orientation), time_range_(time_range) {} +ScrollTimeline::~ScrollTimeline() {} + std::unique_ptr<ScrollTimeline> ScrollTimeline::CreateImplInstance() const { - return std::make_unique<ScrollTimeline>(scroller_id_, orientation_, + return std::make_unique<ScrollTimeline>(pending_id_, orientation_, time_range_); } -double ScrollTimeline::CurrentTime(const ScrollTree& scroll_tree) const { - // If the scroller isn't in the ScrollTree, the element either no longer - // exists or is not currently scrollable. By the spec, return an unresolved - // time value. - if (!scroll_tree.FindNodeFromElementId(scroller_id_)) +double ScrollTimeline::CurrentTime(const ScrollTree& scroll_tree, + bool is_active_tree) const { + // We may be asked for the CurrentTime before the pending tree with our + // scroller has been activated, or after the scroller has been removed (e.g. + // if it is no longer composited). In these cases the best we can do is to + // return an unresolved time value. + if ((is_active_tree && !active_id_) || (!is_active_tree && !pending_id_)) return std::numeric_limits<double>::quiet_NaN(); - gfx::ScrollOffset offset = scroll_tree.current_scroll_offset(scroller_id_); + ElementId scroller_id = + is_active_tree ? active_id_.value() : pending_id_.value(); + + // The scroller may not be in the ScrollTree if it is not currently scrollable + // (e.g. has overflow: visible). By the spec, return an unresolved time value. + if (!scroll_tree.FindNodeFromElementId(scroller_id)) + return std::numeric_limits<double>::quiet_NaN(); + + gfx::ScrollOffset offset = scroll_tree.current_scroll_offset(scroller_id); DCHECK_GE(offset.x(), 0); DCHECK_GE(offset.y(), 0); gfx::ScrollOffset scroll_dimensions = scroll_tree.MaxScrollOffset( - scroll_tree.FindNodeFromElementId(scroller_id_)->id); + scroll_tree.FindNodeFromElementId(scroller_id)->id); double current_offset = (orientation_ == Vertical) ? offset.y() : offset.x(); double max_offset = (orientation_ == Vertical) ? scroll_dimensions.y() @@ -57,4 +70,21 @@ double ScrollTimeline::CurrentTime(const ScrollTree& scroll_tree) const { return (std::abs(current_offset) / max_offset) * time_range_; } +void ScrollTimeline::PushPropertiesTo(ScrollTimeline* impl_timeline) { + DCHECK(impl_timeline); + impl_timeline->pending_id_ = pending_id_; +} + +void ScrollTimeline::PromoteScrollTimelinePendingToActive() { + active_id_ = pending_id_; +} + +void ScrollTimeline::SetScrollerId(base::Optional<ElementId> pending_id) { + // When the scroller id changes it will first be modified in the pending tree. + // Then later (when the pending tree is promoted to active) + // |PromoteScrollTimelinePendingToActive| will be called and will set the + // |active_id_|. + pending_id_ = pending_id; +} + } // namespace cc diff --git a/chromium/cc/animation/scroll_timeline.h b/chromium/cc/animation/scroll_timeline.h index 7389d4df479..1efd2a77963 100644 --- a/chromium/cc/animation/scroll_timeline.h +++ b/chromium/cc/animation/scroll_timeline.h @@ -5,6 +5,7 @@ #ifndef CC_ANIMATION_SCROLL_TIMELINE_H_ #define CC_ANIMATION_SCROLL_TIMELINE_H_ +#include "base/optional.h" #include "cc/animation/animation_export.h" #include "cc/trees/element_id.h" @@ -26,10 +27,10 @@ class CC_ANIMATION_EXPORT ScrollTimeline { public: enum ScrollDirection { Horizontal, Vertical }; - ScrollTimeline(ElementId scroller_id, + ScrollTimeline(base::Optional<ElementId> scroller_id, ScrollDirection orientation, double time_range); - virtual ~ScrollTimeline() {} + virtual ~ScrollTimeline(); // Create a copy of this ScrollTimeline intended for the impl thread in the // compositor. @@ -38,13 +39,21 @@ class CC_ANIMATION_EXPORT ScrollTimeline { // Calculate the current time of the ScrollTimeline. This is either a double // value or std::numeric_limits<double>::quiet_NaN() if the current time is // unresolved. - virtual double CurrentTime(const ScrollTree& scroll_tree) const; + virtual double CurrentTime(const ScrollTree& scroll_tree, + bool is_active_tree) const; + + void SetScrollerId(base::Optional<ElementId> scroller_id); + + void PushPropertiesTo(ScrollTimeline* impl_timeline); + + void PromoteScrollTimelinePendingToActive(); private: - // The scroller which this ScrollTimeline is based on. It is expected that - // this scroller will exist in the scroll property tree, or otherwise calling - // CurrentTime will fail. - ElementId scroller_id_; + // The scroller which this ScrollTimeline is based on. The same underlying + // scroll source may have different ids in the pending and active tree (see + // http://crbug.com/847588). + base::Optional<ElementId> active_id_; + base::Optional<ElementId> pending_id_; // The orientation of the ScrollTimeline indicates which axis of the scroller // it should base its current time on - either the horizontal or vertical. diff --git a/chromium/cc/animation/scroll_timeline_unittest.cc b/chromium/cc/animation/scroll_timeline_unittest.cc index 4b410137b97..3fc5cbe6306 100644 --- a/chromium/cc/animation/scroll_timeline_unittest.cc +++ b/chromium/cc/animation/scroll_timeline_unittest.cc @@ -17,7 +17,7 @@ class ScrollTimelineTest : public ::testing::Test { // For simplicity we make the property_tree main thread; this avoids the // need to deal with the synced scroll offset code. property_trees_.is_main_thread = true; - property_trees_.is_active = true; + property_trees_.is_active = false; // We add a single node that is scrolling a 550x1100 contents inside a // 50x100 container. @@ -55,16 +55,16 @@ TEST_F(ScrollTimelineTest, BasicCurrentTimeCalculations) { // Unscrolled, both timelines should read a current time of 0. scroll_tree().SetScrollOffset(scroller_id(), gfx::ScrollOffset()); - EXPECT_FLOAT_EQ(0, vertical_timeline.CurrentTime(scroll_tree())); - EXPECT_FLOAT_EQ(0, horizontal_timeline.CurrentTime(scroll_tree())); + EXPECT_FLOAT_EQ(0, vertical_timeline.CurrentTime(scroll_tree(), false)); + EXPECT_FLOAT_EQ(0, horizontal_timeline.CurrentTime(scroll_tree(), false)); // Now do some scrolling and make sure that the ScrollTimelines update. scroll_tree().SetScrollOffset(scroller_id(), gfx::ScrollOffset(75, 50)); // As noted above, we have mapped the time range such that current time should // just be the scroll offset. - EXPECT_FLOAT_EQ(50, vertical_timeline.CurrentTime(scroll_tree())); - EXPECT_FLOAT_EQ(75, horizontal_timeline.CurrentTime(scroll_tree())); + EXPECT_FLOAT_EQ(50, vertical_timeline.CurrentTime(scroll_tree(), false)); + EXPECT_FLOAT_EQ(75, horizontal_timeline.CurrentTime(scroll_tree(), false)); } TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForTimeRange) { @@ -75,7 +75,60 @@ TEST_F(ScrollTimelineTest, CurrentTimeIsAdjustedForTimeRange) { double halfwayY = (content_size().height() - container_size().height()) / 2.; scroll_tree().SetScrollOffset(scroller_id(), gfx::ScrollOffset(0, halfwayY)); - EXPECT_FLOAT_EQ(50, timeline.CurrentTime(scroll_tree())); + EXPECT_FLOAT_EQ(50, timeline.CurrentTime(scroll_tree(), false)); +} + +// This test ensures that the ScrollTimeline's active scroller id is correct. We +// had a few crashes caused by assuming that the id would be available in the +// active tree before the activation happened; see http://crbug.com/853231 +TEST_F(ScrollTimelineTest, ActiveTimeIsSetOnlyAfterPromotion) { + PropertyTrees pending_tree; + PropertyTrees active_tree; + + pending_tree.is_active = false; + active_tree.is_active = true; + + // For simplicity we pretend the trees are main thread; this avoids the need + // to deal with the synced scroll offset code. + pending_tree.is_main_thread = true; + active_tree.is_main_thread = true; + + // Initially only the pending tree has the scroll node. + ElementId scroller_id(1); + ScrollNode node; + node.scrollable = true; + node.bounds = content_size(); + node.container_bounds = container_size(); + + int node_id = pending_tree.scroll_tree.Insert(node, 0); + pending_tree.element_id_to_scroll_node_index[scroller_id] = node_id; + + double halfwayY = (content_size().height() - container_size().height()) / 2.; + pending_tree.scroll_tree.SetScrollOffset(scroller_id, + gfx::ScrollOffset(0, halfwayY)); + + ScrollTimeline main_timeline(scroller_id, ScrollTimeline::Vertical, 100); + + // Now create an impl version of the ScrollTimeline. Initilly this should only + // have a pending scroller id, as the active tree may not yet have the + // scroller in it (as in this case). + std::unique_ptr<ScrollTimeline> impl_timeline = + main_timeline.CreateImplInstance(); + + EXPECT_TRUE( + std::isnan(impl_timeline->CurrentTime(active_tree.scroll_tree, true))); + EXPECT_FLOAT_EQ(50, + impl_timeline->CurrentTime(pending_tree.scroll_tree, false)); + + // Now fake a tree activation; this should cause the ScrollTimeline to update + // its active scroller id. Note that we deliberately pass in the pending_tree + // and just claim it is the active tree; this avoids needing to properly + // implement tree swapping just for the test. + impl_timeline->PromoteScrollTimelinePendingToActive(); + EXPECT_FLOAT_EQ(50, + impl_timeline->CurrentTime(pending_tree.scroll_tree, true)); + EXPECT_FLOAT_EQ(50, + impl_timeline->CurrentTime(pending_tree.scroll_tree, false)); } } // namespace cc diff --git a/chromium/cc/animation/single_keyframe_effect_animation.cc b/chromium/cc/animation/single_keyframe_effect_animation.cc index 4c5d3eb0da0..d102c3924b3 100644 --- a/chromium/cc/animation/single_keyframe_effect_animation.cc +++ b/chromium/cc/animation/single_keyframe_effect_animation.cc @@ -26,17 +26,24 @@ SingleKeyframeEffectAnimation::Create(int id) { } SingleKeyframeEffectAnimation::SingleKeyframeEffectAnimation(int id) - : Animation(id) { - DCHECK(id_); - AddKeyframeEffect(std::make_unique<KeyframeEffect>(NextKeyframeEffectId())); -} + : SingleKeyframeEffectAnimation(id, nullptr) {} SingleKeyframeEffectAnimation::SingleKeyframeEffectAnimation( int id, size_t keyframe_effect_id) + : SingleKeyframeEffectAnimation( + id, + std::make_unique<KeyframeEffect>(keyframe_effect_id)) {} + +SingleKeyframeEffectAnimation::SingleKeyframeEffectAnimation( + int id, + std::unique_ptr<KeyframeEffect> keyframe_effect) : Animation(id) { DCHECK(id_); - AddKeyframeEffect(std::make_unique<KeyframeEffect>(keyframe_effect_id)); + if (!keyframe_effect) + keyframe_effect.reset(new KeyframeEffect(NextKeyframeEffectId())); + + AddKeyframeEffect(std::move(keyframe_effect)); } SingleKeyframeEffectAnimation::~SingleKeyframeEffectAnimation() {} diff --git a/chromium/cc/animation/single_keyframe_effect_animation.h b/chromium/cc/animation/single_keyframe_effect_animation.h index c406892e415..6baeb7669c8 100644 --- a/chromium/cc/animation/single_keyframe_effect_animation.h +++ b/chromium/cc/animation/single_keyframe_effect_animation.h @@ -44,7 +44,7 @@ class CC_ANIMATION_EXPORT SingleKeyframeEffectAnimation : public Animation { KeyframeEffect* keyframe_effect() const; void AddKeyframeModel(std::unique_ptr<KeyframeModel> keyframe_model); void PauseKeyframeModel(int keyframe_model_id, double time_offset); - void RemoveKeyframeModel(int keyframe_model_id); + virtual void RemoveKeyframeModel(int keyframe_model_id); void AbortKeyframeModel(int keyframe_model_id); bool NotifyKeyframeModelFinishedForTesting( @@ -60,6 +60,9 @@ class CC_ANIMATION_EXPORT SingleKeyframeEffectAnimation : public Animation { protected: explicit SingleKeyframeEffectAnimation(int id); explicit SingleKeyframeEffectAnimation(int id, size_t keyframe_effect_id); + explicit SingleKeyframeEffectAnimation(int id, + std::unique_ptr<KeyframeEffect>); + ~SingleKeyframeEffectAnimation() override; DISALLOW_COPY_AND_ASSIGN(SingleKeyframeEffectAnimation); diff --git a/chromium/cc/animation/worklet_animation.cc b/chromium/cc/animation/worklet_animation.cc index a905b94ce12..34a9746df3c 100644 --- a/chromium/cc/animation/worklet_animation.cc +++ b/chromium/cc/animation/worklet_animation.cc @@ -4,29 +4,57 @@ #include "cc/animation/worklet_animation.h" +#include "cc/animation/animation_id_provider.h" +#include "cc/animation/keyframe_effect.h" #include "cc/animation/scroll_timeline.h" +#include "cc/trees/animation_options.h" namespace cc { WorkletAnimation::WorkletAnimation( - int id, + int cc_animation_id, + WorkletAnimationId worklet_animation_id, const std::string& name, std::unique_ptr<ScrollTimeline> scroll_timeline, + std::unique_ptr<AnimationOptions> options, bool is_controlling_instance) - : SingleKeyframeEffectAnimation(id), + : WorkletAnimation(cc_animation_id, + worklet_animation_id, + name, + std::move(scroll_timeline), + std::move(options), + is_controlling_instance, + nullptr) {} + +WorkletAnimation::WorkletAnimation( + int cc_animation_id, + WorkletAnimationId worklet_animation_id, + const std::string& name, + std::unique_ptr<ScrollTimeline> scroll_timeline, + std::unique_ptr<AnimationOptions> options, + bool is_controlling_instance, + std::unique_ptr<KeyframeEffect> effect) + : SingleKeyframeEffectAnimation(cc_animation_id, std::move(effect)), + worklet_animation_id_(worklet_animation_id), name_(name), scroll_timeline_(std::move(scroll_timeline)), + options_(std::move(options)), + local_time_(base::nullopt), + start_time_(base::nullopt), last_current_time_(base::nullopt), + state_(State::PENDING), is_impl_instance_(is_controlling_instance) {} WorkletAnimation::~WorkletAnimation() = default; scoped_refptr<WorkletAnimation> WorkletAnimation::Create( - int id, + WorkletAnimationId worklet_animation_id, const std::string& name, - std::unique_ptr<ScrollTimeline> scroll_timeline) { - return WrapRefCounted( - new WorkletAnimation(id, name, std::move(scroll_timeline), false)); + std::unique_ptr<ScrollTimeline> scroll_timeline, + std::unique_ptr<AnimationOptions> options) { + return WrapRefCounted(new WorkletAnimation( + AnimationIdProvider::NextAnimationId(), worklet_animation_id, name, + std::move(scroll_timeline), std::move(options), false)); } scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const { @@ -34,13 +62,18 @@ scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const { if (scroll_timeline_) impl_timeline = scroll_timeline_->CreateImplInstance(); - return WrapRefCounted( - new WorkletAnimation(id(), name(), std::move(impl_timeline), true)); + return WrapRefCounted(new WorkletAnimation(id(), worklet_animation_id_, + name(), std::move(impl_timeline), + CloneOptions(), true)); } -void WorkletAnimation::SetLocalTime(base::TimeDelta local_time) { - local_time_ = local_time; - SetNeedsPushProperties(); +void WorkletAnimation::PushPropertiesTo(Animation* animation_impl) { + Animation::PushPropertiesTo(animation_impl); + WorkletAnimation* worklet_animation_impl = ToWorkletAnimation(animation_impl); + if (scroll_timeline_) { + scroll_timeline_->PushPropertiesTo( + worklet_animation_impl->scroll_timeline_.get()); + } } void WorkletAnimation::Tick(base::TimeTicks monotonic_time) { @@ -48,36 +81,102 @@ void WorkletAnimation::Tick(base::TimeTicks monotonic_time) { // skip ticking all animations on main thread in http://crbug.com/762717. if (!is_impl_instance_) return; + if (!local_time_.has_value()) + return; // As the output of a WorkletAnimation is driven by a script-provided local // time, we don't want the underlying effect to participate in the normal // animations lifecycle. To avoid this we pause the underlying keyframe effect // at the local time obtained from the user script - essentially turning each // call to |WorkletAnimation::Tick| into a seek in the effect. - keyframe_effect()->Pause(local_time_); + keyframe_effect()->Pause(local_time_.value()); keyframe_effect()->Tick(monotonic_time); } -// TODO(crbug.com/780151): The current time returned should be an offset against -// the animation's start time and based on the playback rate, not just the -// timeline time directly. -double WorkletAnimation::CurrentTime(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree) { - if (scroll_timeline_) { - return scroll_timeline_->CurrentTime(scroll_tree); +void WorkletAnimation::UpdateInputState(MutatorInputState* input_state, + base::TimeTicks monotonic_time, + const ScrollTree& scroll_tree, + bool is_active_tree) { + // Record the monotonic time to be the start time first time state is + // generated. This time is used as the origin for computing the current time. + if (!start_time_.has_value()) + start_time_ = monotonic_time; + + // Skip running worklet animations with unchanged input time and reuse + // their value from the previous animation call. + if (!NeedsUpdate(monotonic_time, scroll_tree, is_active_tree)) + return; + + double current_time = + CurrentTime(monotonic_time, scroll_tree, is_active_tree); + last_current_time_ = current_time; + + switch (state_) { + case State::PENDING: + input_state->Add( + {worklet_animation_id(), name(), current_time, CloneOptions()}); + state_ = State::RUNNING; + break; + case State::RUNNING: + input_state->Update({worklet_animation_id(), current_time}); + break; + case State::REMOVED: + input_state->Remove(worklet_animation_id()); + break; } +} + +void WorkletAnimation::SetOutputState( + const MutatorOutputState::AnimationState& state) { + local_time_ = state.local_time; +} - // TODO(crbug.com/783333): Support DocumentTimeline's originTime concept. - return (monotonic_time - base::TimeTicks()).InMillisecondsF(); +// TODO(crbug.com/780151): Multiply the result by the play back rate. +double WorkletAnimation::CurrentTime(base::TimeTicks monotonic_time, + const ScrollTree& scroll_tree, + bool is_active_tree) { + // Note that we have intentionally decided not to offset the scroll timeline + // by the start time. See: https://github.com/w3c/csswg-drafts/issues/2075 + if (scroll_timeline_) + return scroll_timeline_->CurrentTime(scroll_tree, is_active_tree); + return (monotonic_time - start_time_.value()).InMillisecondsF(); } bool WorkletAnimation::NeedsUpdate(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree) { - double current_time = CurrentTime(monotonic_time, scroll_tree); + const ScrollTree& scroll_tree, + bool is_active_tree) { + // If we don't have a start time it means that an update was never sent to + // the worklet therefore we need one. + if (!scroll_timeline_ && !start_time_.has_value()) + return true; + + DCHECK(state_ == State::PENDING || last_current_time_.has_value()); + if (state_ == State::REMOVED) + return true; + + double current_time = + CurrentTime(monotonic_time, scroll_tree, is_active_tree); bool needs_update = last_current_time_ != current_time; - last_current_time_ = current_time; return needs_update; } +void WorkletAnimation::SetScrollSourceId( + base::Optional<ElementId> scroller_id) { + // Calling this method implies that we are a ScrollTimeline based animation, + // so the below call is done unchecked. + scroll_timeline_->SetScrollerId(scroller_id); + SetNeedsPushProperties(); +} + +void WorkletAnimation::PromoteScrollTimelinePendingToActive() { + if (scroll_timeline_) + scroll_timeline_->PromoteScrollTimelinePendingToActive(); +} + +void WorkletAnimation::RemoveKeyframeModel(int keyframe_model_id) { + state_ = State::REMOVED; + SingleKeyframeEffectAnimation::RemoveKeyframeModel(keyframe_model_id); +} + bool WorkletAnimation::IsWorkletAnimation() const { return true; } diff --git a/chromium/cc/animation/worklet_animation.h b/chromium/cc/animation/worklet_animation.h index cdde572fd28..1ab919d555e 100644 --- a/chromium/cc/animation/worklet_animation.h +++ b/chromium/cc/animation/worklet_animation.h @@ -5,14 +5,21 @@ #ifndef CC_ANIMATION_WORKLET_ANIMATION_H_ #define CC_ANIMATION_WORKLET_ANIMATION_H_ +#include "base/gtest_prod_util.h" #include "base/optional.h" #include "base/time/time.h" #include "cc/animation/animation_export.h" -#include "cc/animation/keyframe_effect.h" +#include "cc/animation/animation_host.h" #include "cc/animation/single_keyframe_effect_animation.h" +#include "cc/trees/property_tree.h" namespace cc { +namespace { +FORWARD_DECLARE_TEST(WorkletAnimationTest, NonImplInstanceDoesNotTickKeyframe); +} // namespace + +class AnimationOptions; class ScrollTimeline; // A WorkletAnimation is an animation that allows its animation @@ -21,39 +28,79 @@ class ScrollTimeline; class CC_ANIMATION_EXPORT WorkletAnimation final : public SingleKeyframeEffectAnimation { public: - WorkletAnimation(int id, + enum class State { PENDING, RUNNING, REMOVED }; + WorkletAnimation(int cc_animation_id, + WorkletAnimationId worklet_animation_id, const std::string& name, std::unique_ptr<ScrollTimeline> scroll_timeline, + std::unique_ptr<AnimationOptions> options, bool is_controlling_instance); static scoped_refptr<WorkletAnimation> Create( - int id, + WorkletAnimationId worklet_animation_id, const std::string& name, - std::unique_ptr<ScrollTimeline> scroll_timeline); + std::unique_ptr<ScrollTimeline> scroll_timeline, + std::unique_ptr<AnimationOptions> options); scoped_refptr<Animation> CreateImplInstance() const override; + WorkletAnimationId worklet_animation_id() { return worklet_animation_id_; } const std::string& name() const { return name_; } const ScrollTimeline* scroll_timeline() const { return scroll_timeline_.get(); } - void SetLocalTime(base::TimeDelta local_time); bool IsWorkletAnimation() const override; void Tick(base::TimeTicks monotonic_time) override; + void UpdateInputState(MutatorInputState* input_state, + base::TimeTicks monotonic_time, + const ScrollTree& scroll_tree, + bool is_active_tree); + void SetOutputState(const MutatorOutputState::AnimationState& state); + + void PushPropertiesTo(Animation* animation_impl) override; + + // Should be called when the scroll source of the ScrollTimeline attached to + // this animation has a change in ElementId. Such a change happens when the + // scroll source changes compositing state. + void SetScrollSourceId(base::Optional<ElementId> scroller_id); + + // Should be called when the pending tree is promoted to active, as this may + // require updating the ElementId for the ScrollTimeline scroll source. + void PromoteScrollTimelinePendingToActive() override; + + void RemoveKeyframeModel(int keyframe_model_id) override; + + private: + FRIEND_TEST_ALL_PREFIXES(WorkletAnimationTest, + NonImplInstanceDoesNotTickKeyframe); + WorkletAnimation(int cc_animation_id, + WorkletAnimationId worklet_animation_id, + const std::string& name, + std::unique_ptr<ScrollTimeline> scroll_timeline, + std::unique_ptr<AnimationOptions> options, + bool is_controlling_instance, + std::unique_ptr<KeyframeEffect> effect); + + ~WorkletAnimation() override; + // Returns the current time to be passed into the underlying AnimationWorklet. // The current time is based on the timeline associated with the animation. double CurrentTime(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree); + const ScrollTree& scroll_tree, + bool is_active_tree); // Returns true if the worklet animation needs to be updated which happens iff // its current time is going to be different from last time given these input. bool NeedsUpdate(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree); + const ScrollTree& scroll_tree, + bool is_active_tree); - private: - ~WorkletAnimation() override; + std::unique_ptr<AnimationOptions> CloneOptions() const { + return options_ ? options_->Clone() : nullptr; + } + WorkletAnimationId worklet_animation_id_; std::string name_; // The ScrollTimeline associated with the underlying animation. If null, the @@ -64,13 +111,28 @@ class CC_ANIMATION_EXPORT WorkletAnimation final // some other future implementation. std::unique_ptr<ScrollTimeline> scroll_timeline_; - base::TimeDelta local_time_; + std::unique_ptr<AnimationOptions> options_; + + // Local time is used as an input to the keyframe effect of this animation. + // The value comes from the user script that runs inside the animation worklet + // global scope. + base::Optional<base::TimeDelta> local_time_; + base::Optional<base::TimeTicks> start_time_; + // Last current time used for updatig. We use this to skip updating if current + // time has not changed since last update. base::Optional<double> last_current_time_; + State state_; + bool is_impl_instance_; }; +inline WorkletAnimation* ToWorkletAnimation(Animation* animation) { + DCHECK(animation->IsWorkletAnimation()); + return static_cast<WorkletAnimation*>(animation); +} + } // namespace cc #endif // CC_ANIMATION_WORKLET_ANIMATION_H_ diff --git a/chromium/cc/animation/worklet_animation_unittest.cc b/chromium/cc/animation/worklet_animation_unittest.cc index 5e3020b75e9..74ac4ab9925 100644 --- a/chromium/cc/animation/worklet_animation_unittest.cc +++ b/chromium/cc/animation/worklet_animation_unittest.cc @@ -13,6 +13,7 @@ #include "testing/gmock/include/gmock/gmock.h" using ::testing::Mock; +using ::testing::NiceMock; using ::testing::Return; using ::testing::_; @@ -20,6 +21,12 @@ namespace cc { namespace { +class MockKeyframeEffect : public KeyframeEffect { + public: + MockKeyframeEffect() : KeyframeEffect(0) {} + MOCK_METHOD1(Tick, void(base::TimeTicks monotonic_time)); +}; + class WorkletAnimationTest : public AnimationTimelinesTest { public: WorkletAnimationTest() = default; @@ -30,31 +37,47 @@ class WorkletAnimationTest : public AnimationTimelinesTest { client_impl_.RegisterElement(element_id_, ElementListType::PENDING); client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE); - worklet_animation_ = - WorkletAnimation::Create(worklet_animation_id_, "test_name", nullptr); - + worklet_animation_ = WorkletAnimation::Create( + worklet_animation_id_, "test_name", nullptr, nullptr); + int cc_id = worklet_animation_->id(); worklet_animation_->AttachElement(element_id_); host_->AddAnimationTimeline(timeline_); timeline_->AttachAnimation(worklet_animation_); host_->PushPropertiesTo(host_impl_); timeline_impl_ = host_impl_->GetTimelineById(timeline_id_); - worklet_animation_impl_ = static_cast<WorkletAnimation*>( - timeline_impl_->GetAnimationById(worklet_animation_id_)); + worklet_animation_impl_ = + ToWorkletAnimation(timeline_impl_->GetAnimationById(cc_id)); } scoped_refptr<WorkletAnimation> worklet_animation_; scoped_refptr<WorkletAnimation> worklet_animation_impl_; - int worklet_animation_id_ = 11; + WorkletAnimationId worklet_animation_id_{11, 12}; }; class MockScrollTimeline : public ScrollTimeline { public: MockScrollTimeline() : ScrollTimeline(ElementId(), ScrollTimeline::Vertical, 0) {} - MOCK_CONST_METHOD1(CurrentTime, double(const ScrollTree&)); + MOCK_CONST_METHOD2(CurrentTime, double(const ScrollTree&, bool)); }; +TEST_F(WorkletAnimationTest, NonImplInstanceDoesNotTickKeyframe) { + std::unique_ptr<MockKeyframeEffect> effect = + std::make_unique<MockKeyframeEffect>(); + MockKeyframeEffect* mock_effect = effect.get(); + + scoped_refptr<WorkletAnimation> worklet_animation = + WrapRefCounted(new WorkletAnimation( + 1, worklet_animation_id_, "test_name", nullptr, nullptr, + false /* not impl instance*/, std::move(effect))); + + EXPECT_CALL(*mock_effect, Tick(_)).Times(0); + worklet_animation->SetOutputState( + {worklet_animation_id_, base::TimeDelta::FromSecondsD(1)}); + worklet_animation->Tick(base::TimeTicks()); +} + TEST_F(WorkletAnimationTest, LocalTimeIsUsedWithAnimations) { AttachWorkletAnimation(); @@ -72,7 +95,7 @@ TEST_F(WorkletAnimationTest, LocalTimeIsUsedWithAnimations) { host_impl_->ActivateAnimations(); base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2); - worklet_animation_impl_->SetLocalTime(local_time); + worklet_animation_impl_->SetOutputState({worklet_animation_id_, local_time}); TickAnimationsTransferEvents(base::TimeTicks(), 0u); @@ -82,13 +105,12 @@ TEST_F(WorkletAnimationTest, LocalTimeIsUsedWithAnimations) { client_impl_.ExpectOpacityPropertyMutated( element_id_, ElementListType::ACTIVE, expected_opacity); } - // Tests that verify interaction of AnimationHost with LayerTreeMutator. // TODO(majidvp): Perhaps moves these to AnimationHostTest. TEST_F(WorkletAnimationTest, LayerTreeMutatorsIsMutatedWithCorrectInputState) { AttachWorkletAnimation(); - MockLayerTreeMutator* mock_mutator = new MockLayerTreeMutator(); + MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>(); host_impl_->SetLayerTreeMutator( base::WrapUnique<LayerTreeMutator>(mock_mutator)); ON_CALL(*mock_mutator, HasAnimators()).WillByDefault(Return(true)); @@ -115,7 +137,7 @@ TEST_F(WorkletAnimationTest, LayerTreeMutatorsIsMutatedWithCorrectInputState) { TEST_F(WorkletAnimationTest, LayerTreeMutatorsIsMutatedOnlyWhenInputChanges) { AttachWorkletAnimation(); - MockLayerTreeMutator* mock_mutator = new MockLayerTreeMutator(); + MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>(); host_impl_->SetLayerTreeMutator( base::WrapUnique<LayerTreeMutator>(mock_mutator)); ON_CALL(*mock_mutator, HasAnimators()).WillByDefault(Return(true)); @@ -144,15 +166,187 @@ TEST_F(WorkletAnimationTest, LayerTreeMutatorsIsMutatedOnlyWhenInputChanges) { Mock::VerifyAndClearExpectations(mock_mutator); } -TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrolltimeline) { +TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) { auto scroll_timeline = std::make_unique<MockScrollTimeline>(); - EXPECT_CALL(*scroll_timeline, CurrentTime(_)).WillOnce(Return(1234)); + EXPECT_CALL(*scroll_timeline, CurrentTime(_, _)).WillRepeatedly(Return(1234)); scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create( - worklet_animation_id_, "test_name", std::move(scroll_timeline)); + worklet_animation_id_, "test_name", std::move(scroll_timeline), nullptr); + + ScrollTree scroll_tree; + std::unique_ptr<MutatorInputState> state = + std::make_unique<MutatorInputState>(); + worklet_animation->UpdateInputState(state.get(), base::TimeTicks::Now(), + scroll_tree, true); + std::unique_ptr<AnimationWorkletInput> input = + state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(1234, input->added_and_updated_animations[0].current_time); +} + +TEST_F(WorkletAnimationTest, + CurrentTimeFromDocumentTimelineIsOffsetByStartTime) { + scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create( + worklet_animation_id_, "test_name", nullptr, nullptr); + + base::TimeTicks first_ticks = + base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111); + base::TimeTicks second_ticks = + base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 123.4); + base::TimeTicks third_ticks = + base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 246.8); + + ScrollTree scroll_tree; + std::unique_ptr<MutatorInputState> state = + std::make_unique<MutatorInputState>(); + worklet_animation->UpdateInputState(state.get(), first_ticks, scroll_tree, + true); + // First state request sets the start time and thus current time should be 0. + std::unique_ptr<AnimationWorkletInput> input = + state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(0, input->added_and_updated_animations[0].current_time); + state.reset(new MutatorInputState); + worklet_animation->UpdateInputState(state.get(), second_ticks, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(123.4, input->updated_animations[0].current_time); + // Should always offset from start time. + state.reset(new MutatorInputState()); + worklet_animation->UpdateInputState(state.get(), third_ticks, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(246.8, input->updated_animations[0].current_time); +} + +// This test verifies that worklet animation state is properly updated. +TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) { + AttachWorkletAnimation(); + + MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>(); + host_impl_->SetLayerTreeMutator( + base::WrapUnique<LayerTreeMutator>(mock_mutator)); + ON_CALL(*mock_mutator, HasAnimators()).WillByDefault(Return(true)); + + const float start_opacity = .7f; + const float end_opacity = .3f; + const double duration = 1.; + + int keyframe_model_id = AddOpacityTransitionToAnimation( + worklet_animation_.get(), duration, start_opacity, end_opacity, true); ScrollTree scroll_tree; - EXPECT_EQ(1234, worklet_animation->CurrentTime(base::TimeTicks::Now(), - scroll_tree)); + std::unique_ptr<MutatorEvents> events = host_->CreateEvents(); + std::unique_ptr<MutatorInputState> state = + std::make_unique<MutatorInputState>(); + + host_->PushPropertiesTo(host_impl_); + host_impl_->ActivateAnimations(); + + KeyframeModel* keyframe_model = + worklet_animation_impl_->GetKeyframeModel(TargetProperty::OPACITY); + ASSERT_TRUE(keyframe_model); + + base::TimeTicks time; + worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, + true); + std::unique_ptr<AnimationWorkletInput> input = + state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 1u); + EXPECT_EQ("test_name", input->added_and_updated_animations[0].name); + EXPECT_EQ(input->updated_animations.size(), 0u); + EXPECT_EQ(input->removed_animations.size(), 0u); + + // The state of WorkletAnimation is updated to RUNNING after calling + // UpdateInputState above. + state.reset(new MutatorInputState()); + time += base::TimeDelta::FromSecondsD(0.1); + worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 0u); + EXPECT_EQ(input->updated_animations.size(), 1u); + EXPECT_EQ(input->removed_animations.size(), 0u); + + // Operating on individual KeyframeModel doesn't affect the state of + // WorkletAnimation. + keyframe_model->SetRunState(KeyframeModel::FINISHED, time); + state.reset(new MutatorInputState()); + time += base::TimeDelta::FromSecondsD(0.1); + worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 0u); + EXPECT_EQ(input->updated_animations.size(), 1u); + EXPECT_EQ(input->removed_animations.size(), 0u); + + // WorkletAnimation sets state to REMOVED when JavaScript fires cancel() which + // leads to RemoveKeyframeModel. + worklet_animation_impl_->RemoveKeyframeModel(keyframe_model_id); + host_impl_->UpdateAnimationState(true, events.get()); + state.reset(new MutatorInputState()); + worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 0u); + EXPECT_EQ(input->updated_animations.size(), 0u); + EXPECT_EQ(input->removed_animations.size(), 1u); + EXPECT_EQ(input->removed_animations[0], worklet_animation_id_); +} + +// This test verifies that worklet animation gets skipped properly. +TEST_F(WorkletAnimationTest, SkipUnchangedAnimations) { + AttachWorkletAnimation(); + + MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>(); + host_impl_->SetLayerTreeMutator( + base::WrapUnique<LayerTreeMutator>(mock_mutator)); + ON_CALL(*mock_mutator, HasAnimators()).WillByDefault(Return(true)); + + const float start_opacity = .7f; + const float end_opacity = .3f; + const double duration = 1.; + + int keyframe_model_id = AddOpacityTransitionToAnimation( + worklet_animation_.get(), duration, start_opacity, end_opacity, true); + + ScrollTree scroll_tree; + std::unique_ptr<MutatorEvents> events = host_->CreateEvents(); + std::unique_ptr<MutatorInputState> state = + std::make_unique<MutatorInputState>(); + + host_->PushPropertiesTo(host_impl_); + host_impl_->ActivateAnimations(); + + base::TimeTicks time; + worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, + true); + std::unique_ptr<AnimationWorkletInput> input = + state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->added_and_updated_animations.size(), 1u); + EXPECT_EQ(input->updated_animations.size(), 0u); + + state.reset(new MutatorInputState()); + // No update on the input state if input time stays the same. + worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_FALSE(input); + + state.reset(new MutatorInputState()); + // Different input time causes the input state to be updated. + time += base::TimeDelta::FromSecondsD(0.1); + worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->updated_animations.size(), 1u); + + state.reset(new MutatorInputState()); + // Input state gets updated when the worklet animation is to be removed even + // the input time doesn't change. + worklet_animation_impl_->RemoveKeyframeModel(keyframe_model_id); + worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, + true); + input = state->TakeWorkletState(worklet_animation_id_.scope_id); + EXPECT_EQ(input->updated_animations.size(), 0u); + EXPECT_EQ(input->removed_animations.size(), 1u); } } // namespace diff --git a/chromium/cc/base/list_container.h b/chromium/cc/base/list_container.h index 81235e1bc0a..ac7b820fffd 100644 --- a/chromium/cc/base/list_container.h +++ b/chromium/cc/base/list_container.h @@ -136,6 +136,16 @@ class ListContainer { return result; } + // Insert |count| new elements of |DerivedElementType| after |at|. If |at| is + // end() elements will be inserted to the empty list. This will invalidate all + // outstanding pointers and iterators. Return a valid iterator for the + // beginning of the newly inserted segment. + template <typename DerivedElementType> + Iterator InsertAfterAndInvalidateAllPointers(Iterator at, size_t count) { + return InsertBeforeAndInvalidateAllPointers<DerivedElementType>( + at != end() ? ++at : at, count); + } + ListContainer& operator=(ListContainer&& other) { helper_.data_.swap(other.helper_.data_); return *this; diff --git a/chromium/cc/base/list_container_unittest.cc b/chromium/cc/base/list_container_unittest.cc index e46be3bf875..c58597b2abd 100644 --- a/chromium/cc/base/list_container_unittest.cc +++ b/chromium/cc/base/list_container_unittest.cc @@ -8,6 +8,8 @@ #include <algorithm> #include <vector> + +#include "base/stl_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -714,18 +716,17 @@ TEST(ListContainerTest, DeletionWhileIterating) { TEST(ListContainerTest, InsertBeforeBegin) { ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); - std::vector<SimpleDerivedElement*> sde_list; const int size = 4; for (int i = 0; i < size; ++i) { - sde_list.push_back(list.AllocateAndConstruct<SimpleDerivedElement>()); - sde_list.back()->set_value(i); + SimpleDerivedElement* element = + list.AllocateAndConstruct<SimpleDerivedElement>(); + element->set_value(i); } EXPECT_EQ(static_cast<size_t>(size), list.size()); const int count = 2; - ListContainer<DerivedElement>::Iterator iter = - list.InsertBeforeAndInvalidateAllPointers<SimpleDerivedElement>( - list.begin(), count); + auto iter = list.InsertBeforeAndInvalidateAllPointers<SimpleDerivedElement>( + list.begin(), count); for (int i = 0; i < count; ++i) { static_cast<SimpleDerivedElement*>(*iter)->set_value(100 + i); ++iter; @@ -744,18 +745,17 @@ TEST(ListContainerTest, InsertBeforeBegin) { TEST(ListContainerTest, InsertBeforeEnd) { ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); - std::vector<SimpleDerivedElement*> sde_list; const int size = 4; for (int i = 0; i < size; ++i) { - sde_list.push_back(list.AllocateAndConstruct<SimpleDerivedElement>()); - sde_list.back()->set_value(i); + SimpleDerivedElement* element = + list.AllocateAndConstruct<SimpleDerivedElement>(); + element->set_value(i); } EXPECT_EQ(static_cast<size_t>(size), list.size()); const int count = 3; - ListContainer<DerivedElement>::Iterator iter = - list.InsertBeforeAndInvalidateAllPointers<SimpleDerivedElement>( - list.end(), count); + auto iter = list.InsertBeforeAndInvalidateAllPointers<SimpleDerivedElement>( + list.end(), count); for (int i = 0; i < count; ++i) { static_cast<SimpleDerivedElement*>(*iter)->set_value(100 + i); ++iter; @@ -776,9 +776,8 @@ TEST(ListContainerTest, InsertBeforeEmpty) { kCurrentLargestDerivedElementSize, 0); const int count = 3; - ListContainer<DerivedElement>::Iterator iter = - list.InsertBeforeAndInvalidateAllPointers<SimpleDerivedElement>( - list.end(), count); + auto iter = list.InsertBeforeAndInvalidateAllPointers<SimpleDerivedElement>( + list.end(), count); for (int i = 0; i < count; ++i) { static_cast<SimpleDerivedElement*>(*iter)->set_value(100 + i); ++iter; @@ -797,24 +796,24 @@ TEST(ListContainerTest, InsertBeforeEmpty) { TEST(ListContainerTest, InsertBeforeMany) { ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); - std::vector<SimpleDerivedElement*> sde_list; // Create a partial list of 1,...,99. int initial_list[] = { 0, 1, 4, 5, 6, 7, 8, 9, 11, 12, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 34, 36, 37, 51, 52, 54, 56, 60, 64, 65, 70, 75, 76, 80, 81, 83, 86, 87, 90, 93, 95, 97, 98, }; - const size_t size = sizeof(initial_list) / sizeof(initial_list[0]); + const size_t size = base::size(initial_list); for (size_t i = 0; i < size; ++i) { - sde_list.push_back(list.AllocateAndConstruct<SimpleDerivedElement>()); - sde_list.back()->set_value(initial_list[i]); + SimpleDerivedElement* element = + list.AllocateAndConstruct<SimpleDerivedElement>(); + element->set_value(initial_list[i]); } EXPECT_EQ(static_cast<size_t>(size), list.size()); // Insert the missing elements. - ListContainer<DerivedElement>::Iterator iter = list.begin(); + auto iter = list.begin(); while (iter != list.end()) { - ListContainer<DerivedElement>::Iterator iter_next = iter; + auto iter_next = iter; ++iter_next; int value = static_cast<SimpleDerivedElement*>(*iter)->get_value(); @@ -841,6 +840,133 @@ TEST(ListContainerTest, InsertBeforeMany) { EXPECT_EQ(100, iter_index); } +TEST(ListContainerTest, InsertAfterBegin) { + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); + const int size = 4; + for (int i = 0; i < size; ++i) { + SimpleDerivedElement* element = + list.AllocateAndConstruct<SimpleDerivedElement>(); + element->set_value(i); + } + EXPECT_EQ(static_cast<size_t>(size), list.size()); + + const int count = 2; + auto iter = list.InsertAfterAndInvalidateAllPointers<SimpleDerivedElement>( + list.begin(), count); + for (int i = 0; i < count; ++i) { + static_cast<SimpleDerivedElement*>(*iter)->set_value(100 + i); + ++iter; + } + + const int expected_result[] = {0, 100, 101, 1, 2, 3}; + int iter_index = 0; + for (iter = list.begin(); iter != list.end(); ++iter) { + EXPECT_EQ(expected_result[iter_index], + static_cast<SimpleDerivedElement*>(*iter)->get_value()); + ++iter_index; + } + EXPECT_EQ(size + count, iter_index); +} + +TEST(ListContainerTest, InsertAfterEnd) { + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); + const int size = 4; + for (int i = 0; i < size; ++i) { + SimpleDerivedElement* element = + list.AllocateAndConstruct<SimpleDerivedElement>(); + element->set_value(i); + } + EXPECT_EQ(static_cast<size_t>(size), list.size()); + + const int count = 3; + auto iter = list.InsertAfterAndInvalidateAllPointers<SimpleDerivedElement>( + list.end(), count); + for (int i = 0; i < count; ++i) { + static_cast<SimpleDerivedElement*>(*iter)->set_value(100 + i); + ++iter; + } + + const int expected_result[] = {0, 1, 2, 3, 100, 101, 102}; + int iter_index = 0; + for (iter = list.begin(); iter != list.end(); ++iter) { + EXPECT_EQ(expected_result[iter_index], + static_cast<SimpleDerivedElement*>(*iter)->get_value()); + ++iter_index; + } + EXPECT_EQ(size + count, iter_index); +} + +TEST(ListContainerTest, InsertAfterEmpty) { + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); + + const int count = 3; + auto iter = list.InsertAfterAndInvalidateAllPointers<SimpleDerivedElement>( + list.end(), count); + for (int i = 0; i < count; ++i) { + static_cast<SimpleDerivedElement*>(*iter)->set_value(100 + i); + ++iter; + } + + const int expected_result[] = {100, 101, 102}; + int iter_index = 0; + for (iter = list.begin(); iter != list.end(); ++iter) { + EXPECT_EQ(expected_result[iter_index], + static_cast<SimpleDerivedElement*>(*iter)->get_value()); + ++iter_index; + } + EXPECT_EQ(count, iter_index); +} + +TEST(ListContainerTest, InsertAfterMany) { + ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, + kCurrentLargestDerivedElementSize, 0); + // Create a partial list of 1,...,99. + int initial_list[] = { + 0, 1, 4, 5, 6, 7, 8, 9, 11, 12, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 32, 34, 36, 37, 51, 52, 54, 56, + 60, 64, 65, 70, 75, 76, 80, 81, 83, 86, 87, 90, 93, 95, 97, 98, + }; + const size_t size = base::size(initial_list); + for (size_t i = 0; i < size; ++i) { + SimpleDerivedElement* element = + list.AllocateAndConstruct<SimpleDerivedElement>(); + element->set_value(initial_list[i]); + } + EXPECT_EQ(static_cast<size_t>(size), list.size()); + + // Insert the missing elements. + auto iter = list.begin(); + while (iter != list.end()) { + auto iter_next = iter; + ++iter_next; + + int value = static_cast<SimpleDerivedElement*>(*iter)->get_value(); + int value_next = + iter_next != list.end() + ? static_cast<SimpleDerivedElement*>(*iter_next)->get_value() + : 100; + int count = value_next - value - 1; + + iter = list.InsertAfterAndInvalidateAllPointers<SimpleDerivedElement>( + iter, count); + for (int i = value + 1; i < value_next; ++i) { + static_cast<SimpleDerivedElement*>(*iter)->set_value(i); + ++iter; + } + } + + int iter_index = 0; + for (iter = list.begin(); iter != list.end(); ++iter) { + EXPECT_EQ(iter_index, + static_cast<SimpleDerivedElement*>(*iter)->get_value()); + ++iter_index; + } + EXPECT_EQ(100, iter_index); +} + TEST(ListContainerTest, SimpleManipulationWithIndexSimpleDerivedElement) { ListContainer<DerivedElement> list(kCurrentLargestDerivedElementAlign, kCurrentLargestDerivedElementSize, 0); diff --git a/chromium/cc/benchmarks/micro_benchmark.cc b/chromium/cc/benchmarks/micro_benchmark.cc index 448c62dfdc5..5f23e66ee37 100644 --- a/chromium/cc/benchmarks/micro_benchmark.cc +++ b/chromium/cc/benchmarks/micro_benchmark.cc @@ -34,8 +34,6 @@ void MicroBenchmark::NotifyDone(std::unique_ptr<base::Value> result) { is_done_ = true; } -void MicroBenchmark::RunOnLayer(Layer* layer) {} - void MicroBenchmark::RunOnLayer(PictureLayer* layer) {} bool MicroBenchmark::ProcessMessage(std::unique_ptr<base::Value> value) { diff --git a/chromium/cc/benchmarks/micro_benchmark.h b/chromium/cc/benchmarks/micro_benchmark.h index 918e78d4a30..913956440bf 100644 --- a/chromium/cc/benchmarks/micro_benchmark.h +++ b/chromium/cc/benchmarks/micro_benchmark.h @@ -16,11 +16,10 @@ class Value; } // namespace base namespace cc { - -class Layer; class LayerTreeHost; class PictureLayer; class MicroBenchmarkImpl; + class CC_EXPORT MicroBenchmark { public: using DoneCallback = base::OnceCallback<void(std::unique_ptr<base::Value>)>; @@ -33,7 +32,6 @@ class CC_EXPORT MicroBenchmark { int id() const { return id_; } void set_id(int id) { id_ = id; } - virtual void RunOnLayer(Layer* layer); virtual void RunOnLayer(PictureLayer* layer); virtual bool ProcessMessage(std::unique_ptr<base::Value> value); diff --git a/chromium/cc/input/browser_controls_offset_manager.cc b/chromium/cc/input/browser_controls_offset_manager.cc index 33ce93fc98d..e55bf940440 100644 --- a/chromium/cc/input/browser_controls_offset_manager.cc +++ b/chromium/cc/input/browser_controls_offset_manager.cc @@ -41,7 +41,7 @@ BrowserControlsOffsetManager::BrowserControlsOffsetManager( animation_start_value_(0.f), animation_stop_value_(0.f), animation_direction_(NO_ANIMATION), - permitted_state_(BOTH), + permitted_state_(BrowserControlsState::kBoth), accumulated_scroll_delta_(0.f), baseline_top_content_offset_(0.f), baseline_bottom_content_offset_(0.f), @@ -87,18 +87,22 @@ void BrowserControlsOffsetManager::UpdateBrowserControlsState( BrowserControlsState constraints, BrowserControlsState current, bool animate) { - DCHECK(!(constraints == SHOWN && current == HIDDEN)); - DCHECK(!(constraints == HIDDEN && current == SHOWN)); + DCHECK(!(constraints == BrowserControlsState::kShown && + current == BrowserControlsState::kHidden)); + DCHECK(!(constraints == BrowserControlsState::kHidden && + current == BrowserControlsState::kShown)); permitted_state_ = constraints; // Don't do anything if it doesn't matter which state the controls are in. - if (constraints == BOTH && current == BOTH) + if (constraints == BrowserControlsState::kBoth && + current == BrowserControlsState::kBoth) return; // Don't do anything if there is no change in offset. float final_shown_ratio = 1.f; - if (constraints == HIDDEN || current == HIDDEN) + if (constraints == BrowserControlsState::kHidden || + current == BrowserControlsState::kHidden) final_shown_ratio = 0.f; if (final_shown_ratio == TopControlsShownRatio()) { ResetAnimations(); @@ -134,9 +138,10 @@ gfx::Vector2dF BrowserControlsOffsetManager::ScrollBy( if (pinch_gesture_active_) return pending_delta; - if (permitted_state_ == SHOWN && pending_delta.y() > 0) + if (permitted_state_ == BrowserControlsState::kShown && pending_delta.y() > 0) return pending_delta; - else if (permitted_state_ == HIDDEN && pending_delta.y() < 0) + else if (permitted_state_ == BrowserControlsState::kHidden && + pending_delta.y() < 0) return pending_delta; accumulated_scroll_delta_ += pending_delta.y(); @@ -189,7 +194,7 @@ void BrowserControlsOffsetManager::MainThreadHasStoppedFlinging() { gfx::Vector2dF BrowserControlsOffsetManager::Animate( base::TimeTicks monotonic_time) { - if (!has_animation() || !client_->HaveRootScrollLayer()) + if (!has_animation() || !client_->HaveRootScrollNode()) return gfx::Vector2dF(); float old_offset = ContentTopOffset(); diff --git a/chromium/cc/input/browser_controls_offset_manager_client.h b/chromium/cc/input/browser_controls_offset_manager_client.h index 12e68aa3ae8..df1a742669d 100644 --- a/chromium/cc/input/browser_controls_offset_manager_client.h +++ b/chromium/cc/input/browser_controls_offset_manager_client.h @@ -14,7 +14,7 @@ class CC_EXPORT BrowserControlsOffsetManagerClient { virtual void SetCurrentBrowserControlsShownRatio(float ratio) = 0; virtual float CurrentBrowserControlsShownRatio() const = 0; virtual void DidChangeBrowserControlsPosition() = 0; - virtual bool HaveRootScrollLayer() const = 0; + virtual bool HaveRootScrollNode() const = 0; protected: virtual ~BrowserControlsOffsetManagerClient() {} diff --git a/chromium/cc/input/browser_controls_offset_manager_unittest.cc b/chromium/cc/input/browser_controls_offset_manager_unittest.cc index 098a701ece5..3caad9cd2ec 100644 --- a/chromium/cc/input/browser_controls_offset_manager_unittest.cc +++ b/chromium/cc/input/browser_controls_offset_manager_unittest.cc @@ -50,7 +50,7 @@ class MockBrowserControlsOffsetManagerClient update_draw_properties_needed_ = true; } - bool HaveRootScrollLayer() const override { return true; } + bool HaveRootScrollNode() const override { return true; } float BottomControlsHeight() const override { return bottom_controls_height_; @@ -509,7 +509,8 @@ TEST(BrowserControlsOffsetManagerTest, TEST(BrowserControlsOffsetManagerTest, ScrollByWithZeroHeightControlsIsNoop) { MockBrowserControlsOffsetManagerClient client(0.f, 0.5f, 0.5f); BrowserControlsOffsetManager* manager = client.manager(); - manager->UpdateBrowserControlsState(BOTH, BOTH, false); + manager->UpdateBrowserControlsState(BrowserControlsState::kBoth, + BrowserControlsState::kBoth, false); manager->ScrollBegin(); gfx::Vector2dF pending = manager->ScrollBy(gfx::Vector2dF(0.f, 20.f)); @@ -579,11 +580,13 @@ TEST(BrowserControlsOffsetManagerTest, BrowserControlsOffsetManager* manager = client.manager(); EXPECT_FLOAT_EQ(1.f, client.CurrentBrowserControlsShownRatio()); - manager->UpdateBrowserControlsState(BOTH, HIDDEN, true); + manager->UpdateBrowserControlsState(BrowserControlsState::kBoth, + BrowserControlsState::kHidden, true); EXPECT_TRUE(manager->has_animation()); EXPECT_FLOAT_EQ(1.f, client.CurrentBrowserControlsShownRatio()); - manager->UpdateBrowserControlsState(BOTH, SHOWN, true); + manager->UpdateBrowserControlsState(BrowserControlsState::kBoth, + BrowserControlsState::kShown, true); EXPECT_FALSE(manager->has_animation()); EXPECT_FLOAT_EQ(1.f, client.CurrentBrowserControlsShownRatio()); } diff --git a/chromium/cc/input/browser_controls_state.h b/chromium/cc/input/browser_controls_state.h index 2d9208d883f..38310496c5a 100644 --- a/chromium/cc/input/browser_controls_state.h +++ b/chromium/cc/input/browser_controls_state.h @@ -6,11 +6,8 @@ #define CC_INPUT_BROWSER_CONTROLS_STATE_H_ namespace cc { -enum BrowserControlsState { - SHOWN = 1, - HIDDEN = 2, - BOTH = 3 -}; + +enum class BrowserControlsState { kShown = 1, kHidden = 2, kBoth = 3 }; } #endif // CC_INPUT_BROWSER_CONTROLS_STATE_H_ diff --git a/chromium/cc/input/event_listener_properties.h b/chromium/cc/input/event_listener_properties.h index e1932afe2a9..dce12538e09 100644 --- a/chromium/cc/input/event_listener_properties.h +++ b/chromium/cc/input/event_listener_properties.h @@ -14,7 +14,7 @@ enum class EventListenerClass { kMouseWheel, // This value includes "touchend" and "touchcancel" events. kTouchEndOrCancel, - kNumClasses + kLast = kTouchEndOrCancel }; enum class EventListenerProperties { @@ -22,7 +22,7 @@ enum class EventListenerProperties { kPassive, kBlocking, kBlockingAndPassive, - kMax + kLast = kBlockingAndPassive }; } // namespace cc diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index 51bc8f74b94..a35306042a3 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -62,7 +62,6 @@ class CC_EXPORT InputHandlerClient { virtual void WillShutdown() = 0; virtual void Animate(base::TimeTicks time) = 0; - virtual void MainThreadHasStoppedFlinging() = 0; virtual void ReconcileElasticOverscrollAndRootScroll() = 0; virtual void UpdateRootLayerStateForSynchronousInputHandler( const gfx::ScrollOffset& total_scroll_offset, @@ -113,7 +112,6 @@ class CC_EXPORT InputHandler { enum ScrollInputType { TOUCHSCREEN, WHEEL, - NON_BUBBLING_GESTURE }; enum class TouchStartOrMoveEventListenerType { @@ -125,8 +123,7 @@ class CC_EXPORT InputHandler { // Binds a client to this handler to receive notifications. Only one client // can be bound to an InputHandler. The client must live at least until the // handler calls WillShutdown() on the client. - virtual void BindToClient(InputHandlerClient* client, - bool wheel_scroll_latching_enabled) = 0; + virtual void BindToClient(InputHandlerClient* client) = 0; // Selects a layer to be scrolled using the |scroll_state| start position. // Returns SCROLL_STARTED if the layer at the coordinates can be scrolled, @@ -165,10 +162,6 @@ class CC_EXPORT InputHandler { // ScrollBegin() returned SCROLL_STARTED. virtual InputHandlerScrollResult ScrollBy(ScrollState* scroll_state) = 0; - // Returns SCROLL_STARTED if a layer was actively being scrolled, - // SCROLL_IGNORED if not. - virtual ScrollStatus FlingScrollBegin() = 0; - virtual void MouseMoveAt(const gfx::Point& mouse_position) = 0; virtual void MouseDown() = 0; virtual void MouseUp() = 0; @@ -205,6 +198,11 @@ class CC_EXPORT InputHandler { virtual EventListenerProperties GetEventListenerProperties( EventListenerClass event_class) const = 0; + // Returns true if |viewport_point| hits a wheel event handler region that + // could block scrolling. + virtual bool HasWheelEventHandlerAt( + const gfx::Point& viewport_point) const = 0; + // It returns the type of a touch start or move event listener at // |viewport_point|. Whether the page should be given the opportunity to // suppress scrolling by consuming touch events that started at diff --git a/chromium/cc/input/scroll_elasticity_helper.cc b/chromium/cc/input/scroll_elasticity_helper.cc index 11d1393399d..1394e8ad6fc 100644 --- a/chromium/cc/input/scroll_elasticity_helper.cc +++ b/chromium/cc/input/scroll_elasticity_helper.cc @@ -67,11 +67,14 @@ gfx::ScrollOffset ScrollElasticityHelperImpl::MaxScrollOffset() const { } void ScrollElasticityHelperImpl::ScrollBy(const gfx::Vector2dF& delta) { - LayerImpl* root_scroll_layer = host_impl_->OuterViewportScrollLayer() - ? host_impl_->OuterViewportScrollLayer() - : host_impl_->InnerViewportScrollLayer(); - if (root_scroll_layer) - root_scroll_layer->ScrollBy(delta); + ScrollNode* root_scroll_node = host_impl_->OuterViewportScrollNode() + ? host_impl_->OuterViewportScrollNode() + : host_impl_->InnerViewportScrollNode(); + if (root_scroll_node) { + LayerTreeImpl* tree_impl = host_impl_->active_tree(); + tree_impl->property_trees()->scroll_tree.ScrollBy(root_scroll_node, delta, + tree_impl); + } } void ScrollElasticityHelperImpl::RequestOneBeginFrame() { diff --git a/chromium/cc/input/scroll_snap_data.cc b/chromium/cc/input/scroll_snap_data.cc index 58b225849d4..9e873aea523 100644 --- a/chromium/cc/input/scroll_snap_data.cc +++ b/chromium/cc/input/scroll_snap_data.cc @@ -4,81 +4,54 @@ #include "cc/input/scroll_snap_data.h" +#include <algorithm> #include <cmath> -#include "base/optional.h" namespace cc { namespace { -bool IsVisible(const gfx::ScrollOffset& point, - const gfx::RectF& visible_region) { - return point.x() >= visible_region.x() && - point.x() <= visible_region.right() && - point.y() >= visible_region.y() && - point.y() <= visible_region.bottom(); +bool IsMutualVisible(const SnapSearchResult& a, const SnapSearchResult& b) { + return a.visible_range().Contains(b.snap_offset()) && + b.visible_range().Contains(a.snap_offset()); } -bool IsMutualVisible(const SnapAreaData& area_x, const SnapAreaData& area_y) { - gfx::ScrollOffset position(area_x.snap_position.x(), - area_y.snap_position.y()); - return IsVisible(position, area_x.visible_region) && - IsVisible(position, area_y.visible_region); +bool SnappableOnAxis(const SnapAreaData& area, SearchAxis search_axis) { + return search_axis == SearchAxis::kX + ? area.scroll_snap_align.alignment_inline != SnapAlignment::kNone + : area.scroll_snap_align.alignment_block != SnapAlignment::kNone; } -bool SnappableOnAxis(const SnapAreaData& area, SearchAxis search_axis) { - return search_axis == SearchAxis::kX ? area.snap_axis == SnapAxis::kX || - area.snap_axis == SnapAxis::kBoth - : area.snap_axis == SnapAxis::kY || - area.snap_axis == SnapAxis::kBoth; +} // namespace + +bool SnapVisibleRange::Contains(float value) const { + if (start_ < end_) + return start_ <= value && value <= end_; + return end_ <= value && value <= start_; } -// Finds the best SnapArea candidate that minimizes the distance between current -// and candidate positions, while satisfying three invariants: -// - |candidate_position| is in |target_region| -// - |current_position| is in candidate's visible region -// - |target_position| is in candidate's visible region - -// |current_position| is the scroll position of the container before snapping. -// |target_position| is the snap position we have found on the other axis. -// |target_region| is the visible region of the target position's area. -base::Optional<SnapAreaData> FindClosestValidArea( - SearchAxis search_axis, - const gfx::ScrollOffset& current_position, - const gfx::ScrollOffset& target_position, - const gfx::RectF& target_region, - const gfx::ScrollOffset& proximity_range, - const SnapAreaList& list) { - if (list.empty()) - return base::nullopt; - - base::Optional<SnapAreaData> closest_area; - float smallest_distance = - search_axis == SearchAxis::kX ? proximity_range.x() : proximity_range.y(); - for (const SnapAreaData& area : list) { - if (!SnappableOnAxis(area, search_axis)) - continue; +SnapSearchResult::SnapSearchResult(float offset, const SnapVisibleRange& range) + : snap_offset_(offset) { + set_visible_range(range); +} - gfx::ScrollOffset candidate_position = - search_axis == SearchAxis::kX - ? gfx::ScrollOffset(area.snap_position.x(), target_position.y()) - : gfx::ScrollOffset(target_position.x(), area.snap_position.y()); - if (!IsVisible(candidate_position, target_region) || - !IsVisible(current_position, area.visible_region) || - !IsVisible(target_position, area.visible_region)) - continue; +void SnapSearchResult::set_visible_range(const SnapVisibleRange& range) { + DCHECK(range.start() <= range.end()); + visible_range_ = range; +} - gfx::ScrollOffset offset = current_position - candidate_position; - float distance = search_axis == SearchAxis::kX ? std::abs(offset.x()) - : std::abs(offset.y()); - if (distance < smallest_distance) { - smallest_distance = distance; - closest_area = area; - } - } - return closest_area; +void SnapSearchResult::Clip(float max_snap, float max_visible) { + snap_offset_ = std::max(std::min(snap_offset_, max_snap), 0.0f); + visible_range_ = SnapVisibleRange( + std::max(std::min(visible_range_.start(), max_visible), 0.0f), + std::max(std::min(visible_range_.end(), max_visible), 0.0f)); } -} // namespace +void SnapSearchResult::Union(const SnapSearchResult& other) { + DCHECK(snap_offset_ == other.snap_offset_); + visible_range_ = SnapVisibleRange( + std::min(visible_range_.start(), other.visible_range_.start()), + std::max(visible_range_.end(), other.visible_range_.end())); +} SnapContainerData::SnapContainerData() : proximity_range_(gfx::ScrollOffset(std::numeric_limits<float>::max(), @@ -89,32 +62,26 @@ SnapContainerData::SnapContainerData(ScrollSnapType type) proximity_range_(gfx::ScrollOffset(std::numeric_limits<float>::max(), std::numeric_limits<float>::max())) {} -SnapContainerData::SnapContainerData(ScrollSnapType type, gfx::ScrollOffset max) +SnapContainerData::SnapContainerData(ScrollSnapType type, + const gfx::RectF& rect, + const gfx::ScrollOffset& max) : scroll_snap_type_(type), + rect_(rect), max_position_(max), proximity_range_(gfx::ScrollOffset(std::numeric_limits<float>::max(), std::numeric_limits<float>::max())) {} SnapContainerData::SnapContainerData(const SnapContainerData& other) = default; -SnapContainerData::SnapContainerData(SnapContainerData&& other) - : scroll_snap_type_(other.scroll_snap_type_), - max_position_(other.max_position_), - proximity_range_(other.proximity_range_), - snap_area_list_(std::move(other.snap_area_list_)) {} +SnapContainerData::SnapContainerData(SnapContainerData&& other) = default; SnapContainerData::~SnapContainerData() = default; SnapContainerData& SnapContainerData::operator=( const SnapContainerData& other) = default; -SnapContainerData& SnapContainerData::operator=(SnapContainerData&& other) { - scroll_snap_type_ = other.scroll_snap_type_; - max_position_ = other.max_position_; - proximity_range_ = other.proximity_range_; - snap_area_list_ = std::move(other.snap_area_list_); - return *this; -} +SnapContainerData& SnapContainerData::operator=(SnapContainerData&& other) = + default; void SnapContainerData::AddSnapAreaData(SnapAreaData snap_area_data) { snap_area_list_.push_back(snap_area_data); @@ -131,18 +98,22 @@ bool SnapContainerData::FindSnapPosition( if (!should_snap_on_x && !should_snap_on_y) return false; - base::Optional<SnapAreaData> closest_x, closest_y; + base::Optional<SnapSearchResult> closest_x, closest_y; // A region that includes every reachable scroll position. gfx::RectF scrollable_region(0, 0, max_position_.x(), max_position_.y()); if (should_snap_on_x) { - closest_x = FindClosestValidArea(SearchAxis::kX, current_position, - current_position, scrollable_region, - proximity_range_, snap_area_list_); + // Start from current offset in the cross axis and assume it's always + // visible. + SnapSearchResult initial_snap_position_y = { + current_position.y(), SnapVisibleRange(0, max_position_.x())}; + closest_x = FindClosestValidArea(SearchAxis::kX, current_position.x(), + initial_snap_position_y); } if (should_snap_on_y) { - closest_y = FindClosestValidArea(SearchAxis::kY, current_position, - current_position, scrollable_region, - proximity_range_, snap_area_list_); + SnapSearchResult initial_snap_position_x = { + current_position.x(), SnapVisibleRange(0, max_position_.y())}; + closest_y = FindClosestValidArea(SearchAxis::kY, current_position.y(), + initial_snap_position_x); } if (!closest_x.has_value() && !closest_y.has_value()) @@ -155,39 +126,160 @@ bool SnapContainerData::FindSnapPosition( if (closest_x.has_value() && closest_y.has_value() && !IsMutualVisible(closest_x.value(), closest_y.value())) { bool candidate_on_x_axis_is_closer = - std::abs(closest_x.value().snap_position.x() - current_position.x()) <= - std::abs(closest_y.value().snap_position.y() - current_position.y()); - if (candidate_on_x_axis_is_closer) { - gfx::ScrollOffset snapped(closest_x.value().snap_position.x(), - current_position.y()); - closest_y = FindClosestValidArea( - SearchAxis::kY, current_position, snapped, - closest_x.value().visible_region, proximity_range_, snap_area_list_); - } else { - gfx::ScrollOffset snapped(current_position.x(), - closest_y.value().snap_position.y()); - closest_x = FindClosestValidArea( - SearchAxis::kX, current_position, snapped, - closest_y.value().visible_region, proximity_range_, snap_area_list_); - } + std::abs(closest_x.value().snap_offset() - current_position.x()) <= + std::abs(closest_y.value().snap_offset() - current_position.y()); + if (candidate_on_x_axis_is_closer) + closest_y = FindClosestValidArea(SearchAxis::kY, current_position.y(), + closest_x.value()); + else + closest_x = FindClosestValidArea(SearchAxis::kX, current_position.x(), + closest_y.value()); } *snap_position = current_position; if (closest_x.has_value()) - snap_position->set_x(closest_x.value().snap_position.x()); + snap_position->set_x(closest_x.value().snap_offset()); if (closest_y.has_value()) - snap_position->set_y(closest_y.value().snap_position.y()); - + snap_position->set_y(closest_y.value().snap_offset()); return true; } +base::Optional<SnapSearchResult> SnapContainerData::FindClosestValidArea( + SearchAxis axis, + float current_offset, + const SnapSearchResult& cros_axis_snap_result) const { + base::Optional<SnapSearchResult> result; + base::Optional<SnapSearchResult> inplace; + // The valid snap offsets immediately before and after the current offset. + float prev = std::numeric_limits<float>::lowest(); + float next = std::numeric_limits<float>::max(); + float smallest_distance = + axis == SearchAxis::kX ? proximity_range_.x() : proximity_range_.y(); + for (const SnapAreaData& area : snap_area_list_) { + if (!SnappableOnAxis(area, axis)) + continue; + + SnapSearchResult candidate = GetSnapSearchResult(axis, area); + if (IsSnapportCoveredOnAxis(axis, current_offset, area.rect)) { + // Since snap area is currently covering the snapport, we consider the + // current offset as a valid snap position. + SnapSearchResult inplace_candidate = candidate; + inplace_candidate.set_snap_offset(current_offset); + if (IsMutualVisible(inplace_candidate, cros_axis_snap_result)) { + // If we've already found a valid overflowing area before, we enlarge + // the area's visible region. + if (inplace.has_value()) + inplace.value().Union(inplace_candidate); + else + inplace = inplace_candidate; + // Even if a snap area covers the snapport, we need to continue this + // search to find previous and next snap positions and also to have + // alternative snap candidates if this inplace candidate is ultimately + // rejected. And this covering snap area has its own alignment that may + // generates a snap position rejecting the current inplace candidate. + } + } + if (!IsMutualVisible(candidate, cros_axis_snap_result)) + continue; + float distance = std::abs(candidate.snap_offset() - current_offset); + if (distance < smallest_distance) { + smallest_distance = distance; + result = candidate; + } + if (candidate.snap_offset() < current_offset && + candidate.snap_offset() > prev) + prev = candidate.snap_offset(); + if (candidate.snap_offset() > current_offset && + candidate.snap_offset() < next) + next = candidate.snap_offset(); + } + // According to the spec [1], if the snap area is covering the snapport, the + // scroll offset is a valid snap position only if the distance between the + // geometrically previous and subsequent snap positions in that axis is larger + // than size of the snapport in that axis. + // [1] https://drafts.csswg.org/css-scroll-snap-1/#snap-overflow + float size = axis == SearchAxis::kX ? rect_.width() : rect_.height(); + if (inplace.has_value() && + (prev == std::numeric_limits<float>::lowest() || + next == std::numeric_limits<float>::max() || next - prev > size)) { + return inplace; + } + + return result; +} + +SnapSearchResult SnapContainerData::GetSnapSearchResult( + SearchAxis axis, + const SnapAreaData& area) const { + SnapSearchResult result; + if (axis == SearchAxis::kX) { + result.set_visible_range(SnapVisibleRange(area.rect.y() - rect_.bottom(), + area.rect.bottom() - rect_.y())); + // https://www.w3.org/TR/css-scroll-snap-1/#scroll-snap-align + switch (area.scroll_snap_align.alignment_inline) { + case SnapAlignment::kStart: + result.set_snap_offset(area.rect.x() - rect_.x()); + break; + case SnapAlignment::kCenter: + result.set_snap_offset(area.rect.CenterPoint().x() - + rect_.CenterPoint().x()); + break; + case SnapAlignment::kEnd: + result.set_snap_offset(area.rect.right() - rect_.right()); + break; + default: + NOTREACHED(); + } + result.Clip(max_position_.x(), max_position_.y()); + } else { + result.set_visible_range(SnapVisibleRange(area.rect.x() - rect_.right(), + area.rect.right() - rect_.x())); + switch (area.scroll_snap_align.alignment_block) { + case SnapAlignment::kStart: + result.set_snap_offset(area.rect.y() - rect_.y()); + break; + case SnapAlignment::kCenter: + result.set_snap_offset(area.rect.CenterPoint().y() - + rect_.CenterPoint().y()); + break; + case SnapAlignment::kEnd: + result.set_snap_offset(area.rect.bottom() - rect_.bottom()); + break; + default: + NOTREACHED(); + } + result.Clip(max_position_.y(), max_position_.x()); + } + return result; +} + +bool SnapContainerData::IsSnapportCoveredOnAxis( + SearchAxis axis, + float current_offset, + const gfx::RectF& area_rect) const { + if (axis == SearchAxis::kX) { + if (area_rect.width() < rect_.width()) + return false; + float left = area_rect.x() - rect_.x(); + float right = area_rect.right() - rect_.right(); + return current_offset >= left && current_offset <= right; + } else { + if (area_rect.height() < rect_.height()) + return false; + float top = area_rect.y() - rect_.y(); + float bottom = area_rect.bottom() - rect_.bottom(); + return current_offset >= top && current_offset <= bottom; + } +} + std::ostream& operator<<(std::ostream& ostream, const SnapAreaData& area_data) { - return ostream << area_data.snap_position.ToString() << "\t" - << "visible in: " << area_data.visible_region.ToString(); + return ostream << area_data.rect.ToString(); } std::ostream& operator<<(std::ostream& ostream, const SnapContainerData& container_data) { + ostream << "container_rect: " << container_data.rect().ToString(); + ostream << "area_rects: "; for (size_t i = 0; i < container_data.size(); ++i) { ostream << container_data.at(i) << "\n"; } diff --git a/chromium/cc/input/scroll_snap_data.h b/chromium/cc/input/scroll_snap_data.h index dbc0b9128f5..e44b9d882e2 100644 --- a/chromium/cc/input/scroll_snap_data.h +++ b/chromium/cc/input/scroll_snap_data.h @@ -7,6 +7,7 @@ #include <vector> +#include "base/optional.h" #include "cc/cc_export.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/scroll_offset.h" @@ -84,6 +85,50 @@ struct ScrollSnapAlign { SnapAlignment alignment_block; }; +// We should really use gfx::RangeF. However, it includes windows.h which would +// bring in complexity to the compilation. https://crbug.com/855717 +class SnapVisibleRange { + public: + SnapVisibleRange() {} + SnapVisibleRange(float start, float end) : start_(start), end_(end) {} + bool Contains(float value) const; + float start() const { return start_; } + float end() const { return end_; } + + private: + float start_; + float end_; +}; + +// This class includes snap offset and visible range needed to perform a snap +// operation on one axis for a specific area. The data can be used to determine +// whether this snap area provides a valid snap position for the current scroll. +class SnapSearchResult { + public: + SnapSearchResult() {} + SnapSearchResult(float offset, const SnapVisibleRange& range); + // Clips the |snap_offset| between 0 and |max_snap|. And clips the + // |visible_range| between 0 and |max_visible|. + void Clip(float max_snap, float max_visible); + + // Union the visible_range of the two SnapSearchResult if they represent two + // snap areas that are both covering the snapport at the current offset. + void Union(const SnapSearchResult& other); + + float snap_offset() const { return snap_offset_; } + void set_snap_offset(float offset) { snap_offset_ = offset; } + + SnapVisibleRange visible_range() const { return visible_range_; } + void set_visible_range(const SnapVisibleRange& range); + + private: + float snap_offset_; + // This is the range on the cross axis, within which the SnapArea generating + // this |snap_offset| is visible. We expect the range to be in order (as + // opposed to reversed), i.e., start < end. + SnapVisibleRange visible_range_; +}; + // Snap area is a bounding box that could be snapped to when a scroll happens in // its scroll container. // This data structure describes the data needed for SnapCoordinator if we want @@ -96,45 +141,27 @@ struct SnapAreaData { SnapAreaData() {} - SnapAreaData(SnapAxis axis, - gfx::ScrollOffset position, - gfx::RectF visible, - bool msnap) - : snap_axis(axis), - snap_position(position), - visible_region(visible), - must_snap(msnap) {} + SnapAreaData(const ScrollSnapAlign& align, const gfx::RectF& rec, bool msnap) + : scroll_snap_align(align), rect(rec), must_snap(msnap) {} bool operator==(const SnapAreaData& other) const { - return (other.snap_axis == snap_axis) && - (other.snap_position == snap_position) && - (other.visible_region == visible_region) && - (other.must_snap == must_snap); + return (other.scroll_snap_align == scroll_snap_align) && + (other.rect == rect) && (other.must_snap == must_snap); } bool operator!=(const SnapAreaData& other) const { return !(*this == other); } - // The axes along which the area has specified snap positions. - SnapAxis snap_axis; - - // The scroll_position to snap the area at the specified alignment in that - // axis. - // This is in the same coordinate with blink's scroll position, which is the - // location of the top/left of the scroll viewport in the top/left of the - // overflow rect. - gfx::ScrollOffset snap_position; + // Specifies how the snap area should be aligned with its snap container when + // snapped. The alignment_inline and alignment_block represent the alignments + // on x axis and y axis repectively. + ScrollSnapAlign scroll_snap_align; - // The area is only visible when the current scroll offset is within - // |visible_region|. - // See https://drafts.csswg.org/css-scroll-snap-1/#snap-scope - gfx::RectF visible_region; + // The snap area rect relative to its snap container's boundary + gfx::RectF rect; // Whether this area has scroll-snap-stop: always. // See https://www.w3.org/TR/css-scroll-snap-1/#scroll-snap-stop bool must_snap; - - // TODO(sunyunjia): Add fields for visibility requirement and large area - // snapping. }; typedef std::vector<SnapAreaData> SnapAreaList; @@ -148,7 +175,9 @@ class CC_EXPORT SnapContainerData { public: SnapContainerData(); explicit SnapContainerData(ScrollSnapType type); - SnapContainerData(ScrollSnapType type, gfx::ScrollOffset max); + SnapContainerData(ScrollSnapType type, + const gfx::RectF& rect, + const gfx::ScrollOffset& max); SnapContainerData(const SnapContainerData& other); SnapContainerData(SnapContainerData&& other); ~SnapContainerData(); @@ -158,7 +187,7 @@ class CC_EXPORT SnapContainerData { bool operator==(const SnapContainerData& other) const { return (other.scroll_snap_type_ == scroll_snap_type_) && - (other.max_position_ == max_position_) && + (other.rect_ == rect_) && (other.max_position_ == max_position_) && (other.proximity_range_ == proximity_range_) && (other.snap_area_list_ == snap_area_list_); } @@ -179,6 +208,9 @@ class CC_EXPORT SnapContainerData { void set_scroll_snap_type(ScrollSnapType type) { scroll_snap_type_ = type; } ScrollSnapType scroll_snap_type() const { return scroll_snap_type_; } + void set_rect(const gfx::RectF& rect) { rect_ = rect; } + gfx::RectF rect() const { return rect_; } + void set_max_position(gfx::ScrollOffset position) { max_position_ = position; } @@ -190,11 +222,42 @@ class CC_EXPORT SnapContainerData { gfx::ScrollOffset proximity_range() const { return proximity_range_; } private: + // Finds the best SnapArea candidate that minimizes the distance between + // current and candidate positions, while satisfying two invariants: + // - |candidate.snap_offset| is within |cross_axis_snap_result|'s visible + // range on |axis|. + // - |cross_axis_snap_result.snap_offset| is within |candidate|'s visible + // range on the cross axis. + // |cross_axis_snap_result| is what we've found to snap on the cross axis, + // or the original scroll offset if this is the first iteration of search. + // Returns the candidate as SnapSearchResult that includes the area's + // |snap_offset| and its visible range on the cross axis. + base::Optional<SnapSearchResult> FindClosestValidArea( + SearchAxis axis, + float current_offset, + const SnapSearchResult& cross_axis_snap_result) const; + + // Returns all the info needed to snap at this area on the given axis, + // including: + // - The offset at which the snap area and the snap container meet the + // requested alignment. + // - The visible range within which the snap area is visible on the cross + // axis. + SnapSearchResult GetSnapSearchResult(SearchAxis axis, + const SnapAreaData& data) const; + + bool IsSnapportCoveredOnAxis(SearchAxis axis, + float current_offset, + const gfx::RectF& area_rect) const; + // Specifies whether a scroll container is a scroll snap container, how // strictly it snaps, and which axes are considered. // See https://www.w3.org/TR/css-scroll-snap-1/#scroll-snap-type for details. ScrollSnapType scroll_snap_type_; + // The rect of the snap_container relative to its boundary. + gfx::RectF rect_; + // The maximal scroll position of the SnapContainer, in the same coordinate // with blink's scroll position. gfx::ScrollOffset max_position_; diff --git a/chromium/cc/input/scroll_snap_data_unittest.cc b/chromium/cc/input/scroll_snap_data_unittest.cc index 794f83faeaf..a688448ab2d 100644 --- a/chromium/cc/input/scroll_snap_data_unittest.cc +++ b/chromium/cc/input/scroll_snap_data_unittest.cc @@ -10,93 +10,158 @@ namespace cc { class ScrollSnapDataTest : public testing::Test {}; +TEST_F(ScrollSnapDataTest, StartAlignmentCalculation) { + SnapContainerData container( + ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), + gfx::RectF(10, 10, 200, 300), gfx::ScrollOffset(600, 800)); + SnapAreaData area(ScrollSnapAlign(SnapAlignment::kStart), + gfx::RectF(100, 150, 100, 100), false); + container.AddSnapAreaData(area); + gfx::ScrollOffset current_position(0, 0); + gfx::ScrollOffset snap_position; + EXPECT_TRUE( + container.FindSnapPosition(current_position, true, true, &snap_position)); + EXPECT_EQ(90, snap_position.x()); + EXPECT_EQ(140, snap_position.y()); +} + +TEST_F(ScrollSnapDataTest, CenterAlignmentCalculation) { + SnapContainerData container( + ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), + gfx::RectF(10, 10, 200, 300), gfx::ScrollOffset(600, 800)); + SnapAreaData area(ScrollSnapAlign(SnapAlignment::kCenter), + gfx::RectF(100, 150, 100, 100), false); + container.AddSnapAreaData(area); + gfx::ScrollOffset current_position(0, 0); + gfx::ScrollOffset snap_position; + EXPECT_TRUE( + container.FindSnapPosition(current_position, true, true, &snap_position)); + EXPECT_EQ(40, snap_position.x()); + EXPECT_EQ(40, snap_position.y()); +} + +TEST_F(ScrollSnapDataTest, EndAlignmentCalculation) { + SnapContainerData container( + ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), + gfx::RectF(10, 10, 200, 200), gfx::ScrollOffset(600, 800)); + SnapAreaData area(ScrollSnapAlign(SnapAlignment::kEnd), + gfx::RectF(150, 200, 100, 100), false); + container.AddSnapAreaData(area); + gfx::ScrollOffset current_position(0, 0); + gfx::ScrollOffset snap_position; + EXPECT_TRUE( + container.FindSnapPosition(current_position, true, true, &snap_position)); + EXPECT_EQ(40, snap_position.x()); + EXPECT_EQ(90, snap_position.y()); +} + +TEST_F(ScrollSnapDataTest, UnreachableSnapPositionCalculation) { + SnapContainerData container( + ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), + gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(100, 100)); + SnapAreaData area(ScrollSnapAlign(SnapAlignment::kStart, SnapAlignment::kEnd), + gfx::RectF(200, 0, 100, 100), false); + container.AddSnapAreaData(area); + gfx::ScrollOffset current_position(50, 50); + gfx::ScrollOffset snap_position; + EXPECT_TRUE( + container.FindSnapPosition(current_position, true, true, &snap_position)); + // Aligning to start on x would lead the scroll offset larger than max, and + // aligning to end on y would lead the scroll offset smaller than zero. So + // we expect these are clamped. + EXPECT_EQ(100, snap_position.x()); + EXPECT_EQ(0, snap_position.y()); +} + TEST_F(ScrollSnapDataTest, FindsClosestSnapPositionIndependently) { - SnapContainerData data( + SnapContainerData container( ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), - gfx::ScrollOffset(360, 380)); - gfx::ScrollOffset current_position(100, 100); + gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(600, 800)); SnapAreaData snap_x_only( - SnapAxis::kX, gfx::ScrollOffset(80, SnapAreaData::kInvalidScrollPosition), - gfx::RectF(0, 0, 360, 380), false); + ScrollSnapAlign(SnapAlignment::kStart, SnapAlignment::kNone), + gfx::RectF(80, 0, 150, 150), false); SnapAreaData snap_y_only( - SnapAxis::kY, gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 70), - gfx::RectF(0, 0, 360, 380), false); - SnapAreaData snap_on_both(SnapAxis::kBoth, gfx::ScrollOffset(50, 150), - gfx::RectF(0, 0, 360, 380), false); - data.AddSnapAreaData(snap_x_only); - data.AddSnapAreaData(snap_y_only); - data.AddSnapAreaData(snap_on_both); + ScrollSnapAlign(SnapAlignment::kNone, SnapAlignment::kStart), + gfx::RectF(0, 70, 150, 150), false); + SnapAreaData snap_on_both(ScrollSnapAlign(SnapAlignment::kStart), + gfx::RectF(50, 150, 150, 150), false); + gfx::ScrollOffset current_position(100, 100); + container.AddSnapAreaData(snap_x_only); + container.AddSnapAreaData(snap_y_only); + container.AddSnapAreaData(snap_on_both); gfx::ScrollOffset snap_position; EXPECT_TRUE( - data.FindSnapPosition(current_position, true, true, &snap_position)); + container.FindSnapPosition(current_position, true, true, &snap_position)); EXPECT_EQ(80, snap_position.x()); EXPECT_EQ(70, snap_position.y()); } TEST_F(ScrollSnapDataTest, FindsClosestSnapPositionOnAxisValueBoth) { - SnapContainerData data( + SnapContainerData container( ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), - gfx::ScrollOffset(360, 380)); - gfx::ScrollOffset current_position(40, 150); + gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(600, 800)); SnapAreaData snap_x_only( - SnapAxis::kX, gfx::ScrollOffset(80, SnapAreaData::kInvalidScrollPosition), - gfx::RectF(0, 0, 360, 380), false); + ScrollSnapAlign(SnapAlignment::kStart, SnapAlignment::kNone), + gfx::RectF(80, 0, 150, 150), false); SnapAreaData snap_y_only( - SnapAxis::kY, gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 70), - gfx::RectF(0, 0, 360, 380), false); - SnapAreaData snap_on_both(SnapAxis::kBoth, gfx::ScrollOffset(50, 150), - gfx::RectF(0, 0, 360, 380), false); - data.AddSnapAreaData(snap_x_only); - data.AddSnapAreaData(snap_y_only); - data.AddSnapAreaData(snap_on_both); + ScrollSnapAlign(SnapAlignment::kNone, SnapAlignment::kStart), + gfx::RectF(0, 70, 150, 150), false); + SnapAreaData snap_on_both(ScrollSnapAlign(SnapAlignment::kStart), + gfx::RectF(50, 150, 150, 150), false); + gfx::ScrollOffset current_position(40, 120); + container.AddSnapAreaData(snap_x_only); + container.AddSnapAreaData(snap_y_only); + container.AddSnapAreaData(snap_on_both); gfx::ScrollOffset snap_position; EXPECT_TRUE( - data.FindSnapPosition(current_position, true, true, &snap_position)); + container.FindSnapPosition(current_position, true, true, &snap_position)); EXPECT_EQ(50, snap_position.x()); EXPECT_EQ(150, snap_position.y()); } TEST_F(ScrollSnapDataTest, DoesNotSnapOnNonScrolledAxis) { - SnapContainerData data( + SnapContainerData container( ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), - gfx::ScrollOffset(360, 380)); - gfx::ScrollOffset current_position(100, 100); + gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(600, 800)); SnapAreaData snap_x_only( - SnapAxis::kX, gfx::ScrollOffset(80, SnapAreaData::kInvalidScrollPosition), - gfx::RectF(0, 0, 360, 380), false); + ScrollSnapAlign(SnapAlignment::kStart, SnapAlignment::kNone), + gfx::RectF(80, 0, 150, 150), false); SnapAreaData snap_y_only( - SnapAxis::kY, gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 70), - gfx::RectF(0, 0, 360, 380), false); - data.AddSnapAreaData(snap_x_only); - data.AddSnapAreaData(snap_y_only); + ScrollSnapAlign(SnapAlignment::kNone, SnapAlignment::kStart), + gfx::RectF(0, 70, 150, 150), false); + gfx::ScrollOffset current_position(100, 100); + container.AddSnapAreaData(snap_x_only); + container.AddSnapAreaData(snap_y_only); gfx::ScrollOffset snap_position; - EXPECT_TRUE( - data.FindSnapPosition(current_position, true, false, &snap_position)); + EXPECT_TRUE(container.FindSnapPosition(current_position, true, false, + &snap_position)); EXPECT_EQ(80, snap_position.x()); EXPECT_EQ(100, snap_position.y()); } TEST_F(ScrollSnapDataTest, DoesNotSnapOnNonVisibleAreas) { - SnapContainerData data( + SnapContainerData container( ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), - gfx::ScrollOffset(360, 380)); - gfx::ScrollOffset current_position(100, 100); - SnapAreaData non_visible_x(SnapAxis::kBoth, gfx::ScrollOffset(70, 70), - gfx::RectF(0, 0, 90, 200), false); - SnapAreaData non_visible_y(SnapAxis::kBoth, gfx::ScrollOffset(70, 70), - gfx::RectF(0, 0, 200, 90), false); - data.AddSnapAreaData(non_visible_x); - data.AddSnapAreaData(non_visible_y); + gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(600, 800)); + SnapAreaData snap_x_only( + ScrollSnapAlign(SnapAlignment::kStart, SnapAlignment::kNone), + gfx::RectF(300, 400, 100, 100), false); + SnapAreaData snap_y_only( + ScrollSnapAlign(SnapAlignment::kNone, SnapAlignment::kStart), + gfx::RectF(400, 300, 100, 100), false); + gfx::ScrollOffset current_position(0, 0); + container.AddSnapAreaData(snap_x_only); + container.AddSnapAreaData(snap_y_only); gfx::ScrollOffset snap_position; EXPECT_FALSE( - data.FindSnapPosition(current_position, true, true, &snap_position)); + container.FindSnapPosition(current_position, true, true, &snap_position)); } TEST_F(ScrollSnapDataTest, SnapOnClosestAxisFirstIfVisibilityConflicts) { - SnapContainerData data( + SnapContainerData container( ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), - gfx::ScrollOffset(360, 380)); - gfx::ScrollOffset current_position(100, 100); + gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(600, 800)); + gfx::ScrollOffset current_position(0, 0); // Both the areas are currently visible. // However, if we snap to them on x and y independently, none is visible after @@ -104,38 +169,37 @@ TEST_F(ScrollSnapDataTest, SnapOnClosestAxisFirstIfVisibilityConflicts) { // After that, we look for another snap point on y axis which does not // conflict with the snap point on x. SnapAreaData snap_x( - SnapAxis::kX, gfx::ScrollOffset(80, SnapAreaData::kInvalidScrollPosition), - gfx::RectF(60, 60, 60, 60), false); + ScrollSnapAlign(SnapAlignment::kStart, SnapAlignment::kNone), + gfx::RectF(150, 0, 100, 100), false); SnapAreaData snap_y1( - SnapAxis::kY, - gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 130), - gfx::RectF(90, 90, 60, 60), false); + ScrollSnapAlign(SnapAlignment::kNone, SnapAlignment::kStart), + gfx::RectF(0, 180, 100, 100), false); SnapAreaData snap_y2( - SnapAxis::kY, gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 60), - gfx::RectF(50, 50, 60, 60), false); - data.AddSnapAreaData(snap_x); - data.AddSnapAreaData(snap_y1); - data.AddSnapAreaData(snap_y2); + ScrollSnapAlign(SnapAlignment::kNone, SnapAlignment::kStart), + gfx::RectF(250, 80, 100, 100), false); + container.AddSnapAreaData(snap_x); + container.AddSnapAreaData(snap_y1); + container.AddSnapAreaData(snap_y2); gfx::ScrollOffset snap_position; EXPECT_TRUE( - data.FindSnapPosition(current_position, true, true, &snap_position)); - EXPECT_EQ(80, snap_position.x()); - EXPECT_EQ(60, snap_position.y()); + container.FindSnapPosition(current_position, true, true, &snap_position)); + EXPECT_EQ(150, snap_position.x()); + EXPECT_EQ(80, snap_position.y()); } TEST_F(ScrollSnapDataTest, DoesNotSnapToPositionsOutsideProximityRange) { - SnapContainerData data( + SnapContainerData container( ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), - gfx::ScrollOffset(360, 380)); - data.set_proximity_range(gfx::ScrollOffset(50, 50)); + gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(600, 800)); + container.set_proximity_range(gfx::ScrollOffset(50, 50)); gfx::ScrollOffset current_position(100, 100); - SnapAreaData area(SnapAxis::kBoth, gfx::ScrollOffset(80, 160), - gfx::RectF(50, 50, 200, 200), false); - data.AddSnapAreaData(area); + SnapAreaData area(ScrollSnapAlign(SnapAlignment::kStart), + gfx::RectF(80, 160, 100, 100), false); + container.AddSnapAreaData(area); gfx::ScrollOffset snap_position; EXPECT_TRUE( - data.FindSnapPosition(current_position, true, true, &snap_position)); + container.FindSnapPosition(current_position, true, true, &snap_position)); // The snap position on x, 80, is within the proximity range of [50, 150]. // However, the snap position on y, 160, is outside the proximity range of diff --git a/chromium/cc/input/snap_fling_controller.h b/chromium/cc/input/snap_fling_controller.h index 5199b23faa6..20f305651c2 100644 --- a/chromium/cc/input/snap_fling_controller.h +++ b/chromium/cc/input/snap_fling_controller.h @@ -24,8 +24,8 @@ class SnapFlingCurve; class SnapFlingClient { public: virtual bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement, - gfx::Vector2dF* initial_offset, - gfx::Vector2dF* target_offset) const = 0; + gfx::Vector2dF* out_initial_offset, + gfx::Vector2dF* out_target_offset) const = 0; virtual gfx::Vector2dF ScrollByForSnapFling(const gfx::Vector2dF& delta) = 0; virtual void ScrollEndForSnapFling() = 0; virtual void RequestAnimationForSnapFling() = 0; diff --git a/chromium/cc/input/snap_fling_curve.cc b/chromium/cc/input/snap_fling_curve.cc index 645c9c5440a..b61be7b9925 100644 --- a/chromium/cc/input/snap_fling_curve.cc +++ b/chromium/cc/input/snap_fling_curve.cc @@ -4,6 +4,7 @@ #include "cc/input/snap_fling_curve.h" +#include <algorithm> #include <cmath> #include "build/build_config.h" @@ -43,6 +44,11 @@ double CalculateFirstDelta(double distance, double frames) { return distance * (1 - kRatio) / (1 - std::pow(kRatio, frames)); } +bool IsWithinOnePixel(gfx::Vector2dF actual, gfx::Vector2dF target) { + return std::abs(actual.x() - target.x()) < 1 && + std::abs(actual.y() - target.y()) < 1; +} + } // namespace gfx::Vector2dF SnapFlingCurve::EstimateDisplacement( @@ -71,16 +77,7 @@ SnapFlingCurve::SnapFlingCurve(const gfx::Vector2dF& start_offset, SnapFlingCurve::~SnapFlingCurve() = default; -double SnapFlingCurve::GetCurrentCurveDistance(base::TimeTicks time_stamp) { - double current_distance = GetDistanceFromDisplacement(current_displacement_); - base::TimeDelta current_time = time_stamp - start_time_; - - // Finishes the curve if the time elapsed is longer than |duration_|, or the - // remaining distance is less than 1. - if (current_time >= duration_ || current_distance >= total_distance_ - 1) { - return total_distance_; - } - +double SnapFlingCurve::GetCurrentCurveDistance(base::TimeDelta current_time) { double current_frame = current_time.InMillisecondsF() / kMsPerFrame + 1; double sum = first_delta_ * (1 - std::pow(kRatio, current_frame)) / (1 - kRatio); @@ -93,13 +90,15 @@ gfx::Vector2dF SnapFlingCurve::GetScrollDelta(base::TimeTicks time_stamp) { // The the snap offset may never be reached due to clamping or other factors. // To avoid a never ending snap curve, we force the curve to end if the time - // has passed a maximum Duration. - if (time_stamp - start_time_ > kMaximumSnapDuration) { + // has passed |duration_| or the remaining displacement is less than 1. + base::TimeDelta current_time = time_stamp - start_time_; + if (current_time >= std::min(duration_, kMaximumSnapDuration) || + IsWithinOnePixel(current_displacement_, total_displacement_)) { is_finished_ = true; return total_displacement_ - current_displacement_; } - double new_distance = GetCurrentCurveDistance(time_stamp); + double new_distance = GetCurrentCurveDistance(current_time); gfx::Vector2dF new_displacement(new_distance * ratio_x_, new_distance * ratio_y_); @@ -108,8 +107,6 @@ gfx::Vector2dF SnapFlingCurve::GetScrollDelta(base::TimeTicks time_stamp) { void SnapFlingCurve::UpdateCurrentOffset(const gfx::Vector2dF& current_offset) { current_displacement_ = current_offset - start_offset_; - if (current_displacement_ == total_displacement_) - is_finished_ = true; } bool SnapFlingCurve::IsFinished() const { diff --git a/chromium/cc/input/snap_fling_curve.h b/chromium/cc/input/snap_fling_curve.h index b5eaf62fa3d..4aad8d1ef70 100644 --- a/chromium/cc/input/snap_fling_curve.h +++ b/chromium/cc/input/snap_fling_curve.h @@ -41,8 +41,8 @@ class CC_EXPORT SnapFlingCurve { base::TimeDelta duration() const { return duration_; } private: - // Returns the curve's current distance at |time_stamp|. - double GetCurrentCurveDistance(base::TimeTicks time_stamp); + // Returns the curve's current distance at |current_time|. + double GetCurrentCurveDistance(base::TimeDelta current_time); // The initial scroll offset of the scroller. const gfx::Vector2dF start_offset_; diff --git a/chromium/cc/input/snap_fling_curve_unittest.cc b/chromium/cc/input/snap_fling_curve_unittest.cc index 028006f7670..3682ded6657 100644 --- a/chromium/cc/input/snap_fling_curve_unittest.cc +++ b/chromium/cc/input/snap_fling_curve_unittest.cc @@ -40,15 +40,12 @@ TEST(SnapFlingCurveTest, AdvanceHalfwayThrough) { EXPECT_FALSE(curve.IsFinished()); } -TEST(SnapFlingCurveTest, AdvanceFullyThroughOnlyFinishesAfterUpdate) { +TEST(SnapFlingCurveTest, AdvanceFullyThrough) { SnapFlingCurve curve(gfx::Vector2dF(100, 100), gfx::Vector2dF(500, 500), base::TimeTicks()); gfx::Vector2dF delta = curve.GetScrollDelta(base::TimeTicks() + curve.duration()); EXPECT_EQ(gfx::Vector2dF(400, 400), delta); - EXPECT_FALSE(curve.IsFinished()); - - curve.UpdateCurrentOffset(gfx::Vector2dF(500, 500)); EXPECT_TRUE(curve.IsFinished()); } @@ -56,8 +53,6 @@ TEST(SnapFlingCurveTest, ReturnsZeroAfterFinished) { SnapFlingCurve curve(gfx::Vector2dF(100, 100), gfx::Vector2dF(500, 500), base::TimeTicks()); curve.UpdateCurrentOffset(gfx::Vector2dF(500, 500)); - EXPECT_TRUE(curve.IsFinished()); - gfx::Vector2dF delta = curve.GetScrollDelta(base::TimeTicks()); EXPECT_EQ(gfx::Vector2dF(), delta); EXPECT_TRUE(curve.IsFinished()); @@ -67,5 +62,20 @@ TEST(SnapFlingCurveTest, ReturnsZeroAfterFinished) { EXPECT_TRUE(curve.IsFinished()); } +TEST(SnapFlingCurveTest, FlingFinishesWithinOnePixel) { + SnapFlingCurve curve(gfx::Vector2dF(0, 0), gfx::Vector2dF(100.5, 99.5), + base::TimeTicks()); + EXPECT_FALSE(curve.IsFinished()); + + curve.UpdateCurrentOffset(gfx::Vector2dF(99, 101)); + // IsFinished() is updated in GetScrollDelta(). + curve.GetScrollDelta(base::TimeTicks()); + EXPECT_FALSE(curve.IsFinished()); + + curve.UpdateCurrentOffset(gfx::Vector2dF(100, 100)); + curve.GetScrollDelta(base::TimeTicks()); + EXPECT_TRUE(curve.IsFinished()); +} + } // namespace test } // namespace cc diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index 890f7ec426c..c45a685da4f 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -136,9 +136,11 @@ class HudSoftwareBacking : public ResourcePool::SoftwareBacking { bool HeadsUpDisplayLayerImpl::WillDraw( DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) { - if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) + viz::ClientResourceProvider* resource_provider) { + if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE && + !LayerImpl::WillDraw(draw_mode, resource_provider)) { return false; + } int max_texture_size = layer_tree_impl()->max_texture_size(); internal_contents_scale_ = GetIdealContentsScale(); @@ -147,7 +149,7 @@ bool HeadsUpDisplayLayerImpl::WillDraw( internal_content_bounds_.SetToMin( gfx::Size(max_texture_size, max_texture_size)); - return LayerImpl::WillDraw(draw_mode, resource_provider); + return true; } void HeadsUpDisplayLayerImpl::AppendQuads(viz::RenderPass* render_pass, @@ -173,7 +175,7 @@ void HeadsUpDisplayLayerImpl::AppendQuads(viz::RenderPass* render_pass, void HeadsUpDisplayLayerImpl::UpdateHudTexture( DrawMode draw_mode, LayerTreeFrameSink* layer_tree_frame_sink, - LayerTreeResourceProvider* resource_provider, + viz::ClientResourceProvider* resource_provider, bool gpu_raster, const viz::RenderPassList& list) { if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) @@ -231,7 +233,6 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( backing->texture_id = alloc.texture_id; backing->texture_target = alloc.texture_target; backing->overlay_candidate = alloc.overlay_candidate; - backing->mailbox = gpu::Mailbox::Generate(); context_provider->ContextGL()->ProduceTextureDirectCHROMIUM( backing->texture_id, backing->mailbox.name); pool_resource.set_gpu_backing(std::move(backing)); @@ -276,7 +277,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( { ScopedGpuRaster gpu_raster(context_provider); - LayerTreeResourceProvider::ScopedSkSurface scoped_surface( + viz::ClientResourceProvider::ScopedSkSurface scoped_surface( context_provider->GrContext(), backing->texture_id, backing->texture_target, pool_resource.size(), pool_resource.format(), false /* can_use_lcd_text */, 0 /* msaa_sample_count */); @@ -289,7 +290,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( } backing->mailbox_sync_token = - LayerTreeResourceProvider::GenerateSyncTokenHelper(gl); + viz::ClientResourceProvider::GenerateSyncTokenHelper(gl); } else if (draw_mode == DRAW_MODE_HARDWARE) { // If not using |gpu_raster| but using gpu compositing, we DrawHudContents() // into a software bitmap and upload it to a texture for compositing. @@ -312,12 +313,13 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( SkPixmap pixmap; staging_surface_->peekPixels(&pixmap); gl->BindTexture(backing->texture_target, backing->texture_id); + DCHECK(GLSupportsFormat(pool_resource.format())); gl->TexSubImage2D( backing->texture_target, 0, 0, 0, pool_resource.size().width(), pool_resource.size().height(), GLDataFormat(pool_resource.format()), GLDataType(pool_resource.format()), pixmap.addr()); backing->mailbox_sync_token = - LayerTreeResourceProvider::GenerateSyncTokenHelper(gl); + viz::ClientResourceProvider::GenerateSyncTokenHelper(gl); } else { // If not using gpu compositing, we DrawHudContents() directly into a shared // memory bitmap, wrapped in an SkSurface, that can be shared to the display diff --git a/chromium/cc/layers/heads_up_display_layer_impl.h b/chromium/cc/layers/heads_up_display_layer_impl.h index b6f5070ddd3..6102b0da115 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.h +++ b/chromium/cc/layers/heads_up_display_layer_impl.h @@ -24,10 +24,13 @@ class SkPaint; class SkTypeface; struct SkRect; +namespace viz { +class ClientResourceProvider; +} + namespace cc { class FrameRateCounter; class LayerTreeFrameSink; -class LayerTreeResourceProvider; class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { public: @@ -41,12 +44,12 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; bool WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) override; + viz::ClientResourceProvider* resource_provider) override; void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; void UpdateHudTexture(DrawMode draw_mode, LayerTreeFrameSink* frame_sink, - LayerTreeResourceProvider* resource_provider, + viz::ClientResourceProvider* resource_provider, bool gpu_raster, const viz::RenderPassList& list); diff --git a/chromium/cc/layers/heads_up_display_layer_impl_unittest.cc b/chromium/cc/layers/heads_up_display_layer_impl_unittest.cc index 6397ea81bb0..f5dc43ee1bb 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl_unittest.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl_unittest.cc @@ -18,7 +18,7 @@ namespace { void CheckDrawLayer(HeadsUpDisplayLayerImpl* layer, LayerTreeFrameSink* frame_sink, - LayerTreeResourceProvider* resource_provider, + viz::ClientResourceProvider* resource_provider, viz::ContextProvider* context_provider, DrawMode draw_mode) { std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create(); @@ -47,10 +47,11 @@ TEST(HeadsUpDisplayLayerImplTest, ResourcelessSoftwareDrawAfterResourceLoss) { FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.CreatePendingTree(); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); std::unique_ptr<HeadsUpDisplayLayerImpl> layer_ptr = HeadsUpDisplayLayerImpl::Create(host_impl.pending_tree(), 1); layer_ptr->SetBounds(gfx::Size(100, 100)); + layer_ptr->set_visible_layer_rect(gfx::Rect(100, 100)); HeadsUpDisplayLayerImpl* layer = layer_ptr.get(); @@ -80,10 +81,11 @@ TEST(HeadsUpDisplayLayerImplTest, CPUAndGPURasterCanvas) { FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.CreatePendingTree(); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); std::unique_ptr<HeadsUpDisplayLayerImpl> layer_ptr = HeadsUpDisplayLayerImpl::Create(host_impl.pending_tree(), 1); layer_ptr->SetBounds(gfx::Size(100, 100)); + layer_ptr->set_visible_layer_rect(gfx::Rect(100, 100)); HeadsUpDisplayLayerImpl* layer = layer_ptr.get(); @@ -97,7 +99,7 @@ TEST(HeadsUpDisplayLayerImplTest, CPUAndGPURasterCanvas) { host_impl.ReleaseLayerTreeFrameSink(); layer_tree_frame_sink = FakeLayerTreeFrameSink::CreateSoftware(); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); // Check SW canvas drawing is ok. CheckDrawLayer(layer, host_impl.layer_tree_frame_sink(), diff --git a/chromium/cc/layers/heads_up_display_unittest.cc b/chromium/cc/layers/heads_up_display_unittest.cc index b1e7368d8bd..840f82f8ef4 100644 --- a/chromium/cc/layers/heads_up_display_unittest.cc +++ b/chromium/cc/layers/heads_up_display_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "cc/layers/heads_up_display_layer.h" + #include "cc/layers/layer.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/layer_tree_host.h" @@ -18,26 +19,11 @@ class HeadsUpDisplayTest : public LayerTreeTest { } }; -class DrawsContentLayer : public Layer { - public: - static scoped_refptr<DrawsContentLayer> Create() { - return base::WrapRefCounted(new DrawsContentLayer()); - } - bool DrawsContent() const override { return true; } - - private: - DrawsContentLayer() = default; - ~DrawsContentLayer() override = default; -}; - class HudWithRootLayerChange : public HeadsUpDisplayTest { public: - HudWithRootLayerChange() - : root_layer1_(DrawsContentLayer::Create()), - root_layer2_(DrawsContentLayer::Create()), - num_commits_(0) {} - void BeginTest() override { + root_layer1_ = Layer::Create(); + root_layer2_ = Layer::Create(); root_layer1_->SetBounds(gfx::Size(30, 30)); root_layer2_->SetBounds(gfx::Size(30, 30)); @@ -76,7 +62,7 @@ class HudWithRootLayerChange : public HeadsUpDisplayTest { break; case 6: EXPECT_EQ(root_layer2_.get(), layer_tree_host()->hud_layer()->parent()); - // Change directly back to the last root layer/ + // Change directly back to the last root layer. layer_tree_host()->SetRootLayer(root_layer1_); break; case 7: @@ -89,12 +75,12 @@ class HudWithRootLayerChange : public HeadsUpDisplayTest { void AfterTest() override {} private: - scoped_refptr<DrawsContentLayer> root_layer1_; - scoped_refptr<DrawsContentLayer> root_layer2_; - int num_commits_; + scoped_refptr<Layer> root_layer1_; + scoped_refptr<Layer> root_layer2_; + int num_commits_ = 0; }; -MULTI_THREAD_TEST_F(HudWithRootLayerChange); +SINGLE_AND_MULTI_THREAD_TEST_F(HudWithRootLayerChange); } // namespace } // namespace cc diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index c5fb253d85e..823a5df49c7 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -19,7 +19,7 @@ #include "cc/input/main_thread_scrolling_reason.h" #include "cc/layers/layer_client.h" #include "cc/layers/layer_impl.h" -#include "cc/layers/scrollbar_layer_interface.h" +#include "cc/layers/picture_layer.h" #include "cc/tiles/frame_viewer_instrumentation.h" #include "cc/trees/draw_property_utils.h" #include "cc/trees/effect_node.h" @@ -85,7 +85,7 @@ Layer::Layer() clip_tree_index_(ClipTree::kInvalidNodeId), scroll_tree_index_(ScrollTree::kInvalidNodeId), property_tree_sequence_number_(-1), - should_flatten_transform_from_property_tree_(false), + should_flatten_screen_space_transform_from_property_tree_(false), draws_content_(false), should_check_backface_visibility_(false), cache_render_surface_(false), @@ -95,8 +95,7 @@ Layer::Layer() needs_show_scrollbars_(false), has_transform_node_(false), subtree_has_copy_request_(false), - safe_opaque_background_color_(0), - num_unclipped_descendants_(0) {} + safe_opaque_background_color_(0) {} Layer::~Layer() { // Our parent should be holding a reference to us so there should be no @@ -150,7 +149,7 @@ void Layer::SetLayerTreeHost(LayerTreeHost* host) { inputs_.mask_layer->SetLayerTreeHost(host); if (host && !host->IsUsingLayerLists() && - GetMutatorHost()->IsElementAnimating(element_id())) { + host->mutator_host()->IsElementAnimating(element_id())) { host->SetNeedsCommit(); } } @@ -186,11 +185,6 @@ void Layer::SetNeedsPushProperties() { layer_tree_host_->AddLayerShouldPushProperties(this); } -void Layer::ResetNeedsPushPropertiesForTesting() { - if (layer_tree_host_) - layer_tree_host_->RemoveLayerShouldPushProperties(this); -} - bool Layer::IsPropertyChangeAllowed() const { if (!layer_tree_host_) return true; @@ -361,16 +355,6 @@ void Layer::RemoveAllChildren() { } } -void Layer::SetChildren(const LayerList& children) { - DCHECK(IsPropertyChangeAllowed()); - if (children == inputs_.children) - return; - - RemoveAllChildren(); - for (size_t i = 0; i < children.size(); ++i) - AddChild(children[i]); -} - bool Layer::HasAncestor(const Layer* ancestor) const { for (const Layer* layer = parent(); layer; layer = layer->parent()) { if (layer == ancestor) @@ -423,9 +407,10 @@ void Layer::SetBackgroundColor(SkColor background_color) { void Layer::SetSafeOpaqueBackgroundColor(SkColor background_color) { DCHECK(IsPropertyChangeAllowed()); - if (safe_opaque_background_color_ == background_color) + SkColor opaque_color = SkColorSetA(background_color, 255); + if (safe_opaque_background_color_ == opaque_color) return; - safe_opaque_background_color_ = background_color; + safe_opaque_background_color_ = opaque_color; SetNeedsPushProperties(); } @@ -448,7 +433,7 @@ void Layer::SetMasksToBounds(bool masks_to_bounds) { SetSubtreePropertyChanged(); } -void Layer::SetMaskLayer(Layer* mask_layer) { +void Layer::SetMaskLayer(PictureLayer* mask_layer) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.mask_layer.get() == mask_layer) return; @@ -644,8 +629,10 @@ void Layer::SetPosition(const gfx::PointF& position) { return; SetSubtreePropertyChanged(); - TransformNode* transform_node = GetTransformNode(); - if (transform_node) { + if (has_transform_node_) { + TransformNode* transform_node = + layer_tree_host_->property_trees()->transform_tree.Node( + transform_tree_index_); transform_node->update_post_local_transform(position, transform_origin()); transform_node->needs_local_transform_update = true; transform_node->transform_changed = true; @@ -684,23 +671,21 @@ void Layer::SetTransform(const gfx::Transform& transform) { SetSubtreePropertyChanged(); if (layer_tree_host_) { if (has_transform_node_) { - PropertyTrees* property_trees = layer_tree_host_->property_trees(); - if (TransformNode* transform_node = - property_trees->transform_tree.Node(transform_tree_index())) { - // We need to trigger a rebuild if we could have affected 2d axis - // alignment. We'll check to see if transform and inputs_.transform are - // axis align with respect to one another. - DCHECK_EQ(transform_tree_index(), transform_node->id); - bool preserves_2d_axis_alignment = - Are2dAxisAligned(inputs_.transform, transform); - transform_node->local = transform; - transform_node->needs_local_transform_update = true; - transform_node->transform_changed = true; - layer_tree_host_->property_trees()->transform_tree.set_needs_update( - true); - if (!preserves_2d_axis_alignment) - SetPropertyTreesNeedRebuild(); - } + TransformNode* transform_node = + layer_tree_host_->property_trees()->transform_tree.Node( + transform_tree_index_); + // We need to trigger a rebuild if we could have affected 2d axis + // alignment. We'll check to see if transform and inputs_.transform are + // axis align with respect to one another. + DCHECK_EQ(transform_tree_index(), transform_node->id); + bool preserves_2d_axis_alignment = + Are2dAxisAligned(inputs_.transform, transform); + transform_node->local = transform; + transform_node->needs_local_transform_update = true; + transform_node->transform_changed = true; + layer_tree_host_->property_trees()->transform_tree.set_needs_update(true); + if (!preserves_2d_axis_alignment) + SetPropertyTreesNeedRebuild(); } else { SetPropertyTreesNeedRebuild(); } @@ -720,8 +705,10 @@ void Layer::SetTransformOrigin(const gfx::Point3F& transform_origin) { return; SetSubtreePropertyChanged(); - TransformNode* transform_node = GetTransformNode(); - if (transform_node) { + if (has_transform_node_) { + TransformNode* transform_node = + layer_tree_host_->property_trees()->transform_tree.Node( + transform_tree_index_); DCHECK_EQ(transform_tree_index(), transform_node->id); transform_node->update_pre_local_transform(transform_origin); transform_node->update_post_local_transform(position(), transform_origin); @@ -735,10 +722,6 @@ void Layer::SetTransformOrigin(const gfx::Point3F& transform_origin) { SetNeedsCommit(); } -bool Layer::ScrollOffsetAnimationWasInterrupted() const { - return GetMutatorHost()->ScrollOffsetAnimationWasInterrupted(element_id()); -} - void Layer::SetScrollParent(Layer* parent) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.scroll_parent == parent) @@ -1039,6 +1022,13 @@ int Layer::scroll_tree_index() const { return scroll_tree_index_; } +void Layer::SetOffsetToTransformParent(gfx::Vector2dF offset) { + if (offset_to_transform_parent_ == offset) + return; + offset_to_transform_parent_ = offset; + SetNeedsPushProperties(); +} + void Layer::InvalidatePropertyTreesIndices() { SetTransformTreeIndex(TransformTree::kInvalidNodeId); SetClipTreeIndex(ClipTree::kInvalidNodeId); @@ -1168,8 +1158,8 @@ void Layer::SetLayerClient(base::WeakPtr<LayerClient> client) { inputs_.debug_info = nullptr; } -bool Layer::IsSnapped() { - return scrollable(); +bool Layer::IsSnappedToPixelGridInTarget() { + return false; } void Layer::PushPropertiesTo(LayerImpl* layer) { @@ -1189,7 +1179,7 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { layer->SetEffectTreeIndex(effect_tree_index()); layer->SetClipTreeIndex(clip_tree_index()); layer->SetScrollTreeIndex(scroll_tree_index()); - layer->set_offset_to_transform_parent(offset_to_transform_parent_); + layer->SetOffsetToTransformParent(offset_to_transform_parent_); layer->SetDrawsContent(DrawsContent()); layer->SetHitTestableWithoutDrawsContent( hit_testable_without_draws_content()); @@ -1203,10 +1193,22 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { inputs_.main_thread_scrolling_reasons); layer->SetNonFastScrollableRegion(inputs_.non_fast_scrollable_region); layer->SetTouchActionRegion(inputs_.touch_action_region); + // TODO(sunxd): Pass the correct region for wheel event handlers, see + // https://crbug.com/841364. + if (layer_tree_host()->event_listener_properties( + EventListenerClass::kMouseWheel) == + EventListenerProperties::kBlocking || + layer_tree_host()->event_listener_properties( + EventListenerClass::kMouseWheel) == + EventListenerProperties::kBlockingAndPassive) { + layer->SetWheelEventHandlerRegion(Region(gfx::Rect(bounds()))); + } else { + layer->SetWheelEventHandlerRegion(Region()); + } layer->SetContentsOpaque(inputs_.contents_opaque); layer->SetPosition(inputs_.position); - layer->set_should_flatten_transform_from_property_tree( - should_flatten_transform_from_property_tree_); + layer->SetShouldFlattenScreenSpaceTransformFromPropertyTree( + should_flatten_screen_space_transform_from_property_tree_); layer->SetUseParentBackfaceVisibility(inputs_.use_parent_backface_visibility); layer->SetShouldCheckBackfaceVisibility(should_check_backface_visibility_); @@ -1221,10 +1223,11 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { // 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 // instead of trying to undo that scrolling later. - if (ScrollOffsetAnimationWasInterrupted()) - layer->layer_tree_impl() - ->property_trees() - ->scroll_tree.SetScrollOffsetClobberActiveValue(layer->element_id()); + if (layer_tree_host_->mutator_host()->ScrollOffsetAnimationWasInterrupted( + element_id())) { + PropertyTrees* trees = layer->layer_tree_impl()->property_trees(); + trees->scroll_tree.SetScrollOffsetClobberActiveValue(layer->element_id()); + } if (needs_show_scrollbars_) layer->set_needs_show_scrollbars(true); @@ -1239,8 +1242,6 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { layer->SetHasWillChangeTransformHint(has_will_change_transform_hint()); layer->SetNeedsPushProperties(); - layer->SetTrilinearFiltering(trilinear_filtering()); - // Reset any state that should be cleared for the next update. needs_show_scrollbars_ = false; subtree_property_changed_ = false; @@ -1326,6 +1327,15 @@ void Layer::SetSubtreePropertyChanged() { SetNeedsPushProperties(); } +void Layer::SetShouldFlattenScreenSpaceTransformFromPropertyTree( + bool should_flatten) { + if (should_flatten_screen_space_transform_from_property_tree_ == + should_flatten) + return; + should_flatten_screen_space_transform_from_property_tree_ = should_flatten; + SetNeedsPushProperties(); +} + void Layer::SetMayContainVideo(bool yes) { if (may_contain_video_ == yes) return; @@ -1350,23 +1360,10 @@ void Layer::OnOpacityAnimated(float opacity) { inputs_.opacity = opacity; } -TransformNode* Layer::GetTransformNode() const { - return has_transform_node_ - ? layer_tree_host_->property_trees()->transform_tree.Node( - transform_tree_index_) - : nullptr; -} - void Layer::OnTransformAnimated(const gfx::Transform& transform) { inputs_.transform = transform; } -bool Layer::HasTickingAnimationForTesting() const { - return layer_tree_host_ - ? GetMutatorHost()->HasTickingKeyframeModelForTesting(element_id()) - : false; -} - void Layer::SetHasWillChangeTransformHint(bool has_will_change) { if (inputs_.has_will_change_transform_hint == has_will_change) return; @@ -1378,21 +1375,18 @@ void Layer::SetTrilinearFiltering(bool trilinear_filtering) { if (inputs_.trilinear_filtering == trilinear_filtering) return; inputs_.trilinear_filtering = trilinear_filtering; + // When true, makes a RenderSurface which makes an effect node. + SetPropertyTreesNeedRebuild(); + // Adding a RenderSurface may change how things in the subtree appear, since + // it flattens transforms. + SetSubtreePropertyChanged(); SetNeedsCommit(); } -MutatorHost* Layer::GetMutatorHost() const { - return layer_tree_host_ ? layer_tree_host_->mutator_host() : nullptr; -} - ElementListType Layer::GetElementTypeForAnimation() const { return ElementListType::ACTIVE; } -ScrollbarLayerInterface* Layer::ToScrollbarLayer() { - return nullptr; -} - void Layer::RemoveFromClipTree() { if (clip_children_.get()) { std::set<Layer*> copy = *clip_children_; @@ -1415,9 +1409,7 @@ void Layer::AddDrawableDescendants(int num) { parent()->AddDrawableDescendants(num); } -void Layer::RunMicroBenchmark(MicroBenchmark* benchmark) { - benchmark->RunOnLayer(this); -} +void Layer::RunMicroBenchmark(MicroBenchmark* benchmark) {} void Layer::SetElementId(ElementId id) { DCHECK(IsPropertyChangeAllowed()); @@ -1441,12 +1433,6 @@ void Layer::SetElementId(ElementId id) { SetNeedsCommit(); } -bool Layer::has_copy_requests_in_target_subtree() { - return layer_tree_host_->property_trees() - ->effect_tree.Node(effect_tree_index()) - ->subtree_has_copy_request; -} - gfx::Transform Layer::ScreenSpaceTransform() const { DCHECK_NE(transform_tree_index_, TransformTree::kInvalidNodeId); return draw_property_utils::ScreenSpaceTransform( diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 32f87b182cd..ff1e60cce3d 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -29,7 +29,6 @@ #include "cc/paint/filter_operations.h" #include "cc/paint/paint_record.h" #include "cc/trees/element_id.h" -#include "cc/trees/mutator_host_client.h" #include "cc/trees/property_tree.h" #include "cc/trees/target_property.h" #include "third_party/skia/include/core/SkColor.h" @@ -56,42 +55,80 @@ class LayerImpl; class LayerTreeHost; class LayerTreeHostCommon; class LayerTreeImpl; -class MutatorHost; -class ScrollbarLayerInterface; +class PictureLayer; // Base class for composited layers. Special layer types are derived from -// this class. +// this class. Each layer is an independent unit in the compositor, be that +// for transforming or for content. If a layer has content it can be +// transformed efficiently without requiring the content to be recreated. +// Layers form a tree, with each layer having 0 or more children, and a single +// parent (or none at the root). Layers within the tree, other than the root +// layer, are kept alive by that tree relationship, with refpointer ownership +// from parents to children. class CC_EXPORT Layer : public base::RefCounted<Layer> { public: using LayerListType = LayerList; + // An invalid layer id, as all layer ids are positive. enum LayerIdLabels { INVALID_ID = -1, }; + // A layer can be attached to another layer as a mask for it. These + // describe how the mask would be generated as a texture in that case. enum LayerMaskType { NOT_MASK = 0, MULTI_TEXTURE_MASK, SINGLE_TEXTURE_MASK, }; + // Factory to create a new Layer, with a unique id. static scoped_refptr<Layer> Create(); + // Sets an optional client on this layer, that will be called when relevant + // events happen. The client is a WeakPtr so it can be destroyed without + // unsetting itself as the client. + void SetLayerClient(base::WeakPtr<LayerClient> client); + LayerClient* GetLayerClientForTesting() const { return inputs_.client.get(); } + + // A unique and stable id for the Layer. Ids are always positive. int id() const { return inputs_.layer_id; } + // Returns a pointer to the highest ancestor of this layer, or itself. Layer* RootLayer(); + // Returns a pointer to the direct ancestor of this layer if it exists, + // or null. Layer* parent() { return parent_; } const Layer* parent() const { return parent_; } + // Appends |child| to the list of children of this layer, and maintains + // ownership of a reference to that |child|. void AddChild(scoped_refptr<Layer> child); + // Inserts |child| into the list of children of this layer, before position + // |index| (0 based) and maintains ownership of a reference to that |child|. void InsertChild(scoped_refptr<Layer> child, size_t index); + // Removes an existing child |reference| from this layer's list of children, + // and inserts |new_layer| it its place in the list. This layer maintains + // ownership of a reference to the |new_layer|. The |new_layer| may be null, + // in which case |reference| is simply removed from the list of children, + // which ends this layers ownership of the child. void ReplaceChild(Layer* reference, scoped_refptr<Layer> new_layer); + // Removes this layer from the list of children in its parent, removing the + // parent's ownership of this layer. void RemoveFromParent(); + // Removes all children from this layer's list of children, removing ownership + // of those children. void RemoveAllChildren(); - void SetChildren(const LayerList& children); + // Returns true if |ancestor| is this layer's parent or higher ancestor. bool HasAncestor(const Layer* ancestor) const; + // The list of children of this layer. const LayerList& children() const { return inputs_.children; } - Layer* child_at(size_t index) { return inputs_.children[index].get(); } + + // Gets the LayerTreeHost that this layer is attached to, or null if not. + // A layer is attached to a LayerTreeHost if it or an ancestor layer is set as + // the root layer of a LayerTreeHost (while noting only a layer without a + // parent may be set as the root layer). + LayerTreeHost* layer_tree_host() const { return layer_tree_host_; } // This requests the layer and its subtree be rendered and given to the // callback. If the copy is unable to be produced (the layer is destroyed @@ -99,57 +136,114 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // request's source property is set, any prior uncommitted requests having the // same source will be aborted. void RequestCopyOfOutput(std::unique_ptr<viz::CopyOutputRequest> request); + // True if a copy request has been inserted on this layer and a commit has not + // occured yet. 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<viz::CopyOutputRequest>>* requests); - + // Set and get the background color for the layer. This color is not used by + // basic Layers, but subclasses may make use of it. virtual void SetBackgroundColor(SkColor background_color); SkColor background_color() const { return inputs_.background_color; } + + // Internal to property tree generation. Sets an opaque background color for + // the layer, to be used in place of the background_color() if the layer says + // contents_opaque() is true. void SetSafeOpaqueBackgroundColor(SkColor background_color); - // If contents_opaque(), return an opaque color else return a - // non-opaque color. Tries to return background_color(), if possible. + // Returns a background color with opaque-ness equal to the value of + // contents_opaque(). + // If the layer says contents_opaque() is true, this returns the value set by + // SetSafeOpaqueBackgroundColor() which should be an opaque color. Otherwise, + // it returns something non-opaque. It prefers to return the + // background_color(), but if the background_color() is opaque (and this layer + // claims to not be), then SK_ColorTRANSPARENT is returned. SkColor SafeOpaqueBackgroundColor() const; - // A layer's bounds are in logical, non-page-scaled pixels (however, the - // root layer's bounds are in physical pixels). + // Set and get the position of this layer, relative to its parent. This is + // specified in layer space, which excludes device scale and page scale + // factors, and ignoring transforms for this layer or ancestor layers. The + // root layer's position is not used as it always appears at the origin of + // the viewport. + void SetPosition(const gfx::PointF& position); + const gfx::PointF& position() const { return inputs_.position; } + + // Set and get the layers bounds. This is specified in layer space, which + // excludes device scale and page scale factors, and ignoring transforms for + // this layer or ancestor layers. + // + // The root layer in the tree has bounds in viewport space, which includes + // the device scale factor. void SetBounds(const gfx::Size& bounds); const gfx::Size& bounds() const { return inputs_.bounds; } + // Set and get the behaviour to be applied for compositor-thread scrolling of + // this layer beyond the beginning or end of the layer's content. void SetOverscrollBehavior(const OverscrollBehavior& behavior); OverscrollBehavior overscroll_behavior() const { return inputs_.overscroll_behavior; } + // Set and get the snapping behaviour for compositor-thread scrolling of + // this layer. The default value of null means there is no snapping for the + // layer. void SetSnapContainerData(base::Optional<SnapContainerData> data); const base::Optional<SnapContainerData>& snap_container_data() const { return inputs_.snap_container_data; } + // Set or get that this layer clips its subtree to within its bounds. Content + // of children will be intersected with the bounds of this layer when true. void SetMasksToBounds(bool masks_to_bounds); bool masks_to_bounds() const { return inputs_.masks_to_bounds; } - void SetMaskLayer(Layer* mask_layer); - Layer* mask_layer() { return inputs_.mask_layer.get(); } - const Layer* mask_layer() const { return inputs_.mask_layer.get(); } + // Set or get a layer that is not an ancestor of this layer, but which should + // be clipped to this layer's bounds if SetMasksToBounds() is set to true. + // The parent layer does *not* retain ownership of a reference on this layer. + void SetClipParent(Layer* ancestor); + Layer* clip_parent() { return inputs_.clip_parent; } + + // The set of layers which are not in this layers subtree but which should be + // clipped to only appear within this layer's bounds. + std::set<Layer*>* clip_children() { return clip_children_.get(); } + const std::set<Layer*>* clip_children() const { return clip_children_.get(); } + + // Set or get a layer that will mask the contents of this layer. The alpha + // channel of the mask layer's content is used as an alpha mask of this + // layer's content. IOW the mask's alpha is multiplied by this layer's alpha + // for each matching pixel. + void SetMaskLayer(PictureLayer* mask_layer); + PictureLayer* mask_layer() { return inputs_.mask_layer.get(); } + const PictureLayer* mask_layer() const { return inputs_.mask_layer.get(); } // Marks the |dirty_rect| as being changed, which will cause a commit and // the compositor to submit a new frame with a damage rect that includes the - // layer's dirty area. + // layer's dirty area. This rect is in layer space, the same as bounds(). virtual void SetNeedsDisplayRect(const gfx::Rect& dirty_rect); // Marks the entire layer's bounds as being changed, which will cause a commit // and the compositor to submit a new frame with a damage rect that includes - // the entire layer. + // the entire layer. Note that if the layer resizes afterward, but before + // commit, the dirty rect would not cover the layer, however then the layer + // bounds change would implicitly damage the full layer. void SetNeedsDisplay() { SetNeedsDisplayRect(gfx::Rect(bounds())); } + // Returns the union of previous calls to SetNeedsDisplayRect() and + // SetNeedsDisplay() that have not been committed to the compositor thread. + const gfx::Rect& update_rect() const { return inputs_.update_rect; } + // Set or get the opacity which should be applied to the contents of the layer + // and its subtree (together as a single composited entity) when blending them + // into their target. Note that this does not speak to the contents of this + // layer, which may be opaque or not (see contents_opaque()). Note that the + // opacity is cumulative since it applies to the layer's subtree. virtual void SetOpacity(float opacity); float opacity() const { return inputs_.opacity; } + // Gets the true opacity that will be used for blending the contents of this + // layer and its subtree into its target during composite. This value is the + // same as the user-specified opacity() unless the layer should not be visible + // at all for other reasons, in which case the opacity here becomes 0. float EffectiveOpacity() const; - virtual bool OpacityCanAnimateOnImplThread() const; + // Set or get the blend mode to be applied when blending the contents of the + // layer and its subtree (together as a single composited entity) when + // blending them into their target. void SetBlendMode(SkBlendMode blend_mode); SkBlendMode blend_mode() const { return inputs_.blend_mode; } @@ -162,97 +256,149 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { return inputs_.is_root_for_isolated_group; } - // Make the layer hit testable even if |draws_content_| is false. - void SetHitTestableWithoutDrawsContent(bool should_hit_test); - bool hit_testable_without_draws_content() const { - return inputs_.hit_testable_without_draws_content; - } - + // Set or get the list of filter effects to be applied to the contents of the + // layer and its subtree (together as a single composited entity) when + // drawing them into their target. void SetFilters(const FilterOperations& filters); const FilterOperations& filters() const { return inputs_.filters; } - // Background filters are filters applied to what is behind this layer, when - // they are viewed through non-opaque regions in this layer. + // Set or get the origin to be used when applying the filters given to + // SetFilters(). By default the origin is at the origin of this layer, but + // may be moved positively or negatively relative to that. The origin effects + // any filters which do not apply uniformly to the entire layer and its + // subtree. + void SetFiltersOrigin(const gfx::PointF& origin); + gfx::PointF filters_origin() const { return inputs_.filters_origin; } + + // Set or get the list of filters that should be applied to the content this + // layer and its subtree will be drawn into. The effect is clipped to only + // apply directly behind this layer and its subtree. void SetBackgroundFilters(const FilterOperations& filters); const FilterOperations& background_filters() const { return inputs_.background_filters; } + // Set or get an optimization hint that the contents of this layer are fully + // opaque or not. If true, every pixel of content inside the layer's bounds + // must be opaque or visual errors can occur. This applies only to this layer + // and not to children, and does not imply the layer should be composited + // opaquely, as effects may be applied such as opacity() or filters(). void SetContentsOpaque(bool opaque); bool contents_opaque() const { return inputs_.contents_opaque; } - void SetPosition(const gfx::PointF& position); - const gfx::PointF& position() const { return inputs_.position; } + // Set or get whether this layer should be a hit test target even if not + // visible. Normally if DrawsContent() is false, making the layer not + // contribute to the final composited output, the layer will not be eligable + // for hit testing since it is invisible. Set this to true to allow the layer + // to be hit tested regardless. + void SetHitTestableWithoutDrawsContent(bool should_hit_test); + bool hit_testable_without_draws_content() const { + return inputs_.hit_testable_without_draws_content; + } + // Set or gets if this layer is a container for fixed position layers in its + // subtree. Such layers will be positioned and transformed relative to this + // layer instead of their direct parent. + // // A layer that is a container for fixed position layers cannot be both // scrollable and have a non-identity transform. void SetIsContainerForFixedPositionLayers(bool container); bool IsContainerForFixedPositionLayers() const; - void SetIsResizedByBrowserControls(bool resized); - bool IsResizedByBrowserControls() const; - + // Set or get constraints applied to the layer's position, where it may be + // in a fixed position relative to the nearest ancestor that returns true for + // IsContainerForFixedPositionLayers(). This may also specify which edges + // of the layer are fixed to the same edges of the container ancestor. When + // fixed position, this layer's transform will be appended to the container + // ancestor's transform instead of to this layer's direct parent's. void SetPositionConstraint(const LayerPositionConstraint& constraint); const LayerPositionConstraint& position_constraint() const { return inputs_.position_constraint; } + // Set or get constraints applied to the layer's position, where it may act + // like a normal layer until, during scroll, its position triggers it to + // become fixed position relative to its scroller. See CSS position: sticky + // for more details. void SetStickyPositionConstraint( const LayerStickyPositionConstraint& constraint); const LayerStickyPositionConstraint& sticky_position_constraint() const { return inputs_.sticky_position_constraint; } - TransformNode* GetTransformNode() const; + // On some platforms (Android renderer) the viewport may resize during scroll + // on the compositor thread. During this resize and until the main thread + // matches, position fixed layers may need to have their position adjusted on + // the compositor thread to keep them fixed in place. If + // IsContainerForFixedPositionLayers() is true for this layer, these set and + // get whether fixed position descendants of this layer should have this + // adjustment to their position applied during such a viewport resize. + void SetIsResizedByBrowserControls(bool resized); + bool IsResizedByBrowserControls() const; + // Set or get the transform to be used when compositing this layer into its + // target. The transform is inherited by this layers children. void SetTransform(const gfx::Transform& transform); const gfx::Transform& transform() const { return inputs_.transform; } + // Gets the transform, including transform origin and position, of this layer + // and its ancestors, device scale and page scale factors, into the device + // viewport. + gfx::Transform ScreenSpaceTransform() const; + + // Set or get the origin to be used when applying the transform. The value is + // a position in layer space, relative to the top left corner of this layer. + // For instance, if set to the center of the layer, with a transform to rotate + // 180deg around the X axis, it would flip the layer vertically around the + // center of the layer, leaving it occupying the same space. Whereas set to + // the top left of the layer, the rotation wouldoccur around the top of the + // layer, moving it vertically while flipping it. void SetTransformOrigin(const gfx::Point3F&); const gfx::Point3F& transform_origin() const { return inputs_.transform_origin; } + // Set or get a scroll parent layer. It is not an ancestor of this layer, but + // this layer will be moved by the scroll parent's scroll offset. void SetScrollParent(Layer* parent); - Layer* scroll_parent() { return inputs_.scroll_parent; } - void SetClipParent(Layer* ancestor); - - Layer* clip_parent() { return inputs_.clip_parent; } - - std::set<Layer*>* clip_children() { return clip_children_.get(); } - const std::set<Layer*>* clip_children() const { - return clip_children_.get(); - } - - gfx::Transform ScreenSpaceTransform() const; - - void set_num_unclipped_descendants(size_t descendants) { - num_unclipped_descendants_ = descendants; - } - size_t num_unclipped_descendants() const { - return num_unclipped_descendants_; - } - + // Set or get the scroll offset of the layer. The content of the layer, and + // position of its subtree, as well as other layers for which this layer is + // their scroll parent, and their subtrees) is moved up by the amount of + // offset specified here. void SetScrollOffset(const gfx::ScrollOffset& scroll_offset); - - const gfx::ScrollOffset& scroll_offset() const { + // Accessor named to match LayerImpl for templated code. + const gfx::ScrollOffset& CurrentScrollOffset() const { return inputs_.scroll_offset; } + // Called internally during commit to update the layer with state from the // compositor thread. Not to be called externally by users of this class. void SetScrollOffsetFromImplSide(const gfx::ScrollOffset& scroll_offset); - // Marks this layer as being scrollable and needing an associated scroll node. - // The scroll node's bounds and container_bounds will be kept in sync - // with this layer. Once scrollable, a Layer cannot become un-scrollable. + // Marks this layer as being scrollable and needing an associated scroll node, + // and specifies the total size of the content to be scrolled (ie the max + // scroll offsets. The size should be a union of the layer and its subtree, as + // well as any layers for whom this layer is their scroll parent, and their + // subtrees, when they are transformed into this layer's space. Thus + // transforms of children affect the size of the |scroll_container_bounds|. + // Once scrollable, a Layer cannot become un-scrollable. void SetScrollable(const gfx::Size& scroll_container_bounds); + bool scrollable() const { return inputs_.scrollable; } const gfx::Size& scroll_container_bounds() const { return inputs_.scroll_container_bounds; } - bool scrollable() const { return inputs_.scrollable; } + // Set or get if this layer is able to be scrolled along each axis. These are + // independant of the scrollable state, or size of the scrollable area + // specified in SetScrollable(), as these may be enabled or disabled + // dynamically, while SetScrollable() defines what would be possible if these + // are enabled. + // When disabled, overscroll elasticity will not be used if the scroll offset + // ends up past the maximum range. And when enabled, with overlay scrollbars, + // the scrollbars will be shown when the scroll offset changes if these are + // set to true. void SetUserScrollable(bool horizontal, bool vertical); bool user_scrollable_horizontal() const { return inputs_.user_scrollable_horizontal; @@ -261,65 +407,118 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { return inputs_.user_scrollable_vertical; } + // Set or get if this layer is able to be scrolled on the compositor thread. + // This only applies for layers that are marked as scrollable, not for layers + // that are moved by a scroll parent. When any reason is present, the layer + // will not be scrolled on the compositor thread. The reasons are a set of + // bitflags from MainThreadScrollingReason, used to track the reason for + // debugging and reporting. + // AddMainThreadScrollingReasons() is used to add flags to the current set, + // and ClearMainThreadScrollingReasons() removes flags from the current set. void AddMainThreadScrollingReasons(uint32_t main_thread_scrolling_reasons); void ClearMainThreadScrollingReasons( uint32_t main_thread_scrolling_reasons_to_clear); uint32_t main_thread_scrolling_reasons() const { return inputs_.main_thread_scrolling_reasons; } - bool should_scroll_on_main_thread() const { - return !!inputs_.main_thread_scrolling_reasons; - } + // Set or get an area of this layer within which initiating a scroll can not + // be done from the compositor thread. Within this area, if the user attempts + // to start a scroll, the events must be sent to the main thread and processed + // there. void SetNonFastScrollableRegion(const Region& non_fast_scrollable_region); const Region& non_fast_scrollable_region() const { return inputs_.non_fast_scrollable_region; } + // Set or get the set of touch actions allowed across each point of this + // layer. The |touch_action_region| can specify, for any number of areas, + // which touch actions are allowed in each area. The result is the + // intersection of overlapping areas. These allowed actions control if + // a touch event can initiate a scroll or zoom on the compositor thread. void SetTouchActionRegion(TouchActionRegion touch_action_region); const TouchActionRegion& touch_action_region() const { return inputs_.touch_action_region; } + // Sets a RepeatingCallback that is run during a main frame, before layers are + // asked to prepare content with Update(), if the scroll offset for the layer + // was changed by the InputHandlerClient, on the compositor thread (or on the + // main thread in single-thread mode). It may be set to a null callback, in + // which case nothing is called. void set_did_scroll_callback( - base::Callback<void(const gfx::ScrollOffset&, const ElementId&)> + base::RepeatingCallback<void(const gfx::ScrollOffset&, const ElementId&)> callback) { inputs_.did_scroll_callback = std::move(callback); } + // Set or get if the layer and its subtree should be cached as a texture in + // the display compositor. This is used as an optimization when it is known + // that the layer will be animated without changing its content, or any of its + // subtree. + // + // Note that this also disables occlusion culling, as the entire texture will + // be drawn so that it is not left with incomplete areas. This should only be + // used when paying the cost of creating an intermediate texture is worth it, + // even when the layer's subtree may be occluded, or not visible in the final + // output. void SetCacheRenderSurface(bool cache_render_surface); bool cache_render_surface() const { return cache_render_surface_; } + // Set or get if the layer and its subtree will be drawn through an + // intermediate texture, called a RenderSurface. This mimics the need + // for a RenderSurface that is caused by compositing effects such as masks + // without needing to set up such effects. void SetForceRenderSurfaceForTesting(bool force_render_surface); bool force_render_surface_for_testing() const { return force_render_surface_for_testing_; } - const gfx::ScrollOffset& CurrentScrollOffset() const { - return inputs_.scroll_offset; - } - + // Set or get if this layer should continue to be visible when rotated such + // that its back face is facing toward the camera. If false, the layer will + // disappear when its back face is visible, but if true, the mirror image of + // its front face will be shown. For instance, with a 180deg rotation around + // the middle of the layer on the Y axis, if this is false then nothing is + // visible. But if true, the layer is seen with its contents flipped along the + // Y axis. Being single-sided applies transitively to the subtree of this + // layer. If it is hidden because of its back face being visible, then its + // subtree will be too (even if a subtree layer's front face would have been + // visible). + // + // Note that should_check_backface_visibility() is the final computed value + // for back face visibility, which is only for internal use. void SetDoubleSided(bool double_sided); bool double_sided() const { return inputs_.double_sided; } - void SetShouldFlattenTransform(bool flatten); - bool should_flatten_transform() const { - return inputs_.should_flatten_transform; - } - - bool Is3dSorted() const { return inputs_.sorting_context_id != 0; } - + // Set or get if SetDoubleSided() for this layer should be ignored and + // inherited directly from this layer's parent instead. Used to attach this + // layer's backface visibility to the value of its parent. + // + // Note that should_check_backface_visibility() is the final computed value + // for back face visibility, which is only for internal use. void SetUseParentBackfaceVisibility(bool use); bool use_parent_backface_visibility() const { return inputs_.use_parent_backface_visibility; } - void SetShouldCheckBackfaceVisibility(bool should_check_backface_visibility); - bool should_check_backface_visibility() const { - return should_check_backface_visibility_; + // Set or get if the subtree of this layer is composited in 3d-space, or if + // the layers are flattened into the plane of this layer. This supports the + // transform-style CSS property. + void SetShouldFlattenTransform(bool flatten); + bool should_flatten_transform() const { + return inputs_.should_flatten_transform; } - virtual void SetLayerTreeHost(LayerTreeHost* host); + // Set or get a 3d sorting context for this layer, where adjacent layers (in a + // pre-order traversal) with the same id are sorted as a group and may occlude + // each other based on their z-position, including intersecting each other and + // each occluding the other layer partially. Layers in different sorting + // contexts will be composited and occlude in tree order (children occlude + // ancestors and earlier siblings in the children list). If the |id| is 0, + // then the layer is not part of any sorting context, and is always composited + // in tree order. + void Set3dSortingContextId(int id); + int sorting_context_id() const { return inputs_.sorting_context_id; } // When true the layer may contribute to the compositor's output. When false, // it does not. This property does not apply to children of the layer, they @@ -327,61 +526,162 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // if it has content to contribute, but when false, this prevents it from // doing so. void SetIsDrawable(bool is_drawable); - - void SetHideLayerAndSubtree(bool hide); - bool hide_layer_and_subtree() const { return inputs_.hide_layer_and_subtree; } - - void SetFiltersOrigin(const gfx::PointF& origin); - gfx::PointF filters_origin() const { return inputs_.filters_origin; } - - int NumDescendantsThatDrawContent() const; - // Is true if the layer will contribute content to the compositor's output. // Will be false if SetIsDrawable(false) is called. But will also be false if // the layer itself has no content to contribute, even though the layer was // given SetIsDrawable(true). - // This is only virtual for tests. - // TODO(awoloszyn): Remove this once we no longer need it for tests - virtual bool DrawsContent() const; + bool DrawsContent() const; - // This methods typically need to be overwritten by derived classes. - // Returns true iff anything was updated that needs to be committed. - virtual bool Update(); - virtual void SetLayerMaskType(Layer::LayerMaskType type) {} - virtual bool HasSlowPaths() const; - virtual bool HasNonAAPaint() const; + // Returns the number of layers in this layers subtree (excluding itself) for + // which DrawsContent() is true. + int NumDescendantsThatDrawContent() const; - void UpdateDebugInfo(); + // Set or get if this layer and its subtree should be part of the compositor's + // output to the screen. When set to true, the layer's subtree does not appear + // to the user, but still remains part of the tree with all its normal drawing + // properties. This can be used to execute a CopyOutputRequest on this layer + // or another in its subtree, since the layers are still able to be drawn by + // the compositor, while not being composed into the result shown to the user. + void SetHideLayerAndSubtree(bool hide); + bool hide_layer_and_subtree() const { return inputs_.hide_layer_and_subtree; } - void SetLayerClient(base::WeakPtr<LayerClient> client); - LayerClient* GetLayerClientForTesting() const { return inputs_.client.get(); } + // The index of this layer's node in the various property trees. These are + // only valid after a main frame, when Update() is called on the layer, and + // remain valid and in in the same state until the next main frame, or until + // the layer is removed from its LayerTreeHost. Otherwise kInvalidNodeId is + // returned. + int transform_tree_index() const; + int clip_tree_index() const; + int effect_tree_index() const; + int scroll_tree_index() const; - virtual bool IsSnapped(); + // While all layers have an index into the transform tree, this value + // indicates whether the transform tree node was created for this layer. + void SetHasTransformNode(bool val) { has_transform_node_ = val; } + bool has_transform_node() { return has_transform_node_; } - virtual void PushPropertiesTo(LayerImpl* layer); + // Sets that the content shown in this layer may be a video. This may be used + // by the system compositor to distinguish between animations updating the + // screen and video, which the user would be watching. This allows + // optimizations like turning off the display when video is not playing, + // without interfering with video playback. + void SetMayContainVideo(bool yes); + + // Stable identifier for clients. See comment in cc/trees/element_id.h. + void SetElementId(ElementId id); + ElementId element_id() const { return inputs_.element_id; } - LayerTreeHost* GetLayerTreeHostForTesting() const { return layer_tree_host_; } + // Sets or gets a hint that the transform on this layer (including its + // position) may be changed often in the future. The layer may change its + // strategy for generating content as a result. PictureLayers will not attempt + // to raster crisply as the transform changes, allowing the client to trade + // off crisp content at each scale for a smoother visual and cheaper + // animation. + void SetHasWillChangeTransformHint(bool has_will_change); + bool has_will_change_transform_hint() const { + return inputs_.has_will_change_transform_hint; + } - virtual ScrollbarLayerInterface* ToScrollbarLayer(); + // Sets or gets if trilinear filtering should be used to scaling the contents + // of this layer and its subtree. When set the layer and its subtree will be + // composited together as a single unit, mip maps will be generated of the + // subtree together, and trilinear filtering applied when supported, if + // scaling during composite of the content from this layer and its subtree + // into the target. + void SetTrilinearFiltering(bool trilinear_filtering); + bool trilinear_filtering() const { return inputs_.trilinear_filtering; } + // Called on the scroll layer to trigger showing the overlay scrollbars. + void ShowScrollbars() { needs_show_scrollbars_ = true; } + + // For tracing. Gets a recorded rasterization of this layer's contents that + // can be displayed inside representations of this layer. May return null, in + // which case the layer won't be shown with any content in the tracing + // display. virtual sk_sp<SkPicture> GetPicture() const; - // Constructs a LayerImpl of the correct runtime type for this Layer type. + // For tracing. Calls out to the LayerClient to get tracing data that will + // be attached to this layer's tracing outputs under the 'debug_info' key. + void UpdateDebugInfo(); + + // For telemetry testing. Runs a given test behaviour implemented in + // |benchmark| for this layer. The base class does nothing as benchmarks + // only exist for subclass layer types. For each subclass that the + // MicroBenchmark supports, the class should override this method and run the + // |benchmark| against this layer. + virtual void RunMicroBenchmark(MicroBenchmark* benchmark); + + // Internal method to create the compositor thread type for this Layer. + // Subclasses should override this method if they want to return their own + // subclass of LayerImpl instead. virtual std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl); - bool NeedsDisplayForTesting() const { return !inputs_.update_rect.IsEmpty(); } - void ResetNeedsDisplayForTesting() { inputs_.update_rect = gfx::Rect(); } + // Internal method to copy all state from this Layer to the compositor thread. + // Should be overridden by any subclass that has additional state, to copy + // that state as well. The |layer| passed in will be of the type created by + // CreateLayerImpl(), so can be safely down-casted if the subclass uses a + // different type for the compositor thread. + virtual void PushPropertiesTo(LayerImpl* layer); - // Mark the layer as needing to push its properties to the LayerImpl during - // commit. - void SetNeedsPushProperties(); - void ResetNeedsPushPropertiesForTesting(); + // Internal method to be overridden by Layer subclasses that need to do work + // during a main frame. The method should compute any state that will need to + // propogated to the compositor thread for the next commit, and return true + // if there is anything new to commit. If all layers return false, the commit + // may be aborted. + virtual bool Update(); + // Internal method to be overriden by Layer subclasses that override Update() + // and require rasterization. After Update() is called, this is immediately + // called, and should return whether the layer will require rasterization of + // paths that will be difficult/slow to raster. Only layers that do + // rasterization via TileManager need to override this, other layers that have + // content generated in other ways may leave it as the default. + virtual bool HasSlowPaths() const; + // Internal method to be overriden by Layer subclasses that override Update() + // and require rasterization. After Update() is called, this is immediately + // called, and should return whether the layer will require rasterization of a + // drawing operation that must not be anti-aliased. In this case using MSAA to + // antialias the entire layer's content would produce an incorrect result. + // This result is considered sticky, once a layer returns true, so false + // positives should be avoided. Only layers that do rasterization via + // TileManager need to override this, other layers that have content generated + // in other ways may leave it as the default. + virtual bool HasNonAAPaint() const; - virtual void RunMicroBenchmark(MicroBenchmark* benchmark); + // Internal to property tree construction. This allows a layer to request that + // its transform should be snapped such that the layer aligns with the pixel + // grid in its rendering target. This ensures that the layer is not fuzzy + // (unless it is being scaled). Layers may override this to return true, by + // default layers are not snapped. + virtual bool IsSnappedToPixelGridInTarget(); + + // Internal method that is called when a Layer is attached to a LayerTreeHost. + // This would happen when + // a) the Layer is added to an existing Layer tree that is attached to a + // LayerTreeHost. + // b) the Layer is made the root layer of a LayerTreeHost. + // c) the Layer is part of a Layer tree, and an ancestor is attached to a + // LayerTreeHost via a) or b). + // The |host| is the new LayerTreeHost which the Layer is now attached to. + // Subclasses may override this if they have data or resources which are + // specific to a LayerTreeHost that should be updated or reset. After this + // returns the Layer will hold a pointer to the new LayerTreeHost. + virtual void SetLayerTreeHost(LayerTreeHost* host); - void Set3dSortingContextId(int id); - int sorting_context_id() const { return inputs_.sorting_context_id; } + // Internal method to mark this layer as needing to push its state to the + // compositor thread during the next commit. The PushPropertiesTo() method + // will be called for this layer during the next commit only if this method + // was called before it. + void SetNeedsPushProperties(); + + // Internal method to call the LayerClient, if there is one, to inform it when + // overlay scrollbars have been completely hidden (due to lack of scrolling by + // the user). + void SetScrollbarsHiddenFromImplSide(bool hidden); + // Internal to property tree construction. A generation number for the + // property trees, to verify the layer's indices are pointers into the trees + // currently held by the LayerTreeHost. The number is updated when property + // trees are built from the Layer tree. void set_property_tree_sequence_number(int sequence_number) { property_tree_sequence_number_ = sequence_number; } @@ -389,91 +689,74 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { return property_tree_sequence_number_; } + // Internal to property tree construction. Sets the index for this Layer's + // node in each property tree. void SetTransformTreeIndex(int index); - int transform_tree_index() const; - void SetClipTreeIndex(int index); - int clip_tree_index() const; - void SetEffectTreeIndex(int index); - int effect_tree_index() const; - void SetScrollTreeIndex(int index); - int scroll_tree_index() const; - void set_offset_to_transform_parent(gfx::Vector2dF offset) { - if (offset_to_transform_parent_ == offset) - return; - offset_to_transform_parent_ = offset; - SetNeedsPushProperties(); - } + // Internal to property tree construction. Set or get the position of this + // layer relative to the origin after transforming according to this layer's + // index into the transform tree. This translation is appended to the + // transform that comes from the transform tree for this layer. + void SetOffsetToTransformParent(gfx::Vector2dF offset); gfx::Vector2dF offset_to_transform_parent() const { return offset_to_transform_parent_; } - // TODO(enne): This needs a different name. It is a calculated value - // from the property tree builder and not a synonym for "should - // flatten transform". - void set_should_flatten_transform_from_property_tree(bool should_flatten) { - if (should_flatten_transform_from_property_tree_ == should_flatten) - return; - should_flatten_transform_from_property_tree_ = should_flatten; - SetNeedsPushProperties(); - } - bool should_flatten_transform_from_property_tree() const { - return should_flatten_transform_from_property_tree_; - } - - const gfx::Rect& visible_layer_rect_for_testing() const { - return visible_layer_rect_; - } - void set_visible_layer_rect(const gfx::Rect& rect) { - visible_layer_rect_ = rect; - } - - // This is for tracking damage. + // Internal to property tree construction. Indicates that a property changed + // on this layer that may affect the position or content of all layers in this + // layer's subtree, including itself. This causes the layer's subtree to be + // considered damaged and re-displayed to the user. void SetSubtreePropertyChanged(); bool subtree_property_changed() const { return subtree_property_changed_; } - void SetMayContainVideo(bool yes); - - 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; } - - bool HasTickingAnimationForTesting() const; - - void SetHasWillChangeTransformHint(bool has_will_change); - bool has_will_change_transform_hint() const { - return inputs_.has_will_change_transform_hint; - } - - void SetTrilinearFiltering(bool trilinear_filtering); - bool trilinear_filtering() const { return inputs_.trilinear_filtering; } - - MutatorHost* GetMutatorHost() const; - + // Internal to property tree construction. Returns ElementListType::ACTIVE + // as main thread layers do not have a pending/active tree split, and + // animations should run normally on the main thread layer tree. ElementListType GetElementTypeForAnimation() const; - void SetScrollbarsHiddenFromImplSide(bool hidden); - - const gfx::Rect& update_rect() const { return inputs_.update_rect; } + // Internal to property tree construction. Whether this layer may animate its + // opacity on the compositor thread. Layer subclasses may override this to + // report true. If true, assumptions about opacity can not be made on the main + // thread. + virtual bool OpacityCanAnimateOnImplThread() const; - LayerTreeHost* layer_tree_host() const { return layer_tree_host_; } + // Internal to property tree construction. Set to true if this layer or any + // layer below it in the tree has a CopyOutputRequest pending commit. + void SetSubtreeHasCopyRequest(bool subtree_has_copy_request); + // Internal to property tree construction. Returns true if this layer or any + // layer below it in the tree has a CopyOutputRequest pending commit. + bool SubtreeHasCopyRequest() const; + // Internal to property tree construction. Removes all CopyOutputRequests from + // this layer, moving them into |requests|. + void TakeCopyRequests( + std::vector<std::unique_ptr<viz::CopyOutputRequest>>* requests); - // Called on the scroll layer to trigger showing the overlay scrollbars. - void ShowScrollbars() { needs_show_scrollbars_ = true; } + // Internal to property tree construction. Set if the layer should not be + // shown when its back face is visible to the user. This is a derived value + // from SetDoubleSided() and SetUseParentBackfaceVisibility(). + void SetShouldCheckBackfaceVisibility(bool should_check_backface_visibility); + bool should_check_backface_visibility() const { + return should_check_backface_visibility_; + } - bool has_transform_node() { return has_transform_node_; } - void SetHasTransformNode(bool val) { has_transform_node_ = val; } + // Internal to property tree construction. The value here derives from + // should_flatten_transform() along with other state, and is for internal use + // in order to flatten the layer's ScreenSpaceTransform() in cases where the + // property tree did not handle it. + void SetShouldFlattenScreenSpaceTransformFromPropertyTree(bool should); + bool should_flatten_screen_space_transform_from_property_tree() const { + return should_flatten_screen_space_transform_from_property_tree_; + } protected: friend class LayerImpl; friend class TreeSynchronizer; - virtual ~Layer(); + Layer(); + virtual ~Layer(); // These SetNeeds functions are in order of severity of update: // @@ -495,11 +778,18 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // Will recalculate whether the layer draws content and set draws_content_ // appropriately. void UpdateDrawsContent(bool has_drawable_content); + // May be overridden by subclasses if they have optional content, to return + // false if there is no content to be displayed. If they do have content, then + // they should return the value from this base class method. virtual bool HasDrawableContent() const; // Called when the layer's number of drawable descendants changes. void AddDrawableDescendants(int num); + // For debugging. Returns false if the LayerTreeHost this layer is attached to + // is in the process of updating layers for a BeginMainFrame. Layer properties + // should be changed by the client before the BeginMainFrame, and should not + // be changed while the frame is being generated for commit. bool IsPropertyChangeAllowed() const; // When true, the layer is about to perform an update. Any commit requests @@ -516,8 +806,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void OnOpacityAnimated(float opacity); void OnTransformAnimated(const gfx::Transform& transform); - bool ScrollOffsetAnimationWasInterrupted() const; - void AddClipChild(Layer* child); void RemoveClipChild(Layer* child); @@ -557,17 +845,12 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { LayerList children; - // The update rect is the region of the compositor resource that was - // actually updated by the compositor. For layers that may do updating - // outside the compositor's control (i.e. plugin layers), this information - // is not available and the update rect will remain empty. - // Note this rect is in layer space (not content space). gfx::Rect update_rect; gfx::Size bounds; bool masks_to_bounds; - scoped_refptr<Layer> mask_layer; + scoped_refptr<PictureLayer> mask_layer; float opacity; SkBlendMode blend_mode; @@ -649,7 +932,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { base::WeakPtr<LayerClient> client; std::unique_ptr<base::trace_event::TracedValue> debug_info; - base::Callback<void(const gfx::ScrollOffset&, const ElementId&)> + base::RepeatingCallback<void(const gfx::ScrollOffset&, const ElementId&)> did_scroll_callback; std::vector<std::unique_ptr<viz::CopyOutputRequest>> copy_requests; @@ -674,7 +957,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { int scroll_tree_index_; int property_tree_sequence_number_; gfx::Vector2dF offset_to_transform_parent_; - bool should_flatten_transform_from_property_tree_ : 1; + bool should_flatten_screen_space_transform_from_property_tree_ : 1; bool draws_content_ : 1; bool should_check_backface_visibility_ : 1; // Force use of and cache render surface. @@ -683,9 +966,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { bool subtree_property_changed_ : 1; bool may_contain_video_ : 1; bool needs_show_scrollbars_ : 1; - // Whether the nodes referred to by *_tree_index_ "belong" to this - // layer. Only applicable if LayerTreeSettings.use_layer_lists is - // false. bool has_transform_node_ : 1; // This value is valid only when LayerTreeHost::has_copy_request() is true bool subtree_has_copy_request_ : 1; @@ -693,10 +973,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { std::unique_ptr<std::set<Layer*>> clip_children_; - // These all act like draw properties, so don't need push properties. - gfx::Rect visible_layer_rect_; - size_t num_unclipped_descendants_; - DISALLOW_COPY_AND_ASSIGN(Layer); }; diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index 2eee6d48f5a..e5dad6df494 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -23,7 +23,6 @@ #include "cc/input/main_thread_scrolling_reason.h" #include "cc/input/scroll_state.h" #include "cc/layers/layer.h" -#include "cc/resources/layer_tree_resource_provider.h" #include "cc/trees/clip_node.h" #include "cc/trees/draw_property_utils.h" #include "cc/trees/effect_node.h" @@ -34,6 +33,7 @@ #include "cc/trees/proxy.h" #include "cc/trees/scroll_node.h" #include "cc/trees/transform_node.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/quads/debug_border_draw_quad.h" #include "components/viz/common/quads/render_pass.h" @@ -52,7 +52,7 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) main_thread_scrolling_reasons_( MainThreadScrollingReason::kNotScrollingOnMain), scrollable_(false), - should_flatten_transform_from_property_tree_(false), + should_flatten_screen_space_transform_from_property_tree_(false), layer_property_changed_not_from_property_trees_(false), layer_property_changed_from_property_trees_(false), may_contain_video_(false), @@ -74,7 +74,6 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) current_draw_mode_(DRAW_MODE_NONE), debug_info_(nullptr), has_will_change_transform_hint_(false), - trilinear_filtering_(false), needs_push_properties_(false), scrollbars_hidden_(false), needs_show_scrollbars_(false), @@ -84,15 +83,14 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) DCHECK(layer_tree_impl_); layer_tree_impl_->RegisterLayer(this); - layer_tree_impl_->AddToElementMap(this); + layer_tree_impl_->AddToElementLayerList(element_id_); SetNeedsPushProperties(); } LayerImpl::~LayerImpl() { - DCHECK_EQ(DRAW_MODE_NONE, current_draw_mode_); layer_tree_impl_->UnregisterLayer(this); - layer_tree_impl_->RemoveFromElementMap(this); + layer_tree_impl_->RemoveFromElementLayerList(element_id_); TRACE_EVENT_OBJECT_DELETED_WITH_ID( TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::LayerImpl", this); } @@ -101,14 +99,6 @@ void LayerImpl::SetHasWillChangeTransformHint(bool has_will_change) { has_will_change_transform_hint_ = has_will_change; } -void LayerImpl::SetTrilinearFiltering(bool trilinear_filtering) { - trilinear_filtering_ = trilinear_filtering; -} - -MutatorHost* LayerImpl::GetMutatorHost() const { - return layer_tree_impl_ ? layer_tree_impl_->mutator_host() : nullptr; -} - ElementListType LayerImpl::GetElementTypeForAnimation() const { return IsActive() ? ElementListType::ACTIVE : ElementListType::PENDING; } @@ -175,16 +165,18 @@ void LayerImpl::PopulateScaledSharedQuadState(viz::SharedQuadState* state, } bool LayerImpl::WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) { - // WillDraw/DidDraw must be matched. - DCHECK_NE(DRAW_MODE_NONE, draw_mode); - DCHECK_EQ(DRAW_MODE_NONE, current_draw_mode_); + viz::ClientResourceProvider* resource_provider) { + if (visible_layer_rect().IsEmpty() || + draw_properties().occlusion_in_content_space.IsOccluded( + visible_layer_rect())) { + return false; + } + current_draw_mode_ = draw_mode; return true; } -void LayerImpl::DidDraw(LayerTreeResourceProvider* resource_provider) { - DCHECK_NE(DRAW_MODE_NONE, current_draw_mode_); +void LayerImpl::DidDraw(viz::ClientResourceProvider* resource_provider) { current_draw_mode_ = DRAW_MODE_NONE; } @@ -294,8 +286,8 @@ std::unique_ptr<LayerImpl> LayerImpl::CreateLayerImpl( return LayerImpl::Create(tree_impl, layer_id_); } -bool LayerImpl::IsSnapped() { - return scrollable(); +bool LayerImpl::IsSnappedToPixelGridInTarget() { + return false; } void LayerImpl::PushPropertiesTo(LayerImpl* layer) { @@ -309,8 +301,8 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->has_transform_node_ = has_transform_node_; layer->offset_to_transform_parent_ = offset_to_transform_parent_; layer->main_thread_scrolling_reasons_ = main_thread_scrolling_reasons_; - layer->should_flatten_transform_from_property_tree_ = - should_flatten_transform_from_property_tree_; + layer->should_flatten_screen_space_transform_from_property_tree_ = + should_flatten_screen_space_transform_from_property_tree_; layer->masks_to_bounds_ = masks_to_bounds_; layer->contents_opaque_ = contents_opaque_; layer->may_contain_video_ = may_contain_video_; @@ -321,6 +313,7 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { hit_testable_without_draws_content_; layer->non_fast_scrollable_region_ = non_fast_scrollable_region_; layer->touch_action_region_ = touch_action_region_; + layer->wheel_event_handler_region_ = wheel_event_handler_region_; layer->background_color_ = background_color_; layer->safe_opaque_background_color_ = safe_opaque_background_color_; layer->position_ = position_; @@ -329,7 +322,6 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->clip_tree_index_ = clip_tree_index_; layer->scroll_tree_index_ = scroll_tree_index_; layer->has_will_change_transform_hint_ = has_will_change_transform_hint_; - layer->trilinear_filtering_ = trilinear_filtering_; layer->scrollbars_hidden_ = scrollbars_hidden_; if (needs_show_scrollbars_) layer->needs_show_scrollbars_ = needs_show_scrollbars_; @@ -421,6 +413,11 @@ std::unique_ptr<base::DictionaryValue> LayerImpl::LayerAsJson() { result->Set("TouchRegion", std::move(region)); } + if (!wheel_event_handler_region_.IsEmpty()) { + std::unique_ptr<base::Value> region = wheel_event_handler_region_.AsValue(); + result->Set("WheelRegion", std::move(region)); + } + return result; } @@ -476,7 +473,7 @@ void LayerImpl::NoteLayerPropertyChangedFromPropertyTrees() { void LayerImpl::ValidateQuadResourcesInternal(viz::DrawQuad* quad) const { #if DCHECK_IS_ON() - const LayerTreeResourceProvider* resource_provider = + const viz::ClientResourceProvider* resource_provider = layer_tree_impl_->resource_provider(); for (viz::ResourceId resource_id : quad->resources) resource_provider->ValidateResource(resource_id); @@ -643,9 +640,9 @@ void LayerImpl::SetElementId(ElementId element_id) { TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "LayerImpl::SetElementId", "element", element_id.AsValue().release()); - layer_tree_impl_->RemoveFromElementMap(this); + layer_tree_impl_->RemoveFromElementLayerList(element_id_); element_id_ = element_id; - layer_tree_impl_->AddToElementMap(this); + layer_tree_impl_->AddToElementLayerList(element_id_); SetNeedsPushProperties(); } @@ -683,6 +680,10 @@ void LayerImpl::DidBeginTracing() {} void LayerImpl::ReleaseResources() {} +void LayerImpl::OnPurgeMemory() { + ReleaseResources(); +} + void LayerImpl::ReleaseTileResources() {} void LayerImpl::RecreateTileResources() {} @@ -753,6 +754,11 @@ void LayerImpl::AsValueInto(base::trace_event::TracedValue* state) const { touch_action_region_.region().AsValueInto(state); state->EndArray(); } + if (!wheel_event_handler_region_.IsEmpty()) { + state->BeginArray("wheel_event_handler_region"); + wheel_event_handler_region_.AsValueInto(state); + state->EndArray(); + } if (!non_fast_scrollable_region_.IsEmpty()) { state->BeginArray("non_fast_scrollable_region"); non_fast_scrollable_region_.AsValueInto(state); @@ -765,8 +771,6 @@ void LayerImpl::AsValueInto(base::trace_event::TracedValue* state) const { state->SetBoolean("has_will_change_transform_hint", has_will_change_transform_hint()); - state->SetBoolean("trilinear_filtering", trilinear_filtering()); - MainThreadScrollingReason::AddToTracedValue(main_thread_scrolling_reasons_, *state); diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h index d130a695d45..8a2f9a68c15 100644 --- a/chromium/cc/layers/layer_impl.h +++ b/chromium/cc/layers/layer_impl.h @@ -33,7 +33,6 @@ #include "cc/layers/touch_action_region.h" #include "cc/tiles/tile_priority.h" #include "cc/trees/element_id.h" -#include "cc/trees/mutator_host_client.h" #include "cc/trees/target_property.h" #include "components/viz/common/quads/shared_quad_state.h" #include "third_party/skia/include/core/SkColor.h" @@ -51,6 +50,7 @@ class DictionaryValue; } namespace viz { +class ClientResourceProvider; class RenderPass; } @@ -58,9 +58,7 @@ namespace cc { class AppendQuadsData; class LayerTreeImpl; -class LayerTreeResourceProvider; class MicroBenchmarkImpl; -class MutatorHost; class PrioritizedTile; class ScrollbarLayerImplBase; class SimpleEnclosedRegion; @@ -107,18 +105,19 @@ class CC_EXPORT LayerImpl { void SetScrollTreeIndex(int index); int scroll_tree_index() const { return scroll_tree_index_; } - void set_offset_to_transform_parent(const gfx::Vector2dF& offset) { + void SetOffsetToTransformParent(const gfx::Vector2dF& offset) { offset_to_transform_parent_ = offset; } gfx::Vector2dF offset_to_transform_parent() const { return offset_to_transform_parent_; } - void set_should_flatten_transform_from_property_tree(bool should_flatten) { - should_flatten_transform_from_property_tree_ = should_flatten; + void SetShouldFlattenScreenSpaceTransformFromPropertyTree( + bool should_flatten) { + should_flatten_screen_space_transform_from_property_tree_ = should_flatten; } - bool should_flatten_transform_from_property_tree() const { - return should_flatten_transform_from_property_tree_; + bool should_flatten_screen_space_transform_from_property_tree() const { + return should_flatten_screen_space_transform_from_property_tree_; } bool is_clipped() const { return draw_properties_.is_clipped; } @@ -134,14 +133,12 @@ class CC_EXPORT LayerImpl { // WillDraw must be called before AppendQuads. If WillDraw returns false, // AppendQuads and DidDraw will not be called. If WillDraw returns true, // DidDraw is guaranteed to be called before another WillDraw or before - // the layer is destroyed. To enforce this, any class that overrides - // WillDraw/DidDraw must call the base class version only if WillDraw - // returns true. + // the layer is destroyed. virtual bool WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider); + viz::ClientResourceProvider* resource_provider); virtual void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) {} - virtual void DidDraw(LayerTreeResourceProvider* resource_provider); + virtual void DidDraw(viz::ClientResourceProvider* resource_provider); // Verify that the resource ids in the quad are valid. void ValidateQuadResources(viz::DrawQuad* quad) const { @@ -332,6 +329,16 @@ class CC_EXPORT LayerImpl { return touch_action_region_; } + // Set or get the region that contains wheel event handler. + // The |wheel_event_handler_region| specify the area where wheel event handler + // could block impl scrolling. + void SetWheelEventHandlerRegion(const Region& wheel_event_handler_region) { + wheel_event_handler_region_ = wheel_event_handler_region; + } + const Region& wheel_event_handler_region() const { + return wheel_event_handler_region_; + } + // Note this rect is in layer space (not content space). void SetUpdateRect(const gfx::Rect& update_rect); const gfx::Rect& update_rect() const { return update_rect_; } @@ -365,8 +372,13 @@ class CC_EXPORT LayerImpl { // that rendered this layer was lost. virtual void ReleaseResources(); + // Releases resources in response to memory pressure. The default + // implementation just calls ReleaseResources() and subclasses will override + // if that's not appropriate. + virtual void OnPurgeMemory(); + // Release tile resources held by this layer. Called when a rendering mode - // switch has occured and tiles are no longer valid. + // switch has occurred and tiles are no longer valid. virtual void ReleaseTileResources(); // Recreate tile resources held by this layer after they were released by a @@ -374,9 +386,13 @@ class CC_EXPORT LayerImpl { virtual void RecreateTileResources(); virtual std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl); - virtual bool IsSnapped(); virtual void PushPropertiesTo(LayerImpl* layer); + // Internal to property tree construction (which only happens in tests on a + // LayerImpl tree. See Layer::IsSnappedToPixelGridInTarget() for explanation, + // as this mirrors that method. + virtual bool IsSnappedToPixelGridInTarget(); + virtual void GetAllPrioritizedTilesForTracing( std::vector<PrioritizedTile>* prioritized_tiles) const; virtual void AsValueInto(base::trace_event::TracedValue* dict) const; @@ -427,11 +443,6 @@ class CC_EXPORT LayerImpl { return has_will_change_transform_hint_; } - void SetTrilinearFiltering(bool trilinear_filtering); - bool trilinear_filtering() const { return trilinear_filtering_; } - - MutatorHost* GetMutatorHost() const; - ElementListType GetElementTypeForAnimation() const; void set_needs_show_scrollbars(bool yes) { needs_show_scrollbars_ = yes; } @@ -493,7 +504,7 @@ class CC_EXPORT LayerImpl { // |scroll_container_bounds|). bool scrollable_ : 1; - bool should_flatten_transform_from_property_tree_ : 1; + bool should_flatten_screen_space_transform_from_property_tree_ : 1; // Tracks if drawing-related properties have changed since last redraw. // TODO(wutao): We want to distinquish the sources of change so that we can @@ -525,6 +536,7 @@ class CC_EXPORT LayerImpl { Region non_fast_scrollable_region_; TouchActionRegion touch_action_region_; + Region wheel_event_handler_region_; SkColor background_color_; SkColor safe_opaque_background_color_; @@ -566,7 +578,6 @@ class CC_EXPORT LayerImpl { base::trace_event::TracedValue* debug_info_; bool has_will_change_transform_hint_ : 1; - bool trilinear_filtering_ : 1; bool needs_push_properties_ : 1; bool scrollbars_hidden_ : 1; diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc index 52977e8b2b2..551eced4977 100644 --- a/chromium/cc/layers/layer_impl_unittest.cc +++ b/chromium/cc/layers/layer_impl_unittest.cc @@ -122,7 +122,7 @@ TEST(LayerImplTest, VerifyPendingLayerChangesAreTrackedProperly) { FakeLayerTreeFrameSink::Create3d(); FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); host_impl.CreatePendingTree(); std::unique_ptr<LayerImpl> root_ptr = LayerImpl::Create(host_impl.pending_tree(), 2); @@ -208,7 +208,7 @@ TEST(LayerImplTest, VerifyActiveLayerChangesAreTrackedProperly) { FakeLayerTreeFrameSink::Create3d(); FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); std::unique_ptr<LayerImpl> root_ptr = LayerImpl::Create(host_impl.active_tree(), 2); LayerImpl* root = root_ptr.get(); @@ -285,7 +285,7 @@ TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties) { FakeLayerTreeFrameSink::Create3d(); FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); host_impl.active_tree()->SetRootLayerForTesting( LayerImpl::Create(host_impl.active_tree(), 1)); LayerImpl* root = host_impl.active_tree()->root_layer_for_testing(); @@ -397,7 +397,7 @@ TEST(LayerImplTest, SafeOpaqueBackgroundColor) { FakeLayerTreeFrameSink::Create3d(); FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); host_impl.active_tree()->SetRootLayerForTesting( LayerImpl::Create(host_impl.active_tree(), 1)); LayerImpl* layer = host_impl.active_tree()->root_layer_for_testing(); diff --git a/chromium/cc/layers/layer_list_iterator.cc b/chromium/cc/layers/layer_list_iterator.cc index 1637b842331..7ff7c68ca6e 100644 --- a/chromium/cc/layers/layer_list_iterator.cc +++ b/chromium/cc/layers/layer_list_iterator.cc @@ -36,7 +36,7 @@ static LayerImpl* ChildAt(LayerImpl* layer, int index) { } static Layer* ChildAt(Layer* layer, int index) { - return layer->child_at(index); + return layer->children()[index].get(); } template <typename LayerType> diff --git a/chromium/cc/layers/layer_list_iterator_unittest.cc b/chromium/cc/layers/layer_list_iterator_unittest.cc index 0c984bfc8d1..f3f62fc11f4 100644 --- a/chromium/cc/layers/layer_list_iterator_unittest.cc +++ b/chromium/cc/layers/layer_list_iterator_unittest.cc @@ -207,7 +207,7 @@ TEST(LayerListIteratorTest, VerifyTraversalOrderImpl) { FakeLayerTreeFrameSink::Create3d(); FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); // This test constructs the following tree. // 1 @@ -260,7 +260,7 @@ TEST(LayerListIteratorTest, VerifySingleLayerImpl) { FakeLayerTreeFrameSink::Create3d(); FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); // This test constructs a tree consisting of a single layer. std::unique_ptr<LayerImpl> layer1 = @@ -295,7 +295,7 @@ TEST(LayerListReverseIteratorTest, VerifyTraversalOrderImpl) { FakeLayerTreeFrameSink::Create3d(); FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); // This test constructs the following tree. // 1 @@ -350,7 +350,7 @@ TEST(LayerListReverseIteratorTest, VerifySingleLayerImpl) { FakeLayerTreeFrameSink::Create3d(); FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); // This test constructs a tree consisting of a single layer. std::unique_ptr<LayerImpl> layer1 = diff --git a/chromium/cc/layers/layer_position_constraint_unittest.cc b/chromium/cc/layers/layer_position_constraint_unittest.cc index aa03cc5629d..aa58b7f57af 100644 --- a/chromium/cc/layers/layer_position_constraint_unittest.cc +++ b/chromium/cc/layers/layer_position_constraint_unittest.cc @@ -20,20 +20,6 @@ namespace cc { namespace { -class LayerWithForcedDrawsContent : public Layer { - public: - LayerWithForcedDrawsContent() = default; - - bool DrawsContent() const override; - - private: - ~LayerWithForcedDrawsContent() override = default; -}; - -bool LayerWithForcedDrawsContent::DrawsContent() const { - return true; -} - void SetLayerPropertiesForTesting(Layer* layer, const gfx::Transform& transform, const gfx::Point3F& transform_origin, @@ -94,9 +80,10 @@ class LayerPositionConstraintTest : public testing::Test { outer_viewport_container_layer_ = Layer::Create(); child_transform_layer_ = Layer::Create(); child_ = Layer::Create(); - grand_child_ = base::WrapRefCounted(new LayerWithForcedDrawsContent()); - great_grand_child_ = - base::WrapRefCounted(new LayerWithForcedDrawsContent()); + grand_child_ = Layer::Create(); + grand_child_->SetIsDrawable(true); + great_grand_child_ = Layer::Create(); + great_grand_child_->SetIsDrawable(true); gfx::Transform IdentityMatrix; gfx::Point3F transform_origin; @@ -569,8 +556,8 @@ TEST_F(LayerPositionConstraintTest, // transform. // Add one more layer to the test tree for this scenario. - scoped_refptr<Layer> fixed_position_child = - base::WrapRefCounted(new LayerWithForcedDrawsContent()); + scoped_refptr<Layer> fixed_position_child = Layer::Create(); + fixed_position_child->SetIsDrawable(true); SetLayerPropertiesForTesting(fixed_position_child.get(), gfx::Transform(), gfx::Point3F(), gfx::PointF(), gfx::Size(100, 100), true); @@ -729,8 +716,8 @@ TEST_F( // surfaces is accumulated properly in the final matrix transform. // Add one more layer to the test tree for this scenario. - scoped_refptr<Layer> fixed_position_child = - base::WrapRefCounted(new LayerWithForcedDrawsContent()); + scoped_refptr<Layer> fixed_position_child = Layer::Create(); + fixed_position_child->SetIsDrawable(true); SetLayerPropertiesForTesting(fixed_position_child.get(), gfx::Transform(), gfx::Point3F(), gfx::PointF(), gfx::Size(100, 100), true); @@ -1066,8 +1053,8 @@ TEST_F(LayerPositionConstraintTest, // would still have to compensate with respect to its container. // Add one more layer to the hierarchy for this test. - scoped_refptr<Layer> great_great_grand_child = - base::WrapRefCounted(new LayerWithForcedDrawsContent()); + scoped_refptr<Layer> great_great_grand_child = Layer::Create(); + great_great_grand_child->SetIsDrawable(true); great_grand_child_->AddChild(great_great_grand_child); child_->SetIsContainerForFixedPositionLayers(true); @@ -1122,8 +1109,8 @@ TEST_F(LayerPositionConstraintTest, ScrollCompensationForOuterViewportBoundsDelta) { // This test checks for correct scroll compensation when the fixed-position // container is the outer viewport scroll layer and has non-zero bounds delta. - scoped_refptr<Layer> fixed_child = - base::WrapRefCounted(new LayerWithForcedDrawsContent()); + scoped_refptr<Layer> fixed_child = Layer::Create(); + fixed_child->SetIsDrawable(true); fixed_child->SetBounds(gfx::Size(300, 300)); child_->AddChild(fixed_child); fixed_child->SetPositionConstraint(fixed_to_top_left_); diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index 594552cc344..d8c5f9616d4 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -13,8 +13,10 @@ #include "cc/base/math_util.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/picture_layer.h" #include "cc/layers/solid_color_scrollbar_layer.h" #include "cc/test/animation_test_common.h" +#include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_impl_task_runner_provider.h" #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_layer_tree_host_client.h" @@ -115,6 +117,10 @@ class MockLayerTreeHost : public LayerTreeHost { MOCK_METHOD0(SetNeedsFullTreeSync, void()); }; +bool LayerNeedsDisplay(Layer* layer) { + return !layer->update_rect().IsEmpty(); +} + class LayerTest : public testing::Test { public: LayerTest() @@ -161,6 +167,11 @@ class LayerTest : public testing::Test { animation_host_ = nullptr; } + void SimulateCommitForLayer(Layer* layer) { + layer->PushPropertiesTo( + layer->CreateLayerImpl(host_impl_.active_tree()).get()); + } + void VerifyTestTreeInitialState() const { ASSERT_EQ(3U, parent_->children().size()); EXPECT_EQ(child1_, parent_->children()[0]); @@ -246,7 +257,8 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { scoped_refptr<Layer> child = Layer::Create(); scoped_refptr<Layer> child2 = Layer::Create(); scoped_refptr<Layer> grand_child = Layer::Create(); - scoped_refptr<Layer> dummy_layer1 = Layer::Create(); + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask_layer1 = PictureLayer::Create(&client); layer_tree_host_->SetRootLayer(root); root->AddChild(child); @@ -256,6 +268,18 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { child->SetForceRenderSurfaceForTesting(true); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); child2->SetScrollParent(grand_child.get()); + + // Resizing without a mask layer or masks_to_bounds, should only require a + // regular commit. Note that a layer and its mask should match sizes, but + // the mask isn't in the tree yet, so won't need its own commit. + gfx::Size arbitrary_size = gfx::Size(1, 2); + EXPECT_SET_NEEDS_COMMIT(1, root->SetBounds(arbitrary_size)); + EXPECT_SET_NEEDS_COMMIT(0, mask_layer1->SetBounds(arbitrary_size)); + EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(1); + EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetMaskLayer(mask_layer1.get())); + + // Set up the impl layers after the full tree is constructed, including the + // mask layer. SkBlendMode arbitrary_blend_mode = SkBlendMode::kMultiply; std::unique_ptr<LayerImpl> root_impl = LayerImpl::Create(host_impl_.active_tree(), root->id()); @@ -265,30 +289,21 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { LayerImpl::Create(host_impl_.active_tree(), child2->id()); std::unique_ptr<LayerImpl> grand_child_impl = LayerImpl::Create(host_impl_.active_tree(), grand_child->id()); - std::unique_ptr<LayerImpl> dummy_layer1_impl = - LayerImpl::Create(host_impl_.active_tree(), dummy_layer1->id()); + std::unique_ptr<LayerImpl> mask_layer1_impl = + mask_layer1->CreateLayerImpl(host_impl_.active_tree()); - // Resizing without a mask layer or masks_to_bounds, should only require a - // regular commit. Note that a layer and its mask should match sizes, but - // the mask isn't in the tree yet, so won't need its own commit. - gfx::Size arbitrary_size = gfx::Size(1, 2); - EXPECT_SET_NEEDS_COMMIT(1, root->SetBounds(arbitrary_size)); - EXPECT_SET_NEEDS_COMMIT(0, dummy_layer1->SetBounds(arbitrary_size)); - - EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(1); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetMaskLayer(dummy_layer1.get())); EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( root->PushPropertiesTo(root_impl.get()); child->PushPropertiesTo(child_impl.get()); child2->PushPropertiesTo(child2_impl.get()); grand_child->PushPropertiesTo(grand_child_impl.get()); - dummy_layer1->PushPropertiesTo(dummy_layer1_impl.get())); + mask_layer1->PushPropertiesTo(mask_layer1_impl.get());); // Once there is a mask layer, resizes require subtree properties to update. arbitrary_size = gfx::Size(11, 22); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetBounds(arbitrary_size)); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(dummy_layer1->SetBounds(arbitrary_size)); + EXECUTE_AND_VERIFY_SUBTREE_CHANGED(mask_layer1->SetBounds(arbitrary_size)); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetMasksToBounds(true)); @@ -323,6 +338,22 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { grand_child->PushPropertiesTo(grand_child_impl.get())); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); + EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetTrilinearFiltering(true)); + EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( + root->PushPropertiesTo(root_impl.get()); + child->PushPropertiesTo(child_impl.get()); + child2->PushPropertiesTo(child2_impl.get()); + grand_child->PushPropertiesTo(grand_child_impl.get())); + + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); + EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetTrilinearFiltering(false)); + EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( + root->PushPropertiesTo(root_impl.get()); + child->PushPropertiesTo(child_impl.get()); + child2->PushPropertiesTo(child2_impl.get()); + grand_child->PushPropertiesTo(grand_child_impl.get())); + + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetDoubleSided(false)); EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( root->PushPropertiesTo(root_impl.get()); @@ -351,7 +382,7 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { arbitrary_size = gfx::Size(111, 222); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetBounds(arbitrary_size)); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(dummy_layer1->SetBounds(arbitrary_size)); + EXECUTE_AND_VERIFY_SUBTREE_CHANGED(mask_layer1->SetBounds(arbitrary_size)); EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( root->PushPropertiesTo(root_impl.get()); child->PushPropertiesTo(child_impl.get()); @@ -573,11 +604,11 @@ TEST_F(LayerTest, ReplaceChildWithNewChild) { EXPECT_SET_NEEDS_FULL_TREE_SYNC( AtLeast(1), parent_->ReplaceChild(child2_.get(), child4)); - EXPECT_FALSE(parent_->NeedsDisplayForTesting()); - EXPECT_FALSE(child1_->NeedsDisplayForTesting()); - EXPECT_FALSE(child2_->NeedsDisplayForTesting()); - EXPECT_FALSE(child3_->NeedsDisplayForTesting()); - EXPECT_FALSE(child4->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(parent_.get())); + EXPECT_FALSE(LayerNeedsDisplay(child1_.get())); + EXPECT_FALSE(LayerNeedsDisplay(child2_.get())); + EXPECT_FALSE(LayerNeedsDisplay(child3_.get())); + EXPECT_FALSE(LayerNeedsDisplay(child4.get())); ASSERT_EQ(static_cast<size_t>(3), parent_->children().size()); EXPECT_EQ(child1_, parent_->children()[0]); @@ -634,7 +665,7 @@ TEST_F(LayerTest, DeleteRemovedScrollParent) { EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, child2->RemoveFromParent()); - child1->ResetNeedsPushPropertiesForTesting(); + SimulateCommitForLayer(child1.get()); EXPECT_SET_NEEDS_COMMIT(1, child1->SetScrollParent(nullptr)); EXPECT_TRUE( @@ -688,37 +719,6 @@ TEST_F(LayerTest, RemoveAllChildren) { EXPECT_FALSE(child3_->parent()); } -TEST_F(LayerTest, SetChildren) { - scoped_refptr<Layer> old_parent = Layer::Create(); - scoped_refptr<Layer> new_parent = Layer::Create(); - - scoped_refptr<Layer> child1 = Layer::Create(); - scoped_refptr<Layer> child2 = Layer::Create(); - - LayerList new_children; - new_children.push_back(child1); - new_children.push_back(child2); - - // Set up and verify initial test conditions: child1 has a parent, child2 has - // no parent. - old_parent->AddChild(child1); - ASSERT_EQ(0U, new_parent->children().size()); - EXPECT_EQ(old_parent.get(), child1->parent()); - EXPECT_FALSE(child2->parent()); - - EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, - layer_tree_host_->SetRootLayer(new_parent)); - - EXPECT_SET_NEEDS_FULL_TREE_SYNC( - AtLeast(1), new_parent->SetChildren(new_children)); - - ASSERT_EQ(2U, new_parent->children().size()); - EXPECT_EQ(new_parent.get(), child1->parent()); - EXPECT_EQ(new_parent.get(), child2->parent()); - - EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, layer_tree_host_->SetRootLayer(nullptr)); -} - TEST_F(LayerTest, HasAncestor) { scoped_refptr<Layer> parent = Layer::Create(); EXPECT_FALSE(parent->HasAncestor(parent.get())); @@ -812,44 +812,42 @@ TEST_F(LayerTest, CheckSetNeedsDisplayCausesCorrectBehavior) { gfx::Rect out_of_bounds_dirty_rect = gfx::Rect(400, 405, 500, 502); // Before anything, test_layer should not be dirty. - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); // This is just initialization, but SetNeedsCommit behavior is verified anyway // to avoid warnings. EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBounds(test_bounds)); - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); // The real test begins here. - test_layer->ResetNeedsDisplayForTesting(); - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + SimulateCommitForLayer(test_layer.get()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); // Case 1: Layer should accept dirty rects that go beyond its bounds. - test_layer->ResetNeedsDisplayForTesting(); - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); EXPECT_SET_NEEDS_UPDATE( 1, test_layer->SetNeedsDisplayRect(out_of_bounds_dirty_rect)); - EXPECT_TRUE(test_layer->NeedsDisplayForTesting()); - test_layer->ResetNeedsDisplayForTesting(); + EXPECT_TRUE(LayerNeedsDisplay(test_layer.get())); + SimulateCommitForLayer(test_layer.get()); // Case 2: SetNeedsDisplay() without the dirty rect arg. - test_layer->ResetNeedsDisplayForTesting(); - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); EXPECT_SET_NEEDS_UPDATE(1, test_layer->SetNeedsDisplay()); - EXPECT_TRUE(test_layer->NeedsDisplayForTesting()); - test_layer->ResetNeedsDisplayForTesting(); + EXPECT_TRUE(LayerNeedsDisplay(test_layer.get())); + SimulateCommitForLayer(test_layer.get()); // Case 3: SetNeedsDisplay() with an empty rect. - test_layer->ResetNeedsDisplayForTesting(); - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); EXPECT_SET_NEEDS_COMMIT(0, test_layer->SetNeedsDisplayRect(gfx::Rect())); - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); + SimulateCommitForLayer(test_layer.get()); // Case 4: SetNeedsDisplay() with a non-drawable layer EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetIsDrawable(false)); - test_layer->ResetNeedsDisplayForTesting(); - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + SimulateCommitForLayer(test_layer.get()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); EXPECT_SET_NEEDS_UPDATE(0, test_layer->SetNeedsDisplayRect(dirty_rect)); - EXPECT_TRUE(test_layer->NeedsDisplayForTesting()); + EXPECT_TRUE(LayerNeedsDisplay(test_layer.get())); } TEST_F(LayerTest, TestSettingMainThreadScrollingReason) { @@ -859,7 +857,7 @@ TEST_F(LayerTest, TestSettingMainThreadScrollingReason) { EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetIsDrawable(true)); // sanity check of initial test condition - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); uint32_t reasons = 0, reasons_to_clear = 0, reasons_after_clearing = 0; reasons |= MainThreadScrollingReason::kThreadedScrollingDisabled; @@ -912,10 +910,11 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) { layer_tree_host_->SetRootLayer(test_layer)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetIsDrawable(true)); - scoped_refptr<Layer> dummy_layer1 = Layer::Create(); + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask_layer1 = PictureLayer::Create(&client); // sanity check of initial test condition - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); // Next, test properties that should call SetNeedsCommit (but not // SetNeedsDisplay). All properties need to be set to new values in order for @@ -950,11 +949,11 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) { EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetHideLayerAndSubtree(true)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetElementId(ElementId(2))); - EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, test_layer->SetMaskLayer( - dummy_layer1.get())); + EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, + test_layer->SetMaskLayer(mask_layer1.get())); // The above tests should not have caused a change to the needs_display flag. - EXPECT_FALSE(test_layer->NeedsDisplayForTesting()); + EXPECT_FALSE(LayerNeedsDisplay(test_layer.get())); // As layers are removed from the tree, they will cause a tree sync. EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times((AnyNumber())); @@ -1035,8 +1034,9 @@ TEST_F(LayerTest, PushPropertiesCausesLayerPropertyChangedForOpacity) { TEST_F(LayerTest, MaskHasParent) { scoped_refptr<Layer> parent = Layer::Create(); scoped_refptr<Layer> child = Layer::Create(); - scoped_refptr<Layer> mask = Layer::Create(); - scoped_refptr<Layer> mask_replacement = Layer::Create(); + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); + scoped_refptr<PictureLayer> mask_replacement = PictureLayer::Create(&client); parent->AddChild(child); child->SetMaskLayer(mask.get()); @@ -1074,7 +1074,7 @@ class LayerTreeHostFactory { }; void AssertLayerTreeHostMatchesForSubtree(Layer* layer, LayerTreeHost* host) { - EXPECT_EQ(host, layer->GetLayerTreeHostForTesting()); + EXPECT_EQ(host, layer->layer_tree_host()); for (size_t i = 0; i < layer->children().size(); ++i) AssertLayerTreeHostMatchesForSubtree(layer->children()[i].get(), host); @@ -1088,7 +1088,8 @@ class LayerLayerTreeHostTest : public testing::Test {}; TEST_F(LayerLayerTreeHostTest, EnteringTree) { scoped_refptr<Layer> parent = Layer::Create(); scoped_refptr<Layer> child = Layer::Create(); - scoped_refptr<Layer> mask = Layer::Create(); + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); // Set up a detached tree of layers. The host pointer should be nil for these // layers. @@ -1125,7 +1126,7 @@ TEST_F(LayerLayerTreeHostTest, AddingLayerSubtree) { layer_tree_host->SetRootLayer(parent.get()); - EXPECT_EQ(parent->GetLayerTreeHostForTesting(), layer_tree_host.get()); + EXPECT_EQ(parent->layer_tree_host(), layer_tree_host.get()); // Adding a subtree to a layer already associated with a host should set the // host pointer on all layers in that subtree. @@ -1134,7 +1135,8 @@ TEST_F(LayerLayerTreeHostTest, AddingLayerSubtree) { child->AddChild(grand_child); // Masks should pick up the new host too. - scoped_refptr<Layer> child_mask = Layer::Create(); + FakeContentLayerClient client; + scoped_refptr<PictureLayer> child_mask = PictureLayer::Create(&client); child->SetMaskLayer(child_mask.get()); parent->AddChild(child); @@ -1146,7 +1148,8 @@ TEST_F(LayerLayerTreeHostTest, AddingLayerSubtree) { TEST_F(LayerLayerTreeHostTest, ChangeHost) { scoped_refptr<Layer> parent = Layer::Create(); scoped_refptr<Layer> child = Layer::Create(); - scoped_refptr<Layer> mask = Layer::Create(); + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); // Same setup as the previous test. parent->AddChild(child); @@ -1205,10 +1208,9 @@ TEST_F(LayerLayerTreeHostTest, ChangeHostInSubtree) { second_parent->AddChild(second_child); // The moved layer and its children should point to the new host. + EXPECT_EQ(second_layer_tree_host.get(), second_child->layer_tree_host()); EXPECT_EQ(second_layer_tree_host.get(), - second_child->GetLayerTreeHostForTesting()); - EXPECT_EQ(second_layer_tree_host.get(), - second_grand_child->GetLayerTreeHostForTesting()); + second_grand_child->layer_tree_host()); // Test over, cleanup time. first_layer_tree_host->SetRootLayer(nullptr); @@ -1216,10 +1218,12 @@ TEST_F(LayerLayerTreeHostTest, ChangeHostInSubtree) { } TEST_F(LayerLayerTreeHostTest, ReplaceMaskLayer) { + FakeContentLayerClient client; + scoped_refptr<Layer> parent = Layer::Create(); - scoped_refptr<Layer> mask = Layer::Create(); + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); scoped_refptr<Layer> mask_child = Layer::Create(); - scoped_refptr<Layer> mask_replacement = Layer::Create(); + scoped_refptr<PictureLayer> mask_replacement = PictureLayer::Create(&client); parent->SetMaskLayer(mask.get()); mask->AddChild(mask_child); @@ -1234,8 +1238,8 @@ TEST_F(LayerLayerTreeHostTest, ReplaceMaskLayer) { // Replacing the mask should clear out the old mask's subtree's host pointers. parent->SetMaskLayer(mask_replacement.get()); - EXPECT_EQ(nullptr, mask->GetLayerTreeHostForTesting()); - EXPECT_EQ(nullptr, mask_child->GetLayerTreeHostForTesting()); + EXPECT_EQ(nullptr, mask->layer_tree_host()); + EXPECT_EQ(nullptr, mask_child->layer_tree_host()); // Test over, cleanup time. layer_tree_host->SetRootLayer(nullptr); diff --git a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc index 385b044e2f4..bd853412370 100644 --- a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc +++ b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc @@ -50,7 +50,7 @@ void NinePatchLayerLayoutTest(const gfx::Size& bitmap_size, FakeUIResourceLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); std::unique_ptr<NinePatchLayerImpl> layer = NinePatchLayerImpl::Create(host_impl.active_tree(), 1); @@ -115,6 +115,8 @@ void NinePatchLayerLayoutTest(const gfx::Size& bitmap_size, } else { EXPECT_TRUE(layer_remaining.bounds().IsEmpty()); } + + host_impl.DeleteUIResource(uid); } void NinePatchLayerLayoutTestWithOcclusion(const gfx::Size& bitmap_size, @@ -161,7 +163,7 @@ void NinePatchLayerLayoutTestWithOcclusion(const gfx::Size& bitmap_size, FakeUIResourceLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); std::unique_ptr<NinePatchLayerImpl> layer = NinePatchLayerImpl::Create(host_impl.active_tree(), 1); @@ -218,6 +220,8 @@ void NinePatchLayerLayoutTestWithOcclusion(const gfx::Size& bitmap_size, EXPECT_EQ(expected_tex_remaining, tex_remaining.bounds()); Region aperture_region(expected_tex_remaining); EXPECT_EQ(aperture_region, tex_remaining); + + host_impl.DeleteUIResource(uid); } TEST(NinePatchLayerImplTest, VerifyDrawQuads) { diff --git a/chromium/cc/layers/nine_patch_layer_unittest.cc b/chromium/cc/layers/nine_patch_layer_unittest.cc index 969de99e8cf..6ec1d95ac1d 100644 --- a/chromium/cc/layers/nine_patch_layer_unittest.cc +++ b/chromium/cc/layers/nine_patch_layer_unittest.cc @@ -51,7 +51,7 @@ TEST_F(NinePatchLayerTest, SetLayerProperties) { layer_tree_host_->SetRootLayer(test_layer); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); - EXPECT_EQ(test_layer->GetLayerTreeHostForTesting(), layer_tree_host_.get()); + EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get()); test_layer->Update(); diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc index acb02addf13..8bea0f87987 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc @@ -97,10 +97,6 @@ void PaintedOverlayScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { scrollbar_layer->set_track_ui_resource_id(0); } -ScrollbarLayerInterface* PaintedOverlayScrollbarLayer::ToScrollbarLayer() { - return this; -} - void PaintedOverlayScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { // When the LTH is set to null or has changed, then this layer should remove // all of its associated resources. diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.h b/chromium/cc/layers/painted_overlay_scrollbar_layer.h index 7f4c555d6ce..674a7266f71 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.h +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.h @@ -25,7 +25,6 @@ class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerInterface, ElementId scroll_element_id = ElementId()); bool OpacityCanAnimateOnImplThread() const override; - ScrollbarLayerInterface* ToScrollbarLayer() override; // ScrollbarLayerInterface void SetScrollElementId(ElementId element_id) override; diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc index 4f5a61eb060..831dffcf63c 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc @@ -64,7 +64,7 @@ void PaintedOverlayScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { bool PaintedOverlayScrollbarLayerImpl::WillDraw( DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) { + viz::ClientResourceProvider* resource_provider) { DCHECK(draw_mode != DRAW_MODE_RESOURCELESS_SOFTWARE); return LayerImpl::WillDraw(draw_mode, resource_provider); } diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.h b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.h index ca436bd40b9..37bdce9eda1 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.h +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.h @@ -31,7 +31,7 @@ class CC_EXPORT PaintedOverlayScrollbarLayerImpl void PushPropertiesTo(LayerImpl* layer) override; bool WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) override; + viz::ClientResourceProvider* resource_provider) override; void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer_unittest.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer_unittest.cc index c510444b898..db2867dc4a9 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer_unittest.cc @@ -61,8 +61,7 @@ TEST(PaintedOverlayScrollbarLayerTest, PaintTickmarks) { scrollbar_layer->SetBounds(gfx::Size(100, 100)); layer_tree_host->SetRootLayer(scrollbar_layer); - EXPECT_EQ(scrollbar_layer->GetLayerTreeHostForTesting(), - layer_tree_host.get()); + EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host.get()); // Request no paint when initialization. scrollbar_layer->Update(); diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc index 52a2b0b7911..cf65df0e6e2 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_scrollbar_layer.cc @@ -109,10 +109,6 @@ void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { scrollbar_layer->set_is_overlay_scrollbar(is_overlay_); } -ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() { - return this; -} - void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { // When the LTH is set to null or has changed, then this layer should remove // all of its associated resources. diff --git a/chromium/cc/layers/painted_scrollbar_layer.h b/chromium/cc/layers/painted_scrollbar_layer.h index f55db7b8f7f..6f293bf5029 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.h +++ b/chromium/cc/layers/painted_scrollbar_layer.h @@ -25,7 +25,6 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface, ElementId element_id = ElementId()); bool OpacityCanAnimateOnImplThread() const override; - ScrollbarLayerInterface* ToScrollbarLayer() override; // ScrollbarLayerInterface void SetScrollElementId(ElementId element_id) override; diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc index 758cf62d163..912e2a0a322 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc @@ -79,7 +79,7 @@ void PaintedScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { bool PaintedScrollbarLayerImpl::WillDraw( DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) { + viz::ClientResourceProvider* resource_provider) { DCHECK(draw_mode != DRAW_MODE_RESOURCELESS_SOFTWARE); return LayerImpl::WillDraw(draw_mode, resource_provider); } diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.h b/chromium/cc/layers/painted_scrollbar_layer_impl.h index a08bfb3d161..a76b642f30e 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl.h +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.h @@ -30,7 +30,7 @@ class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase { void PushPropertiesTo(LayerImpl* layer) override; bool WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) override; + viz::ClientResourceProvider* resource_provider) override; void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; gfx::Rect GetEnclosingRectInTargetSpace() const override; diff --git a/chromium/cc/layers/painted_scrollbar_layer_unittest.cc b/chromium/cc/layers/painted_scrollbar_layer_unittest.cc index f4767910181..53ce3fabcef 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_unittest.cc @@ -43,8 +43,7 @@ TEST(PaintedScrollbarLayerTest, NeedsPaint) { scrollbar_layer->SetBounds(gfx::Size(100, 100)); layer_tree_host->SetRootLayer(scrollbar_layer); - EXPECT_EQ(scrollbar_layer->GetLayerTreeHostForTesting(), - layer_tree_host.get()); + EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host.get()); // Request no paint, but expect them to be painted because they have not // yet been initialized. diff --git a/chromium/cc/layers/picture_layer.h b/chromium/cc/layers/picture_layer.h index b4d2f7abb27..bbf460b94f9 100644 --- a/chromium/cc/layers/picture_layer.h +++ b/chromium/cc/layers/picture_layer.h @@ -38,13 +38,10 @@ class CC_EXPORT PictureLayer : public Layer { void SetLayerTreeHost(LayerTreeHost* host) override; void PushPropertiesTo(LayerImpl* layer) override; void SetNeedsDisplayRect(const gfx::Rect& layer_rect) override; - bool Update() override; - void SetLayerMaskType(LayerMaskType mask_type) override; sk_sp<SkPicture> GetPicture() const override; - + bool Update() override; bool HasSlowPaths() const override; bool HasNonAAPaint() const override; - void RunMicroBenchmark(MicroBenchmark* benchmark) override; ContentLayerClient* client() { return picture_layer_inputs_.client; } @@ -55,6 +52,7 @@ class CC_EXPORT PictureLayer : public Layer { const DisplayItemList* GetDisplayItemList(); + void SetLayerMaskType(LayerMaskType mask_type); LayerMaskType mask_type() { return mask_type_; } protected: diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index 4dc5f16985f..56bca3794d3 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -336,10 +336,23 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass, gfx::Size texture_size = quad_content_rect.size(); gfx::RectF texture_rect = gfx::RectF(gfx::SizeF(texture_size)); + viz::PictureDrawQuad::ImageAnimationMap image_animation_map; + const auto* controller = layer_tree_impl()->image_animation_controller(); + WhichTree tree = layer_tree_impl()->IsPendingTree() + ? WhichTree::PENDING_TREE + : WhichTree::ACTIVE_TREE; + for (const auto& image_data : raster_source_->GetDisplayItemList() + ->discardable_image_map() + .animated_images_metadata()) { + image_animation_map[image_data.paint_image_id] = + controller->GetFrameIndexForImage(image_data.paint_image_id, tree); + } + auto* quad = render_pass->CreateAndAppendDrawQuad<viz::PictureDrawQuad>(); quad->SetNew(shared_quad_state, geometry_rect, visible_geometry_rect, needs_blending, texture_rect, texture_size, nearest_neighbor_, viz::RGBA_8888, quad_content_rect, max_contents_scale, + std::move(image_animation_map), raster_source_->GetDisplayItemList()); ValidateQuadResources(quad); return; diff --git a/chromium/cc/layers/picture_layer_impl_perftest.cc b/chromium/cc/layers/picture_layer_impl_perftest.cc index e633f9bdfe6..fa1a8571c9a 100644 --- a/chromium/cc/layers/picture_layer_impl_perftest.cc +++ b/chromium/cc/layers/picture_layer_impl_perftest.cc @@ -52,7 +52,7 @@ class PictureLayerImplPerfTest : public testing::Test { void SetUp() override { host_impl_.SetVisible(true); - host_impl_.InitializeRenderer(layer_tree_frame_sink_.get()); + host_impl_.InitializeFrameSink(layer_tree_frame_sink_.get()); } void SetupPendingTree(const gfx::Size& layer_bounds) { diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index 90f76f90953..05de466c6d7 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -38,7 +38,6 @@ #include "components/viz/common/quads/draw_quad.h" #include "components/viz/common/quads/tile_draw_quad.h" #include "components/viz/test/begin_frame_args_test.h" -#include "components/viz/test/test_web_graphics_context_3d.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" @@ -808,7 +807,7 @@ TEST_F(PictureLayerImplTest, PinchGestureTilings) { EXPECT_NE(LOW_RESOLUTION, low_res_tiling->resolution()); // Stop a pinch gesture. - host_impl()->PinchGestureEnd(gfx::Point(), true); + host_impl()->PinchGestureEnd(gfx::Point(), false); // Ensure UpdateTiles won't remove any tilings. active_layer()->MarkAllTilingsUsed(); @@ -938,7 +937,7 @@ TEST_F(PictureLayerImplTest, CleanUpTilings) { 1.f * low_res_factor, active_layer()->tilings()->tiling_at(1)->contents_scale_key()); - host_impl()->PinchGestureEnd(gfx::Point(), true); + host_impl()->PinchGestureEnd(gfx::Point(), false); // Create a 1.2 scale tiling. Now we have 1.0 and 1.2 tilings. Ideal = 1.2. scale = 1.2f; @@ -3769,7 +3768,7 @@ TEST_F(NoLowResPictureLayerImplTest, CleanUpTilings) { active_layer()->CleanUpTilingsOnActiveLayer(used_tilings); ASSERT_EQ(1u, active_layer()->tilings()->num_tilings()); - host_impl()->PinchGestureEnd(gfx::Point(), true); + host_impl()->PinchGestureEnd(gfx::Point(), false); // Create a 1.2 scale tiling. Now we have 1.0 and 1.2 tilings. Ideal = 1.2. scale /= 4.f; diff --git a/chromium/cc/layers/picture_layer_unittest.cc b/chromium/cc/layers/picture_layer_unittest.cc index 1c92c7e9785..7f2661cbbc8 100644 --- a/chromium/cc/layers/picture_layer_unittest.cc +++ b/chromium/cc/layers/picture_layer_unittest.cc @@ -60,7 +60,7 @@ TEST(PictureLayerTest, NoTilesIfEmptyBounds) { FakeLayerTreeFrameSink::CreateSoftware(); FakeLayerTreeHostImpl host_impl( LayerTreeSettings(), &impl_task_runner_provider, &task_graph_runner); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.CreatePendingTree(); std::unique_ptr<FakePictureLayerImpl> layer_impl = FakePictureLayerImpl::Create(host_impl.pending_tree(), 1); @@ -101,7 +101,7 @@ TEST(PictureLayerTest, InvalidateRasterAfterUpdate) { FakeLayerTreeHostImpl host_impl( layer_tree_settings, &impl_task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.CreatePendingTree(); host_impl.pending_tree()->SetRootLayerForTesting( FakePictureLayerImpl::Create(host_impl.pending_tree(), 1)); @@ -142,7 +142,7 @@ TEST(PictureLayerTest, InvalidateRasterWithoutUpdate) { FakeLayerTreeHostImpl host_impl( layer_tree_settings, &impl_task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.CreatePendingTree(); host_impl.pending_tree()->SetRootLayerForTesting( FakePictureLayerImpl::Create(host_impl.pending_tree(), 1)); @@ -187,7 +187,7 @@ TEST(PictureLayerTest, ClearVisibleRectWhenNoTiling) { FakeLayerTreeHostImpl host_impl( layer_tree_settings, &impl_task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - EXPECT_TRUE(host_impl.InitializeRenderer(layer_tree_frame_sink.get())); + EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get())); host_impl.CreatePendingTree(); host_impl.pending_tree()->SetRootLayerForTesting( diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc index 02caf7cb7e7..4df28434eb0 100644 --- a/chromium/cc/layers/render_surface_impl.cc +++ b/chromium/cc/layers/render_surface_impl.cc @@ -616,7 +616,10 @@ void RenderSurfaceImpl::TileMaskLayer( case viz::DrawQuad::SOLID_COLOR: { SkColor temp_color = viz::SolidColorDrawQuad::MaterialCast(temp_quad)->color; - if (!temp_color) + // Check the alpha channel of the color. We apply the mask by + // multiplying with the alpha channel, so if the alpha channel is + // transparent, we skip this quad. + if (SkColorGetA(temp_color) == SK_AlphaTRANSPARENT) continue; SkAlpha solid = SK_AlphaOPAQUE; DCHECK_EQ(SkColorGetA(temp_color), solid); diff --git a/chromium/cc/layers/render_surface_unittest.cc b/chromium/cc/layers/render_surface_unittest.cc index 14f74ecc27e..22175731c7c 100644 --- a/chromium/cc/layers/render_surface_unittest.cc +++ b/chromium/cc/layers/render_surface_unittest.cc @@ -98,7 +98,7 @@ TEST(RenderSurfaceTest, VerifySurfaceChangesAreTrackedProperly) { host_impl.active_tree()->ResetAllChangeTracking(); host_impl.active_tree()->SetRootLayerForTesting(std::move(owning_layer)); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); @@ -158,7 +158,7 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectSharedQuadState) { root_layer->test_properties()->AddChild(std::move(owning_layer)); host_impl.active_tree()->SetRootLayerForTesting(std::move(root_layer)); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); @@ -216,7 +216,7 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectRenderPass) { root_layer->test_properties()->AddChild(std::move(owning_layer)); host_impl.active_tree()->SetRootLayerForTesting(std::move(root_layer)); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); @@ -272,7 +272,7 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceDropsOccludedRenderPassDrawQuads) { root_layer->test_properties()->AddChild(std::move(owning_layer)); host_impl.active_tree()->SetRootLayerForTesting(std::move(root_layer)); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); @@ -341,7 +341,7 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceIgnoreMaskLayerOcclusion) { root_layer->test_properties()->AddChild(std::move(owning_layer)); host_impl.active_tree()->SetRootLayerForTesting(std::move(root_layer)); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc index 131182ac2d0..f90363297e0 100644 --- a/chromium/cc/layers/scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/scrollbar_layer_unittest.cc @@ -38,7 +38,7 @@ #include "cc/trees/tree_synchronizer.h" #include "components/viz/common/quads/solid_color_draw_quad.h" #include "components/viz/test/test_context_provider.h" -#include "components/viz/test/test_web_graphics_context_3d.h" +#include "components/viz/test/test_gles2_interface.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -219,7 +219,6 @@ TEST_F(ScrollbarLayerTest, RepaintOverlayWhenResourceDisposed) { scrollbar_layer->SetBounds(gfx::Size(100, 100)); 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)); } // First call to update should create a resource. The scrollbar itself thinks @@ -965,10 +964,6 @@ TEST_F(AuraScrollbarLayerTest, ScrollbarLayerCreateAfterSetScrollable) { layer_tree_host_->CommitAndCreatePendingTree(); host_impl->ActivateSyncTree(); - LayerImpl* scroll_layer_impl = - host_impl->active_tree()->LayerByElementId(scroll_layer->element_id()); - EXPECT_TRUE(scroll_layer_impl->needs_show_scrollbars()); - std::unique_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true)); scoped_refptr<Layer> scrollbar_layer = SolidColorScrollbarLayer::Create( scrollbar->Orientation(), kThumbThickness, kTrackStart, @@ -1124,11 +1119,9 @@ class ScrollbarLayerTestResourceCreationAndRelease : public ScrollbarLayerTest { layer_tree_root->SetScrollOffset(gfx::ScrollOffset(10, 20)); 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)); testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); - EXPECT_EQ(scrollbar_layer->GetLayerTreeHostForTesting(), - layer_tree_host_.get()); + EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get()); for (int update_counter = 0; update_counter < num_updates; update_counter++) scrollbar_layer->Update(); @@ -1183,11 +1176,9 @@ TEST_F(ScrollbarLayerTestResourceCreationAndRelease, TestResourceUpdate) { scrollbar_layer->SetPosition(gfx::PointF(scrollbar_location)); 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)); testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); - EXPECT_EQ(scrollbar_layer->GetLayerTreeHostForTesting(), - layer_tree_host_.get()); + EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get()); size_t resource_count; int expected_created, expected_deleted; @@ -1310,7 +1301,10 @@ TEST_F(ScrollbarLayerTestResourceCreationAndRelease, TestResourceUpdate) { EXPECT_EQ(gfx::Size(90, 15), fake_ui_resource_manager_->ui_resource_size( scrollbar_layer->track_resource_id())); - scrollbar_layer->ResetNeedsDisplayForTesting(); + // Simulate commit to compositor thread. + scrollbar_layer->PushPropertiesTo( + scrollbar_layer->CreateLayerImpl(layer_tree_host_->active_tree()).get()); + EXPECT_FALSE(scrollbar_layer->Update()); EXPECT_NE(0, scrollbar_layer->track_resource_id()); EXPECT_EQ(0, scrollbar_layer->thumb_resource_id()); @@ -1343,11 +1337,8 @@ class ScaledScrollbarLayerTestResourceCreation : public ScrollbarLayerTest { scrollbar_layer->SetPosition(gfx::PointF(scrollbar_location)); layer_tree_root->SetBounds(gfx::Size(100, 200)); content_layer->SetBounds(gfx::Size(100, 200)); - scrollbar_layer->set_visible_layer_rect( - gfx::Rect(scrollbar_location, scrollbar_layer->bounds())); - EXPECT_EQ(scrollbar_layer->GetLayerTreeHostForTesting(), - layer_tree_host_.get()); + EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get()); layer_tree_host_->SetViewportSizeAndScale( layer_tree_host_->device_viewport_size(), test_scale, @@ -1387,7 +1378,7 @@ TEST_F(ScaledScrollbarLayerTestResourceCreation, ScaledResourceUpload) { viz::TestContextProvider::Create(); // Keep the max texture size reasonable so we don't OOM on low end devices // (crbug.com/642333). - context->UnboundTestContext3d()->set_max_texture_size(512); + context->UnboundTestContextGL()->set_max_texture_size(512); context->BindToCurrentThread(); int max_texture_size = 0; context->ContextGL()->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); @@ -1413,7 +1404,6 @@ class ScaledScrollbarLayerTestScaledRasterization : public ScrollbarLayerTest { scrollbar_layer->SetPosition(gfx::PointF(scrollbar_rect.origin())); scrollbar_layer->fake_scrollbar()->set_location(scrollbar_rect.origin()); scrollbar_layer->fake_scrollbar()->set_track_rect(scrollbar_rect); - scrollbar_layer->set_visible_layer_rect(scrollbar_rect); layer_tree_host_->SetViewportSizeAndScale( layer_tree_host_->device_viewport_size(), test_scale, diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.cc b/chromium/cc/layers/solid_color_scrollbar_layer.cc index 6f4e2e23f84..f514b6dd762 100644 --- a/chromium/cc/layers/solid_color_scrollbar_layer.cc +++ b/chromium/cc/layers/solid_color_scrollbar_layer.cc @@ -64,10 +64,6 @@ SolidColorScrollbarLayer::SolidColorScrollbarLayer( SolidColorScrollbarLayer::~SolidColorScrollbarLayer() = default; -ScrollbarLayerInterface* SolidColorScrollbarLayer::ToScrollbarLayer() { - return this; -} - void SolidColorScrollbarLayer::SetOpacity(float opacity) { // The opacity of a solid color scrollbar layer is always 0 on main thread. DCHECK_EQ(opacity, 0.f); diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.h b/chromium/cc/layers/solid_color_scrollbar_layer.h index 3ff721f6740..69bcc4f1440 100644 --- a/chromium/cc/layers/solid_color_scrollbar_layer.h +++ b/chromium/cc/layers/solid_color_scrollbar_layer.h @@ -26,7 +26,6 @@ class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface, // Layer overrides. bool OpacityCanAnimateOnImplThread() const override; - ScrollbarLayerInterface* ToScrollbarLayer() override; void SetOpacity(float opacity) override; void PushPropertiesTo(LayerImpl* layer) override; diff --git a/chromium/cc/layers/surface_layer.cc b/chromium/cc/layers/surface_layer.cc index 6124fe42fbf..4940951b840 100644 --- a/chromium/cc/layers/surface_layer.cc +++ b/chromium/cc/layers/surface_layer.cc @@ -17,8 +17,19 @@ scoped_refptr<SurfaceLayer> SurfaceLayer::Create() { return base::WrapRefCounted(new SurfaceLayer()); } +scoped_refptr<SurfaceLayer> SurfaceLayer::Create( + UpdateSubmissionStateCB update_submission_state_callback) { + return base::WrapRefCounted( + new SurfaceLayer(std::move(update_submission_state_callback))); +} + SurfaceLayer::SurfaceLayer() = default; +SurfaceLayer::SurfaceLayer( + UpdateSubmissionStateCB update_submission_state_callback) + : update_submission_state_callback_( + std::move(update_submission_state_callback)) {} + SurfaceLayer::~SurfaceLayer() { DCHECK(!layer_tree_host()); } @@ -29,24 +40,61 @@ void SurfaceLayer::SetPrimarySurfaceId(const viz::SurfaceId& surface_id, deadline_policy.use_existing_deadline()) { return; } + + TRACE_EVENT_WITH_FLOW2( + TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"), + "LocalSurfaceId.Embed.Flow", + TRACE_ID_GLOBAL(surface_id.local_surface_id().embed_trace_id()), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", + "SetPrimarySurfaceId", "surface_id", surface_id.ToString()); + + const viz::SurfaceRange old_surface_range(GetSurfaceRange()); + if (layer_tree_host() && old_surface_range.IsValid()) + layer_tree_host()->RemoveSurfaceRange(old_surface_range); + primary_surface_id_ = surface_id; - if (!deadline_policy.use_existing_deadline()) + + const viz::SurfaceRange new_surface_range(GetSurfaceRange()); + if (layer_tree_host() && new_surface_range.IsValid()) + layer_tree_host()->AddSurfaceRange(new_surface_range); + + // We should never block or set a deadline on an invalid + // |primary_surface_id_|. + if (!primary_surface_id_.is_valid()) { + deadline_in_frames_ = 0u; + } else if (!deadline_policy.use_existing_deadline()) { deadline_in_frames_ = deadline_policy.deadline_in_frames(); + } UpdateDrawsContent(HasDrawableContent()); SetNeedsCommit(); } void SurfaceLayer::SetFallbackSurfaceId(const viz::SurfaceId& surface_id) { - if (fallback_surface_id_ == surface_id) + if (fallback_surface_id_ == surface_id) { + // TODO(samans): This was added to fix https://crbug.com/827242. Remove this + // once fallback SurfaceIds aren't tied to SurfaceReferences, and + // viz::Display can handle missing fallbacks. https://crbug.com/857575 + if (layer_tree_host()) + layer_tree_host()->SetNeedsCommitWithForcedRedraw(); return; + } - if (layer_tree_host()) - layer_tree_host()->RemoveSurfaceLayerId(fallback_surface_id_); + TRACE_EVENT_WITH_FLOW2( + TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"), + "LocalSurfaceId.Submission.Flow", + TRACE_ID_GLOBAL(surface_id.local_surface_id().submission_trace_id()), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", + "SetFallbackSurfaceId", "surface_id", surface_id.ToString()); + + const viz::SurfaceRange old_surface_range(GetSurfaceRange()); + if (layer_tree_host() && old_surface_range.IsValid()) + layer_tree_host()->RemoveSurfaceRange(old_surface_range); fallback_surface_id_ = surface_id; - if (layer_tree_host() && fallback_surface_id_.is_valid()) - layer_tree_host()->AddSurfaceLayerId(fallback_surface_id_); + const viz::SurfaceRange new_surface_range(GetSurfaceRange()); + if (layer_tree_host() && new_surface_range.IsValid()) + layer_tree_host()->AddSurfaceRange(new_surface_range); SetNeedsCommit(); } @@ -66,9 +114,16 @@ void SurfaceLayer::SetSurfaceHitTestable(bool surface_hit_testable) { SetNeedsPushProperties(); } +void SurfaceLayer::SetMayContainVideo(bool may_contain_video) { + may_contain_video_ = may_contain_video; +} + std::unique_ptr<LayerImpl> SurfaceLayer::CreateLayerImpl( LayerTreeImpl* tree_impl) { - return SurfaceLayerImpl::Create(tree_impl, id()); + auto layer_impl = SurfaceLayerImpl::Create(tree_impl, id(), + update_submission_state_callback_); + layer_impl->set_may_contain_video(may_contain_video_); + return layer_impl; } bool SurfaceLayer::HasDrawableContent() const { @@ -79,14 +134,15 @@ void SurfaceLayer::SetLayerTreeHost(LayerTreeHost* host) { if (layer_tree_host() == host) { return; } - - if (layer_tree_host() && fallback_surface_id_.is_valid()) - layer_tree_host()->RemoveSurfaceLayerId(fallback_surface_id_); + const viz::SurfaceRange old_surface_range(GetSurfaceRange()); + if (layer_tree_host() && old_surface_range.IsValid()) + layer_tree_host()->RemoveSurfaceRange(old_surface_range); Layer::SetLayerTreeHost(host); - if (layer_tree_host() && fallback_surface_id_.is_valid()) - layer_tree_host()->AddSurfaceLayerId(fallback_surface_id_); + const viz::SurfaceRange new_surface_range(GetSurfaceRange()); + if (layer_tree_host() && new_surface_range.IsValid()) + layer_tree_host()->AddSurfaceRange(new_surface_range); } void SurfaceLayer::PushPropertiesTo(LayerImpl* layer) { @@ -103,4 +159,13 @@ void SurfaceLayer::PushPropertiesTo(LayerImpl* layer) { layer_impl->SetSurfaceHitTestable(surface_hit_testable_); } +viz::SurfaceRange SurfaceLayer::GetSurfaceRange() const { + return viz::SurfaceRange( + fallback_surface_id_.is_valid() + ? base::Optional<viz::SurfaceId>(fallback_surface_id_) + : base::nullopt, + primary_surface_id_.is_valid() ? primary_surface_id_ + : fallback_surface_id_); +} + } // namespace cc diff --git a/chromium/cc/layers/surface_layer.h b/chromium/cc/layers/surface_layer.h index 87c5a26dfe2..863f7bd7654 100644 --- a/chromium/cc/layers/surface_layer.h +++ b/chromium/cc/layers/surface_layer.h @@ -10,16 +10,22 @@ #include "cc/layers/deadline_policy.h" #include "cc/layers/layer.h" #include "components/viz/common/surfaces/surface_info.h" +#include "components/viz/common/surfaces/surface_range.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/size.h" namespace cc { +// If given true, we should submit frames, as we are unoccluded on screen. +// If given false, we should not submit compositor frames. +using UpdateSubmissionStateCB = base::RepeatingCallback<void(bool is_visible)>; + // A layer that renders a surface referencing the output of another compositor // instance or client. class CC_EXPORT SurfaceLayer : public Layer { public: static scoped_refptr<SurfaceLayer> Create(); + static scoped_refptr<SurfaceLayer> Create(UpdateSubmissionStateCB); void SetPrimarySurfaceId(const viz::SurfaceId& surface_id, const DeadlinePolicy& deadline_policy); @@ -35,6 +41,8 @@ class CC_EXPORT SurfaceLayer : public Layer { void SetSurfaceHitTestable(bool surface_hit_testable); bool surface_hit_testable() const { return surface_hit_testable_; } + void SetMayContainVideo(bool); + // Layer overrides. std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; void SetLayerTreeHost(LayerTreeHost* host) override; @@ -54,11 +62,18 @@ class CC_EXPORT SurfaceLayer : public Layer { protected: SurfaceLayer(); + explicit SurfaceLayer(UpdateSubmissionStateCB); bool HasDrawableContent() const override; private: ~SurfaceLayer() override; + // Returns a SurfaceRange corresponding to the surface layer. + viz::SurfaceRange GetSurfaceRange() const; + + UpdateSubmissionStateCB update_submission_state_callback_; + + bool may_contain_video_ = false; viz::SurfaceId primary_surface_id_; viz::SurfaceId fallback_surface_id_; base::Optional<uint32_t> deadline_in_frames_ = 0u; diff --git a/chromium/cc/layers/surface_layer_impl.cc b/chromium/cc/layers/surface_layer_impl.cc index def5281514d..1f9aa257247 100644 --- a/chromium/cc/layers/surface_layer_impl.cc +++ b/chromium/cc/layers/surface_layer_impl.cc @@ -16,14 +16,23 @@ namespace cc { -SurfaceLayerImpl::SurfaceLayerImpl(LayerTreeImpl* tree_impl, int id) - : LayerImpl(tree_impl, id) {} - -SurfaceLayerImpl::~SurfaceLayerImpl() = default; +SurfaceLayerImpl::SurfaceLayerImpl( + LayerTreeImpl* tree_impl, + int id, + UpdateSubmissionStateCB update_submission_state_callback) + : LayerImpl(tree_impl, id), + update_submission_state_callback_( + std::move(update_submission_state_callback)) {} + +SurfaceLayerImpl::~SurfaceLayerImpl() { + if (update_submission_state_callback_) + update_submission_state_callback_.Run(false); +} std::unique_ptr<LayerImpl> SurfaceLayerImpl::CreateLayerImpl( LayerTreeImpl* tree_impl) { - return SurfaceLayerImpl::Create(tree_impl, id()); + return SurfaceLayerImpl::Create(tree_impl, id(), + std::move(update_submission_state_callback_)); } void SurfaceLayerImpl::SetPrimarySurfaceId( @@ -33,6 +42,14 @@ void SurfaceLayerImpl::SetPrimarySurfaceId( deadline_in_frames_ == deadline_in_frames) { return; } + + TRACE_EVENT_WITH_FLOW2( + TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"), + "LocalSurfaceId.Embed.Flow", + TRACE_ID_GLOBAL(surface_id.local_surface_id().embed_trace_id()), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", + "ImplSetPrimarySurfaceId", "surface_id", surface_id.ToString()); + primary_surface_id_ = surface_id; deadline_in_frames_ = deadline_in_frames; NoteLayerPropertyChanged(); @@ -42,6 +59,13 @@ void SurfaceLayerImpl::SetFallbackSurfaceId(const viz::SurfaceId& surface_id) { if (fallback_surface_id_ == surface_id) return; + TRACE_EVENT_WITH_FLOW2( + TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"), + "LocalSurfaceId.Submission.Flow", + TRACE_ID_GLOBAL(surface_id.local_surface_id().submission_trace_id()), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", + "ImplSetFallbackSurfaceId", "surface_id", surface_id.ToString()); + fallback_surface_id_ = surface_id; NoteLayerPropertyChanged(); } @@ -74,6 +98,22 @@ void SurfaceLayerImpl::PushPropertiesTo(LayerImpl* layer) { layer_impl->SetSurfaceHitTestable(surface_hit_testable_); } +bool SurfaceLayerImpl::WillDraw( + DrawMode draw_mode, + viz::ClientResourceProvider* resource_provider) { + bool will_draw = LayerImpl::WillDraw(draw_mode, resource_provider); + // If we have a change in WillDraw (meaning that visibility has changed), we + // want to inform the VideoFrameSubmitter to start or stop submitting + // compositor frames. + if (will_draw_ != will_draw) { + will_draw_ = will_draw; + if (update_submission_state_callback_) + update_submission_state_callback_.Run(will_draw); + } + + return primary_surface_id_.is_valid() && will_draw; +} + void SurfaceLayerImpl::AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) { AppendRainbowDebugBorder(render_pass); @@ -127,12 +167,8 @@ viz::SurfaceDrawQuad* SurfaceLayerImpl::CreateSurfaceDrawQuad( 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 - // viz::SurfaceDrawQuad. viz::SharedQuadState* shared_quad_state = - shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); + render_pass->CreateAndAppendSharedQuadState(); PopulateScaledSharedQuadState(shared_quad_state, device_scale_factor, device_scale_factor, contents_opaque()); diff --git a/chromium/cc/layers/surface_layer_impl.h b/chromium/cc/layers/surface_layer_impl.h index ce243205514..d283f73e55f 100644 --- a/chromium/cc/layers/surface_layer_impl.h +++ b/chromium/cc/layers/surface_layer_impl.h @@ -7,6 +7,7 @@ #include <memory> +#include "base/bind.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "cc/cc_export.h" @@ -17,12 +18,25 @@ namespace cc { +// This must match SurfaceLayer::UpdateSubmissionStateCB. +using UpdateSubmissionStateCB = base::RepeatingCallback<void(bool is_visible)>; + class CC_EXPORT SurfaceLayerImpl : public LayerImpl { public: + static std::unique_ptr<SurfaceLayerImpl> Create( + LayerTreeImpl* tree_impl, + int id, + UpdateSubmissionStateCB update_submission_state_callback) { + return base::WrapUnique(new SurfaceLayerImpl( + tree_impl, id, std::move(update_submission_state_callback))); + } + static std::unique_ptr<SurfaceLayerImpl> Create(LayerTreeImpl* tree_impl, int id) { - return base::WrapUnique(new SurfaceLayerImpl(tree_impl, id)); + return base::WrapUnique( + new SurfaceLayerImpl(tree_impl, id, base::BindRepeating([](bool) {}))); } + ~SurfaceLayerImpl() override; void SetPrimarySurfaceId(const viz::SurfaceId& surface_id, @@ -56,12 +70,14 @@ class CC_EXPORT SurfaceLayerImpl : public LayerImpl { // LayerImpl overrides. std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; void PushPropertiesTo(LayerImpl* layer) override; + bool WillDraw(DrawMode draw_mode, + viz::ClientResourceProvider* resource_provider) override; void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; bool is_surface_layer() const override; protected: - SurfaceLayerImpl(LayerTreeImpl* tree_impl, int id); + SurfaceLayerImpl(LayerTreeImpl* tree_impl, int id, UpdateSubmissionStateCB); private: viz::SurfaceDrawQuad* CreateSurfaceDrawQuad( @@ -74,12 +90,14 @@ class CC_EXPORT SurfaceLayerImpl : public LayerImpl { void AsValueInto(base::trace_event::TracedValue* dict) const override; const char* LayerTypeAsString() const override; + UpdateSubmissionStateCB update_submission_state_callback_; viz::SurfaceId primary_surface_id_; viz::SurfaceId fallback_surface_id_; base::Optional<uint32_t> deadline_in_frames_; bool stretch_content_to_fill_bounds_ = false; bool surface_hit_testable_ = false; + bool will_draw_ = false; DISALLOW_COPY_AND_ASSIGN(SurfaceLayerImpl); }; diff --git a/chromium/cc/layers/surface_layer_impl_unittest.cc b/chromium/cc/layers/surface_layer_impl_unittest.cc index d8a5f6bcb97..36a57d20ecb 100644 --- a/chromium/cc/layers/surface_layer_impl_unittest.cc +++ b/chromium/cc/layers/surface_layer_impl_unittest.cc @@ -44,6 +44,7 @@ TEST(SurfaceLayerImplTest, Occlusion) { LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect(layer_size)); EXPECT_EQ(1u, impl.quad_list().size()); + EXPECT_TRUE(surface_layer_impl->WillDraw(DRAW_MODE_HARDWARE, nullptr)); } { @@ -53,6 +54,7 @@ TEST(SurfaceLayerImplTest, Occlusion) { LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect()); EXPECT_EQ(impl.quad_list().size(), 0u); + EXPECT_FALSE(surface_layer_impl->WillDraw(DRAW_MODE_HARDWARE, nullptr)); } { @@ -66,6 +68,7 @@ TEST(SurfaceLayerImplTest, Occlusion) { // The layer outputs one quad, which is partially occluded. EXPECT_EQ(1u, impl.quad_list().size()); EXPECT_EQ(1u, partially_occluded_count); + EXPECT_TRUE(surface_layer_impl->WillDraw(DRAW_MODE_HARDWARE, nullptr)); } } diff --git a/chromium/cc/layers/surface_layer_unittest.cc b/chromium/cc/layers/surface_layer_unittest.cc index 9eb987a0f9e..c6e8f16bd7e 100644 --- a/chromium/cc/layers/surface_layer_unittest.cc +++ b/chromium/cc/layers/surface_layer_unittest.cc @@ -4,6 +4,7 @@ #include <stdint.h> +#include <iostream> #include <set> #include <vector> @@ -46,7 +47,7 @@ class SurfaceLayerTest : public testing::Test { void SynchronizeTrees() { TreeSynchronizer::PushLayerProperties(layer_tree_host_.get(), host_impl_.pending_tree()); - layer_tree_host_->PushSurfaceIdsTo(host_impl_.pending_tree()); + layer_tree_host_->PushSurfaceRangesTo(host_impl_.pending_tree()); } protected: @@ -98,6 +99,25 @@ TEST_F(SurfaceLayerTest, UseInfiniteDeadlineForNewSurfaceLayer) { EXPECT_EQ(std::numeric_limits<uint32_t>::max(), layer->deadline_in_frames()); } +// This test verifies that if an invalid primary surface ID is set then the +// deadline will be reset to 0 frames. +TEST_F(SurfaceLayerTest, ResetDeadlineOnInvalidSurfaceId) { + scoped_refptr<SurfaceLayer> layer = SurfaceLayer::Create(); + layer_tree_host_->SetRootLayer(layer); + viz::SurfaceId primary_id( + kArbitraryFrameSinkId, + viz::LocalSurfaceId(1, base::UnguessableToken::Create())); + layer->SetPrimarySurfaceId(primary_id, + DeadlinePolicy::UseSpecifiedDeadline(3u)); + EXPECT_EQ(3u, layer->deadline_in_frames()); + + // Reset the surface layer to an invalid SurfaceId. Verify that the deadline + // is reset. + layer->SetPrimarySurfaceId(viz::SurfaceId(), + DeadlinePolicy::UseSpecifiedDeadline(3u)); + EXPECT_EQ(0u, layer->deadline_in_frames()); +} + // This test verifies that SurfaceLayer properties are pushed across to // SurfaceLayerImpl. TEST_F(SurfaceLayerTest, PushProperties) { @@ -115,24 +135,24 @@ TEST_F(SurfaceLayerTest, PushProperties) { layer->SetBackgroundColor(SK_ColorBLUE); layer->SetStretchContentToFillBounds(true); - EXPECT_TRUE(layer_tree_host_->needs_surface_ids_sync()); - EXPECT_EQ(layer_tree_host_->SurfaceLayerIds().size(), 1u); + EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync()); + EXPECT_EQ(layer_tree_host_->SurfaceRanges().size(), 1u); // Verify that pending tree has no surface ids already. - EXPECT_FALSE(host_impl_.pending_tree()->needs_surface_ids_sync()); - EXPECT_EQ(host_impl_.pending_tree()->SurfaceLayerIds().size(), 0u); + EXPECT_FALSE(host_impl_.pending_tree()->needs_surface_ranges_sync()); + EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 0u); std::unique_ptr<SurfaceLayerImpl> layer_impl = SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer->id()); SynchronizeTrees(); // Verify that pending tree received the surface id and also has - // needs_surface_ids_sync set to true as it needs to sync with active tree. - EXPECT_TRUE(host_impl_.pending_tree()->needs_surface_ids_sync()); - EXPECT_EQ(host_impl_.pending_tree()->SurfaceLayerIds().size(), 1u); + // needs_surface_ranges_sync set to true as it needs to sync with active tree. + EXPECT_TRUE(host_impl_.pending_tree()->needs_surface_ranges_sync()); + EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 1u); // Verify we have reset the state on layer tree host. - EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync()); + EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync()); // Verify that the primary and fallback SurfaceIds are pushed through. EXPECT_EQ(primary_id, layer_impl->primary_surface_id()); @@ -145,21 +165,23 @@ TEST_F(SurfaceLayerTest, PushProperties) { kArbitraryFrameSinkId, viz::LocalSurfaceId(2, base::UnguessableToken::Create())); layer->SetFallbackSurfaceId(fallback_id); + layer->SetPrimarySurfaceId(fallback_id, + DeadlinePolicy::UseExistingDeadline()); layer->SetBackgroundColor(SK_ColorGREEN); layer->SetStretchContentToFillBounds(false); // Verify that fallback surface id is not recorded on the layer tree host as // surface synchronization is not enabled. - EXPECT_TRUE(layer_tree_host_->needs_surface_ids_sync()); - EXPECT_EQ(layer_tree_host_->SurfaceLayerIds().size(), 1u); + EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync()); + EXPECT_EQ(layer_tree_host_->SurfaceRanges().size(), 1u); SynchronizeTrees(); - EXPECT_EQ(host_impl_.pending_tree()->SurfaceLayerIds().size(), 1u); + EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 1u); // Verify that the primary viz::SurfaceId stays the same and the new // fallback viz::SurfaceId is pushed through. - EXPECT_EQ(primary_id, layer_impl->primary_surface_id()); + EXPECT_EQ(fallback_id, layer_impl->primary_surface_id()); EXPECT_EQ(fallback_id, layer_impl->fallback_surface_id()); EXPECT_EQ(SK_ColorGREEN, layer_impl->background_color()); // The deadline resets back to 0 (no deadline) after the first commit. @@ -199,9 +221,10 @@ TEST_F(SurfaceLayerTest, CheckSurfaceReferencesForClonedLayer) { SynchronizeTrees(); // Verify that only |old_surface_id| is going to be referenced. - EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), ElementsAre(old_surface_id)); - EXPECT_THAT(host_impl_.pending_tree()->SurfaceLayerIds(), - ElementsAre(old_surface_id)); + EXPECT_THAT(layer_tree_host_->SurfaceRanges(), + ElementsAre(viz::SurfaceRange(old_surface_id))); + EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(), + ElementsAre(viz::SurfaceRange(old_surface_id))); const viz::SurfaceId new_surface_id( kArbitraryFrameSinkId, @@ -215,10 +238,12 @@ TEST_F(SurfaceLayerTest, CheckSurfaceReferencesForClonedLayer) { SynchronizeTrees(); // Verify that both surface ids are going to be referenced. - EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), - ElementsAre(old_surface_id, new_surface_id)); - EXPECT_THAT(host_impl_.pending_tree()->SurfaceLayerIds(), - ElementsAre(old_surface_id, new_surface_id)); + EXPECT_THAT(layer_tree_host_->SurfaceRanges(), + ElementsAre(viz::SurfaceRange(old_surface_id), + viz::SurfaceRange(new_surface_id))); + EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(), + ElementsAre(viz::SurfaceRange(old_surface_id), + viz::SurfaceRange(new_surface_id))); // Unparent the old layer like it's being destroyed at the end of animation. layer1->SetLayerTreeHost(nullptr); @@ -226,15 +251,16 @@ TEST_F(SurfaceLayerTest, CheckSurfaceReferencesForClonedLayer) { SynchronizeTrees(); // Verify that only |new_surface_id| is going to be referenced. - EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), ElementsAre(new_surface_id)); - EXPECT_THAT(host_impl_.pending_tree()->SurfaceLayerIds(), - ElementsAre(new_surface_id)); + EXPECT_THAT(layer_tree_host_->SurfaceRanges(), + ElementsAre(viz::SurfaceRange(new_surface_id))); + EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(), + ElementsAre(viz::SurfaceRange(new_surface_id))); // Cleanup for destruction. layer2->SetLayerTreeHost(nullptr); } -// This test verifies LayerTreeHost::needs_surface_ids_sync() is correct when +// This test verifies LayerTreeHost::needs_surface_ranges_sync() is correct when // there are cloned surface layers. TEST_F(SurfaceLayerTest, CheckNeedsSurfaceIdsSyncForClonedLayers) { const viz::SurfaceId surface_id( @@ -246,17 +272,17 @@ TEST_F(SurfaceLayerTest, CheckNeedsSurfaceIdsSyncForClonedLayers) { layer1->SetPrimarySurfaceId(surface_id, DeadlinePolicy::UseDefaultDeadline()); layer1->SetFallbackSurfaceId(surface_id); - // Verify the surface id is in SurfaceLayerIds() and needs_surface_ids_sync() - // is true. - EXPECT_TRUE(layer_tree_host_->needs_surface_ids_sync()); - EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), SizeIs(1)); + // Verify the surface id is in SurfaceLayerIds() and + // needs_surface_ranges_sync() is true. + EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync()); + EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(1)); std::unique_ptr<SurfaceLayerImpl> layer_impl1 = SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer1->id()); SynchronizeTrees(); - // After syncchronizing trees verify needs_surface_ids_sync() is false. - EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync()); + // After syncchronizing trees verify needs_surface_ranges_sync() is false. + EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync()); // Create the second layer that is a clone of the first. scoped_refptr<SurfaceLayer> layer2 = SurfaceLayer::Create(); @@ -265,29 +291,30 @@ TEST_F(SurfaceLayerTest, CheckNeedsSurfaceIdsSyncForClonedLayers) { layer2->SetFallbackSurfaceId(surface_id); // Verify that after creating the second layer with the same surface id that - // needs_surface_ids_sync() is still false. - EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync()); - EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), SizeIs(1)); + // needs_surface_ranges_sync() is still false. + EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync()); + EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(1)); std::unique_ptr<SurfaceLayerImpl> layer_impl2 = SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer2->id()); SynchronizeTrees(); - // Verify needs_surface_ids_sync() is still false after synchronizing trees. - EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync()); + // Verify needs_surface_ranges_sync() is still false after synchronizing + // trees. + EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync()); // Destroy one of the layers, leaving one layer with the surface id. layer1->SetLayerTreeHost(nullptr); - // Verify needs_surface_ids_sync() is still false. - EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync()); + // Verify needs_surface_ranges_sync() is still false. + EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync()); // Destroy the last layer, this should change the set of layer surface ids. layer2->SetLayerTreeHost(nullptr); - // Verify SurfaceLayerIds() is empty and needs_surface_ids_sync() is true. - EXPECT_TRUE(layer_tree_host_->needs_surface_ids_sync()); - EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), SizeIs(0)); + // Verify SurfaceLayerIds() is empty and needs_surface_ranges_sync() is true. + EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync()); + EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(0)); } } // namespace diff --git a/chromium/cc/layers/texture_layer.cc b/chromium/cc/layers/texture_layer.cc index 359cbf8955d..0b2784bf04d 100644 --- a/chromium/cc/layers/texture_layer.cc +++ b/chromium/cc/layers/texture_layer.cc @@ -192,7 +192,12 @@ bool TextureLayer::Update() { return updated || !update_rect().IsEmpty(); } -bool TextureLayer::IsSnapped() { +bool TextureLayer::IsSnappedToPixelGridInTarget() { + // Often layers are positioned with CSS to "50%", which can often leave them + // with a fractional (N + 0.5) pixel position. This would leave them looking + // fuzzy, so we request that TextureLayers are snapped to the pixel grid, + // since their content is generated externally and we can not adjust for it + // inside the content (unlike for PictureLayers). return true; } diff --git a/chromium/cc/layers/texture_layer.h b/chromium/cc/layers/texture_layer.h index c8c77755b53..b5d5993179c 100644 --- a/chromium/cc/layers/texture_layer.h +++ b/chromium/cc/layers/texture_layer.h @@ -153,7 +153,7 @@ class CC_EXPORT TextureLayer : public Layer, SharedBitmapIdRegistrar { void SetLayerTreeHost(LayerTreeHost* layer_tree_host) override; bool Update() override; - bool IsSnapped() override; + bool IsSnappedToPixelGridInTarget() override; void PushPropertiesTo(LayerImpl* layer) override; // Request a mapping from SharedBitmapId to SharedMemory be registered via the diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc index b0203d5362f..7d34344e47e 100644 --- a/chromium/cc/layers/texture_layer_impl.cc +++ b/chromium/cc/layers/texture_layer_impl.cc @@ -42,7 +42,8 @@ std::unique_ptr<LayerImpl> TextureLayerImpl::CreateLayerImpl( return TextureLayerImpl::Create(tree_impl, id()); } -bool TextureLayerImpl::IsSnapped() { +bool TextureLayerImpl::IsSnappedToPixelGridInTarget() { + // See TextureLayer::IsSnappedToPixelGridInTarget() for explanation of |true|. return true; } @@ -69,8 +70,9 @@ void TextureLayerImpl::PushPropertiesTo(LayerImpl* layer) { to_unregister_bitmap_ids_.clear(); } -bool TextureLayerImpl::WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) { +bool TextureLayerImpl::WillDraw( + DrawMode draw_mode, + viz::ClientResourceProvider* resource_provider) { if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) return false; // These imply some synchronization problem where the compositor is in gpu @@ -86,6 +88,9 @@ bool TextureLayerImpl::WillDraw(DrawMode draw_mode, return false; } + if (!LayerImpl::WillDraw(draw_mode, resource_provider)) + return false; + if (own_resource_) { DCHECK(!resource_id_); if (!transferable_resource_.mailbox_holder.mailbox.IsZero()) { @@ -96,7 +101,7 @@ bool TextureLayerImpl::WillDraw(DrawMode draw_mode, own_resource_ = false; } - return resource_id_ && LayerImpl::WillDraw(draw_mode, resource_provider); + return resource_id_; } void TextureLayerImpl::AppendQuads(viz::RenderPass* render_pass, @@ -165,9 +170,18 @@ SimpleEnclosedRegion TextureLayerImpl::VisibleOpaqueRegion() const { return SimpleEnclosedRegion(); } +void TextureLayerImpl::OnPurgeMemory() { + // Do nothing here intentionally as the LayerTreeFrameSink isn't lost. + // Unregistering SharedBitmapIds with the LayerTreeFrameSink wouldn't free + // the shared memory, as the TextureLayer and/or TextureLayerClient will still + // have a reference to it. +} + void TextureLayerImpl::ReleaseResources() { - FreeTransferableResource(); - resource_id_ = 0; + // Gpu resources are lost when the LayerTreeFrameSink is lost. But software + // resources are still valid, and we can keep them here in that case. + if (!transferable_resource_.is_software) + FreeTransferableResource(); // The LayerTreeFrameSink is gone and being replaced, so we will have to // re-register all SharedBitmapIds on the new LayerTreeFrameSink. We don't @@ -274,10 +288,6 @@ const char* TextureLayerImpl::LayerTypeAsString() const { void TextureLayerImpl::FreeTransferableResource() { if (own_resource_) { - // TODO(crbug.com/826886): Software resources should be kept alive. - // if (transferable_resource_.is_software) - // return; - DCHECK(!resource_id_); if (release_callback_) { // We didn't use the resource, but the client might need the SyncToken @@ -288,9 +298,6 @@ void TextureLayerImpl::FreeTransferableResource() { transferable_resource_ = viz::TransferableResource(); release_callback_ = nullptr; } else if (resource_id_) { - // TODO(crbug.com/826886): Ownership of software resources should be - // reclaimed, including the ReleaseCalback, without running it. - DCHECK(!own_resource_); auto* resource_provider = layer_tree_impl()->resource_provider(); resource_provider->RemoveImportedResource(resource_id_); diff --git a/chromium/cc/layers/texture_layer_impl.h b/chromium/cc/layers/texture_layer_impl.h index 49b6be2ffa1..8a75ebccaea 100644 --- a/chromium/cc/layers/texture_layer_impl.h +++ b/chromium/cc/layers/texture_layer_impl.h @@ -32,15 +32,16 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { std::unique_ptr<LayerImpl> CreateLayerImpl( LayerTreeImpl* layer_tree_impl) override; - bool IsSnapped() override; + bool IsSnappedToPixelGridInTarget() override; void PushPropertiesTo(LayerImpl* layer) override; bool WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) override; + viz::ClientResourceProvider* resource_provider) override; void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; SimpleEnclosedRegion VisibleOpaqueRegion() const override; void ReleaseResources() override; + void OnPurgeMemory() override; // These setter methods don't cause any implicit damage, so the texture client // must explicitly invalidate if they intend to cause a visible change in the @@ -92,13 +93,13 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { // True while the |transferable_resource_| is owned by this layer, and // becomes false once it is passed to another layer or to the - // LayerTreeResourceProvider, at which point we get back a |resource_id_|. + // viz::ClientResourceProvider, at which point we get back a |resource_id_|. bool own_resource_ = false; // A TransferableResource from the layer's client that will be given // to the display compositor. viz::TransferableResource transferable_resource_; // Local ResourceId for the TransferableResource, to be used with the - // compositor's LayerTreeResourceProvider in order to refer to the + // compositor's viz::ClientResourceProvider in order to refer to the // TransferableResource given to it. viz::ResourceId resource_id_ = 0; std::unique_ptr<viz::SingleReleaseCallback> release_callback_; diff --git a/chromium/cc/layers/texture_layer_impl_unittest.cc b/chromium/cc/layers/texture_layer_impl_unittest.cc index 2c626ef727c..7022e5757f1 100644 --- a/chromium/cc/layers/texture_layer_impl_unittest.cc +++ b/chromium/cc/layers/texture_layer_impl_unittest.cc @@ -52,12 +52,8 @@ TEST(TextureLayerImplTest, Occlusion) { LayerTestCommon::LayerImplTest impl; - auto* gl = impl.layer_tree_frame_sink()->context_provider()->ContextGL(); - - gpu::Mailbox mailbox; - gl->GenMailboxCHROMIUM(mailbox.name); auto resource = viz::TransferableResource::MakeGL( - std::move(mailbox), GL_LINEAR, GL_TEXTURE_2D, + gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(0x234), 0x456)); @@ -113,11 +109,9 @@ TEST(TextureLayerImplTest, ResourceNotFreedOnGpuRasterToggle) { gfx::Size layer_size(1000, 1000); gfx::Size viewport_size(1000, 1000); - auto* gl = impl.layer_tree_frame_sink()->context_provider()->ContextGL(); - viz::TransferableResource resource; resource.is_software = false; - gl->GenMailboxCHROMIUM(resource.mailbox_holder.mailbox.name); + resource.mailbox_holder.mailbox = gpu::Mailbox::Generate(); resource.mailbox_holder.sync_token = gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(0x234), 0x456); diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc index 08919e42ed9..66656e3e005 100644 --- a/chromium/cc/layers/texture_layer_unittest.cc +++ b/chromium/cc/layers/texture_layer_unittest.cc @@ -39,9 +39,10 @@ #include "cc/trees/single_thread_proxy.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/gpu/raster_context_provider.h" -#include "components/viz/common/quads/shared_bitmap.h" +#include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/resources/bitmap_allocation.h" #include "components/viz/common/resources/returned_resource.h" +#include "components/viz/common/resources/shared_bitmap.h" #include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/software_output_device.h" #include "components/viz/test/fake_output_surface.h" @@ -135,11 +136,11 @@ struct CommonResourceObjects { mailbox_name2_, GL_LINEAR, arbitrary_target2, sync_token2_); gfx::Size size(128, 128); shared_bitmap_id_ = viz::SharedBitmap::GenerateId(); - release_callback3_ = + sw_release_callback_ = base::Bind(&MockReleaseCallback::Release2, base::Unretained(&mock_callback_), shared_bitmap_id_); - resource3_ = viz::TransferableResource::MakeSoftware(shared_bitmap_id_, - size, viz::RGBA_8888); + sw_resource_ = viz::TransferableResource::MakeSoftware( + shared_bitmap_id_, size, viz::RGBA_8888); } using RepeatingReleaseCallback = @@ -151,13 +152,13 @@ struct CommonResourceObjects { MockReleaseCallback mock_callback_; RepeatingReleaseCallback release_callback1_; RepeatingReleaseCallback release_callback2_; - RepeatingReleaseCallback release_callback3_; + RepeatingReleaseCallback sw_release_callback_; gpu::SyncToken sync_token1_; gpu::SyncToken sync_token2_; viz::SharedBitmapId shared_bitmap_id_; viz::TransferableResource resource1_; viz::TransferableResource resource2_; - viz::TransferableResource resource3_; + viz::TransferableResource sw_resource_; }; class TextureLayerTest : public testing::Test { @@ -216,6 +217,93 @@ TEST_F(TextureLayerTest, CheckPropertyChangeCausesCorrectBehavior) { EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBlendBackgroundColor(true)); } +class RunOnCommitLayerTreeHostClient : public FakeLayerTreeHostClient { + public: + void set_run_on_commit_and_draw(base::OnceClosure c) { + run_on_commit_and_draw_ = std::move(c); + } + + void DidCommitAndDrawFrame() override { + if (run_on_commit_and_draw_) + std::move(run_on_commit_and_draw_).Run(); + } + + private: + base::OnceClosure run_on_commit_and_draw_; +}; + +// If the compositor is destroyed while TextureLayer has a resource in it, the +// resource should be returned to the client. https://crbug.com/857262 +TEST_F(TextureLayerTest, ShutdownWithResource) { + for (int i = 0; i < 2; ++i) { + bool gpu = i == 0; + SCOPED_TRACE(gpu); + // Make our own LayerTreeHost for this test so we can control the lifetime. + StubLayerTreeHostSingleThreadClient single_thread_client; + RunOnCommitLayerTreeHostClient client; + LayerTreeHost::InitParams params; + params.client = &client; + params.task_graph_runner = &task_graph_runner_; + params.mutator_host = animation_host_.get(); + LayerTreeSettings settings; + params.settings = &settings; + params.main_task_runner = base::ThreadTaskRunnerHandle::Get(); + auto host = + LayerTreeHost::CreateSingleThreaded(&single_thread_client, ¶ms); + + client.SetLayerTreeHost(host.get()); + client.SetUseSoftwareCompositing(!gpu); + + scoped_refptr<TextureLayer> layer = TextureLayer::CreateForMailbox(nullptr); + layer->SetIsDrawable(true); + layer->SetBounds(gfx::Size(10, 10)); + if (gpu) { + layer->SetTransferableResource( + test_data_.resource1_, + viz::SingleReleaseCallback::Create(test_data_.release_callback1_)); + } else { + layer->SetTransferableResource( + test_data_.sw_resource_, + viz::SingleReleaseCallback::Create(test_data_.sw_release_callback_)); + } + + host->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f, + viz::LocalSurfaceId()); + host->SetVisible(true); + host->SetRootLayer(layer); + + // Commit and activate the TransferableResource in the TextureLayer. + { + base::RunLoop loop; + client.set_run_on_commit_and_draw(loop.QuitClosure()); + loop.Run(); + } + + // Destroy the LayerTreeHost and the compositor-thread LayerImpl trees + // while the resource is still in the layer. The resource should be released + // back to the TextureLayer's client, but is post-tasked back so... + host = nullptr; + + // We have to wait for the posted ReleaseCallback to run. + // Our LayerTreeHostClient makes a FakeLayerTreeFrameSink which returns all + // resources when its detached, so the resources will not be in use in the + // display compositor, and will be returned as not lost. + if (gpu) { + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, _, false)) + .Times(1); + } else { + EXPECT_CALL(test_data_.mock_callback_, + Release2(test_data_.shared_bitmap_id_, _, false)) + .Times(1); + } + { + base::RunLoop loop; + loop.RunUntilIdle(); + } + } +} + class TestMailboxHolder : public TextureLayer::TransferableResourceHolder { public: using TextureLayer::TransferableResourceHolder::Create; @@ -273,8 +361,8 @@ TEST_F(TextureLayerWithResourceTest, ReplaceMailboxOnMainThreadBeforeCommit) { EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); test_layer->SetTransferableResource( - test_data_.resource3_, - viz::SingleReleaseCallback::Create(test_data_.release_callback3_)); + test_data_.sw_resource_, + viz::SingleReleaseCallback::Create(test_data_.sw_release_callback_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -759,7 +847,13 @@ class TextureLayerImplWithResourceTest : public TextureLayerTest { layer_tree_host_ = MockLayerTreeHost::Create( &fake_client_, &task_graph_runner_, animation_host_.get()); host_impl_.SetVisible(true); - EXPECT_TRUE(host_impl_.InitializeRenderer(layer_tree_frame_sink_.get())); + EXPECT_TRUE(host_impl_.InitializeFrameSink(layer_tree_frame_sink_.get())); + } + + std::unique_ptr<TextureLayerImpl> CreateTextureLayer() { + auto layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1); + layer->set_visible_layer_rect(gfx::Rect(100, 100)); + return layer; } bool WillDraw(TextureLayerImpl* layer, DrawMode mode) { @@ -785,8 +879,7 @@ TEST_F(TextureLayerImplWithResourceTest, TestWillDraw) { .Times(AnyNumber()); // Hardware mode. { - std::unique_ptr<TextureLayerImpl> impl_layer = - TextureLayerImpl::Create(host_impl_.active_tree(), 1); + std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer(); impl_layer->SetTransferableResource( test_data_.resource1_, viz::SingleReleaseCallback::Create(test_data_.release_callback1_)); @@ -802,8 +895,7 @@ TEST_F(TextureLayerImplWithResourceTest, TestWillDraw) { // Software mode. { - std::unique_ptr<TextureLayerImpl> impl_layer = - TextureLayerImpl::Create(host_impl_.active_tree(), 1); + std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer(); impl_layer->SetTransferableResource( test_data_.resource1_, viz::SingleReleaseCallback::Create(test_data_.release_callback1_)); @@ -811,26 +903,23 @@ TEST_F(TextureLayerImplWithResourceTest, TestWillDraw) { } { - std::unique_ptr<TextureLayerImpl> impl_layer = - TextureLayerImpl::Create(host_impl_.active_tree(), 1); + std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer(); impl_layer->SetTransferableResource(viz::TransferableResource(), nullptr); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } { // Software resource. - std::unique_ptr<TextureLayerImpl> impl_layer = - TextureLayerImpl::Create(host_impl_.active_tree(), 1); + std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer(); impl_layer->SetTransferableResource( - test_data_.resource3_, - viz::SingleReleaseCallback::Create(test_data_.release_callback3_)); + test_data_.sw_resource_, + viz::SingleReleaseCallback::Create(test_data_.sw_release_callback_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } // Resourceless software mode. { - std::unique_ptr<TextureLayerImpl> impl_layer = - TextureLayerImpl::Create(host_impl_.active_tree(), 1); + std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer(); impl_layer->SetTransferableResource( test_data_.resource1_, viz::SingleReleaseCallback::Create(test_data_.release_callback1_)); @@ -902,8 +991,7 @@ TEST_F(TextureLayerImplWithResourceTest, TestImplLayerCallbacks) { TEST_F(TextureLayerImplWithResourceTest, TestDestructorCallbackOnCreatedResource) { - std::unique_ptr<TextureLayerImpl> impl_layer; - impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1); + std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer(); ASSERT_TRUE(impl_layer); EXPECT_CALL(test_data_.mock_callback_, @@ -1523,6 +1611,86 @@ class SoftwareTextureLayerSwitchTreesTest : public SoftwareTextureLayerTest { SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTextureLayerSwitchTreesTest); +// Verify that duplicate SharedBitmapIds aren't registered if resources are +// purged due to memory pressure. +class SoftwareTextureLayerPurgeMemoryTest : public SoftwareTextureLayerTest { + protected: + void BeginTest() override { + PostSetNeedsCommitToMainThread(); + + const gfx::Size size(1, 1); + const viz::ResourceFormat format = viz::RGBA_8888; + + id_ = viz::SharedBitmap::GenerateId(); + bitmap_ = base::MakeRefCounted<CrossThreadSharedBitmap>( + id_, viz::bitmap_allocation::AllocateMappedBitmap(size, format), size, + format); + } + + void DidCommitAndDrawFrame() override { + step_ = layer_tree_host()->SourceFrameNumber(); + switch (step_) { + case 1: + // The test starts by inserting the TextureLayer to the tree. + root_->AddChild(texture_layer_); + // And registers a SharedBitmapId, which should be given to the + // LayerTreeFrameSink. + registration_ = texture_layer_->RegisterSharedBitmapId(id_, bitmap_); + // Give the TextureLayer a resource so it contributes to the frame. It + // doesn't need to register the SharedBitmapId otherwise. + texture_layer_->SetTransferableResource( + viz::TransferableResource::MakeSoftware(id_, gfx::Size(1, 1), + viz::RGBA_8888), + viz::SingleReleaseCallback::Create( + base::BindOnce([](const gpu::SyncToken&, bool) {}))); + break; + case 2: + // Draw again after OnPurgeMemory() was called on the impl thread so we + // can verify that duplicate SharedBitmapIds aren't registered by + // TextureLayerImpl. + texture_layer_->SetNeedsDisplay(); + break; + case 3: + // Release the TransferableResource before shutdown. + texture_layer_->ClearClient(); + break; + case 4: + EndTest(); + } + } + + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + // TextureLayerImpl will have registered the SharedBitmapId at this point. + // Call OnPurgeMemory() to ensure that the same SharedBitmapId doesn't get + // registered again on the next draw. + if (step_ == 1) + static_cast<base::MemoryCoordinatorClient*>(host_impl)->OnPurgeMemory(); + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + if (step_ == 0) { + // Before commit 1, the |texture_layer_| has no SharedBitmapId yet. + EXPECT_THAT(frame_sink_->owned_bitmaps(), testing::IsEmpty()); + verified_frames_++; + } else { + // After commit 1, we added a SharedBitmapId to |texture_layer_|. + EXPECT_THAT(frame_sink_->owned_bitmaps(), testing::ElementsAre(id_)); + verified_frames_++; + } + } + + void AfterTest() override { EXPECT_EQ(4, verified_frames_); } + + int step_ = 0; + int verified_frames_ = 0; + viz::SharedBitmapId id_; + SharedBitmapIdRegistration registration_; + scoped_refptr<CrossThreadSharedBitmap> bitmap_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTextureLayerPurgeMemoryTest); + class SoftwareTextureLayerMultipleRegisterTest : public SoftwareTextureLayerTest { protected: @@ -1747,8 +1915,10 @@ class SoftwareTextureLayerLoseFrameSinkTest : public SoftwareTextureLayerTest { texture_layer_->SetTransferableResource( viz::TransferableResource::MakeSoftware(id_, gfx::Size(1, 1), viz::RGBA_8888), - viz::SingleReleaseCallback::Create( - base::BindOnce([](const gpu::SyncToken&, bool) {}))); + viz::SingleReleaseCallback::Create(base::BindOnce( + &SoftwareTextureLayerLoseFrameSinkTest::ReleaseCallback, + base::Unretained(this)))); + EXPECT_FALSE(released_); break; case 2: // The frame sink is lost. The host will make a new one and submit @@ -1758,24 +1928,15 @@ class SoftwareTextureLayerLoseFrameSinkTest : public SoftwareTextureLayerTest { layer_tree_host()->ReleaseLayerTreeFrameSink().get(); layer_tree_host()->SetVisible(true); texture_layer_->SetNeedsDisplay(); - - // TODO(crbug.com/826886): We shouldn't need SetTransferableResource(), - // but right now the TextureLayerImpl will drop the software resource - // when the frame sink is lost. It needs to be able to handle this for - // VizDisplayCompositor. - texture_layer_->ClearClient(); - texture_layer_->SetTransferableResource( - viz::TransferableResource::MakeSoftware(id_, gfx::Size(1, 1), - viz::RGBA_8888), - viz::SingleReleaseCallback::Create( - base::BindOnce([](const gpu::SyncToken&, bool) {}))); + EXPECT_FALSE(released_); break; case 3: - // Release the TransferableResource before shutdown. + // Even though the frame sink was lost, the software resource given to + // the TextureLayer was not lost/returned. + EXPECT_FALSE(released_); + // Release the TransferableResource before shutdown, the test ends when + // it is released. texture_layer_->ClearClient(); - break; - case 4: - EndTest(); } } @@ -1804,10 +1965,20 @@ class SoftwareTextureLayerLoseFrameSinkTest : public SoftwareTextureLayerTest { } } + void ReleaseCallback(const gpu::SyncToken& token, bool lost) { + // The software resource is not released when the LayerTreeFrameSink is lost + // since software resources are not destroyed by the GPU process dying. It + // is released only after we call TextureLayer::ClearClient(). + EXPECT_EQ(layer_tree_host()->SourceFrameNumber(), 4); + released_ = true; + EndTest(); + } + void AfterTest() override { EXPECT_EQ(3, verified_frames_); } int step_ = 0; int verified_frames_ = 0; + bool released_ = false; viz::SharedBitmapId id_; SharedBitmapIdRegistration registration_; scoped_refptr<CrossThreadSharedBitmap> bitmap_; diff --git a/chromium/cc/layers/ui_resource_layer_impl.cc b/chromium/cc/layers/ui_resource_layer_impl.cc index 9821c27ad62..e34f189493d 100644 --- a/chromium/cc/layers/ui_resource_layer_impl.cc +++ b/chromium/cc/layers/ui_resource_layer_impl.cc @@ -86,7 +86,7 @@ void UIResourceLayerImpl::SetVertexOpacity(const float vertex_opacity[4]) { bool UIResourceLayerImpl::WillDraw( DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) { + viz::ClientResourceProvider* resource_provider) { if (!ui_resource_id_ || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) return false; return LayerImpl::WillDraw(draw_mode, resource_provider); diff --git a/chromium/cc/layers/ui_resource_layer_impl.h b/chromium/cc/layers/ui_resource_layer_impl.h index ed37db2cfd1..bd94720a0e2 100644 --- a/chromium/cc/layers/ui_resource_layer_impl.h +++ b/chromium/cc/layers/ui_resource_layer_impl.h @@ -19,8 +19,11 @@ namespace base { class DictionaryValue; } +namespace viz { +class ClientResourceProvider; +} + namespace cc { -class LayerTreeResourceProvider; class CC_EXPORT UIResourceLayerImpl : public LayerImpl { public: @@ -45,7 +48,7 @@ class CC_EXPORT UIResourceLayerImpl : public LayerImpl { void PushPropertiesTo(LayerImpl* layer) override; bool WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) override; + viz::ClientResourceProvider* resource_provider) override; void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; diff --git a/chromium/cc/layers/ui_resource_layer_impl_unittest.cc b/chromium/cc/layers/ui_resource_layer_impl_unittest.cc index b248d3da78e..db2f6d25863 100644 --- a/chromium/cc/layers/ui_resource_layer_impl_unittest.cc +++ b/chromium/cc/layers/ui_resource_layer_impl_unittest.cc @@ -71,7 +71,7 @@ TEST(UIResourceLayerImplTest, VerifyDrawQuads) { FakeUIResourceLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); // Make sure we're appending quads when there are valid values. gfx::Size bitmap_size(100, 100); @@ -82,6 +82,7 @@ TEST(UIResourceLayerImplTest, VerifyDrawQuads) { std::unique_ptr<UIResourceLayerImpl> layer = GenerateUIResourceLayer(&host_impl, bitmap_size, layer_size, opaque, uid); QuadSizeTest(&host_impl, std::move(layer), expected_quad_size); + host_impl.DeleteUIResource(uid); // Make sure we're not appending quads when there are invalid values. expected_quad_size = 0; @@ -92,6 +93,7 @@ TEST(UIResourceLayerImplTest, VerifyDrawQuads) { opaque, uid); QuadSizeTest(&host_impl, std::move(layer), expected_quad_size); + host_impl.DeleteUIResource(uid); } void NeedsBlendingTest(FakeUIResourceLayerTreeHostImpl* host_impl, @@ -124,7 +126,7 @@ TEST(UIResourceLayerImplTest, VerifySetOpaqueOnSkBitmap) { FakeUIResourceLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); gfx::Size bitmap_size(100, 100); gfx::Size layer_size(100, 100); @@ -133,6 +135,7 @@ TEST(UIResourceLayerImplTest, VerifySetOpaqueOnSkBitmap) { std::unique_ptr<UIResourceLayerImpl> layer = GenerateUIResourceLayer(&host_impl, bitmap_size, layer_size, opaque, uid); NeedsBlendingTest(&host_impl, std::move(layer), !opaque); + host_impl.DeleteUIResource(uid); opaque = true; layer = GenerateUIResourceLayer(&host_impl, @@ -141,6 +144,7 @@ TEST(UIResourceLayerImplTest, VerifySetOpaqueOnSkBitmap) { opaque, uid); NeedsBlendingTest(&host_impl, std::move(layer), !opaque); + host_impl.DeleteUIResource(uid); } TEST(UIResourceLayerImplTest, VerifySetOpaqueOnLayer) { @@ -151,7 +155,7 @@ TEST(UIResourceLayerImplTest, VerifySetOpaqueOnLayer) { FakeUIResourceLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); gfx::Size bitmap_size(100, 100); gfx::Size layer_size(100, 100); @@ -162,12 +166,14 @@ TEST(UIResourceLayerImplTest, VerifySetOpaqueOnLayer) { bool opaque = false; layer->SetContentsOpaque(opaque); NeedsBlendingTest(&host_impl, std::move(layer), !opaque); + host_impl.DeleteUIResource(uid); opaque = true; layer = GenerateUIResourceLayer( &host_impl, bitmap_size, layer_size, skbitmap_opaque, uid); layer->SetContentsOpaque(true); NeedsBlendingTest(&host_impl, std::move(layer), !opaque); + host_impl.DeleteUIResource(uid); } TEST(UIResourceLayerImplTest, Occlusion) { diff --git a/chromium/cc/layers/ui_resource_layer_unittest.cc b/chromium/cc/layers/ui_resource_layer_unittest.cc index e4e21aabf65..17397773b5b 100644 --- a/chromium/cc/layers/ui_resource_layer_unittest.cc +++ b/chromium/cc/layers/ui_resource_layer_unittest.cc @@ -57,7 +57,7 @@ TEST_F(UIResourceLayerTest, SetBitmap) { layer_tree_host()->SetRootLayer(test_layer); Mock::VerifyAndClearExpectations(layer_tree_host()); - EXPECT_EQ(test_layer->GetLayerTreeHostForTesting(), layer_tree_host()); + EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host()); test_layer->Update(); @@ -80,7 +80,7 @@ TEST_F(UIResourceLayerTest, SetUIResourceId) { layer_tree_host()->SetRootLayer(test_layer); Mock::VerifyAndClearExpectations(layer_tree_host()); - EXPECT_EQ(test_layer->GetLayerTreeHostForTesting(), layer_tree_host()); + EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host()); test_layer->Update(); diff --git a/chromium/cc/layers/video_frame_provider.h b/chromium/cc/layers/video_frame_provider.h index e610d4df91b..af413093231 100644 --- a/chromium/cc/layers/video_frame_provider.h +++ b/chromium/cc/layers/video_frame_provider.h @@ -39,12 +39,23 @@ class CC_EXPORT VideoFrameProvider { // Callers should use these methods to indicate when it expects and no // longer expects (respectively) to have new frames for the client. Clients // may use this information for power conservation. + // + // Note that the client may also choose to stop driving frame updates, such + // as if it believes that the frames are not visible. In this case, the + // client should report this via IsDrivingFrameUpdates(). virtual void StartRendering() = 0; virtual void StopRendering() = 0; // Notifies the client that GetCurrentFrame() will return new data. virtual void DidReceiveFrame() = 0; + // Should return true if and only if the client is actively driving frame + // updates. Note that this implies that the client has been told to + // StartRendering by the VideoFrameProvider. However, it's okay if the + // client chooses to elide these calls, for example to save power when the + // client knows that the frames are not visible. + virtual bool IsDrivingFrameUpdates() const = 0; + protected: virtual ~Client() {} }; diff --git a/chromium/cc/layers/video_frame_provider_client_impl.cc b/chromium/cc/layers/video_frame_provider_client_impl.cc index ea4499cfebf..51eb3eec062 100644 --- a/chromium/cc/layers/video_frame_provider_client_impl.cc +++ b/chromium/cc/layers/video_frame_provider_client_impl.cc @@ -177,4 +177,9 @@ void VideoFrameProviderClientImpl::DidDrawFrame() { needs_put_current_frame_ = false; } +bool VideoFrameProviderClientImpl::IsDrivingFrameUpdates() const { + // We drive frame updates any time we're rendering, even if we're off-screen. + return rendering_; +} + } // namespace cc diff --git a/chromium/cc/layers/video_frame_provider_client_impl.h b/chromium/cc/layers/video_frame_provider_client_impl.h index bc2f370e674..6c11f72b0a0 100644 --- a/chromium/cc/layers/video_frame_provider_client_impl.h +++ b/chromium/cc/layers/video_frame_provider_client_impl.h @@ -56,6 +56,7 @@ class CC_EXPORT VideoFrameProviderClientImpl void StartRendering() override; void StopRendering() override; void DidReceiveFrame() override; + bool IsDrivingFrameUpdates() const override; const VideoFrameProvider* get_provider_for_testing() const { return provider_; diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc index 4e2843f2ca3..d4a18b06d92 100644 --- a/chromium/cc/layers/video_layer_impl.cc +++ b/chromium/cc/layers/video_layer_impl.cc @@ -10,16 +10,17 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "cc/layers/video_frame_provider_client_impl.h" -#include "cc/resources/layer_tree_resource_provider.h" #include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/occlusion.h" #include "cc/trees/task_runner_provider.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/quads/stream_video_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/yuv_video_draw_quad.h" #include "components/viz/common/resources/single_release_callback.h" #include "media/base/video_frame.h" +#include "media/renderers/video_resource_updater.h" #include "ui/gfx/color_space.h" namespace cc { @@ -77,10 +78,13 @@ void VideoLayerImpl::DidBecomeActive() { } bool VideoLayerImpl::WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) { + viz::ClientResourceProvider* resource_provider) { if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) return false; + if (!LayerImpl::WillDraw(draw_mode, resource_provider)) + return false; + // Explicitly acquire and release the provider mutex so it can be held from // WillDraw to DidDraw. Since the compositor thread is in the middle of // drawing, the layer will not be destroyed before DidDraw is called. @@ -98,12 +102,9 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode, return false; } - if (!LayerImpl::WillDraw(draw_mode, resource_provider)) - return false; - if (!updater_) { const LayerTreeSettings& settings = layer_tree_impl()->settings(); - updater_ = std::make_unique<VideoResourceUpdater>( + updater_ = std::make_unique<media::VideoResourceUpdater>( layer_tree_impl()->context_provider(), layer_tree_impl()->layer_tree_frame_sink(), layer_tree_impl()->resource_provider(), @@ -158,7 +159,7 @@ void VideoLayerImpl::AppendQuads(viz::RenderPass* render_pass, GetSortingContextId(), visible_quad_rect); } -void VideoLayerImpl::DidDraw(LayerTreeResourceProvider* resource_provider) { +void VideoLayerImpl::DidDraw(viz::ClientResourceProvider* resource_provider) { LayerImpl::DidDraw(resource_provider); DCHECK(frame_.get()); diff --git a/chromium/cc/layers/video_layer_impl.h b/chromium/cc/layers/video_layer_impl.h index 30d10551b60..4b3646b0e16 100644 --- a/chromium/cc/layers/video_layer_impl.h +++ b/chromium/cc/layers/video_layer_impl.h @@ -10,12 +10,12 @@ #include "base/macros.h" #include "cc/cc_export.h" #include "cc/layers/layer_impl.h" -#include "cc/resources/video_resource_updater.h" #include "components/viz/common/resources/release_callback.h" #include "media/base/video_rotation.h" namespace media { class VideoFrame; +class VideoResourceUpdater; } namespace cc { @@ -36,10 +36,10 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl { // LayerImpl implementation. std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; bool WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* resource_provider) override; + viz::ClientResourceProvider* resource_provider) override; void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; - void DidDraw(LayerTreeResourceProvider* resource_provider) override; + void DidDraw(viz::ClientResourceProvider* resource_provider) override; SimpleEnclosedRegion VisibleOpaqueRegion() const override; void DidBecomeActive() override; void ReleaseResources() override; @@ -62,7 +62,7 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl { media::VideoRotation video_rotation_; - std::unique_ptr<VideoResourceUpdater> updater_; + std::unique_ptr<media::VideoResourceUpdater> updater_; DISALLOW_COPY_AND_ASSIGN(VideoLayerImpl); }; diff --git a/chromium/cc/layers/video_layer_impl_unittest.cc b/chromium/cc/layers/video_layer_impl_unittest.cc index fc676280302..56d917040a3 100644 --- a/chromium/cc/layers/video_layer_impl_unittest.cc +++ b/chromium/cc/layers/video_layer_impl_unittest.cc @@ -50,6 +50,7 @@ TEST(VideoLayerImplTest, Occlusion) { impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0); video_layer_impl->SetBounds(layer_size); video_layer_impl->SetDrawsContent(true); + video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size)); impl.CalcDrawProps(viewport_size); @@ -313,6 +314,7 @@ TEST(VideoLayerImplTest, SoftwareVideoFrameGeneratesYUVQuad) { impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0); video_layer_impl->SetBounds(layer_size); video_layer_impl->SetDrawsContent(true); + video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size)); impl.host_impl()->active_tree()->BuildLayerListAndPropertyTreesForTesting(); gfx::Rect occluded; @@ -350,6 +352,7 @@ TEST(VideoLayerImplTest, HibitSoftwareVideoFrameGeneratesYUVQuad) { impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0); video_layer_impl->SetBounds(layer_size); video_layer_impl->SetDrawsContent(true); + video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size)); impl.host_impl()->active_tree()->BuildLayerListAndPropertyTreesForTesting(); gfx::Rect occluded; @@ -393,6 +396,7 @@ TEST(VideoLayerImplTest, NativeYUVFrameGeneratesYUVQuad) { impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0); video_layer_impl->SetBounds(layer_size); video_layer_impl->SetDrawsContent(true); + video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size)); impl.host_impl()->active_tree()->BuildLayerListAndPropertyTreesForTesting(); gfx::Rect occluded; @@ -436,6 +440,7 @@ TEST(VideoLayerImplTest, NativeARGBFrameGeneratesTextureQuad) { impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0); video_layer_impl->SetBounds(layer_size); video_layer_impl->SetDrawsContent(true); + video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size)); impl.host_impl()->active_tree()->BuildLayerListAndPropertyTreesForTesting(); gfx::Rect occluded; diff --git a/chromium/cc/layers/viewport.cc b/chromium/cc/layers/viewport.cc index fe4371883bb..b99cee33e8a 100644 --- a/chromium/cc/layers/viewport.cc +++ b/chromium/cc/layers/viewport.cc @@ -30,7 +30,8 @@ void Viewport::Pan(const gfx::Vector2dF& delta) { gfx::Vector2dF pending_delta = delta; float page_scale = host_impl_->active_tree()->current_page_scale_factor(); pending_delta.Scale(1 / page_scale); - InnerScrollLayer()->ScrollBy(pending_delta); + scroll_tree().ScrollBy(InnerScrollNode(), pending_delta, + host_impl_->active_tree()); } Viewport::ScrollResult Viewport::ScrollBy(const gfx::Vector2dF& delta, @@ -38,7 +39,7 @@ Viewport::ScrollResult Viewport::ScrollBy(const gfx::Vector2dF& delta, bool is_direct_manipulation, bool affect_browser_controls, bool scroll_outer_viewport) { - if (!OuterScrollLayer()) + if (!OuterScrollNode()) return ScrollResult(); gfx::Vector2dF content_delta = delta; @@ -48,22 +49,17 @@ Viewport::ScrollResult Viewport::ScrollBy(const gfx::Vector2dF& delta, gfx::Vector2dF pending_content_delta = content_delta; - ScrollTree& scroll_tree = - host_impl_->active_tree()->property_trees()->scroll_tree; - ScrollNode* inner_node = - scroll_tree.Node(InnerScrollLayer()->scroll_tree_index()); + ScrollNode* inner_node = InnerScrollNode(); pending_content_delta -= host_impl_->ScrollSingleNode( inner_node, pending_content_delta, viewport_point, is_direct_manipulation, - &scroll_tree); + &scroll_tree()); ScrollResult result; if (scroll_outer_viewport) { - ScrollNode* outer_node = - scroll_tree.Node(OuterScrollLayer()->scroll_tree_index()); pending_content_delta -= host_impl_->ScrollSingleNode( - outer_node, pending_content_delta, viewport_point, - is_direct_manipulation, &scroll_tree); + OuterScrollNode(), pending_content_delta, viewport_point, + is_direct_manipulation, &scroll_tree()); } result.consumed_delta = delta - AdjustOverscroll(pending_content_delta); @@ -73,30 +69,28 @@ Viewport::ScrollResult Viewport::ScrollBy(const gfx::Vector2dF& delta, } bool Viewport::CanScroll(const ScrollState& scroll_state) const { - if (!OuterScrollLayer()) + auto* outer_node = OuterScrollNode(); + + if (!outer_node) return false; bool result = false; - ScrollTree& scroll_tree = - host_impl_->active_tree()->property_trees()->scroll_tree; - ScrollNode* inner_node = - scroll_tree.Node(InnerScrollLayer()->scroll_tree_index()); - if (inner_node) + if (auto* inner_node = InnerScrollNode()) result |= host_impl_->CanConsumeDelta(*inner_node, scroll_state); - ScrollNode* outer_node = - scroll_tree.Node(OuterScrollLayer()->scroll_tree_index()); - if (outer_node) - result |= host_impl_->CanConsumeDelta(*outer_node, scroll_state); + + result |= host_impl_->CanConsumeDelta(*outer_node, scroll_state); return result; } void Viewport::ScrollByInnerFirst(const gfx::Vector2dF& delta) { - LayerImpl* scroll_layer = InnerScrollLayer(); + gfx::Vector2dF unused_delta = scroll_tree().ScrollBy( + InnerScrollNode(), delta, host_impl_->active_tree()); - gfx::Vector2dF unused_delta = scroll_layer->ScrollBy(delta); - if (!unused_delta.IsZero() && OuterScrollLayer()) - OuterScrollLayer()->ScrollBy(unused_delta); + auto* outer_node = OuterScrollNode(); + if (!unused_delta.IsZero() && outer_node) { + scroll_tree().ScrollBy(outer_node, unused_delta, host_impl_->active_tree()); + } } bool Viewport::ShouldAnimateViewport(const gfx::Vector2dF& viewport_delta, @@ -110,26 +104,21 @@ bool Viewport::ShouldAnimateViewport(const gfx::Vector2dF& viewport_delta, gfx::Vector2dF Viewport::ScrollAnimated(const gfx::Vector2dF& delta, base::TimeDelta delayed_by) { - if (!OuterScrollLayer()) + auto* outer_node = OuterScrollNode(); + if (!outer_node) return gfx::Vector2dF(0, 0); - ScrollTree& scroll_tree = - host_impl_->active_tree()->property_trees()->scroll_tree; - float scale_factor = host_impl_->active_tree()->current_page_scale_factor(); gfx::Vector2dF scaled_delta = delta; scaled_delta.Scale(1.f / scale_factor); - ScrollNode* inner_node = - scroll_tree.Node(InnerScrollLayer()->scroll_tree_index()); + ScrollNode* inner_node = InnerScrollNode(); gfx::Vector2dF inner_delta = host_impl_->ComputeScrollDelta(*inner_node, delta); gfx::Vector2dF pending_delta = scaled_delta - inner_delta; pending_delta.Scale(scale_factor); - ScrollNode* outer_node = - scroll_tree.Node(OuterScrollLayer()->scroll_tree_index()); gfx::Vector2dF outer_delta = host_impl_->ComputeScrollDelta(*outer_node, pending_delta); @@ -142,11 +131,11 @@ gfx::Vector2dF Viewport::ScrollAnimated(const gfx::Vector2dF& delta, // viewports. bool will_animate = false; if (ShouldAnimateViewport(inner_delta, outer_delta)) { - scroll_tree.ScrollBy(outer_node, outer_delta, host_impl_->active_tree()); + scroll_tree().ScrollBy(outer_node, outer_delta, host_impl_->active_tree()); will_animate = host_impl_->ScrollAnimationCreate(inner_node, inner_delta, delayed_by); } else { - scroll_tree.ScrollBy(inner_node, inner_delta, host_impl_->active_tree()); + scroll_tree().ScrollBy(inner_node, inner_delta, host_impl_->active_tree()); will_animate = host_impl_->ScrollAnimationCreate(outer_node, outer_delta, delayed_by); } @@ -161,8 +150,7 @@ gfx::Vector2dF Viewport::ScrollAnimated(const gfx::Vector2dF& delta, } void Viewport::SnapPinchAnchorIfWithinMargin(const gfx::Point& anchor) { - gfx::SizeF viewport_size = gfx::SizeF( - host_impl_->active_tree()->InnerViewportContainerLayer()->bounds()); + gfx::SizeF viewport_size = gfx::SizeF(InnerScrollNode()->container_bounds); if (anchor.x() < kPinchZoomSnapMarginDips) pinch_anchor_adjustment_.set_x(-anchor.x()); @@ -206,19 +194,16 @@ void Viewport::PinchUpdate(float magnify_delta, const gfx::Point& anchor) { // If clamping the inner viewport scroll offset causes a change, it should // be accounted for from the intended move. - move -= InnerScrollLayer()->ClampScrollToMaxScrollOffset(); + move -= scroll_tree().ClampScrollToMaxScrollOffset(InnerScrollNode(), + host_impl_->active_tree()); Pan(move); } void Viewport::PinchEnd(const gfx::Point& anchor, bool snap_to_min) { - LayerTreeImpl* active_tree = host_impl_->active_tree(); - // TODO(mcnee): Remove the InnerViewportScrollLayer() check (and add a - // DCHECK instead), after we no longer send GesturePinch events to - // OOPIF renderers. https://crbug.com/787924 - // Snap-to-min only makes sense for mainframes, so we use the lack of an - // InnerViewportScrollLayer() to detect if this is an OOPIF. - if (snap_to_min && active_tree->InnerViewportScrollLayer()) { + if (snap_to_min) { + LayerTreeImpl* active_tree = host_impl_->active_tree(); + DCHECK(active_tree->InnerViewportScrollNode()); const float kMaxZoomForSnapToMin = 1.05f; const base::TimeDelta kSnapToMinZoomAnimationDuration = base::TimeDelta::FromMilliseconds(200); @@ -226,7 +211,8 @@ void Viewport::PinchEnd(const gfx::Point& anchor, bool snap_to_min) { float min_scale = active_tree->min_page_scale_factor(); // If the page is close to minimum scale at pinch end, snap to minimum. - if (page_scale < min_scale * kMaxZoomForSnapToMin) { + if (page_scale < min_scale * kMaxZoomForSnapToMin && + page_scale != min_scale) { gfx::PointF adjusted_anchor = gfx::PointF(anchor + pinch_anchor_adjustment_); adjusted_anchor = @@ -243,7 +229,7 @@ void Viewport::PinchEnd(const gfx::Point& anchor, bool snap_to_min) { } LayerImpl* Viewport::MainScrollLayer() const { - return OuterScrollLayer(); + return host_impl_->OuterViewportScrollLayer(); } gfx::Vector2dF Viewport::ScrollBrowserControls(const gfx::Vector2dF& delta) { @@ -282,10 +268,10 @@ gfx::Vector2dF Viewport::AdjustOverscroll(const gfx::Vector2dF& delta) const { gfx::ScrollOffset Viewport::MaxTotalScrollOffset() const { gfx::ScrollOffset offset; - offset += InnerScrollLayer()->MaxScrollOffset(); + offset += scroll_tree().MaxScrollOffset(InnerScrollNode()->id); - if (OuterScrollLayer()) - offset += OuterScrollLayer()->MaxScrollOffset(); + if (auto* outer_node = OuterScrollNode()) + offset += scroll_tree().MaxScrollOffset(outer_node->id); return offset; } @@ -293,20 +279,24 @@ gfx::ScrollOffset Viewport::MaxTotalScrollOffset() const { gfx::ScrollOffset Viewport::TotalScrollOffset() const { gfx::ScrollOffset offset; - offset += InnerScrollLayer()->CurrentScrollOffset(); + offset += scroll_tree().current_scroll_offset(InnerScrollNode()->element_id); - if (OuterScrollLayer()) - offset += OuterScrollLayer()->CurrentScrollOffset(); + if (auto* outer_node = OuterScrollNode()) + offset += scroll_tree().current_scroll_offset(outer_node->element_id); return offset; } -LayerImpl* Viewport::InnerScrollLayer() const { - return host_impl_->InnerViewportScrollLayer(); +ScrollNode* Viewport::InnerScrollNode() const { + return host_impl_->InnerViewportScrollNode(); } -LayerImpl* Viewport::OuterScrollLayer() const { - return host_impl_->OuterViewportScrollLayer(); +ScrollNode* Viewport::OuterScrollNode() const { + return host_impl_->OuterViewportScrollNode(); +} + +ScrollTree& Viewport::scroll_tree() const { + return host_impl_->active_tree()->property_trees()->scroll_tree; } } // namespace cc diff --git a/chromium/cc/layers/viewport.h b/chromium/cc/layers/viewport.h index 0d8f3afaddf..275e826919f 100644 --- a/chromium/cc/layers/viewport.h +++ b/chromium/cc/layers/viewport.h @@ -15,6 +15,7 @@ namespace cc { class LayerTreeHostImpl; +struct ScrollNode; // Encapsulates gesture handling logic on the viewport layers. The "viewport" // is made up of two scrolling layers, the inner viewport (visual) and the @@ -87,8 +88,9 @@ class CC_EXPORT Viewport { gfx::ScrollOffset MaxTotalScrollOffset() const; - LayerImpl* InnerScrollLayer() const; - LayerImpl* OuterScrollLayer() const; + ScrollNode* InnerScrollNode() const; + ScrollNode* OuterScrollNode() const; + ScrollTree& scroll_tree() const; void SnapPinchAnchorIfWithinMargin(const gfx::Point& anchor); diff --git a/chromium/cc/mojo_embedder/BUILD.gn b/chromium/cc/mojo_embedder/BUILD.gn new file mode 100644 index 00000000000..3abdc4ca5c9 --- /dev/null +++ b/chromium/cc/mojo_embedder/BUILD.gn @@ -0,0 +1,24 @@ +# 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. + +import("//cc/cc.gni") + +cc_component("mojo_embedder") { + output_name = "cc_mojo_embedder" + sources = [ + "async_layer_tree_frame_sink.cc", + "async_layer_tree_frame_sink.h", + ] + + defines = [ "CC_MOJO_EMBEDDER_IMPLEMENTATION" ] + + deps = [ + "//base", + "//cc", + "//components/viz/client", + "//components/viz/common", + "//mojo/public/cpp/bindings", + "//services/viz/public/interfaces", + ] +} diff --git a/chromium/cc/mojo_embedder/DEPS b/chromium/cc/mojo_embedder/DEPS new file mode 100644 index 00000000000..d5c8e6fc631 --- /dev/null +++ b/chromium/cc/mojo_embedder/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+mojo/public/cpp/bindings", + "+services/viz/public/interfaces/compositing", +] diff --git a/chromium/cc/mojo_embedder/README.md b/chromium/cc/mojo_embedder/README.md new file mode 100644 index 00000000000..2585c5863b2 --- /dev/null +++ b/chromium/cc/mojo_embedder/README.md @@ -0,0 +1,4 @@ +# cc/mojo_embedder/ + +This directory contains mojo bindings for connecting cc to viz via mojo. + diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc new file mode 100644 index 00000000000..8e155e4e4de --- /dev/null +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc @@ -0,0 +1,256 @@ +// 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/mojo_embedder/async_layer_tree_frame_sink.h" + +#include <utility> + +#include "base/bind.h" +#include "base/trace_event/trace_event.h" +#include "cc/trees/layer_tree_frame_sink_client.h" +#include "components/viz/client/hit_test_data_provider.h" +#include "components/viz/client/local_surface_id_provider.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/common/hit_test/hit_test_region_list.h" +#include "components/viz/common/quads/compositor_frame.h" + +namespace cc { +namespace mojo_embedder { + +AsyncLayerTreeFrameSink::InitParams::InitParams() = default; +AsyncLayerTreeFrameSink::InitParams::~InitParams() = default; + +AsyncLayerTreeFrameSink::UnboundMessagePipes::UnboundMessagePipes() = default; +AsyncLayerTreeFrameSink::UnboundMessagePipes::~UnboundMessagePipes() = default; + +bool AsyncLayerTreeFrameSink::UnboundMessagePipes::HasUnbound() const { + return client_request.is_pending() && + (compositor_frame_sink_info.is_valid() ^ + compositor_frame_sink_associated_info.is_valid()); +} + +AsyncLayerTreeFrameSink::UnboundMessagePipes::UnboundMessagePipes( + UnboundMessagePipes&& other) = default; + +AsyncLayerTreeFrameSink::AsyncLayerTreeFrameSink( + scoped_refptr<viz::ContextProvider> context_provider, + scoped_refptr<viz::RasterContextProvider> worker_context_provider, + InitParams* params) + : LayerTreeFrameSink(std::move(context_provider), + std::move(worker_context_provider), + std::move(params->compositor_task_runner), + params->gpu_memory_buffer_manager), + hit_test_data_provider_(std::move(params->hit_test_data_provider)), + local_surface_id_provider_(std::move(params->local_surface_id_provider)), + synthetic_begin_frame_source_( + std::move(params->synthetic_begin_frame_source)), + pipes_(std::move(params->pipes)), + client_binding_(this), + enable_surface_synchronization_(params->enable_surface_synchronization), + wants_animate_only_begin_frames_(params->wants_animate_only_begin_frames), + weak_factory_(this) { + DETACH_FROM_THREAD(thread_checker_); +} + +AsyncLayerTreeFrameSink::~AsyncLayerTreeFrameSink() {} + +bool AsyncLayerTreeFrameSink::BindToClient(LayerTreeFrameSinkClient* client) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (!LayerTreeFrameSink::BindToClient(client)) + return false; + + DCHECK(pipes_.HasUnbound()); + if (pipes_.compositor_frame_sink_info.is_valid()) { + compositor_frame_sink_.Bind(std::move(pipes_.compositor_frame_sink_info)); + compositor_frame_sink_.set_connection_error_with_reason_handler( + base::BindOnce(&AsyncLayerTreeFrameSink::OnMojoConnectionError, + weak_factory_.GetWeakPtr())); + compositor_frame_sink_ptr_ = compositor_frame_sink_.get(); + } else if (pipes_.compositor_frame_sink_associated_info.is_valid()) { + compositor_frame_sink_associated_.Bind( + std::move(pipes_.compositor_frame_sink_associated_info)); + compositor_frame_sink_associated_.set_connection_error_with_reason_handler( + base::BindOnce(&AsyncLayerTreeFrameSink::OnMojoConnectionError, + weak_factory_.GetWeakPtr())); + compositor_frame_sink_ptr_ = compositor_frame_sink_associated_.get(); + } + client_binding_.Bind(std::move(pipes_.client_request), + compositor_task_runner_); + + if (synthetic_begin_frame_source_) { + client->SetBeginFrameSource(synthetic_begin_frame_source_.get()); + } else { + begin_frame_source_ = std::make_unique<viz::ExternalBeginFrameSource>(this); + begin_frame_source_->OnSetBeginFrameSourcePaused(begin_frames_paused_); + client->SetBeginFrameSource(begin_frame_source_.get()); + } + + if (wants_animate_only_begin_frames_) + compositor_frame_sink_->SetWantsAnimateOnlyBeginFrames(); + + return true; +} + +void AsyncLayerTreeFrameSink::DetachFromClient() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + client_->SetBeginFrameSource(nullptr); + begin_frame_source_.reset(); + synthetic_begin_frame_source_.reset(); + client_binding_.Close(); + compositor_frame_sink_.reset(); + compositor_frame_sink_associated_.reset(); + compositor_frame_sink_ptr_ = nullptr; + LayerTreeFrameSink::DetachFromClient(); +} + +void AsyncLayerTreeFrameSink::SetLocalSurfaceId( + const viz::LocalSurfaceId& local_surface_id) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(local_surface_id.is_valid()); + DCHECK(enable_surface_synchronization_); + local_surface_id_ = local_surface_id; +} + +void AsyncLayerTreeFrameSink::SubmitCompositorFrame( + viz::CompositorFrame frame) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(compositor_frame_sink_ptr_); + DCHECK(frame.metadata.begin_frame_ack.has_damage); + DCHECK_LE(viz::BeginFrameArgs::kStartingFrameNumber, + frame.metadata.begin_frame_ack.sequence_number); + TRACE_EVENT_WITH_FLOW1( + "viz,benchmark", "Graphics.Pipeline", + TRACE_ID_GLOBAL(frame.metadata.begin_frame_ack.trace_id), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", + "SubmitCompositorFrame"); + + if (!enable_surface_synchronization_) { + local_surface_id_ = + local_surface_id_provider_->GetLocalSurfaceIdForFrame(frame); + } else { + if (local_surface_id_ == last_submitted_local_surface_id_) { + DCHECK_EQ(last_submitted_device_scale_factor_, + frame.device_scale_factor()); + DCHECK_EQ(last_submitted_size_in_pixels_.height(), + frame.size_in_pixels().height()); + DCHECK_EQ(last_submitted_size_in_pixels_.width(), + frame.size_in_pixels().width()); + } + } + + TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), + "SubmitCompositorFrame", local_surface_id_.hash()); + bool tracing_enabled; + TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), + &tracing_enabled); + + base::Optional<viz::HitTestRegionList> hit_test_region_list; + if (hit_test_data_provider_) + hit_test_region_list = hit_test_data_provider_->GetHitTestData(frame); + else + hit_test_region_list = client_->BuildHitTestData(); + + if (last_submitted_local_surface_id_ != local_surface_id_) { + last_submitted_local_surface_id_ = local_surface_id_; + last_submitted_device_scale_factor_ = frame.device_scale_factor(); + last_submitted_size_in_pixels_ = frame.size_in_pixels(); + + TRACE_EVENT_WITH_FLOW2( + TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"), + "LocalSurfaceId.Submission.Flow", + TRACE_ID_GLOBAL(local_surface_id_.submission_trace_id()), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", + "SubmitCompositorFrame", "surface_id", local_surface_id_.ToString()); + } + + compositor_frame_sink_ptr_->SubmitCompositorFrame( + local_surface_id_, std::move(frame), std::move(hit_test_region_list), + tracing_enabled ? base::TimeTicks::Now().since_origin().InMicroseconds() + : 0); +} + +void AsyncLayerTreeFrameSink::DidNotProduceFrame( + const viz::BeginFrameAck& ack) { + DCHECK(compositor_frame_sink_ptr_); + DCHECK(!ack.has_damage); + DCHECK_LE(viz::BeginFrameArgs::kStartingFrameNumber, ack.sequence_number); + compositor_frame_sink_ptr_->DidNotProduceFrame(ack); +} + +void AsyncLayerTreeFrameSink::DidAllocateSharedBitmap( + mojo::ScopedSharedBufferHandle buffer, + const viz::SharedBitmapId& id) { + DCHECK(compositor_frame_sink_ptr_); + compositor_frame_sink_ptr_->DidAllocateSharedBitmap(std::move(buffer), id); +} + +void AsyncLayerTreeFrameSink::DidDeleteSharedBitmap( + const viz::SharedBitmapId& id) { + DCHECK(compositor_frame_sink_ptr_); + compositor_frame_sink_ptr_->DidDeleteSharedBitmap(id); +} + +void AsyncLayerTreeFrameSink::DidReceiveCompositorFrameAck( + const std::vector<viz::ReturnedResource>& resources) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + client_->ReclaimResources(resources); + client_->DidReceiveCompositorFrameAck(); +} + +void AsyncLayerTreeFrameSink::DidPresentCompositorFrame( + uint32_t presentation_token, + const gfx::PresentationFeedback& feedback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + client_->DidPresentCompositorFrame(presentation_token, feedback); +} + +void AsyncLayerTreeFrameSink::OnBeginFrame(const viz::BeginFrameArgs& args) { + if (!needs_begin_frames_) { + TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline", + TRACE_ID_GLOBAL(args.trace_id), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, + "step", "ReceiveBeginFrameDiscard"); + // We had a race with SetNeedsBeginFrame(false) and still need to let the + // sink know that we didn't use this BeginFrame. + DidNotProduceFrame(viz::BeginFrameAck(args, false)); + } else { + TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline", + TRACE_ID_GLOBAL(args.trace_id), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, + "step", "ReceiveBeginFrame"); + } + if (begin_frame_source_) + begin_frame_source_->OnBeginFrame(args); +} + +void AsyncLayerTreeFrameSink::OnBeginFramePausedChanged(bool paused) { + begin_frames_paused_ = paused; + if (begin_frame_source_) + begin_frame_source_->OnSetBeginFrameSourcePaused(paused); +} + +void AsyncLayerTreeFrameSink::ReclaimResources( + const std::vector<viz::ReturnedResource>& resources) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + client_->ReclaimResources(resources); +} + +void AsyncLayerTreeFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) { + DCHECK(compositor_frame_sink_ptr_); + needs_begin_frames_ = needs_begin_frames; + compositor_frame_sink_ptr_->SetNeedsBeginFrame(needs_begin_frames); +} + +void AsyncLayerTreeFrameSink::OnMojoConnectionError( + uint32_t custom_reason, + const std::string& description) { + if (custom_reason) + DLOG(FATAL) << description; + if (client_) + client_->DidLoseLayerTreeFrameSink(); +} + +} // namespace mojo_embedder +} // namespace cc diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h new file mode 100644 index 00000000000..28bbfdf7252 --- /dev/null +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h @@ -0,0 +1,149 @@ +// 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_MOJO_EMBEDDER_ASYNC_LAYER_TREE_FRAME_SINK_H_ +#define CC_MOJO_EMBEDDER_ASYNC_LAYER_TREE_FRAME_SINK_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "cc/mojo_embedder/mojo_embedder_export.h" +#include "cc/trees/layer_tree_frame_sink.h" +#include "components/viz/common/frame_sinks/begin_frame_source.h" +#include "components/viz/common/gpu/context_provider.h" +#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" +#include "components/viz/common/surfaces/surface_id.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h" + +namespace viz { +class HitTestDataProvider; +class LocalSurfaceIdProvider; +} // namespace viz + +namespace cc { +namespace mojo_embedder { + +// A mojo-based implementation of LayerTreeFrameSink. The typically-used +// implementation for cc instances that do not share a process with the viz +// display compositor. +class CC_MOJO_EMBEDDER_EXPORT AsyncLayerTreeFrameSink + : public LayerTreeFrameSink, + public viz::mojom::CompositorFrameSinkClient, + public viz::ExternalBeginFrameSourceClient { + public: + struct CC_MOJO_EMBEDDER_EXPORT UnboundMessagePipes { + UnboundMessagePipes(); + ~UnboundMessagePipes(); + UnboundMessagePipes(UnboundMessagePipes&& other); + + bool HasUnbound() const; + + // Only one of |compositor_frame_sink_info| or + // |compositor_frame_sink_associated_info| should be set. + viz::mojom::CompositorFrameSinkPtrInfo compositor_frame_sink_info; + viz::mojom::CompositorFrameSinkAssociatedPtrInfo + compositor_frame_sink_associated_info; + viz::mojom::CompositorFrameSinkClientRequest client_request; + }; + + struct CC_MOJO_EMBEDDER_EXPORT InitParams { + InitParams(); + ~InitParams(); + + scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner; + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager = nullptr; + std::unique_ptr<viz::SyntheticBeginFrameSource> + synthetic_begin_frame_source; + std::unique_ptr<viz::HitTestDataProvider> hit_test_data_provider; + std::unique_ptr<viz::LocalSurfaceIdProvider> local_surface_id_provider; + UnboundMessagePipes pipes; + bool enable_surface_synchronization = false; + bool wants_animate_only_begin_frames = false; + }; + + AsyncLayerTreeFrameSink( + scoped_refptr<viz::ContextProvider> context_provider, + scoped_refptr<viz::RasterContextProvider> worker_context_provider, + InitParams* params); + + ~AsyncLayerTreeFrameSink() override; + + const viz::HitTestDataProvider* hit_test_data_provider() const { + return hit_test_data_provider_.get(); + } + + const viz::LocalSurfaceId& local_surface_id() const { + return local_surface_id_; + } + + // LayerTreeFrameSink implementation. + bool BindToClient(LayerTreeFrameSinkClient* client) override; + void DetachFromClient() override; + void SetLocalSurfaceId(const viz::LocalSurfaceId& local_surface_id) override; + void SubmitCompositorFrame(viz::CompositorFrame frame) override; + void DidNotProduceFrame(const viz::BeginFrameAck& ack) override; + void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer, + const viz::SharedBitmapId& id) override; + void DidDeleteSharedBitmap(const viz::SharedBitmapId& id) override; + + private: + // mojom::CompositorFrameSinkClient implementation: + void DidReceiveCompositorFrameAck( + const std::vector<viz::ReturnedResource>& resources) override; + void DidPresentCompositorFrame( + uint32_t presentation_token, + const gfx::PresentationFeedback& feedback) override; + void OnBeginFrame(const viz::BeginFrameArgs& begin_frame_args) override; + void OnBeginFramePausedChanged(bool paused) override; + void ReclaimResources( + const std::vector<viz::ReturnedResource>& resources) override; + + // ExternalBeginFrameSourceClient implementation. + void OnNeedsBeginFrames(bool needs_begin_frames) override; + + void OnMojoConnectionError(uint32_t custom_reason, + const std::string& description); + + bool begin_frames_paused_ = false; + bool needs_begin_frames_ = false; + viz::LocalSurfaceId local_surface_id_; + std::unique_ptr<viz::HitTestDataProvider> hit_test_data_provider_; + std::unique_ptr<viz::LocalSurfaceIdProvider> local_surface_id_provider_; + std::unique_ptr<viz::ExternalBeginFrameSource> begin_frame_source_; + std::unique_ptr<viz::SyntheticBeginFrameSource> synthetic_begin_frame_source_; + + // Message pipes that will be bound when BindToClient() is called. + UnboundMessagePipes pipes_; + + // One of |compositor_frame_sink_| or |compositor_frame_sink_associated_| will + // be bound after calling BindToClient(). |compositor_frame_sink_ptr_| will + // point to message pipe we want to use. + viz::mojom::CompositorFrameSink* compositor_frame_sink_ptr_ = nullptr; + viz::mojom::CompositorFrameSinkPtr compositor_frame_sink_; + viz::mojom::CompositorFrameSinkAssociatedPtr + compositor_frame_sink_associated_; + mojo::Binding<viz::mojom::CompositorFrameSinkClient> client_binding_; + + THREAD_CHECKER(thread_checker_); + const bool enable_surface_synchronization_; + const bool wants_animate_only_begin_frames_; + + viz::LocalSurfaceId last_submitted_local_surface_id_; + float last_submitted_device_scale_factor_ = 1.f; + gfx::Size last_submitted_size_in_pixels_; + + base::WeakPtrFactory<AsyncLayerTreeFrameSink> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(AsyncLayerTreeFrameSink); +}; + +} // namespace mojo_embedder +} // namespace cc + +#endif // CC_MOJO_EMBEDDER_ASYNC_LAYER_TREE_FRAME_SINK_H_ diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc new file mode 100644 index 00000000000..70643db040a --- /dev/null +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc @@ -0,0 +1,118 @@ +// 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/mojo_embedder/async_layer_tree_frame_sink.h" + +#include <memory> + +#include "base/bind.h" +#include "base/memory/scoped_refptr.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread.h" +#include "cc/test/fake_layer_tree_frame_sink_client.h" +#include "components/viz/client/local_surface_id_provider.h" +#include "components/viz/test/test_context_provider.h" +#include "components/viz/test/test_gpu_memory_buffer_manager.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace mojo_embedder { +namespace { + +// Used to track the thread DidLoseLayerTreeFrameSink() is called on (and quit +// a RunLoop). +class ThreadTrackingLayerTreeFrameSinkClient + : public FakeLayerTreeFrameSinkClient { + public: + ThreadTrackingLayerTreeFrameSinkClient( + base::PlatformThreadId* called_thread_id, + base::RunLoop* run_loop) + : called_thread_id_(called_thread_id), run_loop_(run_loop) {} + ~ThreadTrackingLayerTreeFrameSinkClient() override = default; + + // FakeLayerTreeFrameSinkClient: + void DidLoseLayerTreeFrameSink() override { + EXPECT_FALSE(did_lose_layer_tree_frame_sink_called()); + FakeLayerTreeFrameSinkClient::DidLoseLayerTreeFrameSink(); + *called_thread_id_ = base::PlatformThread::CurrentId(); + run_loop_->Quit(); + } + + private: + base::PlatformThreadId* called_thread_id_; + base::RunLoop* run_loop_; + + DISALLOW_COPY_AND_ASSIGN(ThreadTrackingLayerTreeFrameSinkClient); +}; + +TEST(AsyncLayerTreeFrameSinkTest, + DidLoseLayerTreeFrameSinkCalledOnConnectionError) { + base::Thread bg_thread("BG Thread"); + bg_thread.Start(); + + scoped_refptr<viz::TestContextProvider> provider = + viz::TestContextProvider::Create(); + viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager; + + viz::mojom::CompositorFrameSinkPtrInfo sink_info; + viz::mojom::CompositorFrameSinkRequest sink_request = + mojo::MakeRequest(&sink_info); + viz::mojom::CompositorFrameSinkClientPtr client; + viz::mojom::CompositorFrameSinkClientRequest client_request = + mojo::MakeRequest(&client); + + AsyncLayerTreeFrameSink::InitParams init_params; + init_params.compositor_task_runner = bg_thread.task_runner(); + init_params.gpu_memory_buffer_manager = &test_gpu_memory_buffer_manager; + init_params.pipes.compositor_frame_sink_info = std::move(sink_info); + init_params.pipes.client_request = std::move(client_request); + init_params.local_surface_id_provider = + std::make_unique<viz::DefaultLocalSurfaceIdProvider>(); + init_params.enable_surface_synchronization = true; + AsyncLayerTreeFrameSink layer_tree_frame_sink(std::move(provider), nullptr, + &init_params); + + base::PlatformThreadId called_thread_id = base::kInvalidThreadId; + base::RunLoop close_run_loop; + ThreadTrackingLayerTreeFrameSinkClient frame_sink_client(&called_thread_id, + &close_run_loop); + + auto bind_in_background = + [](AsyncLayerTreeFrameSink* layer_tree_frame_sink, + ThreadTrackingLayerTreeFrameSinkClient* frame_sink_client) { + layer_tree_frame_sink->BindToClient(frame_sink_client); + }; + bg_thread.task_runner()->PostTask( + FROM_HERE, base::BindOnce(bind_in_background, + base::Unretained(&layer_tree_frame_sink), + base::Unretained(&frame_sink_client))); + // Closes the pipe, which should trigger calling DidLoseLayerTreeFrameSink() + // (and quitting the RunLoop). There is no need to wait for BindToClient() + // to complete as mojo::Binding error callbacks are processed asynchronously. + sink_request = viz::mojom::CompositorFrameSinkRequest(); + close_run_loop.Run(); + + EXPECT_NE(base::kInvalidThreadId, called_thread_id); + EXPECT_EQ(called_thread_id, bg_thread.GetThreadId()); + + // DetachFromClient() has to be called on the background thread. + base::RunLoop detach_run_loop; + auto detach_in_background = [](AsyncLayerTreeFrameSink* layer_tree_frame_sink, + base::RunLoop* detach_run_loop) { + layer_tree_frame_sink->DetachFromClient(); + detach_run_loop->Quit(); + }; + bg_thread.task_runner()->PostTask( + FROM_HERE, base::BindOnce(detach_in_background, + base::Unretained(&layer_tree_frame_sink), + base::Unretained(&detach_run_loop))); + detach_run_loop.Run(); +} + +} // namespace +} // namespace mojo_embedder +} // namespace cc diff --git a/chromium/cc/mojo_embedder/mojo_embedder_export.h b/chromium/cc/mojo_embedder/mojo_embedder_export.h new file mode 100644 index 00000000000..2eb16adbd7c --- /dev/null +++ b/chromium/cc/mojo_embedder/mojo_embedder_export.h @@ -0,0 +1,29 @@ +// 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_MOJO_EMBEDDER_MOJO_EMBEDDER_EXPORT_H_ +#define CC_MOJO_EMBEDDER_MOJO_EMBEDDER_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(CC_MOJO_EMBEDDER_IMPLEMENTATION) +#define CC_MOJO_EMBEDDER_EXPORT __declspec(dllexport) +#else +#define CC_MOJO_EMBEDDER_EXPORT __declspec(dllimport) +#endif // defined(CC_MOJO_EMBEDDER_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(CC_MOJO_EMBEDDER_IMPLEMENTATION) +#define CC_MOJO_EMBEDDER_EXPORT __attribute__((visibility("default"))) +#else +#define CC_MOJO_EMBEDDER_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define CC_MOJO_EMBEDDER_EXPORT +#endif + +#endif // CC_MOJO_EMBEDDER_MOJO_EMBEDDER_EXPORT_H_ diff --git a/chromium/cc/paint/decoded_draw_image.cc b/chromium/cc/paint/decoded_draw_image.cc index acc0a263b63..32571f3efb3 100644 --- a/chromium/cc/paint/decoded_draw_image.cc +++ b/chromium/cc/paint/decoded_draw_image.cc @@ -22,11 +22,13 @@ DecodedDrawImage::DecodedDrawImage( const SkSize& src_rect_offset, const SkSize& scale_adjustment, SkFilterQuality filter_quality, + bool needs_mips, bool is_budgeted) : transfer_cache_entry_id_(transfer_cache_entry_id), src_rect_offset_(src_rect_offset), scale_adjustment_(scale_adjustment), filter_quality_(filter_quality), + transfer_cache_entry_needs_mips_(needs_mips), is_budgeted_(is_budgeted) {} DecodedDrawImage::DecodedDrawImage() diff --git a/chromium/cc/paint/decoded_draw_image.h b/chromium/cc/paint/decoded_draw_image.h index c9f0d3fe23d..3079fc48b9c 100644 --- a/chromium/cc/paint/decoded_draw_image.h +++ b/chromium/cc/paint/decoded_draw_image.h @@ -28,6 +28,7 @@ class CC_PAINT_EXPORT DecodedDrawImage { const SkSize& src_rect_offset, const SkSize& scale_adjustment, SkFilterQuality filter_quality, + bool needs_mips, bool is_budgeted); DecodedDrawImage(const DecodedDrawImage& other); DecodedDrawImage(); @@ -44,6 +45,9 @@ class CC_PAINT_EXPORT DecodedDrawImage { return std::abs(scale_adjustment_.width() - 1.f) < FLT_EPSILON && std::abs(scale_adjustment_.height() - 1.f) < FLT_EPSILON; } + bool transfer_cache_entry_needs_mips() const { + return transfer_cache_entry_needs_mips_; + } bool is_budgeted() const { return is_budgeted_; } private: @@ -52,6 +56,7 @@ class CC_PAINT_EXPORT DecodedDrawImage { SkSize src_rect_offset_; SkSize scale_adjustment_; SkFilterQuality filter_quality_; + bool transfer_cache_entry_needs_mips_ = false; bool is_budgeted_; }; diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc index 21e5044b906..cc8db474af3 100644 --- a/chromium/cc/paint/display_item_list.cc +++ b/chromium/cc/paint/display_item_list.cc @@ -56,7 +56,8 @@ void DisplayItemList::Raster(SkCanvas* canvas, } void DisplayItemList::Finalize() { - TRACE_EVENT0("cc", "DisplayItemList::Finalize"); + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "DisplayItemList::Finalize"); #if DCHECK_IS_ON() // If this fails a call to StartPaint() was not ended. DCHECK(!IsPainting()); diff --git a/chromium/cc/paint/image_transfer_cache_entry.cc b/chromium/cc/paint/image_transfer_cache_entry.cc index 493a9840a18..fe63071a29f 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.cc +++ b/chromium/cc/paint/image_transfer_cache_entry.cc @@ -4,6 +4,7 @@ #include "cc/paint/image_transfer_cache_entry.h" +#include "base/bind_helpers.h" #include "base/logging.h" #include "base/numerics/checked_math.h" #include "cc/paint/paint_op_reader.h" @@ -11,13 +12,56 @@ #include "third_party/skia/include/gpu/GrContext.h" namespace cc { +namespace { + +// TODO(ericrk): Replace calls to this with calls to SkImage::makeTextureImage, +// once that function handles colorspaces. https://crbug.com/834837 +sk_sp<SkImage> MakeTextureImage(GrContext* context, + sk_sp<SkImage> source_image, + sk_sp<SkColorSpace> target_color_space, + GrMipMapped mip_mapped) { + // Step 1: Upload image and generate mips if necessary. If we will be applying + // a color-space conversion, don't generate mips yet, instead do it after + // conversion, in step 3. + // NOTE: |target_color_space| is only passed over the transfer cache if needed + // (non-null, different from the source color space). + bool add_mips_after_color_conversion = + target_color_space && mip_mapped == GrMipMapped::kYes; + sk_sp<SkImage> uploaded_image = source_image->makeTextureImage( + context, nullptr, + add_mips_after_color_conversion ? GrMipMapped::kNo : mip_mapped); + + // Step 2: Apply a color-space conversion if necessary. + if (uploaded_image && target_color_space) { + // TODO(ericrk): consider adding in the DeleteSkImageAndPreventCaching + // optimization from GpuImageDecodeCache where we forcefully remove the + // intermediate from Skia's cache. + uploaded_image = uploaded_image->makeColorSpace(target_color_space); + } + + // Step 3: If we had a colorspace conversion, we couldn't mipmap in step 1, so + // add mips here. + if (uploaded_image && add_mips_after_color_conversion) { + // TODO(ericrk): consider adding in the DeleteSkImageAndPreventCaching + // optimization from GpuImageDecodeCache where we forcefully remove the + // intermediate from Skia's cache. + uploaded_image = + uploaded_image->makeTextureImage(context, nullptr, GrMipMapped::kYes); + } + + return uploaded_image; +} + +} // namespace ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( const SkPixmap* pixmap, - const SkColorSpace* target_color_space) + const SkColorSpace* target_color_space, + bool needs_mips) : id_(s_next_id_.GetNext()), pixmap_(pixmap), - target_color_space_(target_color_space) { + target_color_space_(target_color_space), + needs_mips_(needs_mips) { size_t target_color_space_size = target_color_space ? target_color_space->writeToMemory(nullptr) : 0u; size_t pixmap_color_space_size = @@ -25,21 +69,19 @@ ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( : 0u; // Compute and cache the size of the data. - // We write the following: - // - Image color type (uint32_t) - // - Image width (uint32_t) - // - Image height (uint32_t) - // - Pixels size (uint32_t) - // - Pixels (variable) base::CheckedNumeric<size_t> safe_size; + safe_size += PaintOpWriter::HeaderBytes(); safe_size += sizeof(uint32_t); // color type safe_size += sizeof(uint32_t); // width safe_size += sizeof(uint32_t); // height + safe_size += sizeof(uint32_t); // has mips safe_size += sizeof(size_t); // pixels size - safe_size += pixmap_->computeByteSize(); - safe_size += PaintOpWriter::HeaderBytes(); safe_size += target_color_space_size + sizeof(size_t); safe_size += pixmap_color_space_size + sizeof(size_t); + // Include 4 bytes of padding so we can always align our data pointer to a + // 4-byte boundary. + safe_size += 4; + safe_size += pixmap_->computeByteSize(); size_ = safe_size.ValueOrDie(); } @@ -62,18 +104,22 @@ bool ClientImageTransferCacheEntry::Serialize(base::span<uint8_t> data) const { // We don't need to populate the SerializeOptions here since the writer is // only used for serializing primitives. PaintOp::SerializeOptions options(nullptr, nullptr, nullptr, nullptr, nullptr, - false, SkMatrix::I()); + false, false, 0, 0, SkMatrix::I()); PaintOpWriter writer(data.data(), data.size(), options); writer.Write(pixmap_->colorType()); writer.Write(pixmap_->width()); writer.Write(pixmap_->height()); + writer.Write(static_cast<uint32_t>(needs_mips_ ? 1 : 0)); size_t pixmap_size = pixmap_->computeByteSize(); writer.WriteSize(pixmap_size); // TODO(enne): we should consider caching these in some form. writer.Write(pixmap_->colorSpace()); writer.Write(target_color_space_); + writer.AlignMemory(4); writer.WriteData(pixmap_size, pixmap_->addr()); - if (writer.size() != data.size()) + + // Size can't be 0 after serialization unless the writer has become invalid. + if (writer.size() == 0u) return false; return true; @@ -94,6 +140,8 @@ size_t ServiceImageTransferCacheEntry::CachedSize() const { bool ServiceImageTransferCacheEntry::Deserialize( GrContext* context, base::span<const uint8_t> data) { + context_ = context; + // We don't need to populate the DeSerializeOptions here since the reader is // only used for de-serializing primitives. PaintOp::DeserializeOptions options(nullptr, nullptr); @@ -104,6 +152,9 @@ bool ServiceImageTransferCacheEntry::Deserialize( reader.Read(&width); uint32_t height; reader.Read(&height); + uint32_t needs_mips; + reader.Read(&needs_mips); + has_mips_ = needs_mips; size_t pixel_size; reader.ReadSize(&pixel_size); size_ = data.size(); @@ -119,10 +170,15 @@ bool ServiceImageTransferCacheEntry::Deserialize( width, height, color_type, kPremul_SkAlphaType, pixmap_color_space); if (image_info.computeMinByteSize() > pixel_size) return false; + + // Align data to a 4-byte boundry, to match what we did when writing. + reader.AlignMemory(4); const volatile void* pixel_data = reader.ExtractReadableMemory(pixel_size); if (!reader.valid()) return false; + DCHECK(SkIsAlign4(reinterpret_cast<uintptr_t>(pixel_data))); + // Const-cast away the "volatile" on |pixel_data|. We specifically understand // that a malicious caller may change our pixels under us, and are OK with // this as the worst case scenario is visual corruption. @@ -132,26 +188,21 @@ bool ServiceImageTransferCacheEntry::Deserialize( // Depending on whether the pixmap will fit in a GPU texture, either create // a software or GPU SkImage. uint32_t max_size = context->maxTextureSize(); - bool fits_on_gpu = width <= max_size && height <= max_size; - if (fits_on_gpu) { + fits_on_gpu_ = width <= max_size && height <= max_size; + if (fits_on_gpu_) { sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr); if (!image) return false; - image_ = image->makeTextureImage(context, nullptr); - if (!image_) - return false; - if (target_color_space) { - image_ = image_->makeColorSpace(target_color_space, - SkTransferFunctionBehavior::kIgnore); - } + image_ = + MakeTextureImage(context, std::move(image), target_color_space, + needs_mips ? GrMipMapped::kYes : GrMipMapped::kNo); } else { sk_sp<SkImage> original = SkImage::MakeFromRaster(pixmap, [](const void*, void*) {}, nullptr); if (!original) return false; if (target_color_space) { - image_ = original->makeColorSpace(target_color_space, - SkTransferFunctionBehavior::kIgnore); + image_ = original->makeColorSpace(target_color_space); // If color space conversion is a noop, use original data. if (image_ == original) image_ = SkImage::MakeRasterCopy(pixmap); @@ -161,10 +212,18 @@ bool ServiceImageTransferCacheEntry::Deserialize( } } - // TODO(enne): consider adding in the DeleteSkImageAndPreventCaching + return !!image_; +} + +void ServiceImageTransferCacheEntry::EnsureMips() { + if (has_mips_) + return; + + has_mips_ = true; + // TODO(ericrk): consider adding in the DeleteSkImageAndPreventCaching // optimization from GpuImageDecodeCache where we forcefully remove the // intermediate from Skia's cache. - return image_; + image_ = image_->makeTextureImage(context_, nullptr, GrMipMapped::kYes); } } // namespace cc diff --git a/chromium/cc/paint/image_transfer_cache_entry.h b/chromium/cc/paint/image_transfer_cache_entry.h index 3e709cafd64..db31ade3796 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.h +++ b/chromium/cc/paint/image_transfer_cache_entry.h @@ -23,9 +23,9 @@ static constexpr uint32_t kInvalidImageTransferCacheEntryId = class CC_PAINT_EXPORT ClientImageTransferCacheEntry : public ClientTransferCacheEntryBase<TransferCacheEntryType::kImage> { public: - explicit ClientImageTransferCacheEntry( - const SkPixmap* pixmap, - const SkColorSpace* target_color_space); + explicit ClientImageTransferCacheEntry(const SkPixmap* pixmap, + const SkColorSpace* target_color_space, + bool needs_mips); ~ClientImageTransferCacheEntry() final; uint32_t Id() const final; @@ -38,6 +38,7 @@ class CC_PAINT_EXPORT ClientImageTransferCacheEntry uint32_t id_; const SkPixmap* const pixmap_; const SkColorSpace* const target_color_space_; + const bool needs_mips_; size_t size_ = 0; static base::AtomicSequenceNumber s_next_id_; }; @@ -56,11 +57,18 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry size_t CachedSize() const final; bool Deserialize(GrContext* context, base::span<const uint8_t> data) final; - const sk_sp<SkImage>& image() { return image_; } + bool fits_on_gpu() const { return fits_on_gpu_; } + const sk_sp<SkImage>& image() const { return image_; } + + // Ensures the cached image has mips. + void EnsureMips(); private: + GrContext* context_; sk_sp<SkImage> image_; + bool has_mips_ = false; size_t size_ = 0; + bool fits_on_gpu_ = false; }; } // namespace cc diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc index cb3168a7783..c75a03d6b14 100644 --- a/chromium/cc/paint/oop_pixeltest.cc +++ b/chromium/cc/paint/oop_pixeltest.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" +#include "cc/base/completion_event.h" #include "cc/base/region.h" #include "cc/layers/recording_source.h" #include "cc/paint/display_item_list.h" @@ -28,6 +29,7 @@ #include "gpu/ipc/gl_in_process_context.h" #include "gpu/skia_bindings/grcontext_for_gles2_interface.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkGraphics.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrContext.h" @@ -55,7 +57,7 @@ class OopPixelTest : public testing::Test { void SetUp() override { raster_context_provider_ = base::MakeRefCounted<TestInProcessContextProvider>( - /*enable_oop_rasterization=*/true); + /*enable_oop_rasterization=*/true, /*support_locking=*/true); const int raster_max_texture_size = raster_context_provider_->ContextCapabilities().max_texture_size; oop_image_cache_.reset(new GpuImageDecodeCache( @@ -64,7 +66,7 @@ class OopPixelTest : public testing::Test { gles2_context_provider_ = base::MakeRefCounted<TestInProcessContextProvider>( - /*enable_oop_rasterization=*/false); + /*enable_oop_rasterization=*/false, /*support_locking=*/true); const int gles2_max_texture_size = raster_context_provider_->ContextCapabilities().max_texture_size; gpu_image_cache_.reset(new GpuImageDecodeCache( @@ -99,6 +101,7 @@ class OopPixelTest : public testing::Test { bool preclear = false; SkColor preclear_color; ImageDecodeCache* image_cache = nullptr; + std::vector<scoped_refptr<DisplayItemList>> additional_lists; }; SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list, @@ -126,7 +129,7 @@ class OopPixelTest : public testing::Test { auto* raster_implementation = raster_context_provider_->RasterInterface(); raster_texture_id = raster_implementation->CreateTexture( false, gfx::BufferUsage::GPU_READ, viz::ResourceFormat::RGBA_8888); - raster_implementation->TexStorage2D(raster_texture_id, 1, width, height); + raster_implementation->TexStorage2D(raster_texture_id, width, height); raster_implementation->TexParameteri(raster_texture_id, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -135,30 +138,32 @@ class OopPixelTest : public testing::Test { RasterColorSpace color_space(options.color_space, ++color_space_id_); + gpu::Mailbox mailbox; + raster_implementation->ProduceTextureDirect(raster_texture_id, + mailbox.name); if (options.preclear) { raster_implementation->BeginRasterCHROMIUM( - raster_texture_id, options.preclear_color, options.msaa_sample_count, - options.use_lcd_text, options.color_type, color_space); + options.preclear_color, options.msaa_sample_count, + options.use_lcd_text, options.color_type, color_space, mailbox.name); raster_implementation->EndRasterCHROMIUM(); } // "Out of process" raster! \o/ raster_implementation->BeginRasterCHROMIUM( - raster_texture_id, options.background_color, options.msaa_sample_count, - options.use_lcd_text, options.color_type, color_space); + options.background_color, options.msaa_sample_count, + options.use_lcd_text, options.color_type, color_space, mailbox.name); raster_implementation->RasterCHROMIUM( display_item_list.get(), &image_provider, options.content_size, options.full_raster_rect, options.playback_rect, options.post_translate, options.post_scale, options.requires_clear); + for (const auto& list : options.additional_lists) { + raster_implementation->RasterCHROMIUM( + list.get(), &image_provider, options.content_size, + options.full_raster_rect, options.playback_rect, + options.post_translate, options.post_scale, options.requires_clear); + } raster_implementation->EndRasterCHROMIUM(); - - // Produce a mailbox and insert an ordering barrier (assumes the raster - // interface and gl are on the same scheduling group). - gpu::Mailbox mailbox; - raster_implementation->GenMailbox(mailbox.name); - raster_implementation->ProduceTextureDirect(raster_texture_id, - mailbox.name); raster_implementation->OrderingBarrierCHROMIUM(); EXPECT_EQ(raster_implementation->GetError(), @@ -319,6 +324,12 @@ class OopImagePixelTest : public OopPixelTest, } }; +class OopClearPixelTest : public OopPixelTest, + public ::testing::WithParamInterface<bool> { + public: + bool IsPartialRaster() const { return GetParam(); } +}; + TEST_F(OopPixelTest, DrawColor) { gfx::Rect rect(10, 10); auto display_item_list = base::MakeRefCounted<DisplayItemList>(); @@ -572,6 +583,50 @@ TEST_P(OopImagePixelTest, DrawRecordShaderWithImageScaled) { ExpectEquals(actual, expected); } +TEST_F(OopImagePixelTest, DrawRecordShaderTranslatedTileRect) { + auto paint_record = sk_make_sp<PaintOpBuffer>(); + + // Arbitrary offsets. The DrawRectOp inside the PaintShader draws + // with this offset, but the tile rect also has this offset, so they + // should cancel out, and it should be as if the DrawRectOp was at the + // origin. + int x_offset = 3901; + int y_offset = -234; + + // Shader here is a tiled 2x3 rectangle with a 1x2 green block in the + // upper left and a 10pixel wide right/bottom border. The shader + // tiling starts from the origin, so starting at 2,1 in the offset_rect + // below cuts off part of that, leaving two green i's. + PaintFlags internal_flags; + internal_flags.setColor(SK_ColorGREEN); + sk_sp<PaintOpBuffer> shader_buffer(new PaintOpBuffer); + shader_buffer->push<DrawRectOp>(SkRect::MakeXYWH(x_offset, y_offset, 1, 2), + internal_flags); + + SkRect tile_rect = SkRect::MakeXYWH(x_offset, y_offset, 2, 3); + sk_sp<PaintShader> paint_record_shader = PaintShader::MakePaintRecord( + shader_buffer, tile_rect, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, nullptr, + PaintShader::ScalingBehavior::kRasterAtScale); + + gfx::Size output_size(10, 10); + + auto display_item_list = base::MakeRefCounted<DisplayItemList>(); + display_item_list->StartPaint(); + display_item_list->push<DrawColorOp>(SK_ColorWHITE, SkBlendMode::kSrc); + display_item_list->push<ScaleOp>(2.f, 2.f); + PaintFlags raster_flags; + raster_flags.setShader(paint_record_shader); + SkRect offset_rect = SkRect::MakeXYWH(2, 1, 10, 10); + display_item_list->push<DrawRectOp>(offset_rect, raster_flags); + display_item_list->EndPaintOfUnpaired(gfx::Rect(output_size)); + display_item_list->Finalize(); + + auto actual = Raster(display_item_list, output_size); + auto expected = RasterExpectedBitmap(display_item_list, output_size); + ExpectEquals(actual, expected); +} + TEST_P(OopImagePixelTest, DrawImageWithTargetColorSpace) { SCOPED_TRACE(base::StringPrintf("UseTooLargeImage: %d, FilterQuality: %d\n", UseTooLargeImage(), FilterQuality())); @@ -724,7 +779,7 @@ TEST_F(OopPixelTest, Preclear) { ExpectEquals(actual, expected); } -TEST_F(OopPixelTest, ClearingOpaqueCorner) { +TEST_P(OopClearPixelTest, ClearingOpaqueCorner) { // Verify that clears work properly for both the right and bottom sides // of an opaque corner tile. @@ -734,7 +789,14 @@ TEST_F(OopPixelTest, ClearingOpaqueCorner) { options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(8, 7)); options.content_size = gfx::Size(options.full_raster_rect.right(), options.full_raster_rect.bottom()); - options.playback_rect = options.full_raster_rect; + if (IsPartialRaster()) { + options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1, + options.full_raster_rect.y() + 1, + options.full_raster_rect.width() - 1, + options.full_raster_rect.height() - 1); + } else { + options.playback_rect = options.full_raster_rect; + } options.background_color = SK_ColorGREEN; float arbitrary_scale = 0.25f; options.post_scale = arbitrary_scale; @@ -754,13 +816,20 @@ TEST_F(OopPixelTest, ClearingOpaqueCorner) { options.resource_size.height()), SkBitmap::kZeroPixels_AllocFlag); - // Expect a two pixel border from texels 7-9 on the column and 6-8 on row. SkCanvas canvas(bitmap); canvas.drawColor(options.preclear_color); SkPaint green; green.setColor(options.background_color); - canvas.drawRect(SkRect::MakeXYWH(7, 0, 2, 8), green); - canvas.drawRect(SkRect::MakeXYWH(0, 6, 9, 2), green); + if (IsPartialRaster()) { + // Expect a two pixel border from texels 7-9 on the column and 6-8 on row, + // ignoring the top row and left column. + canvas.drawRect(SkRect::MakeXYWH(7, 1, 2, 7), green); + canvas.drawRect(SkRect::MakeXYWH(1, 6, 8, 2), green); + } else { + // Expect a two pixel border from texels 7-9 on the column and 6-8 on row. + canvas.drawRect(SkRect::MakeXYWH(7, 0, 2, 8), green); + canvas.drawRect(SkRect::MakeXYWH(0, 6, 9, 2), green); + } ExpectEquals(oop_result, bitmap, "oop"); ExpectEquals(gpu_result, bitmap, "gpu"); @@ -820,12 +889,16 @@ TEST_F(OopPixelTest, ClearingOpaqueCornerPartialRaster) { options.content_size = gfx::Size(options.full_raster_rect.right(), options.full_raster_rect.bottom()); options.playback_rect = - gfx::Rect(arbitrary_offset.x() + 5, arbitrary_offset.y() + 3, 2, 4); + gfx::Rect(arbitrary_offset.x() + 5, arbitrary_offset.y() + 3, 2, 3); options.background_color = SK_ColorGREEN; options.requires_clear = false; options.preclear = true; options.preclear_color = SK_ColorRED; + // Verify this is internal. + EXPECT_NE(options.playback_rect.right(), options.full_raster_rect.right()); + EXPECT_NE(options.playback_rect.bottom(), options.full_raster_rect.bottom()); + // Make a non-empty but noop display list to avoid early outs. auto display_item_list = MakeNoopDisplayItemList(); @@ -846,17 +919,27 @@ TEST_F(OopPixelTest, ClearingOpaqueCornerPartialRaster) { ExpectEquals(gpu_result, bitmap, "gpu"); } -TEST_F(OopPixelTest, ClearingOpaqueRightEdge) { +TEST_P(OopClearPixelTest, ClearingOpaqueRightEdge) { // Verify that a tile that intersects the right edge of content // but not the bottom only clears the right pixels. - RasterOptions options; gfx::Point arbitrary_offset(30, 40); options.resource_size = gfx::Size(10, 10); options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(3, 10)); options.content_size = gfx::Size(options.full_raster_rect.right(), options.full_raster_rect.bottom() + 1000); - options.playback_rect = options.full_raster_rect; + if (IsPartialRaster()) { + // Ignore the left column of pixels here to force partial raster. + // Additionally ignore the bottom row of pixels to make sure + // that things are not cleared outside the rect. + options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1, + options.full_raster_rect.y(), + options.full_raster_rect.width() - 1, + options.full_raster_rect.height() - 1); + } else { + options.playback_rect = options.full_raster_rect; + } + options.background_color = SK_ColorGREEN; options.requires_clear = false; options.preclear = true; @@ -874,18 +957,23 @@ TEST_F(OopPixelTest, ClearingOpaqueRightEdge) { options.resource_size.height()), SkBitmap::kZeroPixels_AllocFlag); - // Expect a two pixel column border from texels 2-4. SkCanvas canvas(bitmap); canvas.drawColor(options.preclear_color); SkPaint green; green.setColor(options.background_color); - canvas.drawRect(SkRect::MakeXYWH(2, 0, 2, 10), green); + if (IsPartialRaster()) { + // Expect a two pixel column border from texels 2-4, ignoring the last row. + canvas.drawRect(SkRect::MakeXYWH(2, 0, 2, 9), green); + } else { + // Expect a two pixel column border from texels 2-4. + canvas.drawRect(SkRect::MakeXYWH(2, 0, 2, 10), green); + } ExpectEquals(oop_result, bitmap, "oop"); ExpectEquals(gpu_result, bitmap, "gpu"); } -TEST_F(OopPixelTest, ClearingOpaqueBottomEdge) { +TEST_P(OopClearPixelTest, ClearingOpaqueBottomEdge) { // Verify that a tile that intersects the bottom edge of content // but not the right only clears the bottom pixels. @@ -895,7 +983,17 @@ TEST_F(OopPixelTest, ClearingOpaqueBottomEdge) { options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(10, 5)); options.content_size = gfx::Size(options.full_raster_rect.right() + 1000, options.full_raster_rect.bottom()); - options.playback_rect = options.full_raster_rect; + if (IsPartialRaster()) { + // Ignore the top row of pixels here to force partial raster. + // Additionally ignore the right column of pixels to make sure + // that things are not cleared outside the rect. + options.playback_rect = gfx::Rect(options.full_raster_rect.x(), + options.full_raster_rect.y() + 1, + options.full_raster_rect.width() - 1, + options.full_raster_rect.height() - 1); + } else { + options.playback_rect = options.full_raster_rect; + } options.background_color = SK_ColorGREEN; float arbitrary_scale = 0.25f; options.post_scale = arbitrary_scale; @@ -915,12 +1013,19 @@ TEST_F(OopPixelTest, ClearingOpaqueBottomEdge) { options.resource_size.height()), SkBitmap::kZeroPixels_AllocFlag); - // Expect a two pixel border from texels 4-6 on the row SkCanvas canvas(bitmap); canvas.drawColor(options.preclear_color); SkPaint green; green.setColor(options.background_color); - canvas.drawRect(SkRect::MakeXYWH(0, 4, 10, 2), green); + + if (IsPartialRaster()) { + // Expect a two pixel border from texels 4-6 on the row, ignoring the last + // column. + canvas.drawRect(SkRect::MakeXYWH(0, 4, 9, 2), green); + } else { + // Expect a two pixel border from texels 4-6 on the row + canvas.drawRect(SkRect::MakeXYWH(0, 4, 10, 2), green); + } ExpectEquals(oop_result, bitmap, "oop"); ExpectEquals(gpu_result, bitmap, "gpu"); @@ -1200,10 +1305,12 @@ TEST_F(OopPixelTest, DrawRectColorSpace) { ExpectEquals(actual, expected); } -scoped_refptr<PaintTextBlob> buildTextBlob() { +scoped_refptr<PaintTextBlob> BuildTextBlob( + PaintTypeface typeface = PaintTypeface()) { SkFontStyle style; - PaintTypeface typeface = - PaintTypeface::FromFamilyNameAndFontStyle("monospace", style); + if (!typeface) { + typeface = PaintTypeface::FromFamilyNameAndFontStyle("monospace", style); + } PaintFont font; font.SetTypeface(typeface); @@ -1235,7 +1342,7 @@ TEST_F(OopPixelTest, DrawTextBlob) { PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); flags.setColor(SK_ColorGREEN); - display_item_list->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags); + display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags); display_item_list->EndPaintOfUnpaired(options.full_raster_rect); display_item_list->Finalize(); @@ -1256,7 +1363,7 @@ TEST_F(OopPixelTest, DrawRecordShaderWithTextScaled) { PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); flags.setColor(SK_ColorGREEN); - paint_record->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags); + paint_record->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags); auto paint_record_shader = PaintShader::MakePaintRecord( paint_record, SkRect::MakeWH(25, 25), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, nullptr); @@ -1287,7 +1394,7 @@ TEST_F(OopPixelTest, DrawRecordFilterWithTextScaled) { PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); flags.setColor(SK_ColorGREEN); - paint_record->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags); + paint_record->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags); auto paint_record_filter = sk_make_sp<RecordPaintFilter>(paint_record, SkRect::MakeWH(100, 100)); @@ -1305,7 +1412,59 @@ TEST_F(OopPixelTest, DrawRecordFilterWithTextScaled) { ExpectEquals(actual, expected); } +void ClearFontCache(CompletionEvent* event) { + SkGraphics::PurgeFontCache(); + event->Signal(); +} + +TEST_F(OopPixelTest, DrawTextMultipleRasterCHROMIUM) { + RasterOptions options; + options.resource_size = gfx::Size(100, 100); + options.content_size = options.resource_size; + options.full_raster_rect = gfx::Rect(options.content_size); + options.playback_rect = options.full_raster_rect; + options.color_space = gfx::ColorSpace::CreateSRGB(); + + auto sk_typeface_1 = SkTypeface::MakeFromName("monospace", SkFontStyle()); + auto sk_typeface_2 = SkTypeface::MakeFromName("roboto", SkFontStyle()); + + auto display_item_list = base::MakeRefCounted<DisplayItemList>(); + display_item_list->StartPaint(); + PaintFlags flags; + flags.setStyle(PaintFlags::kFill_Style); + flags.setColor(SK_ColorGREEN); + display_item_list->push<DrawTextBlobOp>( + BuildTextBlob(PaintTypeface::FromSkTypeface(sk_typeface_1)), 0u, 0u, + flags); + display_item_list->EndPaintOfUnpaired(options.full_raster_rect); + display_item_list->Finalize(); + + // Create another list with a different typeface. + auto display_item_list_2 = base::MakeRefCounted<DisplayItemList>(); + display_item_list_2->StartPaint(); + display_item_list_2->push<DrawTextBlobOp>( + BuildTextBlob(PaintTypeface::FromSkTypeface(sk_typeface_2)), 0u, 0u, + flags); + display_item_list_2->EndPaintOfUnpaired(options.full_raster_rect); + display_item_list_2->Finalize(); + + // Raster both these lists with 2 RasterCHROMIUM commands between a single + // Begin/EndRaster sequence. + options.additional_lists = {display_item_list_2}; + Raster(display_item_list, options); + + // Clear skia's font cache. No entries should remain since the service + // should unpin everything. + EXPECT_GT(SkGraphics::GetFontCacheUsed(), 0u); + CompletionEvent event; + raster_context_provider_->ExecuteOnGpuThread( + base::BindOnce(&ClearFontCache, &event)); + event.Wait(); + EXPECT_EQ(SkGraphics::GetFontCacheUsed(), 0u); +} + INSTANTIATE_TEST_CASE_P(P, OopImagePixelTest, ::testing::Bool()); +INSTANTIATE_TEST_CASE_P(P, OopClearPixelTest, ::testing::Bool()); } // namespace } // namespace cc diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h index c8f959d5067..53bde682c34 100644 --- a/chromium/cc/paint/paint_canvas.h +++ b/chromium/cc/paint/paint_canvas.h @@ -132,13 +132,6 @@ class CC_PAINT_EXPORT PaintCanvas { const SkRect& dst, const PaintFlags* flags, SrcRectConstraint constraint) = 0; - virtual void drawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const PaintFlags* flags) = 0; - void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top) { - drawBitmap(bitmap, left, top, nullptr); - } virtual void drawTextBlob(scoped_refptr<PaintTextBlob> blob, SkScalar x, diff --git a/chromium/cc/paint/paint_filter.h b/chromium/cc/paint/paint_filter.h index cab890edff5..7710d501551 100644 --- a/chromium/cc/paint/paint_filter.h +++ b/chromium/cc/paint/paint_filter.h @@ -93,14 +93,6 @@ class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { return 0; return cached_sk_filter_->countInputs(); } - std::string ToString() const { - if (!cached_sk_filter_) - return "Invalid filter"; - - SkString str; - cached_sk_filter_->toString(&str); - return str.c_str(); - } const CropRect* crop_rect() const { return base::OptionalOrNullptr(crop_rect_); } diff --git a/chromium/cc/paint/paint_image.cc b/chromium/cc/paint/paint_image.cc index 805520f8b37..34bb6759db0 100644 --- a/chromium/cc/paint/paint_image.cc +++ b/chromium/cc/paint/paint_image.cc @@ -8,6 +8,7 @@ #include "base/atomic_sequence_num.h" #include "base/hash.h" +#include "cc/paint/paint_image_builder.h" #include "cc/paint/paint_image_generator.h" #include "cc/paint/paint_record.h" #include "cc/paint/skia_paint_image_generator.h" @@ -82,6 +83,18 @@ PaintImage::ContentId PaintImage::GetNextContentId() { return g_next_image_content_id.GetNext(); } +// static +PaintImage PaintImage::CreateFromBitmap(SkBitmap bitmap) { + if (bitmap.drawsNothing()) + return PaintImage(); + + return PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_image(SkImage::MakeFromBitmap(bitmap), + PaintImage::GetNextContentId()) + .TakePaintImage(); +} + const sk_sp<SkImage>& PaintImage::GetSkImage() const { return cached_sk_image_; } @@ -137,10 +150,8 @@ SkISize PaintImage::GetSupportedDecodeSize( // TODO(vmpstr): If this image is using subset_rect, then we don't support // decoding to any scale other than the original. See the comment in Decode() // explaining this in more detail. - // TODO(vmpstr): For now, always decode to the original size. This can be - // enabled with the following code, and should be done as a follow-up. - // if (paint_image_generator_ && subset_rect_.IsEmpty()) - // return paint_image_generator_->GetSupportedDecodeSize(requested_size); + if (paint_image_generator_ && subset_rect_.IsEmpty()) + return paint_image_generator_->GetSupportedDecodeSize(requested_size); return SkISize::Make(width(), height()); } @@ -217,8 +228,7 @@ bool PaintImage::DecodeFromSkImage(void* memory, auto image = GetSkImageForFrame(frame_index); DCHECK(image); if (color_space) { - image = - image->makeColorSpace(color_space, SkTransferFunctionBehavior::kIgnore); + image = image->makeColorSpace(color_space); if (!image) return false; } diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h index d45fe686ccb..f3f91350d83 100644 --- a/chromium/cc/paint/paint_image.h +++ b/chromium/cc/paint/paint_image.h @@ -94,6 +94,10 @@ class CC_PAINT_EXPORT PaintImage { static Id GetNextId(); static ContentId GetNextContentId(); + // Creates a PaintImage wrapping |bitmap|. Note that the pixels will be copied + // unless the bitmap is marked immutable. + static PaintImage CreateFromBitmap(SkBitmap bitmap); + PaintImage(); PaintImage(const PaintImage& other); PaintImage(PaintImage&& other); @@ -158,6 +162,9 @@ class CC_PAINT_EXPORT PaintImage { // Returns the total number of frames known to exist in this image. size_t FrameCount() const; + // Returns an SkImage for the frame at |index|. + sk_sp<SkImage> GetSkImageForFrame(size_t index) const; + std::string ToString() const; private: @@ -180,9 +187,6 @@ class CC_PAINT_EXPORT PaintImage { void CreateSkImage(); PaintImage MakeSubset(const gfx::Rect& subset) const; - // Returns an SkImage for the frame at |index|. - sk_sp<SkImage> GetSkImageForFrame(size_t index) const; - sk_sp<SkImage> sk_image_; sk_sp<PaintRecord> paint_record_; gfx::Rect paint_record_rect_; diff --git a/chromium/cc/paint/paint_image_unittest.cc b/chromium/cc/paint/paint_image_unittest.cc index 78a2240bdce..d8c59fcaef7 100644 --- a/chromium/cc/paint/paint_image_unittest.cc +++ b/chromium/cc/paint/paint_image_unittest.cc @@ -82,4 +82,27 @@ TEST(PaintImageTest, DecodesCorrectFrames) { generator->reset_frames_decoded(); } +TEST(PaintImageTest, SupportedDecodeSize) { + SkISize full_size = SkISize::Make(10, 10); + std::vector<SkISize> supported_sizes = {SkISize::Make(5, 5)}; + std::vector<FrameMetadata> frames = {FrameMetadata()}; + sk_sp<FakePaintImageGenerator> generator = + sk_make_sp<FakePaintImageGenerator>( + SkImageInfo::MakeN32Premul(full_size.width(), full_size.height()), + frames, true, supported_sizes); + PaintImage image = PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_paint_image_generator(generator) + .set_frame_index(0u) + .TakePaintImage(); + EXPECT_EQ(image.GetSupportedDecodeSize(supported_sizes[0]), + supported_sizes[0]); + + PaintImage subset = PaintImageBuilder::WithCopy(image) + .make_subset(gfx::Rect(8, 8)) + .TakePaintImage(); + EXPECT_EQ(subset.GetSupportedDecodeSize(supported_sizes[0]), + SkISize::Make(8, 8)); +} + } // namespace cc diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc index 45ae2526f15..13867d477a2 100644 --- a/chromium/cc/paint/paint_op_buffer.cc +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -22,6 +22,8 @@ namespace { DrawImage CreateDrawImage(const PaintImage& image, const PaintFlags* flags, const SkMatrix& matrix) { + if (!image) + return DrawImage(); return DrawImage(image, SkIRect::MakeWH(image.width(), image.height()), flags ? flags->getFilterQuality() : kLow_SkFilterQuality, matrix); @@ -325,6 +327,9 @@ PaintOp::SerializeOptions::SerializeOptions( SkStrikeServer* strike_server, SkColorSpace* color_space, bool can_use_lcd_text, + bool context_supports_distance_field_text, + int max_texture_size, + size_t max_texture_bytes, const SkMatrix& original_ctm) : image_provider(image_provider), transfer_cache(transfer_cache), @@ -332,8 +337,16 @@ PaintOp::SerializeOptions::SerializeOptions( strike_server(strike_server), color_space(color_space), can_use_lcd_text(can_use_lcd_text), + context_supports_distance_field_text( + context_supports_distance_field_text), + max_texture_size(max_texture_size), + max_texture_bytes(max_texture_bytes), original_ctm(original_ctm) {} +PaintOp::SerializeOptions::SerializeOptions(const SerializeOptions&) = default; +PaintOp::SerializeOptions& PaintOp::SerializeOptions::operator=( + const SerializeOptions&) = default; + PaintOp::DeserializeOptions::DeserializeOptions( TransferCacheDeserializeHelper* transfer_cache, SkStrikeClient* strike_client) @@ -448,11 +461,14 @@ size_t DrawImageRectOp::Serialize(const PaintOp* base_op, serialized_flags = &op->flags; helper.Write(*serialized_flags); + // This adjustment mirrors DiscardableImageMap::GatherDiscardableImage logic. + SkMatrix matrix = options.canvas->getTotalMatrix(); + matrix.postConcat( + SkMatrix::MakeRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit)); // Note that we don't request subsets here since the GpuImageCache has no // optimizations for using subsets. SkSize scale_adjustment = SkSize::Make(1.f, 1.f); - helper.Write(CreateDrawImage(op->image, serialized_flags, - options.canvas->getTotalMatrix()), + helper.Write(CreateDrawImage(op->image, serialized_flags, matrix), &scale_adjustment); helper.AlignMemory(alignof(SkScalar)); helper.Write(scale_adjustment.width()); diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h index f803f0f27bb..c1477a14281 100644 --- a/chromium/cc/paint/paint_op_buffer.h +++ b/chromium/cc/paint/paint_op_buffer.h @@ -146,7 +146,12 @@ class CC_PAINT_EXPORT PaintOp { SkStrikeServer* strike_server, SkColorSpace* color_space, bool can_use_lcd_text, + bool context_supports_distance_field_text, + int max_texture_size, + size_t max_texture_bytes, const SkMatrix& original_ctm); + SerializeOptions(const SerializeOptions&); + SerializeOptions& operator=(const SerializeOptions&); // Required. ImageProvider* image_provider = nullptr; @@ -155,9 +160,12 @@ class CC_PAINT_EXPORT PaintOp { SkStrikeServer* strike_server = nullptr; SkColorSpace* color_space = nullptr; bool can_use_lcd_text = false; + bool context_supports_distance_field_text = true; + int max_texture_size = 0; + size_t max_texture_bytes = 0.f; + SkMatrix original_ctm = SkMatrix::I(); // Optional. - SkMatrix original_ctm = SkMatrix::I(); // The flags to use when serializing this op. This can be used to override // the flags serialized with the op. Valid only for PaintOpWithFlags. const PaintFlags* flags_to_serialize = nullptr; @@ -591,7 +599,10 @@ class CC_PAINT_EXPORT DrawOvalOp final : public PaintOpWithFlags { const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); - bool IsValid() const { return flags.IsValid() && oval.isFinite(); } + bool IsValid() const { + // Reproduce SkRRect::isValid without converting. + return flags.IsValid() && oval.isFinite() && oval.isSorted(); + } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); diff --git a/chromium/cc/paint/paint_op_buffer_fuzzer.cc b/chromium/cc/paint/paint_op_buffer_fuzzer.cc index dfbbf34c49e..77070e59cec 100644 --- a/chromium/cc/paint/paint_op_buffer_fuzzer.cc +++ b/chromium/cc/paint/paint_op_buffer_fuzzer.cc @@ -37,11 +37,12 @@ class FontSupport : public gpu::ServiceFontManager::Client { private: scoped_refptr<gpu::Buffer> CreateBuffer(uint32_t shm_id) { - std::unique_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); static const size_t kBufferSize = 2048u; - shared_memory->CreateAndMapAnonymous(kBufferSize); - auto buffer = - gpu::MakeBufferFromSharedMemory(std::move(shared_memory), kBufferSize); + base::UnsafeSharedMemoryRegion shared_memory = + base::UnsafeSharedMemoryRegion::Create(kBufferSize); + base::WritableSharedMemoryMapping mapping = shared_memory.Map(); + auto buffer = gpu::MakeBufferFromSharedMemory(std::move(shared_memory), + std::move(mapping)); buffers_[shm_id] = buffer; return buffer; } @@ -49,37 +50,15 @@ class FontSupport : public gpu::ServiceFontManager::Client { base::flat_map<uint32_t, scoped_refptr<gpu::Buffer>> buffers_; }; -// Deserialize an arbitrary number of cc::PaintOps and raster them -// using gpu raster into an SkCanvas. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size <= sizeof(size_t)) - return 0; - - static Environment* env = new Environment(); - ALLOW_UNUSED_LOCAL(env); - base::CommandLine::Init(0, nullptr); - const size_t kMaxSerializedSize = 1000000; +void Raster(scoped_refptr<viz::TestContextProvider> context_provider, + SkStrikeClient* strike_client, + const uint8_t* data, + size_t size) { const size_t kRasterDimension = 32; - - // Partition the data to use some bytes for populating the font cache. - size_t bytes_for_fonts = data[0]; - if (bytes_for_fonts > size) - bytes_for_fonts = size / 2; - - FontSupport font_support; - gpu::ServiceFontManager font_manager(&font_support); - std::vector<SkDiscardableHandleId> locked_handles; - if (bytes_for_fonts > 0u) { - font_manager.Deserialize(reinterpret_cast<const char*>(data), - bytes_for_fonts, &locked_handles); - data += bytes_for_fonts; - size -= bytes_for_fonts; - } + const size_t kMaxSerializedSize = 1000000; SkImageInfo image_info = SkImageInfo::MakeN32( kRasterDimension, kRasterDimension, kOpaque_SkAlphaType); - scoped_refptr<viz::TestContextProvider> context_provider = - viz::TestContextProvider::Create(); context_provider->BindToCurrentThread(); sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget( context_provider->GrContext(), SkBudgeted::kYes, image_info); @@ -87,8 +66,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { cc::PlaybackParams params(nullptr, canvas->getTotalMatrix()); cc::TransferCacheTestHelper transfer_cache_helper; - cc::PaintOp::DeserializeOptions deserialize_options( - &transfer_cache_helper, font_manager.strike_client()); + cc::PaintOp::DeserializeOptions deserialize_options(&transfer_cache_helper, + strike_client); // Need 4 bytes to be able to read the type/skip. while (size >= 4) { @@ -117,6 +96,45 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { size -= bytes_read; data += bytes_read; } +} + +// Deserialize an arbitrary number of cc::PaintOps and raster them +// using gpu raster into an SkCanvas. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= sizeof(size_t)) + return 0; + + static Environment* env = new Environment(); + ALLOW_UNUSED_LOCAL(env); + base::CommandLine::Init(0, nullptr); + + // Partition the data to use some bytes for populating the font cache. + size_t bytes_for_fonts = data[0]; + if (bytes_for_fonts > size) + bytes_for_fonts = size / 2; + + FontSupport font_support; + gpu::ServiceFontManager font_manager(&font_support); + std::vector<SkDiscardableHandleId> locked_handles; + if (bytes_for_fonts > 0u) { + font_manager.Deserialize(reinterpret_cast<const char*>(data), + bytes_for_fonts, &locked_handles); + data += bytes_for_fonts; + size -= bytes_for_fonts; + } + + auto context_provider_no_support = viz::TestContextProvider::Create(); + context_provider_no_support->BindToCurrentThread(); + CHECK(!context_provider_no_support->GrContext()->supportsDistanceFieldText()); + Raster(context_provider_no_support, font_manager.strike_client(), data, size); + + auto context_provider_with_support = viz::TestContextProvider::Create( + std::string("GL_OES_standard_derivatives")); + context_provider_with_support->BindToCurrentThread(); + CHECK( + context_provider_with_support->GrContext()->supportsDistanceFieldText()); + Raster(context_provider_with_support, font_manager.strike_client(), data, + size); font_manager.Unlock(locked_handles); return 0; diff --git a/chromium/cc/paint/paint_op_buffer_serializer.cc b/chromium/cc/paint/paint_op_buffer_serializer.cc index 869d7c5403b..cf4c77a923f 100644 --- a/chromium/cc/paint/paint_op_buffer_serializer.cc +++ b/chromium/cc/paint/paint_op_buffer_serializer.cc @@ -25,7 +25,7 @@ class ScopedFlagsOverride { PaintOp::SerializeOptions* options_; }; -// Copied from LayerTreeResourceProvider. +// Copied from viz::ClientResourceProvider. SkSurfaceProps ComputeSurfaceProps(bool can_use_lcd_text) { uint32_t flags = 0; // Use unknown pixel geometry to disable LCD text. @@ -44,6 +44,18 @@ PlaybackParams MakeParams(const SkCanvas* canvas) { return PlaybackParams(nullptr, canvas->getTotalMatrix()); } +SkTextBlobCacheDiffCanvas::Settings MakeCanvasSettings( + bool context_supports_distance_field_text, + int max_texture_size, + size_t max_texture_bytes) { + SkTextBlobCacheDiffCanvas::Settings settings; + settings.fContextSupportsDistanceFieldText = + context_supports_distance_field_text; + settings.fMaxTextureSize = max_texture_size; + settings.fMaxTextureBytes = max_texture_bytes; + return settings; +} + // Use half of the max int as the extent for the SkNoDrawCanvas. The correct // clip is applied to the canvas during serialization. const int kMaxExtent = std::numeric_limits<int>::max() >> 1; @@ -56,18 +68,26 @@ PaintOpBufferSerializer::PaintOpBufferSerializer( TransferCacheSerializeHelper* transfer_cache, SkStrikeServer* strike_server, SkColorSpace* color_space, - bool can_use_lcd_text) + bool can_use_lcd_text, + bool context_supports_distance_field_text, + int max_texture_size, + size_t max_texture_bytes) : serialize_cb_(std::move(serialize_cb)), image_provider_(image_provider), transfer_cache_(transfer_cache), strike_server_(strike_server), color_space_(color_space), can_use_lcd_text_(can_use_lcd_text), + context_supports_distance_field_text_( + context_supports_distance_field_text), text_blob_canvas_(kMaxExtent, kMaxExtent, SkMatrix::I(), ComputeSurfaceProps(can_use_lcd_text), - strike_server) { + strike_server, + MakeCanvasSettings(context_supports_distance_field_text, + max_texture_size, + max_texture_bytes)) { DCHECK(serialize_cb_); canvas_ = SkCreateColorSpaceXformCanvas(&text_blob_canvas_, sk_ref_sp<SkColorSpace>(color_space)); @@ -86,9 +106,7 @@ void PaintOpBufferSerializer::Serialize(const PaintOpBuffer* buffer, // matrix, as they are only used for serializing the preamble and the initial // save / final restore. SerializeBuffer will create its own SerializeOptions // and PlaybackParams based on the post-preamble canvas. - PaintOp::SerializeOptions options( - image_provider_, transfer_cache_, canvas_.get(), strike_server_, - color_space_, can_use_lcd_text_, canvas_->getTotalMatrix()); + PaintOp::SerializeOptions options = MakeSerializeOptions(); PlaybackParams params = MakeParams(canvas_.get()); Save(options, params); @@ -110,9 +128,7 @@ void PaintOpBufferSerializer::Serialize( const SkMatrix& post_matrix_for_analysis) { DCHECK(canvas_->getTotalMatrix().isIdentity()); - PaintOp::SerializeOptions options( - image_provider_, transfer_cache_, canvas_.get(), strike_server_, - color_space_, can_use_lcd_text_, canvas_->getTotalMatrix()); + PaintOp::SerializeOptions options = MakeSerializeOptions(); PlaybackParams params = MakeParams(canvas_.get()); // TODO(khushalsagar): remove this clip rect if it's not needed. @@ -131,6 +147,72 @@ void PaintOpBufferSerializer::Serialize( SerializeBuffer(buffer, nullptr); } +// This function needs to have the exact same behavior as +// RasterSource::ClearForOpaqueRaster. +void PaintOpBufferSerializer::ClearForOpaqueRaster( + const Preamble& preamble, + const PaintOp::SerializeOptions& options, + const PlaybackParams& params) { + // Clear opaque raster sources. Opaque rasters sources guarantee that all + // pixels inside the opaque region are painted. However, due to scaling + // it's possible that the last row and column might include pixels that + // are not painted. Because this raster source is required to be opaque, + // we may need to do extra clearing outside of the clip. This needs to + // be done for both full and partial raster. + + // The last texel of this content is not guaranteed to be fully opaque, so + // inset by one to generate the fully opaque coverage rect. This rect is + // in device space. + SkIRect coverage_device_rect = SkIRect::MakeWH( + preamble.content_size.width() - preamble.full_raster_rect.x() - 1, + preamble.content_size.height() - preamble.full_raster_rect.y() - 1); + + // If not fully covered, we need to clear one texel inside the coverage + // rect (because of blending during raster) and one texel outside the canvas + // bitmap rect (because of bilinear filtering during draw). See comments + // in RasterSource. + SkIRect device_column = SkIRect::MakeXYWH(coverage_device_rect.right(), 0, 2, + coverage_device_rect.bottom()); + // row includes the corner, column excludes it. + SkIRect device_row = SkIRect::MakeXYWH(0, coverage_device_rect.bottom(), + coverage_device_rect.right() + 2, 2); + + bool right_edge = + preamble.content_size.width() == preamble.playback_rect.right(); + bool bottom_edge = + preamble.content_size.height() == preamble.playback_rect.bottom(); + + // If the playback rect is touching either edge of the content rect + // extend it by one pixel to include the extra texel outside the canvas + // bitmap rect that was added to device column and row above. + SkIRect playback_device_rect = SkIRect::MakeXYWH( + preamble.playback_rect.x() - preamble.full_raster_rect.x(), + preamble.playback_rect.y() - preamble.full_raster_rect.y(), + preamble.playback_rect.width() + (right_edge ? 1 : 0), + preamble.playback_rect.height() + (bottom_edge ? 1 : 0)); + + // Intersect the device column and row with the playback rect and only + // clear inside of that rect if needed. + if (device_column.intersect(playback_device_rect)) { + Save(options, params); + ClipRectOp clip_op(SkRect::MakeFromIRect(device_column), + SkClipOp::kIntersect, false); + SerializeOp(&clip_op, options, params); + DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc); + SerializeOp(&clear_op, options, params); + RestoreToCount(1, options, params); + } + if (device_row.intersect(playback_device_rect)) { + Save(options, params); + ClipRectOp clip_op(SkRect::MakeFromIRect(device_row), SkClipOp::kIntersect, + false); + SerializeOp(&clip_op, options, params); + DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc); + SerializeOp(&clear_op, options, params); + RestoreToCount(1, options, params); + } +} + void PaintOpBufferSerializer::SerializePreamble( const Preamble& preamble, const PaintOp::SerializeOptions& options, @@ -139,58 +221,17 @@ void PaintOpBufferSerializer::SerializePreamble( << "full: " << preamble.full_raster_rect.ToString() << ", playback: " << preamble.playback_rect.ToString(); - // Should full clears be clipped? bool is_partial_raster = preamble.full_raster_rect != preamble.playback_rect; - - // If rastering the entire tile, clear pre-clip. This is so that any - // external texels outside of the playback rect also get cleared. There's - // not enough information at this point to know if this texture is being - // reused from another tile, so the external texels could have been - // cleared to some wrong value. - if (preamble.requires_clear && !is_partial_raster) { - // If the tile is transparent, then just clear the whole thing. + if (!preamble.requires_clear) { + ClearForOpaqueRaster(preamble, options, params); + } else if (!is_partial_raster) { + // If rastering the entire tile, clear to transparent pre-clip. This is so + // that any external texels outside of the playback rect also get cleared. + // There's not enough information at this point to know if this texture is + // being reused from another tile, so the external texels could have been + // cleared to some wrong value. DrawColorOp clear(SK_ColorTRANSPARENT, SkBlendMode::kSrc); SerializeOp(&clear, options, params); - } else if (!is_partial_raster) { - // The last texel of this content is not guaranteed to be fully opaque, so - // inset by one to generate the fully opaque coverage rect . This rect is - // in device space. - SkIRect coverage_device_rect = SkIRect::MakeWH( - preamble.content_size.width() - preamble.full_raster_rect.x() - 1, - preamble.content_size.height() - preamble.full_raster_rect.y() - 1); - - SkIRect playback_device_rect = gfx::RectToSkIRect(preamble.playback_rect); - playback_device_rect.fLeft -= preamble.full_raster_rect.x(); - playback_device_rect.fTop -= preamble.full_raster_rect.y(); - - // If not fully covered, we need to clear one texel inside the coverage rect - // (because of blending during raster) and one texel outside the full raster - // rect (because of bilinear filtering during draw). See comments in - // RasterSource. - SkIRect device_column = SkIRect::MakeXYWH(coverage_device_rect.right(), 0, - 2, coverage_device_rect.bottom()); - // row includes the corner, column excludes it. - SkIRect device_row = SkIRect::MakeXYWH(0, coverage_device_rect.bottom(), - coverage_device_rect.right() + 2, 2); - // Only bother clearing if we need to. - if (SkIRect::Intersects(device_column, playback_device_rect)) { - Save(options, params); - ClipRectOp clip_op(SkRect::MakeFromIRect(device_column), - SkClipOp::kIntersect, false); - SerializeOp(&clip_op, options, params); - DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc); - SerializeOp(&clear_op, options, params); - RestoreToCount(1, options, params); - } - if (SkIRect::Intersects(device_row, playback_device_rect)) { - Save(options, params); - ClipRectOp clip_op(SkRect::MakeFromIRect(device_row), - SkClipOp::kIntersect, false); - SerializeOp(&clip_op, options, params); - DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc); - SerializeOp(&clear_op, options, params); - RestoreToCount(1, options, params); - } } if (!preamble.full_raster_rect.OffsetFromOrigin().IsZero()) { @@ -230,9 +271,7 @@ void PaintOpBufferSerializer::SerializeBuffer( const PaintOpBuffer* buffer, const std::vector<size_t>* offsets) { DCHECK(buffer); - PaintOp::SerializeOptions options( - image_provider_, transfer_cache_, canvas_.get(), strike_server_, - color_space_, can_use_lcd_text_, canvas_->getTotalMatrix()); + PaintOp::SerializeOptions options = MakeSerializeOptions(); PlaybackParams params = MakeParams(canvas_.get()); for (PaintOpBuffer::PlaybackFoldingIterator iter(buffer, offsets); iter; @@ -301,6 +340,16 @@ bool PaintOpBufferSerializer::SerializeOp( DCHECK_GE(bytes, 4u); DCHECK_EQ(bytes % PaintOpBuffer::PaintOpAlign, 0u); + // Only 2 types of ops need to played on the analysis canvas. + // 1) Non-draw ops which affect the transform/clip state on the canvas, since + // we need the correct ctm at which text and images will be rasterized, and + // the clip rect so we can skip sending data for ops which will not be + // rasterized. + // 2) DrawTextBlob ops since they need to be analyzed by the cache diff canvas + // to serialize/lock the requisite glyphs for this op. + if (op->IsDrawOp() && op->GetType() != PaintOpType::DrawTextBlob) + return true; + if (op->IsPaintOpWithFlags() && options.flags_to_serialize) { static_cast<const PaintOpWithFlags*>(op)->RasterWithFlags( canvas_.get(), options.flags_to_serialize, params); @@ -327,6 +376,13 @@ void PaintOpBufferSerializer::RestoreToCount( } } +PaintOp::SerializeOptions PaintOpBufferSerializer::MakeSerializeOptions() { + return PaintOp::SerializeOptions( + image_provider_, transfer_cache_, canvas_.get(), strike_server_, + color_space_, can_use_lcd_text_, context_supports_distance_field_text_, + max_texture_size_, max_texture_bytes_, canvas_->getTotalMatrix()); +} + SimpleBufferSerializer::SimpleBufferSerializer( void* memory, size_t size, @@ -334,7 +390,10 @@ SimpleBufferSerializer::SimpleBufferSerializer( TransferCacheSerializeHelper* transfer_cache, SkStrikeServer* strike_server, SkColorSpace* color_space, - bool can_use_lcd_text) + bool can_use_lcd_text, + bool context_supports_distance_field_text, + int max_texture_size, + size_t max_texture_bytes) : PaintOpBufferSerializer( base::Bind(&SimpleBufferSerializer::SerializeToMemory, base::Unretained(this)), @@ -342,7 +401,10 @@ SimpleBufferSerializer::SimpleBufferSerializer( transfer_cache, strike_server, color_space, - can_use_lcd_text), + can_use_lcd_text, + context_supports_distance_field_text, + max_texture_size, + max_texture_bytes), memory_(memory), total_(size) {} diff --git a/chromium/cc/paint/paint_op_buffer_serializer.h b/chromium/cc/paint/paint_op_buffer_serializer.h index 9f4f21cfa2d..1858e60221f 100644 --- a/chromium/cc/paint/paint_op_buffer_serializer.h +++ b/chromium/cc/paint/paint_op_buffer_serializer.h @@ -23,7 +23,10 @@ class CC_PAINT_EXPORT PaintOpBufferSerializer { TransferCacheSerializeHelper* transfer_cache, SkStrikeServer* strike_server, SkColorSpace* color_space, - bool can_use_lcd_text); + bool can_use_lcd_text, + bool context_supports_distance_field_text, + int max_texture_size, + size_t max_texture_bytes); virtual ~PaintOpBufferSerializer(); struct Preamble { @@ -91,6 +94,10 @@ class CC_PAINT_EXPORT PaintOpBufferSerializer { void RestoreToCount(int count, const PaintOp::SerializeOptions& options, const PlaybackParams& params); + PaintOp::SerializeOptions MakeSerializeOptions(); + void ClearForOpaqueRaster(const Preamble& preamble, + const PaintOp::SerializeOptions& options, + const PlaybackParams& params); SerializeCallback serialize_cb_; ImageProvider* image_provider_; @@ -98,6 +105,9 @@ class CC_PAINT_EXPORT PaintOpBufferSerializer { SkStrikeServer* strike_server_; SkColorSpace* color_space_; bool can_use_lcd_text_; + bool context_supports_distance_field_text_; + int max_texture_size_; + size_t max_texture_bytes_; SkTextBlobCacheDiffCanvas text_blob_canvas_; std::unique_ptr<SkCanvas> canvas_; @@ -113,7 +123,10 @@ class CC_PAINT_EXPORT SimpleBufferSerializer : public PaintOpBufferSerializer { TransferCacheSerializeHelper* transfer_cache, SkStrikeServer* strike_server, SkColorSpace* color_space, - bool can_use_lcd_text); + bool can_use_lcd_text, + bool context_supports_distance_field_text, + int max_texture_size, + size_t max_texture_bytes); ~SimpleBufferSerializer() override; size_t written() const { return written_; } diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc index b7b1d1e37fb..0b3aca669d6 100644 --- a/chromium/cc/paint/paint_op_buffer_unittest.cc +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -1933,7 +1933,10 @@ TEST(PaintOpSerializationTest, CompleteBufferSerialization) { options_provider.image_provider(), options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); serializer.Serialize(&buffer, nullptr, preamble); ASSERT_NE(serializer.written(), 0u); @@ -2010,7 +2013,10 @@ TEST(PaintOpSerializationTest, Preamble) { options_provider.image_provider(), options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); serializer.Serialize(&buffer, nullptr, preamble); ASSERT_NE(serializer.written(), 0u); @@ -2110,7 +2116,10 @@ TEST(PaintOpSerializationTest, SerializesNestedRecords) { options_provider.image_provider(), options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); PaintOpBufferSerializer::Preamble preamble; serializer.Serialize(&buffer, nullptr, preamble); ASSERT_NE(serializer.written(), 0u); @@ -2184,7 +2193,10 @@ TEST(PaintOpBufferTest, ClipsImagesDuringSerialization) { options_provider.image_provider(), options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); PaintOpBufferSerializer::Preamble preamble; preamble.playback_rect = test_case.clip_rect; preamble.full_raster_rect = gfx::Rect(0, 0, test_case.clip_rect.right(), @@ -2247,7 +2259,10 @@ TEST(PaintOpBufferSerializationTest, AlphaFoldingDuringSerialization) { options_provider.image_provider(), options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); serializer.Serialize(&buffer, nullptr, preamble); ASSERT_NE(serializer.written(), 0u); @@ -2866,7 +2881,10 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProviderOOP) { options_provider.image_provider(), options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); serializer.Serialize(&buffer); ASSERT_NE(serializer.written(), 0u); @@ -2975,13 +2993,8 @@ TEST_P(PaintFilterSerializationTest, Basic) { buffer_size += PaintOpWriter::HeaderBytes(); memory.resize(buffer_size); - PaintOp::SerializeOptions serialize_options( - options_provider.image_provider(), - options_provider.transfer_cache_helper(), nullptr, - options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text(), SkMatrix::I()); - PaintOpWriter writer(memory.data(), memory.size(), serialize_options, - GetParam()); + PaintOpWriter writer(memory.data(), memory.size(), + options_provider.serialize_options(), GetParam()); writer.Write(filter.get()); ASSERT_GT(writer.size(), 0u) << PaintFilter::TypeToString(filter->type()); @@ -3017,7 +3030,10 @@ TEST(PaintOpBufferTest, PaintRecordShaderSerialization) { options_provider.image_provider(), options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); serializer.Serialize(&buffer); ASSERT_TRUE(serializer.valid()); ASSERT_GT(serializer.written(), 0u); @@ -3099,13 +3115,9 @@ TEST(PaintOpBufferTest, SecurityConstrainedImageSerialization) { static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize, PaintOpBuffer::PaintOpAlign))); TestOptionsProvider options_provider; - PaintOp::SerializeOptions serialize_options( - options_provider.image_provider(), - options_provider.transfer_cache_helper(), nullptr, - options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text(), SkMatrix::I()); PaintOpWriter writer(memory.get(), PaintOpBuffer::kInitialBufferSize, - serialize_options, enable_security_constraints); + options_provider.serialize_options(), + enable_security_constraints); writer.Write(filter.get()); sk_sp<PaintFilter> out_filter; @@ -3118,6 +3130,39 @@ TEST(PaintOpBufferTest, SecurityConstrainedImageSerialization) { EXPECT_TRUE(*filter == *out_filter); } +TEST(PaintOpBufferTest, DrawImageRectSerializeScaledImages) { + auto buffer = sk_make_sp<PaintOpBuffer>(); + buffer->push<ScaleOp>(0.5f, 2.0f); + + // scales: x dimension = x0.25, y dimension = x5 + // translations here are arbitrary + SkRect src = SkRect::MakeXYWH(3, 4, 20, 6); + SkRect dst = SkRect::MakeXYWH(20, 38, 5, 30); + buffer->push<DrawImageRectOp>( + CreateDiscardablePaintImage(gfx::Size(32, 16)), src, dst, nullptr, + PaintCanvas::SrcRectConstraint::kStrict_SrcRectConstraint); + + std::unique_ptr<char, base::AlignedFreeDeleter> memory( + static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize, + PaintOpBuffer::PaintOpAlign))); + TestOptionsProvider options_provider; + SimpleBufferSerializer serializer( + memory.get(), PaintOpBuffer::kInitialBufferSize, + options_provider.image_provider(), + options_provider.transfer_cache_helper(), + options_provider.strike_server(), options_provider.color_space(), + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); + serializer.Serialize(buffer.get()); + + ASSERT_EQ(options_provider.decoded_images().size(), 1u); + auto scale = options_provider.decoded_images().at(0).scale(); + EXPECT_EQ(scale.width(), 0.5f * 0.25f); + EXPECT_EQ(scale.height(), 2.0f * 5.0f); +} + TEST(PaintOpBufferTest, RecordShadersSerializeScaledImages) { auto record_buffer = sk_make_sp<PaintOpBuffer>(); record_buffer->push<DrawImageOp>( @@ -3143,7 +3188,10 @@ TEST(PaintOpBufferTest, RecordShadersSerializeScaledImages) { options_provider.image_provider(), options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); serializer.Serialize(buffer.get()); ASSERT_EQ(options_provider.decoded_images().size(), 1u); @@ -3180,7 +3228,10 @@ TEST(PaintOpBufferTest, RecordShadersCached) { memory.get(), PaintOpBuffer::kInitialBufferSize, options_provider.image_provider(), transfer_cache, options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); serializer.Serialize(buffer.get()); memory_written = serializer.written(); } @@ -3203,7 +3254,10 @@ TEST(PaintOpBufferTest, RecordShadersCached) { memory_scaled.get(), PaintOpBuffer::kInitialBufferSize, options_provider.image_provider(), transfer_cache, options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); serializer.Serialize(buffer.get()); memory_scaled_written = serializer.written(); } @@ -3302,9 +3356,14 @@ TEST(PaintOpBufferTest, RecordShadersCachedSize) { SimpleBufferSerializer serializer( memory.get(), PaintOpBuffer::kInitialBufferSize, - options_provider.image_provider(), transfer_cache, + options_provider.image_provider(), + options_provider.transfer_cache_helper(), options_provider.strike_server(), options_provider.color_space(), - options_provider.can_use_lcd_text()); + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); + options_provider.context_supports_distance_field_text(); serializer.Serialize(buffer.get()); PaintOp::DeserializeOptions deserialize_options( @@ -3338,4 +3397,34 @@ TEST(PaintOpBufferTest, TotalOpCount) { EXPECT_EQ(3 * len + 2, record_buffer->total_op_count()); } +TEST(PaintOpBufferTest, NullImages) { + PaintOpBuffer buffer; + buffer.push<DrawImageOp>(PaintImage(), 0.f, 0.f, nullptr); + + std::unique_ptr<char, base::AlignedFreeDeleter> memory( + static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize, + PaintOpBuffer::PaintOpAlign))); + TestOptionsProvider options_provider; + SimpleBufferSerializer serializer( + memory.get(), PaintOpBuffer::kInitialBufferSize, + options_provider.image_provider(), + options_provider.transfer_cache_helper(), + options_provider.strike_server(), options_provider.color_space(), + options_provider.can_use_lcd_text(), + options_provider.context_supports_distance_field_text(), + options_provider.max_texture_size(), + options_provider.max_texture_bytes()); + serializer.Serialize(&buffer); + ASSERT_TRUE(serializer.valid()); + ASSERT_GT(serializer.written(), 0u); + + auto deserialized_buffer = + PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(), + options_provider.deserialize_options()); + ASSERT_TRUE(deserialized_buffer); + ASSERT_EQ(deserialized_buffer->size(), 1u); + ASSERT_EQ(deserialized_buffer->GetFirstOp()->GetType(), + PaintOpType::DrawImage); +} + } // namespace cc diff --git a/chromium/cc/paint/paint_op_perftest.cc b/chromium/cc/paint/paint_op_perftest.cc index dfefd8a8afa..336947163a8 100644 --- a/chromium/cc/paint/paint_op_perftest.cc +++ b/chromium/cc/paint/paint_op_perftest.cc @@ -53,7 +53,10 @@ class PaintOpPerfTest : public testing::Test { test_options_provider.transfer_cache_helper(), test_options_provider.strike_server(), test_options_provider.color_space(), - test_options_provider.can_use_lcd_text()); + test_options_provider.can_use_lcd_text(), + test_options_provider.context_supports_distance_field_text(), + test_options_provider.max_texture_size(), + test_options_provider.max_texture_bytes()); serializer.Serialize(&buffer, nullptr, preamble); bytes_written = serializer.written(); timer_.NextLap(); diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc index e9c885c465d..ee3301f744f 100644 --- a/chromium/cc/paint/paint_op_reader.cc +++ b/chromium/cc/paint/paint_op_reader.cc @@ -153,23 +153,6 @@ void PaintOpReader::ReadData(size_t bytes, void* data) { remaining_bytes_ -= bytes; } -void PaintOpReader::ReadArray(size_t count, SkPoint* array) { - size_t bytes = count * sizeof(SkPoint); - if (remaining_bytes_ < bytes) - SetInvalid(); - // Overflow? - if (count > static_cast<size_t>(~0) / sizeof(SkPoint)) - SetInvalid(); - if (!valid_) - return; - if (count == 0) - return; - - memcpy(array, const_cast<const char*>(memory_), bytes); - memory_ += bytes; - remaining_bytes_ -= bytes; -} - void PaintOpReader::ReadSize(size_t* size) { ReadSimple(size); } @@ -320,6 +303,11 @@ void PaintOpReader::Read(PaintImage* image) { if (!valid_) return; + bool needs_mips; + ReadSimple(&needs_mips); + if (!valid_) + return; + // If we encountered a decode failure, we may write an invalid id for the // image. In these cases, just return, leaving the image as nullptr. if (transfer_cache_entry_id == kInvalidImageTransferCacheEntryId) @@ -328,10 +316,16 @@ void PaintOpReader::Read(PaintImage* image) { if (auto* entry = options_.transfer_cache->GetEntryAs<ServiceImageTransferCacheEntry>( transfer_cache_entry_id)) { + if (needs_mips) + entry->EnsureMips(); *image = PaintImageBuilder::WithDefault() .set_id(PaintImage::GetNextId()) .set_image(entry->image(), PaintImage::kNonLazyStableId) .TakePaintImage(); + } else { + // If a transfer cache id exists, we must have a valid entry for it in the + // cache. + SetInvalid(); } } diff --git a/chromium/cc/paint/paint_op_reader.h b/chromium/cc/paint/paint_op_reader.h index c8455fe8448..0188c8d54da 100644 --- a/chromium/cc/paint/paint_op_reader.h +++ b/chromium/cc/paint/paint_op_reader.h @@ -45,7 +45,6 @@ class CC_PAINT_EXPORT PaintOpReader { size_t remaining_bytes() const { return remaining_bytes_; } void ReadData(size_t bytes, void* data); - void ReadArray(size_t count, SkPoint* array); void ReadSize(size_t* size); void Read(SkScalar* data); diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc index 00338a6ba2f..9426b0dfdb4 100644 --- a/chromium/cc/paint/paint_op_writer.cc +++ b/chromium/cc/paint/paint_op_writer.cc @@ -206,10 +206,7 @@ void PaintOpWriter::Write(const PaintFlags& flags) { void PaintOpWriter::Write(const DrawImage& draw_image, SkSize* scale_adjustment) { - // We never ask for subsets during serialization. const PaintImage& paint_image = draw_image.paint_image(); - DCHECK_EQ(paint_image.width(), draw_image.src_rect().width()); - DCHECK_EQ(paint_image.height(), draw_image.src_rect().height()); // Empty image. if (!draw_image.paint_image()) { @@ -217,6 +214,10 @@ void PaintOpWriter::Write(const DrawImage& draw_image, return; } + // We never ask for subsets during serialization. + DCHECK_EQ(paint_image.width(), draw_image.src_rect().width()); + DCHECK_EQ(paint_image.height(), draw_image.src_rect().height()); + // Security constrained serialization inlines the image bitmap. if (enable_security_constraints_) { SkBitmap bm; @@ -247,13 +248,21 @@ void PaintOpWriter::Write(const DrawImage& draw_image, base::Optional<uint32_t> id = decoded_draw_image.transfer_cache_entry_id(); *scale_adjustment = decoded_draw_image.scale_adjustment(); // In the case of a decode failure, id may not be set. Send an invalid ID. - WriteImage(id ? *id : kInvalidImageTransferCacheEntryId); + WriteImage(id.value_or(kInvalidImageTransferCacheEntryId), + decoded_draw_image.transfer_cache_entry_needs_mips()); } -void PaintOpWriter::WriteImage(uint32_t transfer_cache_entry_id) { +void PaintOpWriter::WriteImage(uint32_t transfer_cache_entry_id, + bool needs_mips) { + if (transfer_cache_entry_id == kInvalidImageTransferCacheEntryId) { + Write(static_cast<uint8_t>(PaintOp::SerializedImageType::kNoImage)); + return; + } + Write( static_cast<uint8_t>(PaintOp::SerializedImageType::kTransferCacheEntry)); Write(transfer_cache_entry_id); + Write(needs_mips); } void PaintOpWriter::Write(const sk_sp<SkData>& data) { @@ -325,7 +334,8 @@ sk_sp<PaintShader> PaintOpWriter::TransformShaderIfNecessary( const PaintShader* original, SkFilterQuality quality, uint32_t* paint_image_transfer_cache_entry_id, - gfx::SizeF* paint_record_post_scale) { + gfx::SizeF* paint_record_post_scale, + bool* paint_image_needs_mips) { DCHECK(!enable_security_constraints_); const auto type = original->shader_type(); @@ -334,7 +344,7 @@ sk_sp<PaintShader> PaintOpWriter::TransformShaderIfNecessary( if (type == PaintShader::Type::kImage) { return original->CreateDecodedImage(ctm, quality, options_.image_provider, paint_image_transfer_cache_entry_id, - &quality); + &quality, paint_image_needs_mips); } if (type == PaintShader::Type::kPaintRecord) { @@ -348,11 +358,12 @@ void PaintOpWriter::Write(const PaintShader* shader, SkFilterQuality quality) { sk_sp<PaintShader> transformed_shader; uint32_t paint_image_transfer_cache_id = kInvalidImageTransferCacheEntryId; gfx::SizeF paint_record_post_scale(1.f, 1.f); + bool paint_image_needs_mips = false; if (!enable_security_constraints_ && shader) { transformed_shader = TransformShaderIfNecessary( shader, quality, &paint_image_transfer_cache_id, - &paint_record_post_scale); + &paint_record_post_scale, &paint_image_needs_mips); shader = transformed_shader.get(); } @@ -394,7 +405,7 @@ void PaintOpWriter::Write(const PaintShader* shader, SkFilterQuality quality) { DCHECK_EQ(scale_adjustment.width(), 1.f); DCHECK_EQ(scale_adjustment.height(), 1.f); } else { - WriteImage(paint_image_transfer_cache_id); + WriteImage(paint_image_transfer_cache_id, paint_image_needs_mips); } if (shader->record_) { @@ -436,11 +447,6 @@ void PaintOpWriter::WriteData(size_t bytes, const void* input) { remaining_bytes_ -= bytes; } -void PaintOpWriter::WriteArray(size_t count, const SkPoint* input) { - size_t bytes = sizeof(SkPoint) * count; - WriteData(bytes, input); -} - void PaintOpWriter::AlignMemory(size_t alignment) { // Due to the math below, alignment must be a power of two. DCHECK_GT(alignment, 0u); @@ -755,7 +761,8 @@ void PaintOpWriter::Write(const PaintRecord* record, SimpleBufferSerializer serializer( memory_, remaining_bytes_, options_.image_provider, options_.transfer_cache, options_.strike_server, options_.color_space, - options_.can_use_lcd_text); + options_.can_use_lcd_text, options_.context_supports_distance_field_text, + options_.max_texture_size, options_.max_texture_bytes); serializer.Serialize(record, playback_rect, post_scale, post_matrix_for_analysis); diff --git a/chromium/cc/paint/paint_op_writer.h b/chromium/cc/paint/paint_op_writer.h index ecca69ced59..25b38e949b6 100644 --- a/chromium/cc/paint/paint_op_writer.h +++ b/chromium/cc/paint/paint_op_writer.h @@ -40,8 +40,6 @@ class CC_PAINT_EXPORT PaintOpWriter { // Write a sequence of arbitrary bytes. void WriteData(size_t bytes, const void* input); - void WriteArray(size_t count, const SkPoint* input); - size_t size() const { return valid_ ? size_ - remaining_bytes_ : 0u; } void WriteSize(size_t size); @@ -138,14 +136,15 @@ class CC_PAINT_EXPORT PaintOpWriter { const gfx::SizeF& post_scale, const SkMatrix& post_matrix_for_analysis); void Write(const SkRegion& region); - void WriteImage(uint32_t transfer_cache_entry_id); + void WriteImage(uint32_t transfer_cache_entry_id, bool needs_mips); void EnsureBytes(size_t required_bytes); sk_sp<PaintShader> TransformShaderIfNecessary( const PaintShader* original, SkFilterQuality quality, uint32_t* paint_image_transfer_cache_entry_id, - gfx::SizeF* paint_record_post_scale); + gfx::SizeF* paint_record_post_scale, + bool* paint_image_needs_mips); char* memory_ = nullptr; size_t size_ = 0u; diff --git a/chromium/cc/paint/paint_shader.cc b/chromium/cc/paint/paint_shader.cc index f06e539ddd9..c9b0c8d38d7 100644 --- a/chromium/cc/paint/paint_shader.cc +++ b/chromium/cc/paint/paint_shader.cc @@ -20,7 +20,9 @@ sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record, const gfx::SizeF* raster_scale, ImageProvider* image_provider) { SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(bounds); + SkCanvas* canvas = + recorder.beginRecording(SkRect::MakeWH(bounds.width(), bounds.height())); + canvas->translate(-bounds.fLeft, -bounds.fTop); if (raster_scale) canvas->scale(raster_scale->width(), raster_scale->height()); record->Playback(canvas, PlaybackParams(image_provider)); @@ -36,11 +38,13 @@ bool CompareMatrices(const SkMatrix& a, SkSize scale; SkMatrix a_without_scale; SkMatrix b_without_scale; - if (a.decomposeScale(&scale, &a_without_scale) != - b.decomposeScale(&scale, &b_without_scale)) { + + const bool decomposes = a.decomposeScale(&scale, &a_without_scale); + if (decomposes != b.decomposeScale(&scale, &b_without_scale)) return false; - } + if (!decomposes) + return true; return PaintOp::AreSkMatricesEqual(a_without_scale, b_without_scale); } @@ -237,25 +241,26 @@ bool PaintShader::GetRasterizationTileRect(const SkMatrix& ctm, SkScalarSqrt(matrix.getScaleY() * matrix.getScaleY() + matrix.getSkewY() * matrix.getSkewY())); } - SkSize scaled_size = - SkSize::Make(SkScalarAbs(scale.width() * tile_.width()), - SkScalarAbs(scale.height() * tile_.height())); + + SkScalar tile_area = + tile_.width() * tile_.height() * scale.width() * scale.height(); // Clamp the tile size to about 4M pixels. // TODO(khushalsagar): We need to consider the max texture size as well. static const SkScalar kMaxTileArea = 2048 * 2048; - SkScalar tile_area = scaled_size.width() * scaled_size.height(); if (tile_area > kMaxTileArea) { SkScalar clamp_scale = SkScalarSqrt(kMaxTileArea / tile_area); - scaled_size.set(scaled_size.width() * clamp_scale, - scaled_size.height() * clamp_scale); + scale.set(clamp_scale, clamp_scale); } - scaled_size = scaled_size.toCeil(); - if (scaled_size.isEmpty()) + *tile_rect = SkRect::MakeXYWH( + tile_.fLeft * scale.width(), tile_.fTop * scale.height(), + SkScalarCeilToInt(SkScalarAbs(scale.width() * tile_.width())), + SkScalarCeilToInt(SkScalarAbs(scale.height() * tile_.height()))); + + if (tile_rect->isEmpty()) return false; - *tile_rect = SkRect::MakeWH(scaled_size.width(), scaled_size.height()); return true; } @@ -305,7 +310,8 @@ sk_sp<PaintShader> PaintShader::CreateDecodedImage( SkFilterQuality quality, ImageProvider* image_provider, uint32_t* transfer_cache_entry_id, - SkFilterQuality* raster_quality) const { + SkFilterQuality* raster_quality, + bool* needs_mips) const { DCHECK_EQ(shader_type_, Type::kImage); if (!image_) return nullptr; @@ -348,6 +354,7 @@ sk_sp<PaintShader> PaintShader::CreateDecodedImage( // want to do is cap the filter quality used, but Gpu and Sw cache have // different behaviour. D: *raster_quality = decoded_image.filter_quality(); + *needs_mips = decoded_image.transfer_cache_entry_needs_mips(); return PaintShader::MakeImage(decoded_paint_image, tx_, ty_, &final_matrix); } diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h index 6c2e963fb40..b0444bbfd96 100644 --- a/chromium/cc/paint/paint_shader.h +++ b/chromium/cc/paint/paint_shader.h @@ -159,6 +159,7 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { private: friend class PaintFlags; + friend class PaintOpHelper; friend class PaintOpReader; friend class PaintOpSerializationTestUtils; friend class PaintOpWriter; @@ -192,7 +193,8 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { SkFilterQuality requested_quality, ImageProvider* image_provider, uint32_t* transfer_cache_entry_id, - SkFilterQuality* raster_quality) const; + SkFilterQuality* raster_quality, + bool* needs_mips) const; void SetColorsAndPositions(const SkColor* colors, const SkScalar* positions, diff --git a/chromium/cc/paint/paint_typeface.cc b/chromium/cc/paint/paint_typeface.cc index 2595d84e03a..d6ee8c0bb04 100644 --- a/chromium/cc/paint/paint_typeface.cc +++ b/chromium/cc/paint/paint_typeface.cc @@ -4,8 +4,8 @@ #include "cc/paint/paint_typeface.h" #include "build/build_config.h" +#include "third_party/skia/include/core/SkFontMgr.h" #include "third_party/skia/include/ports/SkFontConfigInterface.h" -#include "third_party/skia/include/ports/SkFontMgr.h" namespace cc { diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc index c601f19296d..2250b26e803 100644 --- a/chromium/cc/paint/record_paint_canvas.cc +++ b/chromium/cc/paint/record_paint_canvas.cc @@ -263,23 +263,6 @@ void RecordPaintCanvas::drawImageRect(const PaintImage& image, list_->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; - // TODO(khushalsagar): Remove this and have callers use PaintImages holding - // bitmap-backed images, since they can maintain the PaintImage::Id. - drawImage(PaintImageBuilder::WithDefault() - .set_id(PaintImage::GetNextId()) - .set_image(SkImage::MakeFromBitmap(bitmap), - PaintImage::GetNextContentId()) - .TakePaintImage(), - left, top, flags); -} - void RecordPaintCanvas::drawTextBlob(scoped_refptr<PaintTextBlob> blob, SkScalar x, SkScalar y, diff --git a/chromium/cc/paint/record_paint_canvas.h b/chromium/cc/paint/record_paint_canvas.h index 5eae2c88916..fb39721603f 100644 --- a/chromium/cc/paint/record_paint_canvas.h +++ b/chromium/cc/paint/record_paint_canvas.h @@ -84,10 +84,6 @@ class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas { const SkRect& dst, const PaintFlags* flags, SrcRectConstraint constraint) override; - void drawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const PaintFlags* flags) override; void drawTextBlob(scoped_refptr<PaintTextBlob> blob, SkScalar x, @@ -109,7 +105,6 @@ class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas { using PaintCanvas::clipRect; using PaintCanvas::clipRRect; using PaintCanvas::clipPath; - using PaintCanvas::drawBitmap; using PaintCanvas::drawColor; using PaintCanvas::drawImage; using PaintCanvas::drawPicture; diff --git a/chromium/cc/paint/scoped_raster_flags.cc b/chromium/cc/paint/scoped_raster_flags.cc index cee4465a8c6..960e6b3acb2 100644 --- a/chromium/cc/paint/scoped_raster_flags.cc +++ b/chromium/cc/paint/scoped_raster_flags.cc @@ -47,10 +47,13 @@ void ScopedRasterFlags::DecodeImageShader(const SkMatrix& ctm) { uint32_t transfer_cache_entry_id = kInvalidImageTransferCacheEntryId; SkFilterQuality raster_quality = flags()->getFilterQuality(); + bool transfer_cache_entry_needs_mips = false; auto decoded_shader = flags()->getShader()->CreateDecodedImage( ctm, flags()->getFilterQuality(), &*decode_stashing_image_provider_, - &transfer_cache_entry_id, &raster_quality); + &transfer_cache_entry_id, &raster_quality, + &transfer_cache_entry_needs_mips); DCHECK_EQ(transfer_cache_entry_id, kInvalidImageTransferCacheEntryId); + DCHECK_EQ(transfer_cache_entry_needs_mips, false); if (!decoded_shader) { decode_failed_ = true; diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc index b5963ca9d05..875b82f782e 100644 --- a/chromium/cc/paint/skia_paint_canvas.cc +++ b/chromium/cc/paint/skia_paint_canvas.cc @@ -300,23 +300,6 @@ void SkiaPaintCanvas::drawImageRect(const PaintImage& image, FlushAfterDrawIfNeeded(); } -void SkiaPaintCanvas::drawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const PaintFlags* flags) { - if (flags) { - ScopedRasterFlags raster_flags(flags, image_provider_, - canvas_->getTotalMatrix(), 255u); - if (!raster_flags.flags()) - return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - canvas_->drawBitmap(bitmap, left, top, &paint); - } else { - canvas_->drawBitmap(bitmap, left, top, nullptr); - } - FlushAfterDrawIfNeeded(); -} - void SkiaPaintCanvas::drawTextBlob(scoped_refptr<PaintTextBlob> blob, SkScalar x, SkScalar y, diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h index c9e1936733e..8a589743f46 100644 --- a/chromium/cc/paint/skia_paint_canvas.h +++ b/chromium/cc/paint/skia_paint_canvas.h @@ -106,10 +106,6 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { const SkRect& dst, const PaintFlags* flags, SrcRectConstraint constraint) override; - void drawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const PaintFlags* flags) override; void drawTextBlob(scoped_refptr<PaintTextBlob> blob, SkScalar x, @@ -130,7 +126,6 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { using PaintCanvas::clipRect; using PaintCanvas::clipRRect; using PaintCanvas::clipPath; - using PaintCanvas::drawBitmap; using PaintCanvas::drawColor; using PaintCanvas::drawImage; using PaintCanvas::drawPicture; diff --git a/chromium/cc/paint/solid_color_analyzer_unittest.cc b/chromium/cc/paint/solid_color_analyzer_unittest.cc index 4c40b602aed..939e517af61 100644 --- a/chromium/cc/paint/solid_color_analyzer_unittest.cc +++ b/chromium/cc/paint/solid_color_analyzer_unittest.cc @@ -114,14 +114,6 @@ TEST_F(SolidColorAnalyzerTest, DrawOval) { EXPECT_FALSE(IsSolidColor()); } -TEST_F(SolidColorAnalyzerTest, DrawBitmap) { - Initialize(); - SkBitmap bitmap; - bitmap.allocN32Pixels(16, 16); - canvas()->drawBitmap(bitmap, 0, 0, nullptr); - EXPECT_FALSE(IsSolidColor()); -} - TEST_F(SolidColorAnalyzerTest, DrawRect) { Initialize(); PaintFlags flags; diff --git a/chromium/cc/paint/transfer_cache_unittest.cc b/chromium/cc/paint/transfer_cache_unittest.cc index d8fb589c35d..9deaf3d0825 100644 --- a/chromium/cc/paint/transfer_cache_unittest.cc +++ b/chromium/cc/paint/transfer_cache_unittest.cc @@ -49,8 +49,7 @@ class TransferCacheTest : public testing::Test { auto result = context_->Initialize( /*service=*/nullptr, attribs, gpu::SharedMemoryLimits(), &gpu_memory_buffer_manager_, &image_factory_, - /*gpu_channel_manager_delegate=*/nullptr, - base::ThreadTaskRunnerHandle::Get()); + /*gpu_channel_manager_delegate=*/nullptr); ASSERT_EQ(result, gpu::ContextResult::kSuccess); ASSERT_TRUE(context_->GetCapabilities().supports_oop_raster); @@ -62,6 +61,8 @@ class TransferCacheTest : public testing::Test { return context_->GetTransferCacheForTest(); } + int decoder_id() { return context_->GetRasterDecoderIdForTest(); } + gpu::raster::RasterInterface* ri() { return context_->GetImplementation(); } gpu::ContextSupport* ContextSupport() { @@ -99,7 +100,9 @@ TEST_F(TransferCacheTest, Basic) { ri()->Finish(); // Validate service-side state. - EXPECT_NE(nullptr, service_cache->GetEntry(entry.Type(), entry.Id())); + EXPECT_NE(nullptr, + service_cache->GetEntry(gpu::ServiceTransferCache::EntryKey( + decoder_id(), entry.Type(), entry.Id()))); // Unlock on client side and flush to service. context_support->UnlockTransferCacheEntries( @@ -114,7 +117,9 @@ TEST_F(TransferCacheTest, Basic) { // Delete on client side, flush, and validate that deletion reaches service. context_support->DeleteTransferCacheEntry(entry.UnsafeType(), entry.Id()); ri()->Finish(); - EXPECT_EQ(nullptr, service_cache->GetEntry(entry.Type(), entry.Id())); + EXPECT_EQ(nullptr, + service_cache->GetEntry(gpu::ServiceTransferCache::EntryKey( + decoder_id(), entry.Type(), entry.Id()))); } TEST_F(TransferCacheTest, Eviction) { @@ -127,7 +132,9 @@ TEST_F(TransferCacheTest, Eviction) { ri()->Finish(); // Validate service-side state. - EXPECT_NE(nullptr, service_cache->GetEntry(entry.Type(), entry.Id())); + EXPECT_NE(nullptr, + service_cache->GetEntry(gpu::ServiceTransferCache::EntryKey( + decoder_id(), entry.Type(), entry.Id()))); // Unlock on client side and flush to service. context_support->UnlockTransferCacheEntries( @@ -136,7 +143,9 @@ TEST_F(TransferCacheTest, Eviction) { // Evict on the service side. service_cache->SetCacheSizeLimitForTesting(0); - EXPECT_EQ(nullptr, service_cache->GetEntry(entry.Type(), entry.Id())); + EXPECT_EQ(nullptr, + service_cache->GetEntry(gpu::ServiceTransferCache::EntryKey( + decoder_id(), entry.Type(), entry.Id()))); // Try to re-lock on the client side. This should fail. EXPECT_FALSE(context_support->ThreadsafeLockTransferCacheEntry( @@ -160,7 +169,8 @@ TEST_F(TransferCacheTest, RawMemoryTransfer) { // Validate service-side data matches. ServiceTransferCacheEntry* service_entry = - service_cache->GetEntry(client_entry.Type(), client_entry.Id()); + service_cache->GetEntry(gpu::ServiceTransferCache::EntryKey( + decoder_id(), client_entry.Type(), client_entry.Id())); EXPECT_EQ(service_entry->Type(), client_entry.Type()); const std::vector<uint8_t> service_data = static_cast<ServiceRawMemoryTransferCacheEntry*>(service_entry)->data(); @@ -185,13 +195,14 @@ TEST_F(TransferCacheTest, ImageMemoryTransfer) { SkPixmap pixmap(info, data.data(), info.minRowBytes()); // Add the entry to the transfer cache - ClientImageTransferCacheEntry client_entry(&pixmap, nullptr); + ClientImageTransferCacheEntry client_entry(&pixmap, nullptr, false); CreateEntry(client_entry); ri()->Finish(); // Validate service-side data matches. ServiceTransferCacheEntry* service_entry = - service_cache->GetEntry(client_entry.Type(), client_entry.Id()); + service_cache->GetEntry(gpu::ServiceTransferCache::EntryKey( + decoder_id(), client_entry.Type(), client_entry.Id())); EXPECT_EQ(service_entry->Type(), client_entry.Type()); sk_sp<SkImage> service_image = static_cast<ServiceImageTransferCacheEntry*>(service_entry)->image(); diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc index f449095504c..dd6a8470529 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.cc +++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc @@ -17,7 +17,7 @@ #include "cc/paint/paint_recorder.h" #include "cc/raster/raster_source.h" #include "cc/raster/scoped_gpu_raster.h" -#include "cc/resources/layer_tree_resource_provider.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/gpu/raster_context_provider.h" #include "components/viz/common/gpu/texture_allocation.h" @@ -71,7 +71,7 @@ class ScopedSkSurfaceForUnpremultiplyAndDither { SkImageInfo n32Info = SkImageInfo::MakeN32Premul( intermediate_size.width(), intermediate_size.height()); SkSurfaceProps surface_props = - LayerTreeResourceProvider::ScopedSkSurface::ComputeSurfaceProps( + viz::ClientResourceProvider::ScopedSkSurface::ComputeSurfaceProps( can_use_lcd_text); surface_ = SkSurface::MakeRenderTarget( context_provider->GrContext(), SkBudgeted::kNo, n32Info, @@ -138,11 +138,11 @@ static void RasterizeSourceOOP( // TODO(enne): Use the |texture_target|? GpuMemoryBuffer backed textures don't // use GL_TEXTURE_2D. - ri->BeginRasterCHROMIUM(texture_id, raster_source->background_color(), - msaa_sample_count, playback_settings.use_lcd_text, + ri->BeginRasterCHROMIUM(raster_source->background_color(), msaa_sample_count, + playback_settings.use_lcd_text, viz::ResourceFormatToClosestSkColorType( /*gpu_compositing=*/true, resource_format), - playback_settings.raster_color_space); + playback_settings.raster_color_space, mailbox.name); float recording_to_raster_scale = transform.scale() / raster_source->recording_scale_factor(); gfx::Size content_size = raster_source->GetContentSize(transform.scale()); @@ -212,7 +212,7 @@ static void RasterizeSource( { ScopedGrContextAccess gr_context_access(context_provider); - base::Optional<LayerTreeResourceProvider::ScopedSkSurface> scoped_surface; + base::Optional<viz::ClientResourceProvider::ScopedSkSurface> scoped_surface; base::Optional<ScopedSkSurfaceForUnpremultiplyAndDither> scoped_dither_surface; SkSurface* surface; @@ -379,13 +379,12 @@ std::unique_ptr<RasterBuffer> GpuRasterBufferProvider::AcquireBufferForRaster( backing->texture_id = alloc.texture_id; backing->texture_target = alloc.texture_target; backing->overlay_candidate = alloc.overlay_candidate; - backing->mailbox = gpu::Mailbox::Generate(); gl->ProduceTextureDirectCHROMIUM(backing->texture_id, backing->mailbox.name); // Save a sync token in the backing so that we always wait on it even if // this task is cancelled between being scheduled and running. backing->returned_sync_token = - LayerTreeResourceProvider::GenerateSyncTokenHelper(gl); + viz::ClientResourceProvider::GenerateSyncTokenHelper(gl); resource.set_gpu_backing(std::move(backing)); } @@ -528,7 +527,7 @@ gpu::SyncToken GpuRasterBufferProvider::PlaybackOnWorkerThread( } // Generate sync token for cross context synchronization. - return LayerTreeResourceProvider::GenerateSyncTokenHelper(ri); + return viz::ClientResourceProvider::GenerateSyncTokenHelper(ri); } bool GpuRasterBufferProvider::ShouldUnpremultiplyAndDitherResource( diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.cc b/chromium/cc/raster/one_copy_raster_buffer_provider.cc index 90f94f65055..c978c85654c 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.cc @@ -173,13 +173,12 @@ OneCopyRasterBufferProvider::AcquireBufferForRaster( backing->texture_id = alloc.texture_id; backing->texture_target = alloc.texture_target; backing->overlay_candidate = alloc.overlay_candidate; - backing->mailbox = gpu::Mailbox::Generate(); gl->ProduceTextureDirectCHROMIUM(backing->texture_id, backing->mailbox.name); // Save a sync token in the backing so that we always wait on it even if // this task is cancelled between being scheduled and running. backing->returned_sync_token = - LayerTreeResourceProvider::GenerateSyncTokenHelper(gl); + viz::ClientResourceProvider::GenerateSyncTokenHelper(gl); resource.set_gpu_backing(std::move(backing)); } @@ -487,7 +486,7 @@ gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread( // Generate sync token on the worker context that will be sent to and waited // for by the display compositor before using the content generated here. - return LayerTreeResourceProvider::GenerateSyncTokenHelper(ri); + return viz::ClientResourceProvider::GenerateSyncTokenHelper(ri); } gfx::BufferUsage OneCopyRasterBufferProvider::StagingBufferUsage() const { diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.h b/chromium/cc/raster/one_copy_raster_buffer_provider.h index 3c823596cba..4f4a1e2fb50 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.h +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.h @@ -11,7 +11,7 @@ #include "base/sequenced_task_runner.h" #include "cc/raster/raster_buffer_provider.h" #include "cc/raster/staging_buffer_pool.h" -#include "cc/resources/layer_tree_resource_provider.h" +#include "components/viz/client/client_resource_provider.h" #include "gpu/command_buffer/common/sync_token.h" namespace gpu { diff --git a/chromium/cc/raster/playback_image_provider.cc b/chromium/cc/raster/playback_image_provider.cc index 75d8ddf0608..a539f0b1fe4 100644 --- a/chromium/cc/raster/playback_image_provider.cc +++ b/chromium/cc/raster/playback_image_provider.cc @@ -47,12 +47,6 @@ PlaybackImageProvider::GetDecodedDrawImage(const DrawImage& draw_image) { return ScopedDecodedDrawImage(); } - if (paint_image.GetSkImage()->isTextureBacked()) { - return ScopedDecodedDrawImage(DecodedDrawImage( - paint_image.GetSkImage(), SkSize::Make(0, 0), SkSize::Make(1.f, 1.f), - draw_image.filter_quality(), true /* is_budgeted */)); - } - const auto& it = settings_->image_to_current_frame_index.find(paint_image.stable_id()); size_t frame_index = it == settings_->image_to_current_frame_index.end() @@ -60,8 +54,13 @@ PlaybackImageProvider::GetDecodedDrawImage(const DrawImage& draw_image) { : it->second; DrawImage adjusted_image(draw_image, 1.f, frame_index, target_color_space_); - auto decoded_draw_image = cache_->GetDecodedImageForDraw(adjusted_image); + if (!cache_->UseCacheForDrawImage(adjusted_image)) { + return ScopedDecodedDrawImage(DecodedDrawImage( + paint_image.GetSkImage(), SkSize::Make(0, 0), SkSize::Make(1.f, 1.f), + draw_image.filter_quality(), true /* is_budgeted */)); + } + auto decoded_draw_image = cache_->GetDecodedImageForDraw(adjusted_image); return ScopedDecodedDrawImage( decoded_draw_image, base::BindOnce(&UnrefImageFromCache, std::move(adjusted_image), cache_, diff --git a/chromium/cc/raster/playback_image_provider_unittest.cc b/chromium/cc/raster/playback_image_provider_unittest.cc index 40036e87032..dd4f6a4a9e8 100644 --- a/chromium/cc/raster/playback_image_provider_unittest.cc +++ b/chromium/cc/raster/playback_image_provider_unittest.cc @@ -46,6 +46,13 @@ class MockDecodeCache : public StubDecodeCache { EXPECT_GE(refed_image_count_, 0); } + bool UseCacheForDrawImage(const DrawImage& image) const override { + return use_cache_for_draw_image_; + } + + void set_use_cache_for_draw_image(bool use) { + use_cache_for_draw_image_ = use; + } int refed_image_count() const { return refed_image_count_; } int images_decoded() const { return images_decoded_; } const DrawImage& last_image() { return last_image_; } @@ -53,6 +60,7 @@ class MockDecodeCache : public StubDecodeCache { private: int refed_image_count_ = 0; int images_decoded_ = 0; + bool use_cache_for_draw_image_ = true; DrawImage last_image_; }; @@ -163,19 +171,9 @@ TEST(PlaybackImageProviderTest, BitmapImages) { EXPECT_EQ(cache.refed_image_count(), 0); } -TEST(PlaybackImageProviderTest, TextureImages) { - // Texture images should never hit the ImageDecodeCache. - sk_sp<const GrGLInterface> gl_interface(GrGLCreateNullInterface()); - auto context = GrContext::MakeGL(std::move(gl_interface)); - auto sk_texture_image = CreateBitmapImage(gfx::Size(10, 10)) - .GetSkImage() - ->makeTextureImage(context.get(), nullptr); - auto image = PaintImageBuilder::WithDefault() - .set_id(PaintImage::GetNextId()) - .set_image(sk_texture_image, PaintImage::GetNextContentId()) - .TakePaintImage(); - +TEST(PlaybackImageProviderTest, IgnoresImagesNotSupportedByCache) { MockDecodeCache cache; + cache.set_use_cache_for_draw_image(false); base::Optional<PlaybackImageProvider::Settings> settings; settings.emplace(); PlaybackImageProvider provider(&cache, gfx::ColorSpace(), @@ -183,7 +181,8 @@ TEST(PlaybackImageProviderTest, TextureImages) { { SkIRect rect = SkIRect::MakeWH(10, 10); SkMatrix matrix = SkMatrix::I(); - auto draw_image = DrawImage(image, rect, kMedium_SkFilterQuality, matrix); + auto draw_image = DrawImage(CreateBitmapImage(gfx::Size(10, 10)), rect, + kMedium_SkFilterQuality, matrix); auto decode = provider.GetDecodedDrawImage(draw_image); EXPECT_TRUE(decode); EXPECT_EQ(cache.refed_image_count(), 0); diff --git a/chromium/cc/raster/raster_buffer_provider.cc b/chromium/cc/raster/raster_buffer_provider.cc index 873a432e0ef..57f2a6d01f3 100644 --- a/chromium/cc/raster/raster_buffer_provider.cc +++ b/chromium/cc/raster/raster_buffer_provider.cc @@ -38,6 +38,15 @@ bool IsSupportedPlaybackToMemoryFormat(viz::ResourceFormat format) { case viz::LUMINANCE_F16: case viz::RGBA_F16: case viz::R16_EXT: + case viz::BGR_565: + case viz::RG_88: + case viz::RGBX_8888: + case viz::BGRX_8888: + case viz::RGBX_1010102: + case viz::BGRX_1010102: + case viz::YVU_420: + case viz::YUV_420_BIPLANAR: + case viz::UYVY_422: return false; } NOTREACHED(); @@ -136,6 +145,15 @@ void RasterBufferProvider::PlaybackToMemory( case viz::RED_8: case viz::LUMINANCE_F16: case viz::R16_EXT: + case viz::BGR_565: + case viz::RG_88: + case viz::RGBX_8888: + case viz::BGRX_8888: + case viz::RGBX_1010102: + case viz::BGRX_1010102: + case viz::YVU_420: + case viz::YUV_420_BIPLANAR: + case viz::UYVY_422: NOTREACHED(); return; } diff --git a/chromium/cc/raster/raster_buffer_provider_perftest.cc b/chromium/cc/raster/raster_buffer_provider_perftest.cc index a91d3bef29d..36db4d2c575 100644 --- a/chromium/cc/raster/raster_buffer_provider_perftest.cc +++ b/chromium/cc/raster/raster_buffer_provider_perftest.cc @@ -16,11 +16,10 @@ #include "cc/raster/raster_buffer_provider.h" #include "cc/raster/synchronous_task_graph_runner.h" #include "cc/raster/zero_copy_raster_buffer_provider.h" -#include "cc/resources/layer_tree_resource_provider.h" #include "cc/resources/resource_pool.h" #include "cc/test/fake_layer_tree_frame_sink.h" -#include "cc/test/fake_resource_provider.h" #include "cc/tiles/tile_task_manager.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/gpu/context_cache_controller.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/resources/platform_color.h" @@ -331,7 +330,7 @@ class RasterBufferProviderPerfTestBase { scoped_refptr<viz::ContextProvider> compositor_context_provider_; scoped_refptr<viz::RasterContextProvider> worker_context_provider_; std::unique_ptr<FakeLayerTreeFrameSink> layer_tree_frame_sink_; - std::unique_ptr<LayerTreeResourceProvider> resource_provider_; + std::unique_ptr<viz::ClientResourceProvider> resource_provider_; scoped_refptr<base::TestSimpleTaskRunner> task_runner_; std::unique_ptr<ResourcePool> resource_pool_; std::unique_ptr<SynchronousTaskGraphRunner> task_graph_runner_; @@ -501,14 +500,12 @@ class RasterBufferProviderPerfTest private: void Create3dResourceProvider() { - resource_provider_ = FakeResourceProvider::CreateLayerTreeResourceProvider( - compositor_context_provider_.get()); + resource_provider_ = std::make_unique<viz::ClientResourceProvider>(true); } void CreateSoftwareResourceProvider() { layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware(); - resource_provider_ = - FakeResourceProvider::CreateLayerTreeResourceProvider(nullptr); + resource_provider_ = std::make_unique<viz::ClientResourceProvider>(true); } std::string TestModifierString() const { @@ -571,8 +568,7 @@ class RasterBufferProviderCommonPerfTest public: // Overridden from testing::Test: void SetUp() override { - resource_provider_ = FakeResourceProvider::CreateLayerTreeResourceProvider( - compositor_context_provider_.get()); + resource_provider_ = std::make_unique<viz::ClientResourceProvider>(true); resource_pool_ = std::make_unique<ResourcePool>( resource_provider_.get(), compositor_context_provider_.get(), task_runner_, ResourcePool::kDefaultExpirationDelay, false); diff --git a/chromium/cc/raster/raster_buffer_provider_unittest.cc b/chromium/cc/raster/raster_buffer_provider_unittest.cc index c50e739199c..b7de488d4a7 100644 --- a/chromium/cc/raster/raster_buffer_provider_unittest.cc +++ b/chromium/cc/raster/raster_buffer_provider_unittest.cc @@ -24,16 +24,14 @@ #include "cc/raster/one_copy_raster_buffer_provider.h" #include "cc/raster/synchronous_task_graph_runner.h" #include "cc/raster/zero_copy_raster_buffer_provider.h" -#include "cc/resources/layer_tree_resource_provider.h" #include "cc/resources/resource_pool.h" #include "cc/test/fake_layer_tree_frame_sink.h" #include "cc/test/fake_raster_source.h" -#include "cc/test/fake_resource_provider.h" #include "cc/tiles/tile_task_manager.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/resources/platform_color.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_gpu_memory_buffer_manager.h" -#include "components/viz/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/raster_interface.h" #include "testing/gtest/include/gtest/gtest.h" @@ -298,14 +296,12 @@ class RasterBufferProviderTest context_provider_->BindToCurrentThread(); worker_context_provider_ = viz::TestContextProvider::CreateWorker(); layer_tree_frame_sink_ = FakeLayerTreeFrameSink::Create3d(); - resource_provider_ = FakeResourceProvider::CreateLayerTreeResourceProvider( - context_provider_.get()); + resource_provider_ = std::make_unique<viz::ClientResourceProvider>(true); } void CreateSoftwareResourceProvider() { layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware(); - resource_provider_ = - FakeResourceProvider::CreateLayerTreeResourceProvider(nullptr); + resource_provider_ = std::make_unique<viz::ClientResourceProvider>(true); } void OnTimeout() { @@ -318,7 +314,7 @@ class RasterBufferProviderTest scoped_refptr<viz::TestContextProvider> worker_context_provider_; std::unique_ptr<ResourcePool> pool_; std::unique_ptr<FakeLayerTreeFrameSink> layer_tree_frame_sink_; - std::unique_ptr<LayerTreeResourceProvider> resource_provider_; + std::unique_ptr<viz::ClientResourceProvider> resource_provider_; std::unique_ptr<TileTaskManager> tile_task_manager_; std::unique_ptr<RasterBufferProvider> raster_buffer_provider_; viz::TestGpuMemoryBufferManager gpu_memory_buffer_manager_; @@ -349,8 +345,8 @@ TEST_P(RasterBufferProviderTest, FailedMapResource) { if (GetParam() == RASTER_BUFFER_PROVIDER_TYPE_BITMAP) return; - viz::TestWebGraphicsContext3D* context3d = context_provider_->TestContext3d(); - context3d->set_times_map_buffer_chromium_succeeds(0); + viz::TestGLES2Interface* gl = context_provider_->TestContextGL(); + gl->set_times_map_buffer_chromium_succeeds(0); AppendTask(0u); ScheduleTasks(); @@ -478,9 +474,8 @@ TEST_P(RasterBufferProviderTest, WaitOnSyncTokenAfterReschedulingTask) { { viz::ContextProvider::ScopedContextLock context_lock( worker_context_provider_.get()); - viz::TestWebGraphicsContext3D* context3d = - worker_context_provider_->TestContext3d(); - EXPECT_TRUE(context3d->last_waited_sync_token().HasData()); + viz::TestGLES2Interface* gl = worker_context_provider_->TestContextGL(); + EXPECT_TRUE(gl->last_waited_sync_token().HasData()); } lock.Release(); diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc index be224fe6d8e..be0cb4fbb3f 100644 --- a/chromium/cc/raster/raster_source.cc +++ b/chromium/cc/raster/raster_source.cc @@ -54,35 +54,21 @@ RasterSource::RasterSource(const RecordingSource* other) recording_scale_factor_(other->recording_scale_factor_) {} RasterSource::~RasterSource() = default; -void RasterSource::ClearForFullRaster( +void RasterSource::ClearForOpaqueRaster( SkCanvas* raster_canvas, const gfx::Size& content_size, const gfx::Rect& canvas_bitmap_rect, const gfx::Rect& canvas_playback_rect) const { - // If this raster source has opaque contents, it is guaranteeing that it - // will draw an opaque rect the size of the layer. If it is not, then we - // must clear this canvas ourselves (i.e. requires_clear_). - if (requires_clear_) { - TrackRasterSourceNeededClear(RasterSourceClearType::kFull); - raster_canvas->clear(SK_ColorTRANSPARENT); - return; - } - // The last texel of this content is not guaranteed to be fully opaque, so - // inset by one to generate the fully opaque coverage rect . This rect is + // inset by one to generate the fully opaque coverage rect. This rect is // in device space. SkIRect coverage_device_rect = SkIRect::MakeWH(content_size.width() - canvas_bitmap_rect.x() - 1, content_size.height() - canvas_bitmap_rect.y() - 1); - // Remove playback rect offset, which is equal to bitmap rect offset, - // as this is full raster. - SkIRect playback_device_rect = SkIRect::MakeWH(canvas_playback_rect.width(), - canvas_playback_rect.height()); - // If not fully covered, we need to clear one texel inside the coverage - // rect (because of blending during raster) and one texel outside the full - // raster rect (because of bilinear filtering during draw). See comments + // rect (because of blending during raster) and one texel outside the canvas + // bitmap rect (because of bilinear filtering during draw). See comments // in RasterSource. SkIRect device_column = SkIRect::MakeXYWH(coverage_device_rect.right(), 0, 2, coverage_device_rect.bottom()); @@ -90,9 +76,22 @@ void RasterSource::ClearForFullRaster( SkIRect device_row = SkIRect::MakeXYWH(0, coverage_device_rect.bottom(), coverage_device_rect.right() + 2, 2); + bool right_edge = content_size.width() == canvas_playback_rect.right(); + bool bottom_edge = content_size.height() == canvas_playback_rect.bottom(); + + // If the playback rect is touching either edge of the content rect + // extend it by one pixel to include the extra texel outside the canvas + // bitmap rect that was added to device column and row above. + SkIRect playback_device_rect = + SkIRect::MakeXYWH(canvas_playback_rect.x() - canvas_bitmap_rect.x(), + canvas_playback_rect.y() - canvas_bitmap_rect.y(), + canvas_playback_rect.width() + (right_edge ? 1 : 0), + canvas_playback_rect.height() + (bottom_edge ? 1 : 0)); + + // Intersect the device column and row with the playback rect and only + // clear inside of that rect if needed. RasterSourceClearType clear_type = RasterSourceClearType::kNone; - // Only bother clearing if we need to. - if (SkIRect::Intersects(device_column, playback_device_rect)) { + if (device_column.intersect(playback_device_rect)) { clear_type = RasterSourceClearType::kBorder; raster_canvas->save(); raster_canvas->clipRect(SkRect::MakeFromIRect(device_column), @@ -100,7 +99,7 @@ void RasterSource::ClearForFullRaster( raster_canvas->drawColor(background_color_, SkBlendMode::kSrc); raster_canvas->restore(); } - if (SkIRect::Intersects(device_row, playback_device_rect)) { + if (device_row.intersect(playback_device_rect)) { clear_type = RasterSourceClearType::kBorder; raster_canvas->save(); raster_canvas->clipRect(SkRect::MakeFromIRect(device_row), @@ -136,9 +135,21 @@ void RasterSource::PlaybackToCanvas( } bool is_partial_raster = canvas_bitmap_rect != canvas_playback_rect; - if (!is_partial_raster && settings.clear_canvas_before_raster) { - ClearForFullRaster(raster_canvas, content_size, canvas_bitmap_rect, - canvas_playback_rect); + if (!requires_clear_) { + // Clear opaque raster sources. Opaque rasters sources guarantee that all + // pixels inside the opaque region are painted. However, due to scaling + // it's possible that the last row and column might include pixels that + // are not painted. Because this raster source is required to be opaque, + // we may need to do extra clearing outside of the clip. This needs to + // be done for both full and partial raster. + ClearForOpaqueRaster(raster_canvas, content_size, canvas_bitmap_rect, + canvas_playback_rect); + } else if (!is_partial_raster) { + // For non-opaque raster sources that are rastering the full tile, + // just clear the entire canvas (even if stretches past the canvas + // bitmap rect) as it's cheap to do so. + TrackRasterSourceNeededClear(RasterSourceClearType::kFull); + raster_canvas->clear(SK_ColorTRANSPARENT); } raster_canvas->save(); @@ -149,10 +160,12 @@ void RasterSource::PlaybackToCanvas( raster_canvas->scale(raster_transform.scale() / recording_scale_factor_, raster_transform.scale() / recording_scale_factor_); - if (is_partial_raster && settings.clear_canvas_before_raster && - requires_clear_) { + if (is_partial_raster && requires_clear_) { // TODO(enne): Should this be considered a partial clear? TrackRasterSourceNeededClear(RasterSourceClearType::kFull); + // Because Skia treats painted regions as transparent by default, we don't + // need to clear outside of the playback rect in the same way that + // ClearForOpaqueRaster must handle. raster_canvas->clear(SK_ColorTRANSPARENT); } diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h index 33e0231fc13..0fa0f86484c 100644 --- a/chromium/cc/raster/raster_source.h +++ b/chromium/cc/raster/raster_source.h @@ -38,7 +38,6 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { // If set to true, we should use LCD text. bool use_lcd_text = true; - bool clear_canvas_before_raster = true; // The ImageProvider used to replace images during playback. ImageProvider* image_provider = nullptr; @@ -133,10 +132,10 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { explicit RasterSource(const RecordingSource* other); virtual ~RasterSource(); - void ClearForFullRaster(SkCanvas* raster_canvas, - const gfx::Size& content_size, - const gfx::Rect& canvas_bitmap_rect, - const gfx::Rect& canvas_playback_rect) const; + void ClearForOpaqueRaster(SkCanvas* raster_canvas, + const gfx::Size& content_size, + const gfx::Rect& canvas_bitmap_rect, + const gfx::Rect& canvas_playback_rect) const; // These members are const as this raster source may be in use on another // thread and so should not be touched after construction. diff --git a/chromium/cc/raster/staging_buffer_pool.cc b/chromium/cc/raster/staging_buffer_pool.cc index 0ac04f79693..f8da555500e 100644 --- a/chromium/cc/raster/staging_buffer_pool.cc +++ b/chromium/cc/raster/staging_buffer_pool.cc @@ -146,7 +146,12 @@ StagingBufferPool::StagingBufferPool( DCHECK(worker_context_provider_); base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( this, "cc::StagingBufferPool", base::ThreadTaskRunnerHandle::Get()); + base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this); + memory_pressure_listener_.reset(new base::MemoryPressureListener( + base::BindRepeating(&StagingBufferPool::OnMemoryPressure, + weak_ptr_factory_.GetWeakPtr()))); + reduce_memory_usage_callback_ = base::Bind( &StagingBufferPool::ReduceMemoryUsage, weak_ptr_factory_.GetWeakPtr()); } @@ -433,4 +438,17 @@ void StagingBufferPool::OnPurgeMemory() { ReleaseBuffersNotUsedSince(base::TimeTicks() + base::TimeDelta::Max()); } +void StagingBufferPool::OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level) { + base::AutoLock lock(lock_); + switch (level) { + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + break; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + ReleaseBuffersNotUsedSince(base::TimeTicks() + base::TimeDelta::Max()); + break; + } +} + } // namespace cc diff --git a/chromium/cc/raster/staging_buffer_pool.h b/chromium/cc/raster/staging_buffer_pool.h index 460a3bbe843..881c34e0acd 100644 --- a/chromium/cc/raster/staging_buffer_pool.h +++ b/chromium/cc/raster/staging_buffer_pool.h @@ -13,6 +13,7 @@ #include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/memory/memory_coordinator_client.h" +#include "base/memory/memory_pressure_listener.h" #include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner.h" #include "base/synchronization/lock.h" @@ -100,6 +101,11 @@ class CC_EXPORT StagingBufferPool // Overriden from base::MemoryCoordinatorClient. void OnPurgeMemory() override; + // TODO(gyuyoung): OnMemoryPressure is deprecated. So this should be removed + // when the memory coordinator is enabled by default. + void OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level); + scoped_refptr<base::SequencedTaskRunner> task_runner_; viz::RasterContextProvider* const worker_context_provider_; const bool use_partial_raster_; @@ -119,6 +125,8 @@ class CC_EXPORT StagingBufferPool bool reduce_memory_usage_pending_; base::Closure reduce_memory_usage_callback_; + std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; + base::WeakPtrFactory<StagingBufferPool> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(StagingBufferPool); diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc index fd2bb2118c8..dcb25d7ca28 100644 --- a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc @@ -11,8 +11,8 @@ #include "base/macros.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" -#include "cc/resources/layer_tree_resource_provider.h" #include "cc/resources/resource_pool.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/resources/platform_color.h" #include "components/viz/common/resources/resource_format_utils.h" @@ -104,7 +104,6 @@ class ZeroCopyRasterBufferImpl : public RasterBuffer { gl->GenTextures(1, &backing_->texture_id); backing_->texture_target = gpu::GetBufferTextureTarget( kBufferUsage, viz::BufferFormat(resource_format_), caps); - backing_->mailbox = gpu::Mailbox::Generate(); gl->ProduceTextureDirectCHROMIUM(backing_->texture_id, backing_->mailbox.name); backing_->overlay_candidate = true; @@ -144,10 +143,15 @@ class ZeroCopyRasterBufferImpl : public RasterBuffer { backing_->image_id); gl->BindTexImage2DCHROMIUM(backing_->texture_target, backing_->image_id); } + if (resource_color_space_.IsValid()) { + gl->SetColorSpaceMetadataCHROMIUM( + backing_->texture_id, + reinterpret_cast<GLColorSpace>(&resource_color_space_)); + } gl->BindTexture(backing_->texture_target, 0); backing_->mailbox_sync_token = - LayerTreeResourceProvider::GenerateSyncTokenHelper(gl); + viz::ClientResourceProvider::GenerateSyncTokenHelper(gl); backing_->gpu_memory_buffer = std::move(gpu_memory_buffer_); } @@ -165,10 +169,10 @@ class ZeroCopyRasterBufferImpl : public RasterBuffer { gpu_memory_buffer_ = gpu_memory_buffer_manager_->CreateGpuMemoryBuffer( resource_size_, viz::BufferFormat(resource_format_), kBufferUsage, gpu::kNullSurfaceHandle); - // GpuMemoryBuffer allocation can fail (https://crbug.com/554541). + // Note that GpuMemoryBuffer allocation can fail. + // https://crbug.com/554541 if (!gpu_memory_buffer_) return; - gpu_memory_buffer_->SetColorSpace(resource_color_space_); } DCHECK_EQ(1u, gfx::NumberOfPlanesForBufferFormat( diff --git a/chromium/cc/resources/cross_thread_shared_bitmap.h b/chromium/cc/resources/cross_thread_shared_bitmap.h index 1a436e91b70..f594ef19df5 100644 --- a/chromium/cc/resources/cross_thread_shared_bitmap.h +++ b/chromium/cc/resources/cross_thread_shared_bitmap.h @@ -10,8 +10,8 @@ #include "base/memory/ref_counted.h" #include "base/memory/shared_memory.h" #include "cc/cc_export.h" -#include "components/viz/common/quads/shared_bitmap.h" #include "components/viz/common/resources/resource_format.h" +#include "components/viz/common/resources/shared_bitmap.h" #include "ui/gfx/geometry/size.h" namespace cc { diff --git a/chromium/cc/resources/layer_tree_resource_provider.cc b/chromium/cc/resources/layer_tree_resource_provider.cc deleted file mode 100644 index df7c76ca592..00000000000 --- a/chromium/cc/resources/layer_tree_resource_provider.cc +++ /dev/null @@ -1,307 +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. - -#include "cc/resources/layer_tree_resource_provider.h" - -#include "base/bits.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/common/resources/resource_format_utils.h" -#include "components/viz/common/resources/resource_sizes.h" -#include "components/viz/common/resources/returned_resource.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "gpu/command_buffer/client/raster_interface.h" -#include "gpu/command_buffer/common/capabilities.h" -#include "third_party/skia/include/core/SkCanvas.h" - -using gpu::gles2::GLES2Interface; - -namespace cc { - -struct LayerTreeResourceProvider::ImportedResource { - viz::TransferableResource resource; - std::unique_ptr<viz::SingleReleaseCallback> release_callback; - int exported_count = 0; - bool marked_for_deletion = false; - - gpu::SyncToken returned_sync_token; - bool returned_lost = false; - - ImportedResource(viz::ResourceId id, - const viz::TransferableResource& resource, - std::unique_ptr<viz::SingleReleaseCallback> release_callback) - : resource(resource), - release_callback(std::move(release_callback)), - // If the resource is immediately deleted, it returns the same SyncToken - // it came with. The client may need to wait on that before deleting the - // backing or reusing it. - returned_sync_token(resource.mailbox_holder.sync_token) { - // Replace the |resource| id with the local id from this - // LayerTreeResourceProvider. - this->resource.id = id; - } - ~ImportedResource() = default; - - ImportedResource(ImportedResource&&) = default; - ImportedResource& operator=(ImportedResource&&) = default; -}; - -LayerTreeResourceProvider::LayerTreeResourceProvider( - viz::ContextProvider* compositor_context_provider, - bool delegated_sync_points_required) - : delegated_sync_points_required_(delegated_sync_points_required), - compositor_context_provider_(compositor_context_provider) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -} - -LayerTreeResourceProvider::~LayerTreeResourceProvider() { - for (auto& pair : imported_resources_) { - ImportedResource& imported = pair.second; - // If the resource is exported we can't report when it can be used again - // once this class is destroyed, so consider the resource lost. - bool is_lost = imported.exported_count || imported.returned_lost; - imported.release_callback->Run(imported.returned_sync_token, is_lost); - } -} - -gpu::SyncToken LayerTreeResourceProvider::GenerateSyncTokenHelper( - gpu::gles2::GLES2Interface* gl) { - DCHECK(gl); - gpu::SyncToken sync_token; - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); - DCHECK(sync_token.HasData() || - gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR); - return sync_token; -} - -gpu::SyncToken LayerTreeResourceProvider::GenerateSyncTokenHelper( - gpu::raster::RasterInterface* ri) { - DCHECK(ri); - gpu::SyncToken sync_token; - ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); - DCHECK(sync_token.HasData() || - ri->GetGraphicsResetStatusKHR() != GL_NO_ERROR); - return sync_token; -} - -void LayerTreeResourceProvider::PrepareSendToParent( - const std::vector<viz::ResourceId>& export_ids, - std::vector<viz::TransferableResource>* list, - viz::ContextProvider* context_provider) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - // This function goes through the array multiple times, store the resources - // as pointers so we don't have to look up the resource id multiple times. - // Make sure the maps do not change while these vectors are alive or they - // will become invalid. - std::vector<ImportedResource*> imports; - imports.reserve(export_ids.size()); - for (const viz::ResourceId id : export_ids) { - auto it = imported_resources_.find(id); - DCHECK(it != imported_resources_.end()); - imports.push_back(&it->second); - } - - // Lazily create any mailboxes and verify all unverified sync tokens. - std::vector<GLbyte*> unverified_sync_tokens; - if (delegated_sync_points_required_) { - for (ImportedResource* imported : imports) { - if (!imported->resource.is_software && - !imported->resource.mailbox_holder.sync_token.verified_flush()) { - unverified_sync_tokens.push_back( - imported->resource.mailbox_holder.sync_token.GetData()); - } - } - } - - if (!unverified_sync_tokens.empty()) { - DCHECK(delegated_sync_points_required_); - DCHECK(context_provider); - context_provider->ContextGL()->VerifySyncTokensCHROMIUM( - unverified_sync_tokens.data(), unverified_sync_tokens.size()); - } - - for (ImportedResource* imported : imports) { - list->push_back(imported->resource); - imported->exported_count++; - } -} - -void LayerTreeResourceProvider::ReceiveReturnsFromParent( - const std::vector<viz::ReturnedResource>& resources) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - for (const viz::ReturnedResource& returned : resources) { - viz::ResourceId local_id = returned.id; - - auto import_it = imported_resources_.find(local_id); - DCHECK(import_it != imported_resources_.end()); - ImportedResource& imported = import_it->second; - - DCHECK_GE(imported.exported_count, returned.count); - imported.exported_count -= returned.count; - imported.returned_lost |= returned.lost; - - if (imported.exported_count) - continue; - - if (returned.sync_token.HasData()) { - DCHECK(!imported.resource.is_software); - imported.returned_sync_token = returned.sync_token; - } - - if (imported.marked_for_deletion) { - imported.release_callback->Run(imported.returned_sync_token, - imported.returned_lost); - imported_resources_.erase(import_it); - } - } -} - -viz::ResourceId LayerTreeResourceProvider::ImportResource( - const viz::TransferableResource& resource, - std::unique_ptr<viz::SingleReleaseCallback> release_callback) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - viz::ResourceId id = next_id_++; - auto result = imported_resources_.emplace( - id, ImportedResource(id, resource, std::move(release_callback))); - DCHECK(result.second); // If false, the id was already in the map. - return id; -} - -void LayerTreeResourceProvider::RemoveImportedResource(viz::ResourceId id) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - auto it = imported_resources_.find(id); - DCHECK(it != imported_resources_.end()); - ImportedResource& imported = it->second; - imported.marked_for_deletion = true; - if (imported.exported_count == 0) { - imported.release_callback->Run(imported.returned_sync_token, - imported.returned_lost); - imported_resources_.erase(it); - } -} - -bool LayerTreeResourceProvider::IsTextureFormatSupported( - viz::ResourceFormat format) const { - gpu::Capabilities caps; - if (compositor_context_provider_) - caps = compositor_context_provider_->ContextCapabilities(); - - switch (format) { - case viz::ALPHA_8: - case viz::RGBA_4444: - case viz::RGBA_8888: - case viz::RGB_565: - case viz::LUMINANCE_8: - return true; - case viz::BGRA_8888: - return caps.texture_format_bgra8888; - case viz::ETC1: - return caps.texture_format_etc1; - case viz::RED_8: - return caps.texture_rg; - case viz::R16_EXT: - return caps.texture_norm16; - case viz::LUMINANCE_F16: - case viz::RGBA_F16: - return caps.texture_half_float_linear; - } - - NOTREACHED(); - return false; -} - -bool LayerTreeResourceProvider::IsRenderBufferFormatSupported( - viz::ResourceFormat format) const { - gpu::Capabilities caps; - if (compositor_context_provider_) - caps = compositor_context_provider_->ContextCapabilities(); - - switch (format) { - case viz::RGBA_4444: - case viz::RGBA_8888: - case viz::RGB_565: - return true; - case viz::BGRA_8888: - return caps.render_buffer_format_bgra8888; - case viz::RGBA_F16: - // TODO(ccameron): This will always return false on pixel tests, which - // makes it un-test-able until we upgrade Mesa. - // https://crbug.com/687720 - return caps.texture_half_float_linear && - caps.color_buffer_half_float_rgba; - case viz::LUMINANCE_8: - case viz::ALPHA_8: - case viz::RED_8: - case viz::ETC1: - case viz::LUMINANCE_F16: - case viz::R16_EXT: - // We don't currently render into these formats. If we need to render into - // these eventually, we should expand this logic. - return false; - } - - NOTREACHED(); - return false; -} - -LayerTreeResourceProvider::ScopedSkSurface::ScopedSkSurface( - GrContext* gr_context, - GLuint texture_id, - GLenum texture_target, - const gfx::Size& size, - viz::ResourceFormat format, - bool can_use_lcd_text, - int msaa_sample_count) { - GrGLTextureInfo texture_info; - texture_info.fID = texture_id; - texture_info.fTarget = texture_target; - texture_info.fFormat = TextureStorageFormat(format); - GrBackendTexture backend_texture(size.width(), size.height(), - GrMipMapped::kNo, texture_info); - SkSurfaceProps surface_props = ComputeSurfaceProps(can_use_lcd_text); - // This type is used only for gpu raster, which implies gpu compositing. - bool gpu_compositing = true; - surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget( - gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, msaa_sample_count, - ResourceFormatToClosestSkColorType(gpu_compositing, format), nullptr, - &surface_props); -} - -LayerTreeResourceProvider::ScopedSkSurface::~ScopedSkSurface() { - if (surface_) - surface_->prepareForExternalIO(); -} - -SkSurfaceProps LayerTreeResourceProvider::ScopedSkSurface::ComputeSurfaceProps( - bool can_use_lcd_text) { - uint32_t flags = 0; - // Use unknown pixel geometry to disable LCD text. - SkSurfaceProps surface_props(flags, kUnknown_SkPixelGeometry); - if (can_use_lcd_text) { - // LegacyFontHost will get LCD text and skia figures out what type to use. - surface_props = - SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType); - } - return surface_props; -} - -void LayerTreeResourceProvider::ValidateResource(viz::ResourceId id) const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(id); - DCHECK(imported_resources_.find(id) != imported_resources_.end()); -} - -bool LayerTreeResourceProvider::InUseByConsumer(viz::ResourceId id) { - auto it = imported_resources_.find(id); - DCHECK(it != imported_resources_.end()); - ImportedResource& imported = it->second; - return imported.exported_count > 0 || imported.returned_lost; -} - -} // namespace cc diff --git a/chromium/cc/resources/layer_tree_resource_provider.h b/chromium/cc/resources/layer_tree_resource_provider.h deleted file mode 100644 index b046ea9d33c..00000000000 --- a/chromium/cc/resources/layer_tree_resource_provider.h +++ /dev/null @@ -1,127 +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_RESOURCES_LAYER_TREE_RESOURCE_PROVIDER_H_ -#define CC_RESOURCES_LAYER_TREE_RESOURCE_PROVIDER_H_ - -#include <vector> - -#include "base/threading/thread_checker.h" -#include "cc/cc_export.h" -#include "components/viz/common/display/renderer_settings.h" -#include "components/viz/common/resources/release_callback.h" -#include "components/viz/common/resources/resource_id.h" -#include "components/viz/common/resources/resource_settings.h" -#include "components/viz/common/resources/single_release_callback.h" -#include "components/viz/common/resources/transferable_resource.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/include/gpu/GrBackendSurface.h" -#include "third_party/skia/include/gpu/GrContext.h" - -namespace gpu { -namespace gles2 { -class GLES2Interface; -} -namespace raster { -class RasterInterface; -} -} // namespace gpu - -namespace viz { -class ContextProvider; -} // namespace viz - -namespace cc { - -// This class is not thread-safe and can only be called from the thread it was -// created on (in practice, the impl thread). -class CC_EXPORT LayerTreeResourceProvider { - public: - LayerTreeResourceProvider(viz::ContextProvider* compositor_context_provider, - bool delegated_sync_points_required); - ~LayerTreeResourceProvider(); - - static gpu::SyncToken GenerateSyncTokenHelper(gpu::gles2::GLES2Interface* gl); - static gpu::SyncToken GenerateSyncTokenHelper( - gpu::raster::RasterInterface* ri); - - // Prepares resources to be transfered to the parent, moving them to - // mailboxes and serializing meta-data into TransferableResources. - // Resources are not removed from the ResourceProvider, but are marked as - // "in use". - void PrepareSendToParent( - const std::vector<viz::ResourceId>& resource_ids, - std::vector<viz::TransferableResource>* transferable_resources, - viz::ContextProvider* context_provider); - - // Receives resources from the parent, moving them from mailboxes. ResourceIds - // passed are in the child namespace. - // NOTE: if the sync_token is set on any viz::TransferableResource, this will - // wait on it. - void ReceiveReturnsFromParent( - const std::vector<viz::ReturnedResource>& transferable_resources); - - // Receives a resource from an external client that can be used in compositor - // frames, via the returned ResourceId. - viz::ResourceId ImportResource(const viz::TransferableResource&, - std::unique_ptr<viz::SingleReleaseCallback>); - // Removes an imported resource, which will call the ReleaseCallback given - // originally, once the resource is no longer in use by any compositor frame. - void RemoveImportedResource(viz::ResourceId); - - // Verify that the ResourceId is valid and is known to this class, for debug - // checks. - void ValidateResource(viz::ResourceId id) const; - - // Checks whether a resource is in use by a consumer. - bool InUseByConsumer(viz::ResourceId id); - - bool IsTextureFormatSupported(viz::ResourceFormat format) const; - - // Returns true if the provided |format| can be used as a render buffer. - // Note that render buffer support implies texture support. - bool IsRenderBufferFormatSupported(viz::ResourceFormat format) const; - - bool IsSoftware() const { return !compositor_context_provider_; } - - class CC_EXPORT ScopedSkSurface { - public: - ScopedSkSurface(GrContext* gr_context, - GLuint texture_id, - GLenum texture_target, - const gfx::Size& size, - viz::ResourceFormat format, - bool can_use_lcd_text, - int msaa_sample_count); - ~ScopedSkSurface(); - - SkSurface* surface() const { return surface_.get(); } - - static SkSurfaceProps ComputeSurfaceProps(bool can_use_lcd_text); - - private: - sk_sp<SkSurface> surface_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSkSurface); - }; - - private: - struct ImportedResource; - - THREAD_CHECKER(thread_checker_); - const bool delegated_sync_points_required_; - viz::ContextProvider* const compositor_context_provider_; - - base::flat_map<viz::ResourceId, ImportedResource> imported_resources_; - // The ResourceIds in LayerTreeResourceProvider start from 1 to avoid - // conflicts with id from viz::DisplayResourceProvider. - viz::ResourceId next_id_ = 1; - - DISALLOW_COPY_AND_ASSIGN(LayerTreeResourceProvider); -}; - -} // namespace cc - -#endif // CC_RESOURCES_LAYER_TREE_RESOURCE_PROVIDER_H_ diff --git a/chromium/cc/resources/layer_tree_resource_provider_unittest.cc b/chromium/cc/resources/layer_tree_resource_provider_unittest.cc deleted file mode 100644 index e78f363d6b8..00000000000 --- a/chromium/cc/resources/layer_tree_resource_provider_unittest.cc +++ /dev/null @@ -1,556 +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. - -#include "cc/resources/layer_tree_resource_provider.h" - -#include <memory> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "components/viz/common/resources/returned_resource.h" -#include "components/viz/common/resources/single_release_callback.h" -#include "components/viz/test/test_context_provider.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; - -namespace cc { -namespace { - -class LayerTreeResourceProviderTest : public testing::TestWithParam<bool> { - protected: - LayerTreeResourceProviderTest() - : use_gpu_(GetParam()), - context_provider_(viz::TestContextProvider::Create()), - bound_(context_provider_->BindToCurrentThread()), - provider_(std::make_unique<LayerTreeResourceProvider>( - use_gpu_ ? context_provider_.get() : nullptr, - delegated_sync_points_required_)) { - DCHECK_EQ(bound_, gpu::ContextResult::kSuccess); - } - - gpu::Mailbox MailboxFromChar(char value) { - gpu::Mailbox mailbox; - memset(mailbox.name, value, sizeof(mailbox.name)); - return mailbox; - } - - gpu::SyncToken SyncTokenFromUInt(uint32_t value) { - return gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x123), value); - } - - viz::TransferableResource MakeTransferableResource( - bool gpu, - char mailbox_char, - uint32_t sync_token_value) { - viz::TransferableResource r; - r.id = mailbox_char; - r.is_software = !gpu; - r.filter = 456; - r.size = gfx::Size(10, 11); - r.mailbox_holder.mailbox = MailboxFromChar(mailbox_char); - if (gpu) { - r.mailbox_holder.sync_token = SyncTokenFromUInt(sync_token_value); - r.mailbox_holder.texture_target = 6; - } - return r; - } - - void Shutdown() { provider_.reset(); } - - bool use_gpu() const { return use_gpu_; } - LayerTreeResourceProvider& provider() const { return *provider_; } - viz::ContextProvider* context_provider() const { - return context_provider_.get(); - } - - void DestroyProvider() { provider_.reset(); } - - private: - bool use_gpu_; - scoped_refptr<viz::TestContextProvider> context_provider_; - gpu::ContextResult bound_; - bool delegated_sync_points_required_ = true; - std::unique_ptr<LayerTreeResourceProvider> provider_; -}; - -INSTANTIATE_TEST_CASE_P(LayerTreeResourceProviderTests, - LayerTreeResourceProviderTest, - ::testing::Values(false, true)); - -class MockReleaseCallback { - public: - MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost)); -}; - -TEST_P(LayerTreeResourceProviderTest, TransferableResourceReleased) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - // The local id is different. - EXPECT_NE(id, tran.id); - - // The same SyncToken that was sent is returned when the resource was never - // exported. The SyncToken may be from any context, and the ReleaseCallback - // may need to wait on it before interacting with the resource on its context. - EXPECT_CALL(release, Released(tran.mailbox_holder.sync_token, false)); - provider().RemoveImportedResource(id); -} - -TEST_P(LayerTreeResourceProviderTest, TransferableResourceSendToParent) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - tran.buffer_format = gfx::BufferFormat::RGBX_8888; - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Export the resource. - std::vector<viz::ResourceId> to_send = {id}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - ASSERT_EQ(exported.size(), 1u); - - // Exported resource matches except for the id which was mapped - // to the local ResourceProvider, and the sync token should be - // verified if it's a gpu resource. - gpu::SyncToken verified_sync_token = tran.mailbox_holder.sync_token; - if (!tran.is_software) - verified_sync_token.SetVerifyFlush(); - EXPECT_EQ(exported[0].id, id); - EXPECT_EQ(exported[0].is_software, tran.is_software); - EXPECT_EQ(exported[0].filter, tran.filter); - EXPECT_EQ(exported[0].size, tran.size); - EXPECT_EQ(exported[0].mailbox_holder.mailbox, tran.mailbox_holder.mailbox); - EXPECT_EQ(exported[0].mailbox_holder.sync_token, verified_sync_token); - EXPECT_EQ(exported[0].mailbox_holder.texture_target, - tran.mailbox_holder.texture_target); - EXPECT_EQ(exported[0].buffer_format, tran.buffer_format); - - // Exported resources are not released when removed, until the export returns. - EXPECT_CALL(release, Released(_, _)).Times(0); - provider().RemoveImportedResource(id); - - // Return the resource, with a sync token if using gpu. - std::vector<viz::ReturnedResource> returned; - returned.push_back({}); - returned.back().id = exported[0].id; - if (use_gpu()) - returned.back().sync_token = SyncTokenFromUInt(31); - returned.back().count = 1; - returned.back().lost = false; - - // The sync token is given to the ReleaseCallback. - EXPECT_CALL(release, Released(returned[0].sync_token, false)); - provider().ReceiveReturnsFromParent(returned); -} - -TEST_P(LayerTreeResourceProviderTest, TransferableResourceSendTwoToParent) { - viz::TransferableResource tran[] = { - MakeTransferableResource(use_gpu(), 'a', 15), - MakeTransferableResource(use_gpu(), 'b', 16)}; - viz::ResourceId id1 = provider().ImportResource( - tran[0], viz::SingleReleaseCallback::Create(base::DoNothing())); - viz::ResourceId id2 = provider().ImportResource( - tran[1], viz::SingleReleaseCallback::Create(base::DoNothing())); - - // Export the resource. - std::vector<viz::ResourceId> to_send = {id1, id2}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - ASSERT_EQ(exported.size(), 2u); - - // Exported resource matches except for the id which was mapped - // to the local ResourceProvider, and the sync token should be - // verified if it's a gpu resource. - for (int i = 0; i < 2; ++i) { - gpu::SyncToken verified_sync_token = tran[i].mailbox_holder.sync_token; - if (!tran[i].is_software) - verified_sync_token.SetVerifyFlush(); - EXPECT_EQ(exported[i].id, to_send[i]); - EXPECT_EQ(exported[i].is_software, tran[i].is_software); - EXPECT_EQ(exported[i].filter, tran[i].filter); - EXPECT_EQ(exported[i].size, tran[i].size); - EXPECT_EQ(exported[i].mailbox_holder.mailbox, - tran[i].mailbox_holder.mailbox); - EXPECT_EQ(exported[i].mailbox_holder.sync_token, verified_sync_token); - EXPECT_EQ(exported[i].mailbox_holder.texture_target, - tran[i].mailbox_holder.texture_target); - EXPECT_EQ(exported[i].buffer_format, tran[i].buffer_format); - } -} - -TEST_P(LayerTreeResourceProviderTest, - TransferableResourceSendToParentTwoTimes) { - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::DoNothing())); - - // Export the resource. - std::vector<viz::ResourceId> to_send = {id}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - ASSERT_EQ(exported.size(), 1u); - EXPECT_EQ(exported[0].id, id); - - // Return the resource, with a sync token if using gpu. - std::vector<viz::ReturnedResource> returned; - returned.push_back({}); - returned.back().id = exported[0].id; - if (use_gpu()) - returned.back().sync_token = SyncTokenFromUInt(31); - returned.back().count = 1; - returned.back().lost = false; - provider().ReceiveReturnsFromParent(returned); - - // Then export again, it still sends. - exported.clear(); - provider().PrepareSendToParent(to_send, &exported, context_provider()); - ASSERT_EQ(exported.size(), 1u); - EXPECT_EQ(exported[0].id, id); -} - -TEST_P(LayerTreeResourceProviderTest, - TransferableResourceLostOnShutdownIfExported) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Export the resource. - std::vector<viz::ResourceId> to_send = {id}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - EXPECT_CALL(release, Released(_, true)); - Shutdown(); -} - -TEST_P(LayerTreeResourceProviderTest, TransferableResourceRemovedAfterReturn) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Export the resource. - std::vector<viz::ResourceId> to_send = {id}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Return the resource. This does not release the resource back to - // the client. - std::vector<viz::ReturnedResource> returned; - returned.push_back({}); - returned.back().id = exported[0].id; - if (use_gpu()) - returned.back().sync_token = SyncTokenFromUInt(31); - returned.back().count = 1; - returned.back().lost = false; - - EXPECT_CALL(release, Released(_, _)).Times(0); - provider().ReceiveReturnsFromParent(returned); - - // Once removed, the resource is released. - EXPECT_CALL(release, Released(returned[0].sync_token, false)); - provider().RemoveImportedResource(id); -} - -TEST_P(LayerTreeResourceProviderTest, TransferableResourceExportedTwice) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Export the resource once. - std::vector<viz::ResourceId> to_send = {id}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Exported resources are not released when removed, until all exports are - // returned. - EXPECT_CALL(release, Released(_, _)).Times(0); - provider().RemoveImportedResource(id); - - // Export the resource twice. - exported = {}; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Return the resource the first time. - std::vector<viz::ReturnedResource> returned; - returned.push_back({}); - returned.back().id = exported[0].id; - if (use_gpu()) - returned.back().sync_token = SyncTokenFromUInt(31); - returned.back().count = 1; - returned.back().lost = false; - provider().ReceiveReturnsFromParent(returned); - - // And a second time, with a different sync token. Now the ReleaseCallback can - // happen, using the latest sync token. - if (use_gpu()) - returned.back().sync_token = SyncTokenFromUInt(47); - EXPECT_CALL(release, Released(returned[0].sync_token, false)); - provider().ReceiveReturnsFromParent(returned); -} - -TEST_P(LayerTreeResourceProviderTest, TransferableResourceReturnedTwiceAtOnce) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Export the resource once. - std::vector<viz::ResourceId> to_send = {id}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Exported resources are not released when removed, until all exports are - // returned. - EXPECT_CALL(release, Released(_, _)).Times(0); - provider().RemoveImportedResource(id); - - // Export the resource twice. - exported = {}; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Return both exports at once. - std::vector<viz::ReturnedResource> returned; - returned.push_back({}); - returned.back().id = exported[0].id; - if (use_gpu()) - returned.back().sync_token = SyncTokenFromUInt(31); - returned.back().count = 2; - returned.back().lost = false; - - // When returned, the ReleaseCallback can happen, using the latest sync token. - EXPECT_CALL(release, Released(returned[0].sync_token, false)); - provider().ReceiveReturnsFromParent(returned); -} - -TEST_P(LayerTreeResourceProviderTest, TransferableResourceLostOnReturn) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Export the resource once. - std::vector<viz::ResourceId> to_send = {id}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Exported resources are not released when removed, until all exports are - // returned. - EXPECT_CALL(release, Released(_, _)).Times(0); - provider().RemoveImportedResource(id); - - // Export the resource twice. - exported = {}; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Return the resource the first time, not lost. - std::vector<viz::ReturnedResource> returned; - returned.push_back({}); - returned.back().id = exported[0].id; - returned.back().count = 1; - returned.back().lost = false; - provider().ReceiveReturnsFromParent(returned); - - // Return a second time, as lost. The viz::ReturnCallback should report it - // lost. - returned.back().lost = true; - EXPECT_CALL(release, Released(_, true)); - provider().ReceiveReturnsFromParent(returned); -} - -TEST_P(LayerTreeResourceProviderTest, TransferableResourceLostOnFirstReturn) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId id = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Export the resource once. - std::vector<viz::ResourceId> to_send = {id}; - std::vector<viz::TransferableResource> exported; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Exported resources are not released when removed, until all exports are - // returned. - EXPECT_CALL(release, Released(_, _)).Times(0); - provider().RemoveImportedResource(id); - - // Export the resource twice. - exported = {}; - provider().PrepareSendToParent(to_send, &exported, context_provider()); - - // Return the resource the first time, marked as lost. - std::vector<viz::ReturnedResource> returned; - returned.push_back({}); - returned.back().id = exported[0].id; - returned.back().count = 1; - returned.back().lost = true; - provider().ReceiveReturnsFromParent(returned); - - // Return a second time, not lost. The first lost signal should not be lost. - returned.back().lost = false; - EXPECT_CALL(release, Released(_, true)); - provider().ReceiveReturnsFromParent(returned); -} - -TEST_P(LayerTreeResourceProviderTest, ReturnedSyncTokensArePassedToClient) { - // SyncTokens are gpu-only. - if (!use_gpu()) - return; - - MockReleaseCallback release; - - GLuint texture; - context_provider()->ContextGL()->GenTextures(1, &texture); - context_provider()->ContextGL()->BindTexture(GL_TEXTURE_2D, texture); - gpu::Mailbox mailbox; - context_provider()->ContextGL()->GenMailboxCHROMIUM(mailbox.name); - context_provider()->ContextGL()->ProduceTextureDirectCHROMIUM(texture, - mailbox.name); - gpu::SyncToken sync_token; - context_provider()->ContextGL()->GenSyncTokenCHROMIUM(sync_token.GetData()); - - auto tran = viz::TransferableResource::MakeGL(mailbox, GL_LINEAR, - GL_TEXTURE_2D, sync_token); - viz::ResourceId resource = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - EXPECT_TRUE(tran.mailbox_holder.sync_token.HasData()); - // All the logic below assumes that the sync token releases are all positive. - EXPECT_LT(0u, tran.mailbox_holder.sync_token.release_count()); - - // Transfer the resource, expect the sync points to be consistent. - std::vector<viz::TransferableResource> list; - provider().PrepareSendToParent({resource}, &list, context_provider()); - ASSERT_EQ(1u, list.size()); - EXPECT_LE(sync_token.release_count(), - list[0].mailbox_holder.sync_token.release_count()); - EXPECT_EQ(0, memcmp(mailbox.name, list[0].mailbox_holder.mailbox.name, - sizeof(mailbox.name))); - - // Make a new texture id from the mailbox. - context_provider()->ContextGL()->WaitSyncTokenCHROMIUM( - list[0].mailbox_holder.sync_token.GetConstData()); - unsigned other_texture = - context_provider()->ContextGL()->CreateAndConsumeTextureCHROMIUM( - mailbox.name); - // Then delete it and make a new SyncToken. - context_provider()->ContextGL()->DeleteTextures(1, &other_texture); - context_provider()->ContextGL()->GenSyncTokenCHROMIUM( - list[0].mailbox_holder.sync_token.GetData()); - EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData()); - - // Receive the resource, then delete it, expect the SyncTokens to be - // consistent. - provider().ReceiveReturnsFromParent( - viz::TransferableResource::ReturnResources(list)); - - gpu::SyncToken returned_sync_token; - EXPECT_CALL(release, Released(_, false)) - .WillOnce(testing::SaveArg<0>(&returned_sync_token)); - provider().RemoveImportedResource(resource); - EXPECT_GE(returned_sync_token.release_count(), - list[0].mailbox_holder.sync_token.release_count()); -} - -TEST_P(LayerTreeResourceProviderTest, LostResourcesAreReturnedLost) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId resource = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Transfer the resource to the parent. - std::vector<viz::TransferableResource> list; - provider().PrepareSendToParent({resource}, &list, context_provider()); - EXPECT_EQ(1u, list.size()); - - // Receive it back marked lost. - std::vector<viz::ReturnedResource> returned_to_child; - returned_to_child.push_back(list[0].ToReturnedResource()); - returned_to_child.back().lost = true; - provider().ReceiveReturnsFromParent(returned_to_child); - - // Delete the resource in the child. Expect the resource to be lost. - EXPECT_CALL(release, Released(_, true)); - provider().RemoveImportedResource(resource); -} - -TEST_P(LayerTreeResourceProviderTest, ShutdownPreservesLostState) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId resource = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Transfer the resource to the parent. - std::vector<viz::TransferableResource> list; - provider().PrepareSendToParent({resource}, &list, context_provider()); - EXPECT_EQ(1u, list.size()); - - // Receive it back marked lost. - std::vector<viz::ReturnedResource> returned_to_child; - returned_to_child.push_back(list[0].ToReturnedResource()); - returned_to_child.back().lost = true; - provider().ReceiveReturnsFromParent(returned_to_child); - - // Shutdown, and expect the resource to be lost.. - EXPECT_CALL(release, Released(_, true)); - DestroyProvider(); -} - -TEST_P(LayerTreeResourceProviderTest, ShutdownLosesExportedResources) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId resource = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Transfer the resource to the parent. - std::vector<viz::TransferableResource> list; - provider().PrepareSendToParent({resource}, &list, context_provider()); - EXPECT_EQ(1u, list.size()); - - // Destroy the LayerTreeResourceProvider, the resource is returned lost. - EXPECT_CALL(release, Released(_, true)); - DestroyProvider(); -} - -TEST_P(LayerTreeResourceProviderTest, ShutdownDoesNotLoseUnexportedResources) { - MockReleaseCallback release; - viz::TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15); - viz::ResourceId resource = provider().ImportResource( - tran, viz::SingleReleaseCallback::Create(base::BindOnce( - &MockReleaseCallback::Released, base::Unretained(&release)))); - - // Transfer the resource to the parent. - std::vector<viz::TransferableResource> list; - provider().PrepareSendToParent({resource}, &list, context_provider()); - EXPECT_EQ(1u, list.size()); - - // Receive it back. - provider().ReceiveReturnsFromParent( - viz::TransferableResource::ReturnResources(list)); - - // Destroy the LayerTreeResourceProvider, the resource is not lost. - EXPECT_CALL(release, Released(_, false)); - DestroyProvider(); -} - -} // namespace -} // namespace cc diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc index a6d8b841c92..47f776cc9d3 100644 --- a/chromium/cc/resources/resource_pool.cc +++ b/chromium/cc/resources/resource_pool.cc @@ -20,7 +20,7 @@ #include "base/trace_event/memory_dump_manager.h" #include "build/build_config.h" #include "cc/base/container_util.h" -#include "cc/resources/layer_tree_resource_provider.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/resources/resource_sizes.h" #include "gpu/command_buffer/client/gles2_interface.h" @@ -50,8 +50,8 @@ bool ResourceMeetsSizeRequirements(const gfx::Size& requested_size, return false; // GetArea will crash on overflow, however all sizes in use are tile sizes. - // These are capped at LayerTreeResourceProvider::max_texture_size(), and will - // not overflow. + // These are capped at viz::ClientResourceProvider::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 @@ -67,7 +67,7 @@ bool ResourceMeetsSizeRequirements(const gfx::Size& requested_size, constexpr base::TimeDelta ResourcePool::kDefaultExpirationDelay; ResourcePool::ResourcePool( - LayerTreeResourceProvider* resource_provider, + viz::ClientResourceProvider* resource_provider, viz::ContextProvider* context_provider, scoped_refptr<base::SingleThreadTaskRunner> task_runner, const base::TimeDelta& expiration_delay, @@ -83,6 +83,9 @@ ResourcePool::ResourcePool( this, "cc::ResourcePool", task_runner_.get()); // Register this component with base::MemoryCoordinatorClientRegistry. base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this); + memory_pressure_listener_.reset( + new base::MemoryPressureListener(base::BindRepeating( + &ResourcePool::OnMemoryPressure, weak_ptr_factory_.GetWeakPtr()))); } ResourcePool::~ResourcePool() { @@ -306,7 +309,6 @@ void ResourcePool::PrepareForExport(const InUsePoolResource& resource) { resource.resource_->size(), resource.resource_->format()); } transferable.format = resource.resource_->format(); - transferable.buffer_format = viz::BufferFormat(transferable.format); transferable.color_space = resource.resource_->color_space(); resource.resource_->set_resource_id(resource_provider_->ImportResource( std::move(transferable), @@ -557,6 +559,18 @@ void ResourcePool::OnMemoryStateChange(base::MemoryState state) { evict_busy_resources_when_unused_ = state == base::MemoryState::SUSPENDED; } +void ResourcePool::OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level) { + switch (level) { + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + break; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + EvictResourcesNotUsedSince(base::TimeTicks() + base::TimeDelta::Max()); + break; + } +} + ResourcePool::PoolResource::PoolResource(size_t unique_id, const gfx::Size& size, viz::ResourceFormat format, @@ -571,7 +585,7 @@ ResourcePool::PoolResource::~PoolResource() = default; void ResourcePool::PoolResource::OnMemoryDump( base::trace_event::ProcessMemoryDump* pmd, int tracing_id, - const LayerTreeResourceProvider* resource_provider, + const viz::ClientResourceProvider* resource_provider, bool is_free) const { base::UnguessableToken shm_guid; base::trace_event::MemoryAllocatorDumpGuid backing_guid; diff --git a/chromium/cc/resources/resource_pool.h b/chromium/cc/resources/resource_pool.h index e401582bc6d..ebc1f56eabe 100644 --- a/chromium/cc/resources/resource_pool.h +++ b/chromium/cc/resources/resource_pool.h @@ -14,15 +14,16 @@ #include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/memory/memory_coordinator_client.h" +#include "base/memory/memory_pressure_listener.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/trace_event/memory_allocator_dump_guid.h" #include "base/trace_event/memory_dump_provider.h" #include "base/unguessable_token.h" #include "cc/cc_export.h" -#include "components/viz/common/quads/shared_bitmap.h" #include "components/viz/common/resources/resource_format.h" #include "components/viz/common/resources/resource_id.h" +#include "components/viz/common/resources/shared_bitmap.h" #include "gpu/command_buffer/common/sync_token.h" #include "third_party/khronos/GLES2/gl2.h" #include "ui/gfx/color_space.h" @@ -34,11 +35,11 @@ class SingleThreadTaskRunner; } namespace viz { +class ClientResourceProvider; class ContextProvider; } namespace cc { -class LayerTreeResourceProvider; class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, public base::MemoryCoordinatorClient { @@ -169,7 +170,7 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, // When holding gpu resources, the |context_provider| should be non-null, // and when holding software resources, it should be null. It is used for // consistency checking as well as for correctness. - ResourcePool(LayerTreeResourceProvider* resource_provider, + ResourcePool(viz::ClientResourceProvider* resource_provider, viz::ContextProvider* context_provider, scoped_refptr<base::SingleThreadTaskRunner> task_runner, const base::TimeDelta& expiration_delay, @@ -226,6 +227,11 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, void OnPurgeMemory() override; void OnMemoryStateChange(base::MemoryState state) override; + // TODO(gyuyoung): OnMemoryPressure is deprecated. So this should be removed + // when the memory coordinator is enabled by default. + void OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level); + size_t GetTotalMemoryUsageForTesting() const { return total_memory_usage_bytes_; } @@ -286,7 +292,7 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, int tracing_id, - const LayerTreeResourceProvider* resource_provider, + const viz::ClientResourceProvider* resource_provider, bool is_free) const; private: @@ -347,7 +353,7 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, bool HasEvictableResources() const; base::TimeTicks GetUsageTimeForLRUResource() const; - LayerTreeResourceProvider* const resource_provider_; + viz::ClientResourceProvider* const resource_provider_; viz::ContextProvider* const context_provider_; const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; const base::TimeDelta resource_expiration_delay_; @@ -370,6 +376,8 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, // Map from the PoolResource |unique_id| to the PoolResource. std::map<size_t, std::unique_ptr<PoolResource>> in_use_resources_; + std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; + base::WeakPtrFactory<ResourcePool> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ResourcePool); diff --git a/chromium/cc/resources/resource_pool_unittest.cc b/chromium/cc/resources/resource_pool_unittest.cc index 509a5a27421..171b1776e06 100644 --- a/chromium/cc/resources/resource_pool_unittest.cc +++ b/chromium/cc/resources/resource_pool_unittest.cc @@ -9,8 +9,9 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "cc/test/fake_resource_provider.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/resources/resource_sizes.h" +#include "components/viz/common/resources/returned_resource.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_shared_bitmap_manager.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,14 +23,17 @@ class ResourcePoolTest : public testing::Test { void SetUp() override { context_provider_ = viz::TestContextProvider::Create(); context_provider_->BindToCurrentThread(); - resource_provider_ = FakeResourceProvider::CreateLayerTreeResourceProvider( - context_provider_.get()); + resource_provider_ = std::make_unique<viz::ClientResourceProvider>(true); task_runner_ = base::ThreadTaskRunnerHandle::Get(); resource_pool_ = std::make_unique<ResourcePool>( resource_provider_.get(), context_provider_.get(), task_runner_, ResourcePool::kDefaultExpirationDelay, false); } + void TearDown() override { + resource_provider_->ShutdownAndReleaseAllResources(); + } + protected: class StubGpuBacking : public ResourcePool::GpuBacking { public: @@ -54,7 +58,7 @@ class ResourcePoolTest : public testing::Test { viz::TestSharedBitmapManager shared_bitmap_manager_; scoped_refptr<viz::TestContextProvider> context_provider_; - std::unique_ptr<LayerTreeResourceProvider> resource_provider_; + std::unique_ptr<viz::ClientResourceProvider> resource_provider_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; std::unique_ptr<ResourcePool> resource_pool_; }; @@ -700,7 +704,6 @@ TEST_F(ResourcePoolTest, MetadataSentToDisplayCompositor) { EXPECT_EQ(transfer[0].mailbox_holder.sync_token, sync_token); EXPECT_EQ(transfer[0].mailbox_holder.texture_target, target); EXPECT_EQ(transfer[0].format, format); - EXPECT_EQ(transfer[0].buffer_format, viz::BufferFormat(format)); EXPECT_TRUE(transfer[0].read_lock_fences_enabled); EXPECT_TRUE(transfer[0].is_overlay_candidate); diff --git a/chromium/cc/resources/shared_bitmap_id_registrar.h b/chromium/cc/resources/shared_bitmap_id_registrar.h index 8586a2408e7..b3533defd53 100644 --- a/chromium/cc/resources/shared_bitmap_id_registrar.h +++ b/chromium/cc/resources/shared_bitmap_id_registrar.h @@ -8,7 +8,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "cc/cc_export.h" -#include "components/viz/common/quads/shared_bitmap.h" +#include "components/viz/common/resources/shared_bitmap.h" namespace cc { class CrossThreadSharedBitmap; diff --git a/chromium/cc/resources/video_resource_updater.cc b/chromium/cc/resources/video_resource_updater.cc deleted file mode 100644 index 5004919630a..00000000000 --- a/chromium/cc/resources/video_resource_updater.cc +++ /dev/null @@ -1,1155 +0,0 @@ -// Copyright 2013 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/resources/video_resource_updater.h" - -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <string> - -#include "base/atomic_sequence_num.h" -#include "base/bind.h" -#include "base/bit_cast.h" -#include "base/memory/shared_memory.h" -#include "base/strings/stringprintf.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_event.h" -#include "cc/base/math_util.h" -#include "cc/paint/skia_paint_canvas.h" -#include "cc/resources/layer_tree_resource_provider.h" -#include "components/viz/common/gpu/context_provider.h" -#include "components/viz/common/gpu/texture_allocation.h" -#include "components/viz/common/quads/render_pass.h" -#include "components/viz/common/quads/stream_video_draw_quad.h" -#include "components/viz/common/quads/texture_draw_quad.h" -#include "components/viz/common/quads/yuv_video_draw_quad.h" -#include "components/viz/common/resources/bitmap_allocation.h" -#include "components/viz/common/resources/resource_sizes.h" -#include "components/viz/common/resources/shared_bitmap_reporter.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/context_support.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "media/base/video_frame.h" -#include "media/renderers/paint_canvas_video_renderer.h" -#include "media/video/half_float_maker.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "third_party/khronos/GLES2/gl2ext.h" -#include "third_party/libyuv/include/libyuv.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "ui/gfx/geometry/size_conversions.h" -#include "ui/gfx/skia_util.h" -#include "ui/gl/gl_enums.h" -#include "ui/gl/trace_util.h" - -namespace cc { -namespace { - -// Generates process-unique IDs to use for tracing video resources. -base::AtomicSequenceNumber g_next_video_resource_updater_id; - -VideoFrameResourceType ExternalResourceTypeForHardwarePlanes( - media::VideoPixelFormat format, - GLuint target, - int num_textures, - gfx::BufferFormat* buffer_format, - bool use_stream_video_draw_quad) { - *buffer_format = gfx::BufferFormat::RGBA_8888; - switch (format) { - case media::PIXEL_FORMAT_ARGB: - case media::PIXEL_FORMAT_XRGB: - case media::PIXEL_FORMAT_RGB32: - case media::PIXEL_FORMAT_UYVY: - switch (target) { - case GL_TEXTURE_EXTERNAL_OES: - if (use_stream_video_draw_quad) - return VideoFrameResourceType::STREAM_TEXTURE; - FALLTHROUGH; - case GL_TEXTURE_2D: - return (format == media::PIXEL_FORMAT_XRGB) - ? VideoFrameResourceType::RGB - : VideoFrameResourceType::RGBA_PREMULTIPLIED; - case GL_TEXTURE_RECTANGLE_ARB: - return VideoFrameResourceType::RGB; - default: - NOTREACHED(); - break; - } - break; - case media::PIXEL_FORMAT_I420: - return VideoFrameResourceType::YUV; - case media::PIXEL_FORMAT_NV12: - DCHECK(target == GL_TEXTURE_EXTERNAL_OES || target == GL_TEXTURE_2D || - target == GL_TEXTURE_RECTANGLE_ARB) - << "Unsupported target " << gl::GLEnums::GetStringEnum(target); - // Single plane textures can be sampled as RGB. - if (num_textures > 1) - return VideoFrameResourceType::YUV; - - *buffer_format = gfx::BufferFormat::YUV_420_BIPLANAR; - return VideoFrameResourceType::RGB; - case media::PIXEL_FORMAT_YV12: - case media::PIXEL_FORMAT_I422: - case media::PIXEL_FORMAT_I444: - case media::PIXEL_FORMAT_I420A: - case media::PIXEL_FORMAT_NV21: - case media::PIXEL_FORMAT_YUY2: - case media::PIXEL_FORMAT_RGB24: - case media::PIXEL_FORMAT_MJPEG: - case media::PIXEL_FORMAT_MT21: - case media::PIXEL_FORMAT_YUV420P9: - case media::PIXEL_FORMAT_YUV422P9: - case media::PIXEL_FORMAT_YUV444P9: - case media::PIXEL_FORMAT_YUV420P10: - case media::PIXEL_FORMAT_YUV422P10: - case media::PIXEL_FORMAT_YUV444P10: - case media::PIXEL_FORMAT_YUV420P12: - case media::PIXEL_FORMAT_YUV422P12: - case media::PIXEL_FORMAT_YUV444P12: - case media::PIXEL_FORMAT_Y16: - case media::PIXEL_FORMAT_UNKNOWN: - break; - } - return VideoFrameResourceType::NONE; -} - -class SyncTokenClientImpl : public media::VideoFrame::SyncTokenClient { - public: - SyncTokenClientImpl(gpu::gles2::GLES2Interface* gl, gpu::SyncToken sync_token) - : gl_(gl), sync_token_(sync_token) {} - ~SyncTokenClientImpl() override = default; - - void GenerateSyncToken(gpu::SyncToken* sync_token) override { - if (sync_token_.HasData()) { - *sync_token = sync_token_; - } else { - gl_->GenSyncTokenCHROMIUM(sync_token->GetData()); - } - } - - void WaitSyncToken(const gpu::SyncToken& sync_token) override { - if (sync_token.HasData()) { - gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); - if (sync_token_.HasData() && sync_token_ != sync_token) { - gl_->WaitSyncTokenCHROMIUM(sync_token_.GetConstData()); - sync_token_.Clear(); - } - } - } - - private: - gpu::gles2::GLES2Interface* gl_; - gpu::SyncToken sync_token_; - DISALLOW_COPY_AND_ASSIGN(SyncTokenClientImpl); -}; - -// Sync tokens passed downstream to the compositor can be unverified. -void GenerateCompositorSyncToken(gpu::gles2::GLES2Interface* gl, - gpu::SyncToken* sync_token) { - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token->GetData()); -} - -// For frames that we receive in software format, determine the dimensions of -// each plane in the frame. -gfx::Size SoftwarePlaneDimension(media::VideoFrame* input_frame, - bool software_compositor, - size_t plane_index) { - gfx::Size coded_size = input_frame->coded_size(); - if (software_compositor) - return coded_size; - - int plane_width = media::VideoFrame::Columns( - plane_index, input_frame->format(), coded_size.width()); - int plane_height = media::VideoFrame::Rows(plane_index, input_frame->format(), - coded_size.height()); - return gfx::Size(plane_width, plane_height); -} - -} // namespace - -VideoFrameExternalResources::VideoFrameExternalResources() = default; -VideoFrameExternalResources::~VideoFrameExternalResources() = default; - -VideoFrameExternalResources::VideoFrameExternalResources( - VideoFrameExternalResources&& other) = default; -VideoFrameExternalResources& VideoFrameExternalResources::operator=( - VideoFrameExternalResources&& other) = default; - -// Resource for a video plane allocated and owned by VideoResourceUpdater. There -// can be multiple plane resources for each video frame, depending on the -// format. These will be reused when possible. -class VideoResourceUpdater::PlaneResource { - public: - PlaneResource(uint32_t plane_resource_id, - const gfx::Size& resource_size, - viz::ResourceFormat resource_format, - bool is_software) - : plane_resource_id_(plane_resource_id), - resource_size_(resource_size), - resource_format_(resource_format), - is_software_(is_software) {} - virtual ~PlaneResource() = default; - - // Casts |this| to SoftwarePlaneResource for software compositing. - SoftwarePlaneResource* AsSoftware(); - - // Casts |this| to HardwarePlaneResource for GPU compositing. - HardwarePlaneResource* AsHardware(); - - // Returns true if this resource matches the unique identifiers of another - // VideoFrame resource. - bool Matches(int unique_frame_id, size_t plane_index) { - return has_unique_frame_id_and_plane_index_ && - unique_frame_id_ == unique_frame_id && plane_index_ == plane_index; - } - - // Sets the unique identifiers for this resource, may only be called when - // there is a single reference to the resource (i.e. |ref_count_| == 1). - void SetUniqueId(int unique_frame_id, size_t plane_index) { - DCHECK_EQ(ref_count_, 1); - plane_index_ = plane_index; - unique_frame_id_ = unique_frame_id; - has_unique_frame_id_and_plane_index_ = true; - } - - // Accessors for resource identifiers provided at construction time. - uint32_t plane_resource_id() const { return plane_resource_id_; } - const gfx::Size& resource_size() const { return resource_size_; } - viz::ResourceFormat resource_format() const { return resource_format_; } - - // Various methods for managing references. See |ref_count_| for details. - void add_ref() { ++ref_count_; } - void remove_ref() { --ref_count_; } - void clear_refs() { ref_count_ = 0; } - bool has_refs() const { return ref_count_ != 0; } - - private: - const uint32_t plane_resource_id_; - const gfx::Size resource_size_; - const viz::ResourceFormat resource_format_; - const bool is_software_; - - // The number of times this resource has been imported vs number of times this - // resource has returned. - int ref_count_ = 0; - - // These two members are used for identifying the data stored in this - // resource; they uniquely identify a media::VideoFrame plane. - int unique_frame_id_ = 0; - size_t plane_index_ = 0u; - // Indicates if the above two members have been set or not. - bool has_unique_frame_id_and_plane_index_ = false; - - DISALLOW_COPY_AND_ASSIGN(PlaneResource); -}; - -class VideoResourceUpdater::SoftwarePlaneResource - : public VideoResourceUpdater::PlaneResource { - public: - SoftwarePlaneResource(uint32_t plane_resource_id, - const gfx::Size& size, - viz::SharedBitmapReporter* shared_bitmap_reporter) - : PlaneResource(plane_resource_id, - size, - viz::ResourceFormat::RGBA_8888, - /*is_software=*/true), - shared_bitmap_reporter_(shared_bitmap_reporter), - shared_bitmap_id_(viz::SharedBitmap::GenerateId()) { - DCHECK(shared_bitmap_reporter_); - - // Allocate SharedMemory and notify display compositor of the allocation. - shared_memory_ = viz::bitmap_allocation::AllocateMappedBitmap( - resource_size(), viz::ResourceFormat::RGBA_8888); - mojo::ScopedSharedBufferHandle handle = - viz::bitmap_allocation::DuplicateAndCloseMappedBitmap( - shared_memory_.get(), resource_size(), - viz::ResourceFormat::RGBA_8888); - shared_bitmap_reporter_->DidAllocateSharedBitmap(std::move(handle), - shared_bitmap_id_); - } - ~SoftwarePlaneResource() override { - shared_bitmap_reporter_->DidDeleteSharedBitmap(shared_bitmap_id_); - } - - const viz::SharedBitmapId& shared_bitmap_id() const { - return shared_bitmap_id_; - } - void* pixels() { return shared_memory_->memory(); } - - // Returns a memory dump GUID consistent across processes. - base::UnguessableToken GetSharedMemoryGuid() const { - return shared_memory_->mapped_id(); - } - - private: - viz::SharedBitmapReporter* const shared_bitmap_reporter_; - const viz::SharedBitmapId shared_bitmap_id_; - std::unique_ptr<base::SharedMemory> shared_memory_; - - DISALLOW_COPY_AND_ASSIGN(SoftwarePlaneResource); -}; - -class VideoResourceUpdater::HardwarePlaneResource - : public VideoResourceUpdater::PlaneResource { - public: - HardwarePlaneResource(uint32_t plane_resource_id, - const gfx::Size& size, - viz::ResourceFormat format, - viz::ContextProvider* context_provider, - viz::TextureAllocation allocation) - : PlaneResource(plane_resource_id, size, format, /*is_software=*/false), - context_provider_(context_provider), - mailbox_(gpu::Mailbox::Generate()), - allocation_(std::move(allocation)) { - DCHECK(context_provider_); - context_provider_->ContextGL()->ProduceTextureDirectCHROMIUM( - allocation_.texture_id, mailbox_.name); - } - ~HardwarePlaneResource() override { - context_provider_->ContextGL()->DeleteTextures(1, &allocation_.texture_id); - } - - const gpu::Mailbox& mailbox() const { return mailbox_; } - GLuint texture_id() const { return allocation_.texture_id; } - GLenum texture_target() const { return allocation_.texture_target; } - bool overlay_candidate() const { return allocation_.overlay_candidate; } - - private: - viz::ContextProvider* const context_provider_; - const gpu::Mailbox mailbox_; - const viz::TextureAllocation allocation_; - - DISALLOW_COPY_AND_ASSIGN(HardwarePlaneResource); -}; - -VideoResourceUpdater::SoftwarePlaneResource* -VideoResourceUpdater::PlaneResource::AsSoftware() { - DCHECK(is_software_); - return static_cast<SoftwarePlaneResource*>(this); -} - -VideoResourceUpdater::HardwarePlaneResource* -VideoResourceUpdater::PlaneResource::AsHardware() { - DCHECK(!is_software_); - return static_cast<HardwarePlaneResource*>(this); -} - -VideoResourceUpdater::VideoResourceUpdater( - viz::ContextProvider* context_provider, - viz::SharedBitmapReporter* shared_bitmap_reporter, - LayerTreeResourceProvider* resource_provider, - bool use_stream_video_draw_quad, - bool use_gpu_memory_buffer_resources, - bool use_r16_texture, - int max_resource_size) - : context_provider_(context_provider), - shared_bitmap_reporter_(shared_bitmap_reporter), - resource_provider_(resource_provider), - use_stream_video_draw_quad_(use_stream_video_draw_quad), - use_gpu_memory_buffer_resources_(use_gpu_memory_buffer_resources), - use_r16_texture_(use_r16_texture), - max_resource_size_(max_resource_size), - tracing_id_(g_next_video_resource_updater_id.GetNext()), - weak_ptr_factory_(this) { - DCHECK(context_provider_ || shared_bitmap_reporter_); - - base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( - this, "cc::VideoResourceUpdater", base::ThreadTaskRunnerHandle::Get()); -} - -VideoResourceUpdater::~VideoResourceUpdater() { - base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( - this); -} - -void VideoResourceUpdater::ObtainFrameResources( - scoped_refptr<media::VideoFrame> video_frame) { - VideoFrameExternalResources external_resources = - CreateExternalResourcesFromVideoFrame(video_frame); - frame_resource_type_ = external_resources.type; - - if (external_resources.type == VideoFrameResourceType::YUV) { - frame_resource_offset_ = external_resources.offset; - frame_resource_multiplier_ = external_resources.multiplier; - frame_bits_per_channel_ = external_resources.bits_per_channel; - } - - DCHECK_EQ(external_resources.resources.size(), - external_resources.release_callbacks.size()); - for (size_t i = 0; i < external_resources.resources.size(); ++i) { - viz::ResourceId resource_id = resource_provider_->ImportResource( - external_resources.resources[i], - viz::SingleReleaseCallback::Create( - std::move(external_resources.release_callbacks[i]))); - frame_resources_.push_back( - {resource_id, external_resources.resources[i].size}); - } - TRACE_EVENT_INSTANT1("media", "VideoResourceUpdater::ObtainFrameResources", - TRACE_EVENT_SCOPE_THREAD, "Timestamp", - video_frame->timestamp().InMicroseconds()); -} - -void VideoResourceUpdater::ReleaseFrameResources() { - for (auto& frame_resource : frame_resources_) - resource_provider_->RemoveImportedResource(frame_resource.id); - frame_resources_.clear(); -} - -void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass, - scoped_refptr<media::VideoFrame> frame, - gfx::Transform transform, - gfx::Size rotated_size, - gfx::Rect visible_layer_rect, - gfx::Rect clip_rect, - bool is_clipped, - bool contents_opaque, - float draw_opacity, - int sorting_context_id, - gfx::Rect visible_quad_rect) { - DCHECK(frame.get()); - - viz::SharedQuadState* shared_quad_state = - render_pass->CreateAndAppendSharedQuadState(); - gfx::Rect rotated_size_rect(rotated_size); - shared_quad_state->SetAll( - transform, rotated_size_rect, visible_layer_rect, clip_rect, is_clipped, - contents_opaque, draw_opacity, SkBlendMode::kSrcOver, sorting_context_id); - - gfx::Rect quad_rect(rotated_size); - gfx::Rect visible_rect = frame->visible_rect(); - bool needs_blending = !contents_opaque; - gfx::Size coded_size = frame->coded_size(); - - const float tex_width_scale = - static_cast<float>(visible_rect.width()) / coded_size.width(); - const float tex_height_scale = - static_cast<float>(visible_rect.height()) / coded_size.height(); - - switch (frame_resource_type_) { - case VideoFrameResourceType::YUV: { - const gfx::Size ya_tex_size = coded_size; - - int u_width = media::VideoFrame::Columns( - media::VideoFrame::kUPlane, frame->format(), coded_size.width()); - int u_height = media::VideoFrame::Rows( - media::VideoFrame::kUPlane, frame->format(), coded_size.height()); - gfx::Size uv_tex_size(u_width, u_height); - - if (frame->HasTextures()) { - if (frame->format() == media::PIXEL_FORMAT_NV12) { - DCHECK_EQ(2u, frame_resources_.size()); - } else { - DCHECK_EQ(media::PIXEL_FORMAT_I420, frame->format()); - DCHECK_EQ(3u, - frame_resources_.size()); // Alpha is not supported yet. - } - } else { - DCHECK_GE(frame_resources_.size(), 3u); - DCHECK(frame_resources_.size() <= 3 || - ya_tex_size == media::VideoFrame::PlaneSize( - frame->format(), media::VideoFrame::kAPlane, - coded_size)); - } - - // Compute the UV sub-sampling factor based on the ratio between - // |ya_tex_size| and |uv_tex_size|. - float uv_subsampling_factor_x = - static_cast<float>(ya_tex_size.width()) / uv_tex_size.width(); - float uv_subsampling_factor_y = - static_cast<float>(ya_tex_size.height()) / uv_tex_size.height(); - gfx::RectF ya_tex_coord_rect(visible_rect); - gfx::RectF uv_tex_coord_rect( - visible_rect.x() / uv_subsampling_factor_x, - visible_rect.y() / uv_subsampling_factor_y, - visible_rect.width() / uv_subsampling_factor_x, - visible_rect.height() / uv_subsampling_factor_y); - - auto* yuv_video_quad = - render_pass->CreateAndAppendDrawQuad<viz::YUVVideoDrawQuad>(); - yuv_video_quad->SetNew( - shared_quad_state, quad_rect, visible_quad_rect, needs_blending, - ya_tex_coord_rect, uv_tex_coord_rect, ya_tex_size, uv_tex_size, - frame_resources_[0].id, frame_resources_[1].id, - frame_resources_.size() > 2 ? frame_resources_[2].id - : frame_resources_[1].id, - frame_resources_.size() > 3 ? frame_resources_[3].id : 0, - frame->ColorSpace(), frame_resource_offset_, - frame_resource_multiplier_, frame_bits_per_channel_); - yuv_video_quad->require_overlay = - frame->metadata()->IsTrue(media::VideoFrameMetadata::REQUIRE_OVERLAY); - yuv_video_quad->is_protected_video = - frame->metadata()->IsTrue(media::VideoFrameMetadata::PROTECTED_VIDEO); - - for (viz::ResourceId resource_id : yuv_video_quad->resources) { - resource_provider_->ValidateResource(resource_id); - } - break; - } - case VideoFrameResourceType::RGBA: - case VideoFrameResourceType::RGBA_PREMULTIPLIED: - case VideoFrameResourceType::RGB: { - DCHECK_EQ(frame_resources_.size(), 1u); - if (frame_resources_.size() < 1u) - break; - bool premultiplied_alpha = - frame_resource_type_ == VideoFrameResourceType::RGBA_PREMULTIPLIED; - gfx::PointF uv_top_left(0.f, 0.f); - gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale); - float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; - bool flipped = false; - bool nearest_neighbor = false; - auto* texture_quad = - render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); - texture_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, - needs_blending, frame_resources_[0].id, - premultiplied_alpha, uv_top_left, uv_bottom_right, - SK_ColorTRANSPARENT, opacity, flipped, - nearest_neighbor, false); - texture_quad->set_resource_size_in_pixels(coded_size); - for (viz::ResourceId resource_id : texture_quad->resources) { - resource_provider_->ValidateResource(resource_id); - } - break; - } - case VideoFrameResourceType::STREAM_TEXTURE: { - DCHECK_EQ(frame_resources_.size(), 1u); - if (frame_resources_.size() < 1u) - break; - gfx::Transform scale; - scale.Scale(tex_width_scale, tex_height_scale); - auto* stream_video_quad = - render_pass->CreateAndAppendDrawQuad<viz::StreamVideoDrawQuad>(); - stream_video_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, - needs_blending, frame_resources_[0].id, - frame_resources_[0].size_in_pixels, scale); - for (viz::ResourceId resource_id : stream_video_quad->resources) { - resource_provider_->ValidateResource(resource_id); - } - break; - } - case VideoFrameResourceType::NONE: - NOTIMPLEMENTED(); - break; - } -} - -VideoFrameExternalResources -VideoResourceUpdater::CreateExternalResourcesFromVideoFrame( - scoped_refptr<media::VideoFrame> video_frame) { - if (video_frame->format() == media::PIXEL_FORMAT_UNKNOWN) - return VideoFrameExternalResources(); - DCHECK(video_frame->HasTextures() || video_frame->IsMappable()); - if (video_frame->HasTextures()) - return CreateForHardwarePlanes(std::move(video_frame)); - else - return CreateForSoftwarePlanes(std::move(video_frame)); -} - -viz::ResourceFormat VideoResourceUpdater::YuvResourceFormat( - int bits_per_channel) { - DCHECK(context_provider_); - const auto& caps = context_provider_->ContextCapabilities(); - if (caps.disable_one_component_textures) - return viz::RGBA_8888; - if (bits_per_channel <= 8) - return caps.texture_rg ? viz::RED_8 : viz::LUMINANCE_8; - if (use_r16_texture_ && caps.texture_norm16) - return viz::R16_EXT; - if (caps.texture_half_float_linear) - return viz::LUMINANCE_F16; - return viz::LUMINANCE_8; -} - -VideoResourceUpdater::PlaneResource* -VideoResourceUpdater::RecycleOrAllocateResource( - const gfx::Size& resource_size, - viz::ResourceFormat resource_format, - const gfx::ColorSpace& color_space, - int unique_id, - int plane_index) { - PlaneResource* recyclable_resource = nullptr; - for (auto& resource : all_resources_) { - // If the plane index is valid (positive, or 0, meaning all planes) - // then we are allowed to return a referenced resource that already - // contains the right frame data. It's safe to reuse it even if - // resource_provider_ holds some references to it, because those - // references are read-only. - if (plane_index != -1 && resource->Matches(unique_id, plane_index)) { - DCHECK(resource->resource_size() == resource_size); - DCHECK(resource->resource_format() == resource_format); - return resource.get(); - } - - // Otherwise check whether this is an unreferenced resource of the right - // format that we can recycle. Remember it, but don't return immediately, - // because we still want to find any reusable resources. - const bool in_use = resource->has_refs(); - - if (!in_use && resource->resource_size() == resource_size && - resource->resource_format() == resource_format) { - recyclable_resource = resource.get(); - } - } - - if (recyclable_resource) - return recyclable_resource; - - // There was nothing available to reuse or recycle. Allocate a new resource. - return AllocateResource(resource_size, resource_format, color_space); -} - -VideoResourceUpdater::PlaneResource* VideoResourceUpdater::AllocateResource( - const gfx::Size& plane_size, - viz::ResourceFormat format, - const gfx::ColorSpace& color_space) { - const uint32_t plane_resource_id = next_plane_resource_id_++; - - if (software_compositor()) { - DCHECK_EQ(format, viz::ResourceFormat::RGBA_8888); - - all_resources_.push_back(std::make_unique<SoftwarePlaneResource>( - plane_resource_id, plane_size, shared_bitmap_reporter_)); - } else { - // Video textures get composited into the display frame, the GPU doesn't - // draw to them directly. - constexpr bool kForFrameBufferAttachment = false; - - viz::TextureAllocation alloc = viz::TextureAllocation::MakeTextureId( - context_provider_->ContextGL(), - context_provider_->ContextCapabilities(), format, - use_gpu_memory_buffer_resources_, kForFrameBufferAttachment); - viz::TextureAllocation::AllocateStorage( - context_provider_->ContextGL(), - context_provider_->ContextCapabilities(), format, plane_size, alloc, - color_space); - - all_resources_.push_back(std::make_unique<HardwarePlaneResource>( - plane_resource_id, plane_size, format, context_provider_, - std::move(alloc))); - } - return all_resources_.back().get(); -} - -void VideoResourceUpdater::CopyHardwarePlane( - media::VideoFrame* video_frame, - const gfx::ColorSpace& resource_color_space, - const gpu::MailboxHolder& mailbox_holder, - VideoFrameExternalResources* external_resources) { - const gfx::Size output_plane_resource_size = video_frame->coded_size(); - // The copy needs to be a direct transfer of pixel data, so we use an RGBA8 - // target to avoid loss of precision or dropping any alpha component. - constexpr viz::ResourceFormat copy_resource_format = - viz::ResourceFormat::RGBA_8888; - - const int no_unique_id = 0; - const int no_plane_index = -1; // Do not recycle referenced textures. - PlaneResource* plane_resource = RecycleOrAllocateResource( - output_plane_resource_size, copy_resource_format, resource_color_space, - no_unique_id, no_plane_index); - HardwarePlaneResource* hardware_resource = plane_resource->AsHardware(); - hardware_resource->add_ref(); - - DCHECK_EQ(hardware_resource->texture_target(), - static_cast<GLenum>(GL_TEXTURE_2D)); - - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - - gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); - uint32_t src_texture_id = - gl->CreateAndConsumeTextureCHROMIUM(mailbox_holder.mailbox.name); - gl->CopySubTextureCHROMIUM( - src_texture_id, 0, GL_TEXTURE_2D, hardware_resource->texture_id(), 0, 0, - 0, 0, 0, output_plane_resource_size.width(), - output_plane_resource_size.height(), false, false, false); - gl->DeleteTextures(1, &src_texture_id); - - // Pass an empty sync token to force generation of a new sync token. - SyncTokenClientImpl client(gl, gpu::SyncToken()); - gpu::SyncToken sync_token = video_frame->UpdateReleaseSyncToken(&client); - - auto transferable_resource = viz::TransferableResource::MakeGL( - hardware_resource->mailbox(), GL_LINEAR, GL_TEXTURE_2D, sync_token); - transferable_resource.color_space = resource_color_space; - transferable_resource.format = copy_resource_format; - transferable_resource.buffer_format = viz::BufferFormat(copy_resource_format); - external_resources->resources.push_back(std::move(transferable_resource)); - - external_resources->release_callbacks.push_back(base::BindOnce( - &VideoResourceUpdater::RecycleResource, weak_ptr_factory_.GetWeakPtr(), - hardware_resource->plane_resource_id())); -} - -VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( - scoped_refptr<media::VideoFrame> video_frame) { - TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes"); - DCHECK(video_frame->HasTextures()); - if (!context_provider_) - return VideoFrameExternalResources(); - - VideoFrameExternalResources external_resources; - gfx::ColorSpace resource_color_space = video_frame->ColorSpace(); - - bool copy_required = - video_frame->metadata()->IsTrue(media::VideoFrameMetadata::COPY_REQUIRED); - - GLuint target = video_frame->mailbox_holder(0).texture_target; - // If |copy_required| then we will copy into a GL_TEXTURE_2D target. - if (copy_required) - target = GL_TEXTURE_2D; - - gfx::BufferFormat buffer_format; - external_resources.type = ExternalResourceTypeForHardwarePlanes( - video_frame->format(), target, video_frame->NumTextures(), &buffer_format, - use_stream_video_draw_quad_); - if (external_resources.type == VideoFrameResourceType::NONE) { - DLOG(ERROR) << "Unsupported Texture format" - << media::VideoPixelFormatToString(video_frame->format()); - return external_resources; - } - if (external_resources.type == VideoFrameResourceType::RGB || - external_resources.type == VideoFrameResourceType::RGBA || - external_resources.type == VideoFrameResourceType::RGBA_PREMULTIPLIED) { - resource_color_space = resource_color_space.GetAsFullRangeRGB(); - } - - const size_t num_textures = video_frame->NumTextures(); - for (size_t i = 0; i < num_textures; ++i) { - const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(i); - if (mailbox_holder.mailbox.IsZero()) - break; - - if (copy_required) { - CopyHardwarePlane(video_frame.get(), resource_color_space, mailbox_holder, - &external_resources); - } else { - auto transfer_resource = viz::TransferableResource::MakeGLOverlay( - mailbox_holder.mailbox, GL_LINEAR, mailbox_holder.texture_target, - mailbox_holder.sync_token, video_frame->coded_size(), - video_frame->metadata()->IsTrue( - media::VideoFrameMetadata::ALLOW_OVERLAY)); - transfer_resource.color_space = resource_color_space; - transfer_resource.read_lock_fences_enabled = - video_frame->metadata()->IsTrue( - media::VideoFrameMetadata::READ_LOCK_FENCES_ENABLED); - transfer_resource.buffer_format = buffer_format; -#if defined(OS_ANDROID) - transfer_resource.is_backed_by_surface_texture = - video_frame->metadata()->IsTrue( - media::VideoFrameMetadata::TEXTURE_OWNER); - transfer_resource.wants_promotion_hint = video_frame->metadata()->IsTrue( - media::VideoFrameMetadata::WANTS_PROMOTION_HINT); -#endif - external_resources.resources.push_back(std::move(transfer_resource)); - external_resources.release_callbacks.push_back( - base::BindOnce(&VideoResourceUpdater::ReturnTexture, - weak_ptr_factory_.GetWeakPtr(), video_frame)); - } - } - return external_resources; -} - -VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( - scoped_refptr<media::VideoFrame> video_frame) { - TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes"); - const media::VideoPixelFormat input_frame_format = video_frame->format(); - - size_t bits_per_channel = video_frame->BitDepth(); - - // Only YUV and Y16 software video frames are supported. - DCHECK(media::IsYuvPlanar(input_frame_format) || - input_frame_format == media::PIXEL_FORMAT_Y16); - - viz::ResourceFormat output_resource_format; - gfx::ColorSpace output_color_space = video_frame->ColorSpace(); - if (input_frame_format == media::PIXEL_FORMAT_Y16) { - // Unable to display directly as yuv planes so convert it to RGBA for - // compositing. - output_resource_format = viz::RGBA_8888; - output_color_space = output_color_space.GetAsFullRangeRGB(); - } else if (!software_compositor()) { - // Can be composited directly from yuv planes. - output_resource_format = YuvResourceFormat(bits_per_channel); - } - - // If GPU compositing is enabled, but the output resource format - // returned by the resource provider is viz::RGBA_8888, then a GPU driver - // bug workaround requires that YUV frames must be converted to RGB - // before texture upload. - bool texture_needs_rgb_conversion = - !software_compositor() && - output_resource_format == viz::ResourceFormat::RGBA_8888; - - size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format); - - // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB - // conversion here. That involves an extra copy of each frame to a bitmap. - // Obviously, this is suboptimal and should be addressed once ubercompositor - // starts shaping up. - if (software_compositor() || texture_needs_rgb_conversion) { - output_resource_format = viz::RGBA_8888; - output_plane_count = 1; - bits_per_channel = 8; - - // The YUV to RGB conversion will be performed when we convert - // from single-channel textures to an RGBA texture via - // ConvertVideoFrameToRGBPixels below. - output_color_space = output_color_space.GetAsFullRangeRGB(); - } - - std::vector<gfx::Size> outplane_plane_sizes; - outplane_plane_sizes.reserve(output_plane_count); - for (size_t i = 0; i < output_plane_count; ++i) { - outplane_plane_sizes.push_back( - SoftwarePlaneDimension(video_frame.get(), software_compositor(), i)); - const gfx::Size& output_plane_resource_size = outplane_plane_sizes.back(); - if (output_plane_resource_size.IsEmpty() || - output_plane_resource_size.width() > max_resource_size_ || - output_plane_resource_size.height() > max_resource_size_) { - // This output plane has invalid geometry so return an empty external - // resources. - return VideoFrameExternalResources(); - } - } - - // Delete recycled resources that are the wrong format or wrong size. - auto can_delete_resource_fn = - [output_resource_format, - &outplane_plane_sizes](const std::unique_ptr<PlaneResource>& resource) { - // Resources that are still being used can't be deleted. - if (resource->has_refs()) - return false; - - return resource->resource_format() != output_resource_format || - !base::ContainsValue(outplane_plane_sizes, - resource->resource_size()); - }; - base::EraseIf(all_resources_, can_delete_resource_fn); - - // Recycle or allocate resources for each video plane. - std::vector<PlaneResource*> plane_resources; - plane_resources.reserve(output_plane_count); - for (size_t i = 0; i < output_plane_count; ++i) { - plane_resources.push_back(RecycleOrAllocateResource( - outplane_plane_sizes[i], output_resource_format, output_color_space, - video_frame->unique_id(), i)); - plane_resources.back()->add_ref(); - } - - VideoFrameExternalResources external_resources; - - external_resources.bits_per_channel = bits_per_channel; - - if (software_compositor() || texture_needs_rgb_conversion) { - DCHECK_EQ(plane_resources.size(), 1u); - PlaneResource* plane_resource = plane_resources[0]; - DCHECK_EQ(plane_resource->resource_format(), viz::RGBA_8888); - - if (!plane_resource->Matches(video_frame->unique_id(), 0)) { - // We need to transfer data from |video_frame| to the plane resource. - if (software_compositor()) { - if (!video_renderer_) - video_renderer_ = std::make_unique<media::PaintCanvasVideoRenderer>(); - - SoftwarePlaneResource* software_resource = plane_resource->AsSoftware(); - - // We know the format is RGBA_8888 from check above. - SkImageInfo info = SkImageInfo::MakeN32Premul( - gfx::SizeToSkISize(software_resource->resource_size())); - - SkBitmap sk_bitmap; - sk_bitmap.installPixels(info, software_resource->pixels(), - info.minRowBytes()); - SkiaPaintCanvas canvas(sk_bitmap); - - // This is software path, so canvas and video_frame are always backed - // by software. - video_renderer_->Copy(video_frame, &canvas, media::Context3D()); - } else { - HardwarePlaneResource* hardware_resource = plane_resource->AsHardware(); - size_t bytes_per_row = viz::ResourceSizes::CheckedWidthInBytes<size_t>( - video_frame->coded_size().width(), viz::ResourceFormat::RGBA_8888); - size_t needed_size = bytes_per_row * video_frame->coded_size().height(); - if (upload_pixels_.size() < needed_size) { - // Clear before resizing to avoid memcpy. - upload_pixels_.clear(); - upload_pixels_.resize(needed_size); - } - - media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels( - video_frame.get(), &upload_pixels_[0], bytes_per_row); - - // Copy pixels into texture. - auto* gl = context_provider_->ContextGL(); - gl->BindTexture(hardware_resource->texture_target(), - hardware_resource->texture_id()); - const gfx::Size& plane_size = hardware_resource->resource_size(); - gl->TexSubImage2D( - hardware_resource->texture_target(), 0, 0, 0, plane_size.width(), - plane_size.height(), GLDataFormat(viz::ResourceFormat::RGBA_8888), - GLDataType(viz::ResourceFormat::RGBA_8888), &upload_pixels_[0]); - } - plane_resource->SetUniqueId(video_frame->unique_id(), 0); - } - - viz::TransferableResource transferable_resource; - if (software_compositor()) { - SoftwarePlaneResource* software_resource = plane_resource->AsSoftware(); - external_resources.type = VideoFrameResourceType::RGBA_PREMULTIPLIED; - transferable_resource = viz::TransferableResource::MakeSoftware( - software_resource->shared_bitmap_id(), - software_resource->resource_size(), - plane_resource->resource_format()); - } else { - HardwarePlaneResource* hardware_resource = plane_resource->AsHardware(); - external_resources.type = VideoFrameResourceType::RGBA; - gpu::SyncToken sync_token; - GenerateCompositorSyncToken(context_provider_->ContextGL(), &sync_token); - transferable_resource = viz::TransferableResource::MakeGLOverlay( - hardware_resource->mailbox(), GL_LINEAR, - hardware_resource->texture_target(), sync_token, - hardware_resource->resource_size(), - hardware_resource->overlay_candidate()); - } - - transferable_resource.color_space = output_color_space; - transferable_resource.format = viz::ResourceFormat::RGBA_8888; - transferable_resource.buffer_format = - viz::BufferFormat(viz::ResourceFormat::RGBA_8888); - external_resources.resources.push_back(std::move(transferable_resource)); - external_resources.release_callbacks.push_back(base::BindOnce( - &VideoResourceUpdater::RecycleResource, weak_ptr_factory_.GetWeakPtr(), - plane_resource->plane_resource_id())); - - return external_resources; - } - - const viz::ResourceFormat yuv_resource_format = - YuvResourceFormat(bits_per_channel); - DCHECK(yuv_resource_format == viz::LUMINANCE_F16 || - yuv_resource_format == viz::R16_EXT || - yuv_resource_format == viz::LUMINANCE_8 || - yuv_resource_format == viz::RED_8) - << yuv_resource_format; - - std::unique_ptr<media::HalfFloatMaker> half_float_maker; - if (yuv_resource_format == viz::LUMINANCE_F16) { - half_float_maker = - media::HalfFloatMaker::NewHalfFloatMaker(bits_per_channel); - external_resources.offset = half_float_maker->Offset(); - external_resources.multiplier = half_float_maker->Multiplier(); - } else if (yuv_resource_format == viz::R16_EXT) { - external_resources.multiplier = 65535.0f / ((1 << bits_per_channel) - 1); - external_resources.offset = 0; - } - - // We need to transfer data from |video_frame| to the plane resources. - for (size_t i = 0; i < plane_resources.size(); ++i) { - HardwarePlaneResource* plane_resource = plane_resources[i]->AsHardware(); - - // Skip the transfer if this |video_frame|'s plane has been processed. - if (plane_resource->Matches(video_frame->unique_id(), i)) - continue; - - const viz::ResourceFormat plane_resource_format = - plane_resource->resource_format(); - DCHECK_EQ(plane_resource_format, yuv_resource_format); - - // TODO(hubbe): Move upload code to media/. - // TODO(reveman): Can use GpuMemoryBuffers here to improve performance. - - // |video_stride_bytes| is the width of the |video_frame| we are uploading - // (including non-frame data to fill in the stride). - const int video_stride_bytes = video_frame->stride(i); - - // |resource_size_pixels| is the size of the destination resource. - const gfx::Size resource_size_pixels = plane_resource->resource_size(); - - const size_t bytes_per_row = - viz::ResourceSizes::CheckedWidthInBytes<size_t>( - resource_size_pixels.width(), plane_resource_format); - // Use 4-byte row alignment (OpenGL default) for upload performance. - // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. - const size_t upload_image_stride = - MathUtil::CheckedRoundUp<size_t>(bytes_per_row, 4u); - - const size_t resource_bit_depth = - static_cast<size_t>(viz::BitsPerPixel(plane_resource_format)); - - // Data downshifting is needed if the resource bit depth is not enough. - const bool needs_bit_downshifting = bits_per_channel > resource_bit_depth; - - // A copy to adjust strides is needed if those are different and both source - // and destination have the same bit depth. - const bool needs_stride_adaptation = - (bits_per_channel == resource_bit_depth) && - (upload_image_stride != static_cast<size_t>(video_stride_bytes)); - - // We need to convert the incoming data if we're transferring to half float, - // if the need a bit downshift or if the strides need to be reconciled. - const bool needs_conversion = plane_resource_format == viz::LUMINANCE_F16 || - needs_bit_downshifting || - needs_stride_adaptation; - - const uint8_t* pixels; - if (!needs_conversion) { - pixels = video_frame->data(i); - } else { - // Avoid malloc for each frame/plane if possible. - const size_t needed_size = - upload_image_stride * resource_size_pixels.height(); - if (upload_pixels_.size() < needed_size) { - // Clear before resizing to avoid memcpy. - upload_pixels_.clear(); - upload_pixels_.resize(needed_size); - } - - if (plane_resource_format == viz::LUMINANCE_F16) { - for (int row = 0; row < resource_size_pixels.height(); ++row) { - uint16_t* dst = reinterpret_cast<uint16_t*>( - &upload_pixels_[upload_image_stride * row]); - const uint16_t* src = reinterpret_cast<uint16_t*>( - video_frame->data(i) + (video_stride_bytes * row)); - half_float_maker->MakeHalfFloats(src, bytes_per_row / 2, dst); - } - } else if (needs_bit_downshifting) { - DCHECK(plane_resource_format == viz::LUMINANCE_8 || - plane_resource_format == viz::RED_8); - const int scale = 0x10000 >> (bits_per_channel - 8); - libyuv::Convert16To8Plane( - reinterpret_cast<uint16_t*>(video_frame->data(i)), - video_stride_bytes / 2, upload_pixels_.data(), upload_image_stride, - scale, bytes_per_row, resource_size_pixels.height()); - } else { - // Make a copy to reconcile stride, size and format being equal. - DCHECK(needs_stride_adaptation); - DCHECK(plane_resource_format == viz::LUMINANCE_8 || - plane_resource_format == viz::RED_8); - libyuv::CopyPlane(video_frame->data(i), video_stride_bytes, - upload_pixels_.data(), upload_image_stride, - resource_size_pixels.width(), - resource_size_pixels.height()); - } - - pixels = &upload_pixels_[0]; - } - - // Copy pixels into texture. TexSubImage2D() is applicable because - // |yuv_resource_format| is LUMINANCE_F16, R16_EXT, LUMINANCE_8 or RED_8. - auto* gl = context_provider_->ContextGL(); - gl->BindTexture(plane_resource->texture_target(), - plane_resource->texture_id()); - gl->TexSubImage2D( - plane_resource->texture_target(), 0, 0, 0, resource_size_pixels.width(), - resource_size_pixels.height(), GLDataFormat(plane_resource_format), - GLDataType(plane_resource_format), pixels); - - plane_resource->SetUniqueId(video_frame->unique_id(), i); - } - - // Set the sync token otherwise resource is assumed to be synchronized. - gpu::SyncToken sync_token; - GenerateCompositorSyncToken(context_provider_->ContextGL(), &sync_token); - - for (size_t i = 0; i < plane_resources.size(); ++i) { - HardwarePlaneResource* plane_resource = plane_resources[i]->AsHardware(); - auto transferable_resource = viz::TransferableResource::MakeGLOverlay( - plane_resource->mailbox(), GL_LINEAR, plane_resource->texture_target(), - sync_token, plane_resource->resource_size(), - plane_resource->overlay_candidate()); - transferable_resource.color_space = output_color_space; - transferable_resource.format = output_resource_format; - transferable_resource.buffer_format = - viz::BufferFormat(output_resource_format); - external_resources.resources.push_back(std::move(transferable_resource)); - external_resources.release_callbacks.push_back(base::BindOnce( - &VideoResourceUpdater::RecycleResource, weak_ptr_factory_.GetWeakPtr(), - plane_resource->plane_resource_id())); - } - - external_resources.type = VideoFrameResourceType::YUV; - return external_resources; -} - -void VideoResourceUpdater::ReturnTexture( - const scoped_refptr<media::VideoFrame>& video_frame, - const gpu::SyncToken& sync_token, - bool lost_resource) { - // TODO(dshwang): Forward to the decoder as a lost resource. - if (lost_resource) - return; - - // The video frame will insert a wait on the previous release sync token. - SyncTokenClientImpl client(context_provider_->ContextGL(), sync_token); - video_frame->UpdateReleaseSyncToken(&client); -} - -void VideoResourceUpdater::RecycleResource(uint32_t plane_resource_id, - const gpu::SyncToken& sync_token, - bool lost_resource) { - auto matches_id_fn = - [plane_resource_id](const std::unique_ptr<PlaneResource>& resource) { - return resource->plane_resource_id() == plane_resource_id; - }; - auto resource_it = - std::find_if(all_resources_.begin(), all_resources_.end(), matches_id_fn); - if (resource_it == all_resources_.end()) - return; - - if (context_provider_ && sync_token.HasData()) { - context_provider_->ContextGL()->WaitSyncTokenCHROMIUM( - sync_token.GetConstData()); - } - - if (lost_resource) { - all_resources_.erase(resource_it); - } else { - (*resource_it)->remove_ref(); - } -} - -bool VideoResourceUpdater::OnMemoryDump( - const base::trace_event::MemoryDumpArgs& args, - base::trace_event::ProcessMemoryDump* pmd) { - for (auto& resource : all_resources_) { - std::string dump_name = - base::StringPrintf("cc/video_memory/updater_%d/resource_%d", - tracing_id_, resource->plane_resource_id()); - base::trace_event::MemoryAllocatorDump* dump = - pmd->CreateAllocatorDump(dump_name); - - const uint64_t total_bytes = - viz::ResourceSizes::UncheckedSizeInBytesAligned<uint64_t>( - resource->resource_size(), resource->resource_format()); - dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, - base::trace_event::MemoryAllocatorDump::kUnitsBytes, - total_bytes); - - // The importance value assigned to the GUID here must be greater than the - // importance value assigned elsewhere so that resource ownership is - // attributed to VideoResourceUpdater. - constexpr int kImportance = 2; - - // Resources are shared across processes and require a shared GUID to - // prevent double counting the memory. - if (software_compositor()) { - base::UnguessableToken shm_guid = - resource->AsSoftware()->GetSharedMemoryGuid(); - pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shm_guid, kImportance); - } else { - base::trace_event::MemoryAllocatorDumpGuid guid = - gl::GetGLTextureClientGUIDForTracing( - context_provider_->ContextSupport()->ShareGroupTracingGUID(), - resource->AsHardware()->texture_id()); - pmd->CreateSharedGlobalAllocatorDump(guid); - pmd->AddOwnershipEdge(dump->guid(), guid, kImportance); - } - } - - return true; -} - -} // namespace cc diff --git a/chromium/cc/resources/video_resource_updater.h b/chromium/cc/resources/video_resource_updater.h deleted file mode 100644 index 0b3f651bed6..00000000000 --- a/chromium/cc/resources/video_resource_updater.h +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2013 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_RESOURCES_VIDEO_RESOURCE_UPDATER_H_ -#define CC_RESOURCES_VIDEO_RESOURCE_UPDATER_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <list> -#include <memory> -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "base/trace_event/memory_dump_provider.h" -#include "cc/cc_export.h" -#include "components/viz/common/resources/release_callback.h" -#include "components/viz/common/resources/resource_format.h" -#include "components/viz/common/resources/resource_id.h" -#include "components/viz/common/resources/transferable_resource.h" -#include "ui/gfx/buffer_types.h" -#include "ui/gfx/geometry/size.h" - -namespace media { -class PaintCanvasVideoRenderer; -class VideoFrame; -} - -namespace gfx { -class Rect; -class Transform; -} // namespace gfx - -namespace viz { -class ContextProvider; -class RenderPass; -class SharedBitmapReporter; -} - -namespace cc { -class LayerTreeResourceProvider; - -// Specifies what type of data is contained in the mailboxes, as well as how -// many mailboxes will be present. -enum class VideoFrameResourceType { - NONE, - YUV, - RGB, - RGBA_PREMULTIPLIED, - RGBA, - STREAM_TEXTURE, -}; - -class CC_EXPORT VideoFrameExternalResources { - public: - VideoFrameResourceType type = VideoFrameResourceType::NONE; - std::vector<viz::TransferableResource> resources; - std::vector<viz::ReleaseCallback> release_callbacks; - - // Used by hardware textures which do not return values in the 0-1 range. - // After a lookup, subtract offset and multiply by multiplier. - float offset = 0.f; - float multiplier = 1.f; - uint32_t bits_per_channel = 8; - - VideoFrameExternalResources(); - VideoFrameExternalResources(VideoFrameExternalResources&& other); - VideoFrameExternalResources& operator=(VideoFrameExternalResources&& other); - ~VideoFrameExternalResources(); -}; - -// VideoResourceUpdater is used by the video system to produce frame content as -// resources consumable by the compositor. -class CC_EXPORT VideoResourceUpdater - : public base::trace_event::MemoryDumpProvider { - public: - // For GPU compositing |context_provider| should be provided and for software - // compositing |shared_bitmap_reporter| should be provided. If there is a - // non-null |context_provider| we assume GPU compositing. - VideoResourceUpdater(viz::ContextProvider* context_provider, - viz::SharedBitmapReporter* shared_bitmap_reporter, - LayerTreeResourceProvider* resource_provider, - bool use_stream_video_draw_quad, - bool use_gpu_memory_buffer_resources, - bool use_r16_texture, - int max_resource_size); - - ~VideoResourceUpdater() override; - - // For each CompositorFrame the following sequence is expected: - // 1. ObtainFrameResources(): Import resources for the next video frame with - // LayerTreeResourceProvider. This will reuse existing GPU or SharedMemory - // buffers if possible, otherwise it will allocate new ones. - // 2. AppendQuads(): Add DrawQuads to CompositorFrame for video. - // 3. ReleaseFrameResources(): After the CompositorFrame has been submitted, - // remove imported resources from LayerTreeResourceProvider. - void ObtainFrameResources(scoped_refptr<media::VideoFrame> video_frame); - void ReleaseFrameResources(); - void AppendQuads(viz::RenderPass* render_pass, - scoped_refptr<media::VideoFrame> frame, - gfx::Transform transform, - gfx::Size rotated_size, - gfx::Rect visible_layer_rect, - gfx::Rect clip_rect, - bool is_clipped, - bool context_opaque, - float draw_opacity, - int sorting_context_id, - gfx::Rect visible_quad_rect); - - // TODO(kylechar): This is only public for testing, make private. - VideoFrameExternalResources CreateExternalResourcesFromVideoFrame( - scoped_refptr<media::VideoFrame> video_frame); - - viz::ResourceFormat YuvResourceFormat(int bits_per_channel); - - private: - class PlaneResource; - class HardwarePlaneResource; - class SoftwarePlaneResource; - - // A resource that will be embedded in a DrawQuad in the next CompositorFrame. - // Each video plane will correspond to one FrameResource. - struct FrameResource { - viz::ResourceId id; - gfx::Size size_in_pixels; - }; - - bool software_compositor() const { return context_provider_ == nullptr; } - - // Obtain a resource of the right format by either recycling an - // unreferenced but appropriately formatted resource, or by - // allocating a new resource. - // Additionally, if the |unique_id| and |plane_index| match, then - // it is assumed that the resource has the right data already and will only be - // used for reading, and so is returned even if it is still referenced. - // Passing -1 for |plane_index| avoids returning referenced - // resources. - PlaneResource* RecycleOrAllocateResource(const gfx::Size& resource_size, - viz::ResourceFormat resource_format, - const gfx::ColorSpace& color_space, - int unique_id, - int plane_index); - PlaneResource* AllocateResource(const gfx::Size& plane_size, - viz::ResourceFormat format, - const gfx::ColorSpace& color_space); - - // Create a copy of a texture-backed source video frame in a new GL_TEXTURE_2D - // texture. This is used when there are multiple GPU threads (Android WebView) - // and the source video frame texture can't be used on the output GL context. - // https://crbug.com/582170 - void CopyHardwarePlane(media::VideoFrame* video_frame, - const gfx::ColorSpace& resource_color_space, - const gpu::MailboxHolder& mailbox_holder, - VideoFrameExternalResources* external_resources); - - // Get resources ready to be appended into DrawQuads. This is used for GPU - // compositing most of the time, except for the cases mentioned in - // CreateForSoftwarePlanes(). - VideoFrameExternalResources CreateForHardwarePlanes( - scoped_refptr<media::VideoFrame> video_frame); - - // Get resources ready to be appended into DrawQuads. This is always used for - // software compositing. This is also used for GPU compositing when the input - // video frame has no textures. - VideoFrameExternalResources CreateForSoftwarePlanes( - scoped_refptr<media::VideoFrame> video_frame); - - void RecycleResource(uint32_t plane_resource_id, - const gpu::SyncToken& sync_token, - bool lost_resource); - void ReturnTexture(const scoped_refptr<media::VideoFrame>& video_frame, - const gpu::SyncToken& sync_token, - bool lost_resource); - - // base::trace_event::MemoryDumpProvider implementation. - bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, - base::trace_event::ProcessMemoryDump* pmd) override; - - viz::ContextProvider* const context_provider_; - viz::SharedBitmapReporter* const shared_bitmap_reporter_; - LayerTreeResourceProvider* const resource_provider_; - const bool use_stream_video_draw_quad_; - const bool use_gpu_memory_buffer_resources_; - // TODO(crbug.com/759456): Remove after r16 is used without the flag. - const bool use_r16_texture_; - const int max_resource_size_; - const int tracing_id_; - std::unique_ptr<media::PaintCanvasVideoRenderer> video_renderer_; - uint32_t next_plane_resource_id_ = 1; - - // Temporary pixel buffer when converting between formats. - std::vector<uint8_t> upload_pixels_; - - VideoFrameResourceType frame_resource_type_; - - float frame_resource_offset_; - float frame_resource_multiplier_; - uint32_t frame_bits_per_channel_; - - // Resources that will be placed into quads by the next call to - // AppendDrawQuads(). - std::vector<FrameResource> frame_resources_; - - // Resources allocated by VideoResourceUpdater. Used to recycle resources so - // we can reduce the number of allocations and data transfers. - std::vector<std::unique_ptr<PlaneResource>> all_resources_; - - base::WeakPtrFactory<VideoResourceUpdater> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(VideoResourceUpdater); -}; - -} // namespace cc - -#endif // CC_RESOURCES_VIDEO_RESOURCE_UPDATER_H_ diff --git a/chromium/cc/resources/video_resource_updater_unittest.cc b/chromium/cc/resources/video_resource_updater_unittest.cc deleted file mode 100644 index a26069cdd48..00000000000 --- a/chromium/cc/resources/video_resource_updater_unittest.cc +++ /dev/null @@ -1,737 +0,0 @@ -// Copyright 2013 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/resources/video_resource_updater.h" - -#include <stddef.h> -#include <stdint.h> - -#include "base/bind.h" -#include "cc/resources/layer_tree_resource_provider.h" -#include "cc/test/fake_layer_tree_frame_sink.h" -#include "cc/test/fake_output_surface_client.h" -#include "cc/test/fake_resource_provider.h" -#include "components/viz/test/fake_output_surface.h" -#include "components/viz/test/test_gles2_interface.h" -#include "components/viz/test/test_web_graphics_context_3d.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "media/base/video_frame.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cc { -namespace { - -class UploadCounterGLES2Interface : public viz::TestGLES2Interface { - public: - void TexSubImage2D(GLenum target, - GLint level, - GLint xoffset, - GLint yoffset, - GLsizei width, - GLsizei height, - GLenum format, - GLenum type, - const void* pixels) override { - ++upload_count_; - } - - void TexStorage2DEXT(GLenum target, - GLint levels, - GLuint internalformat, - GLint width, - GLint height) override {} - - void GenTextures(GLsizei n, GLuint* textures) override { - created_texture_count_ += n; - viz::TestGLES2Interface::GenTextures(n, textures); - } - - void DeleteTextures(GLsizei n, const GLuint* textures) override { - created_texture_count_ -= n; - viz::TestGLES2Interface::DeleteTextures(n, textures); - } - - int UploadCount() { return upload_count_; } - void ResetUploadCount() { upload_count_ = 0; } - - int TextureCreationCount() { return created_texture_count_; } - void ResetTextureCreationCount() { created_texture_count_ = 0; } - - private: - int upload_count_; - int created_texture_count_; -}; - -class VideoResourceUpdaterTest : public testing::Test { - protected: - VideoResourceUpdaterTest() { - std::unique_ptr<UploadCounterGLES2Interface> gl( - new UploadCounterGLES2Interface()); - - gl_ = gl.get(); - gl_->set_support_texture_storage(true); - - context_provider_ = viz::TestContextProvider::Create(std::move(gl)); - context_provider_->BindToCurrentThread(); - } - - // testing::Test implementation. - void SetUp() override { - testing::Test::SetUp(); - layer_tree_frame_sink_software_ = FakeLayerTreeFrameSink::CreateSoftware(); - resource_provider3d_ = - FakeResourceProvider::CreateLayerTreeResourceProvider( - context_provider_.get()); - resource_provider_software_ = - FakeResourceProvider::CreateLayerTreeResourceProvider(nullptr); - } - - std::unique_ptr<VideoResourceUpdater> CreateUpdaterForHardware( - bool use_stream_video_draw_quad = false) { - return std::make_unique<VideoResourceUpdater>( - context_provider_.get(), nullptr, resource_provider3d_.get(), - use_stream_video_draw_quad, /*use_gpu_memory_buffer_resources=*/false, - /*use_r16_texture=*/use_r16_texture_, /*max_resource_size=*/10000); - } - - std::unique_ptr<VideoResourceUpdater> CreateUpdaterForSoftware() { - return std::make_unique<VideoResourceUpdater>( - nullptr, layer_tree_frame_sink_software_.get(), - resource_provider_software_.get(), - /*use_stream_video_draw_quad=*/false, - /*use_gpu_memory_buffer_resources=*/false, - /*use_r16_texture=*/false, - /*max_resource_size=*/10000); - } - - // Note that the number of pixels needed for |size| must be less than or equal - // to the number of pixels needed for size of 100x100. - scoped_refptr<media::VideoFrame> CreateTestYUVVideoFrame( - const gfx::Size& size = gfx::Size(10, 10)) { - constexpr int kMaxDimension = 100; - static uint8_t y_data[kMaxDimension * kMaxDimension] = {0}; - static uint8_t u_data[kMaxDimension * kMaxDimension / 2] = {0}; - static uint8_t v_data[kMaxDimension * kMaxDimension / 2] = {0}; - - CHECK_LE(size.width() * size.height(), kMaxDimension * kMaxDimension); - - scoped_refptr<media::VideoFrame> video_frame = - media::VideoFrame::WrapExternalYuvData( - media::PIXEL_FORMAT_I422, // format - size, // coded_size - gfx::Rect(size), // visible_rect - size, // natural_size - size.width(), // y_stride - size.width() / 2, // u_stride - size.width() / 2, // v_stride - y_data, // y_data - u_data, // u_data - v_data, // v_data - base::TimeDelta()); // timestamp - EXPECT_TRUE(video_frame); - return video_frame; - } - - scoped_refptr<media::VideoFrame> CreateWonkyTestYUVVideoFrame() { - const int kDimension = 10; - const int kYWidth = kDimension + 5; - const int kUWidth = (kYWidth + 1) / 2 + 200; - const int kVWidth = (kYWidth + 1) / 2 + 1; - static uint8_t y_data[kYWidth * kDimension] = {0}; - static uint8_t u_data[kUWidth * kDimension] = {0}; - static uint8_t v_data[kVWidth * kDimension] = {0}; - - scoped_refptr<media::VideoFrame> video_frame = - media::VideoFrame::WrapExternalYuvData( - media::PIXEL_FORMAT_I422, // format - gfx::Size(kYWidth, kDimension), // coded_size - gfx::Rect(2, 0, kDimension, kDimension), // visible_rect - gfx::Size(kDimension, kDimension), // natural_size - -kYWidth, // y_stride (negative) - kUWidth, // u_stride - kVWidth, // v_stride - y_data + kYWidth * (kDimension - 1), // y_data - u_data, // u_data - v_data, // v_data - base::TimeDelta()); // timestamp - EXPECT_TRUE(video_frame); - return video_frame; - } - - scoped_refptr<media::VideoFrame> CreateTestHighBitFrame() { - const int kDimension = 10; - gfx::Size size(kDimension, kDimension); - - scoped_refptr<media::VideoFrame> video_frame(media::VideoFrame::CreateFrame( - media::PIXEL_FORMAT_YUV420P10, size, gfx::Rect(size), size, - base::TimeDelta())); - EXPECT_TRUE(video_frame); - return video_frame; - } - - void SetReleaseSyncToken(const gpu::SyncToken& sync_token) { - release_sync_token_ = sync_token; - } - - scoped_refptr<media::VideoFrame> CreateTestHardwareVideoFrame( - media::VideoPixelFormat format, - unsigned target) { - const int kDimension = 10; - gfx::Size size(kDimension, kDimension); - - gpu::Mailbox mailbox; - mailbox.name[0] = 51; - - gpu::MailboxHolder mailbox_holders[media::VideoFrame::kMaxPlanes] = { - gpu::MailboxHolder(mailbox, kMailboxSyncToken, target)}; - scoped_refptr<media::VideoFrame> video_frame = - media::VideoFrame::WrapNativeTextures( - format, mailbox_holders, - base::Bind(&VideoResourceUpdaterTest::SetReleaseSyncToken, - base::Unretained(this)), - size, // coded_size - gfx::Rect(size), // visible_rect - size, // natural_size - base::TimeDelta()); // timestamp - EXPECT_TRUE(video_frame); - return video_frame; - } - - scoped_refptr<media::VideoFrame> CreateTestRGBAHardwareVideoFrame() { - return CreateTestHardwareVideoFrame(media::PIXEL_FORMAT_ARGB, - GL_TEXTURE_2D); - } - - scoped_refptr<media::VideoFrame> CreateTestStreamTextureHardwareVideoFrame( - bool needs_copy) { - scoped_refptr<media::VideoFrame> video_frame = CreateTestHardwareVideoFrame( - media::PIXEL_FORMAT_ARGB, GL_TEXTURE_EXTERNAL_OES); - video_frame->metadata()->SetBoolean( - media::VideoFrameMetadata::COPY_REQUIRED, needs_copy); - return video_frame; - } - - scoped_refptr<media::VideoFrame> CreateTestYuvHardwareVideoFrame( - media::VideoPixelFormat format, - size_t num_textures, - unsigned target) { - const int kDimension = 10; - gfx::Size size(kDimension, kDimension); - - gpu::MailboxHolder mailbox_holders[media::VideoFrame::kMaxPlanes]; - for (size_t i = 0; i < num_textures; ++i) { - gpu::Mailbox mailbox; - mailbox.name[0] = 50 + 1; - mailbox_holders[i] = - gpu::MailboxHolder(mailbox, kMailboxSyncToken, target); - } - scoped_refptr<media::VideoFrame> video_frame = - media::VideoFrame::WrapNativeTextures( - format, mailbox_holders, - base::Bind(&VideoResourceUpdaterTest::SetReleaseSyncToken, - base::Unretained(this)), - size, // coded_size - gfx::Rect(size), // visible_rect - size, // natural_size - base::TimeDelta()); // timestamp - EXPECT_TRUE(video_frame); - return video_frame; - } - - static const gpu::SyncToken kMailboxSyncToken; - - UploadCounterGLES2Interface* gl_; - scoped_refptr<viz::TestContextProvider> context_provider_; - std::unique_ptr<FakeLayerTreeFrameSink> layer_tree_frame_sink_software_; - std::unique_ptr<LayerTreeResourceProvider> resource_provider3d_; - std::unique_ptr<LayerTreeResourceProvider> resource_provider_software_; - gpu::SyncToken release_sync_token_; - bool use_r16_texture_ = false; -}; - -const gpu::SyncToken VideoResourceUpdaterTest::kMailboxSyncToken = - gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x123), - 7); - -TEST_F(VideoResourceUpdaterTest, SoftwareFrame) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); -} - -TEST_F(VideoResourceUpdaterTest, HighBitFrameNoF16) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestHighBitFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); -} - -class VideoResourceUpdaterTestWithF16 : public VideoResourceUpdaterTest { - public: - VideoResourceUpdaterTestWithF16() : VideoResourceUpdaterTest() { - gl_->set_support_texture_half_float_linear(true); - } -}; - -TEST_F(VideoResourceUpdaterTestWithF16, HighBitFrame) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestHighBitFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - EXPECT_NEAR(resources.multiplier, 2.0, 0.1); - EXPECT_NEAR(resources.offset, 0.5, 0.1); - - // Create the resource again, to test the path where the - // resources are cached. - VideoFrameExternalResources resources2 = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources2.type); - EXPECT_NEAR(resources2.multiplier, 2.0, 0.1); - EXPECT_NEAR(resources2.offset, 0.5, 0.1); -} - -class VideoResourceUpdaterTestWithR16 : public VideoResourceUpdaterTest { - public: - VideoResourceUpdaterTestWithR16() : VideoResourceUpdaterTest() { - use_r16_texture_ = true; - gl_->set_support_texture_norm16(true); - } -}; - -TEST_F(VideoResourceUpdaterTestWithR16, HighBitFrame) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestHighBitFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - - // Max 10-bit values as read by a sampler. - double max_10bit_value = ((1 << 10) - 1) / 65535.0; - EXPECT_NEAR(resources.multiplier * max_10bit_value, 1.0, 0.0001); - EXPECT_NEAR(resources.offset, 0.0, 0.1); - - // Create the resource again, to test the path where the - // resources are cached. - VideoFrameExternalResources resources2 = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources2.type); - EXPECT_NEAR(resources2.multiplier * max_10bit_value, 1.0, 0.0001); - EXPECT_NEAR(resources2.offset, 0.0, 0.1); -} - -TEST_F(VideoResourceUpdaterTest, HighBitFrameSoftwareCompositor) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestHighBitFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); -} - -TEST_F(VideoResourceUpdaterTest, WonkySoftwareFrame) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - scoped_refptr<media::VideoFrame> video_frame = CreateWonkyTestYUVVideoFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); -} - -TEST_F(VideoResourceUpdaterTest, WonkySoftwareFrameSoftwareCompositor) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); - scoped_refptr<media::VideoFrame> video_frame = CreateWonkyTestYUVVideoFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); -} - -TEST_F(VideoResourceUpdaterTest, ReuseResource) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); - video_frame->set_timestamp(base::TimeDelta::FromSeconds(1234)); - - // Allocate the resources for a YUV video frame. - gl_->ResetUploadCount(); - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - EXPECT_EQ(3u, resources.resources.size()); - EXPECT_EQ(3u, resources.release_callbacks.size()); - // Expect exactly three texture uploads, one for each plane. - EXPECT_EQ(3, gl_->UploadCount()); - - // Simulate the ResourceProvider releasing the resources back to the video - // updater. - for (auto& release_callback : resources.release_callbacks) - std::move(release_callback).Run(gpu::SyncToken(), false); - - // Allocate resources for the same frame. - gl_->ResetUploadCount(); - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - EXPECT_EQ(3u, resources.resources.size()); - EXPECT_EQ(3u, resources.release_callbacks.size()); - // The data should be reused so expect no texture uploads. - EXPECT_EQ(0, gl_->UploadCount()); -} - -TEST_F(VideoResourceUpdaterTest, ReuseResourceNoDelete) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); - video_frame->set_timestamp(base::TimeDelta::FromSeconds(1234)); - - // Allocate the resources for a YUV video frame. - gl_->ResetUploadCount(); - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - EXPECT_EQ(3u, resources.resources.size()); - EXPECT_EQ(3u, resources.release_callbacks.size()); - // Expect exactly three texture uploads, one for each plane. - EXPECT_EQ(3, gl_->UploadCount()); - - // Allocate resources for the same frame. - gl_->ResetUploadCount(); - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - EXPECT_EQ(3u, resources.resources.size()); - EXPECT_EQ(3u, resources.release_callbacks.size()); - // The data should be reused so expect no texture uploads. - EXPECT_EQ(0, gl_->UploadCount()); -} - -TEST_F(VideoResourceUpdaterTest, SoftwareFrameSoftwareCompositor) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); -} - -TEST_F(VideoResourceUpdaterTest, ReuseResourceSoftwareCompositor) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); - video_frame->set_timestamp(base::TimeDelta::FromSeconds(1234)); - - // Allocate the resources for a software video frame. - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ(1u, resources.release_callbacks.size()); - // Expect exactly one allocated shared bitmap. - EXPECT_EQ(1u, layer_tree_frame_sink_software_->shared_bitmaps().size()); - auto shared_bitmaps = layer_tree_frame_sink_software_->shared_bitmaps(); - - // Simulate the ResourceProvider releasing the resource back to the video - // updater. - std::move(resources.release_callbacks[0]).Run(gpu::SyncToken(), false); - - // Allocate resources for the same frame. - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ(1u, resources.release_callbacks.size()); - - // Ensure that the same shared bitmap was reused. - EXPECT_EQ(layer_tree_frame_sink_software_->shared_bitmaps(), shared_bitmaps); -} - -TEST_F(VideoResourceUpdaterTest, ReuseResourceNoDeleteSoftwareCompositor) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); - video_frame->set_timestamp(base::TimeDelta::FromSeconds(1234)); - - // Allocate the resources for a software video frame. - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ(1u, resources.release_callbacks.size()); - // Expect exactly one allocated shared bitmap. - EXPECT_EQ(1u, layer_tree_frame_sink_software_->shared_bitmaps().size()); - auto shared_bitmaps = layer_tree_frame_sink_software_->shared_bitmaps(); - - // Allocate resources for the same frame. - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ(1u, resources.release_callbacks.size()); - - // Ensure that the same shared bitmap was reused. - EXPECT_EQ(layer_tree_frame_sink_software_->shared_bitmaps(), shared_bitmaps); -} - -TEST_F(VideoResourceUpdaterTest, ChangeResourceSizeSoftwareCompositor) { - constexpr gfx::Size kSize1(10, 10); - constexpr gfx::Size kSize2(20, 20); - - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); - - // Allocate the resources for a software video frame. - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame( - CreateTestYUVVideoFrame(kSize1)); - // Expect exactly one allocated shared bitmap. - EXPECT_EQ(1u, layer_tree_frame_sink_software_->shared_bitmaps().size()); - auto shared_bitmaps = layer_tree_frame_sink_software_->shared_bitmaps(); - - // Simulate the ResourceProvider releasing the resource back to the video - // updater. - std::move(resources.release_callbacks[0]).Run(gpu::SyncToken(), false); - - // Allocate resources for the next frame with a different size. - resources = updater->CreateExternalResourcesFromVideoFrame( - CreateTestYUVVideoFrame(kSize2)); - - // The first resource was released, so it can be reused but it's the wrong - // size. We should expect the first shared bitmap to be deleted and a new - // shared bitmap to be allocated. - EXPECT_EQ(1u, layer_tree_frame_sink_software_->shared_bitmaps().size()); - EXPECT_NE(layer_tree_frame_sink_software_->shared_bitmaps(), shared_bitmaps); -} - -TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - - scoped_refptr<media::VideoFrame> video_frame = - CreateTestRGBAHardwareVideoFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ(1u, resources.release_callbacks.size()); - - video_frame = CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_I420, 3, - GL_TEXTURE_RECTANGLE_ARB); - - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - EXPECT_EQ(3u, resources.resources.size()); - EXPECT_EQ(3u, resources.release_callbacks.size()); - EXPECT_FALSE(resources.resources[0].read_lock_fences_enabled); - EXPECT_FALSE(resources.resources[1].read_lock_fences_enabled); - EXPECT_FALSE(resources.resources[2].read_lock_fences_enabled); - - video_frame = CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_I420, 3, - GL_TEXTURE_RECTANGLE_ARB); - video_frame->metadata()->SetBoolean( - media::VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true); - - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_TRUE(resources.resources[0].read_lock_fences_enabled); - EXPECT_TRUE(resources.resources[1].read_lock_fences_enabled); - EXPECT_TRUE(resources.resources[2].read_lock_fences_enabled); -} - -TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_StreamTexture) { - // Note that |use_stream_video_draw_quad| is true for this test. - std::unique_ptr<VideoResourceUpdater> updater = - CreateUpdaterForHardware(true); - gl_->ResetTextureCreationCount(); - scoped_refptr<media::VideoFrame> video_frame = - CreateTestStreamTextureHardwareVideoFrame(false); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::STREAM_TEXTURE, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ((GLenum)GL_TEXTURE_EXTERNAL_OES, - resources.resources[0].mailbox_holder.texture_target); - EXPECT_EQ(1u, resources.release_callbacks.size()); - EXPECT_EQ(0, gl_->TextureCreationCount()); - - // A copied stream texture should return an RGBA resource in a new - // GL_TEXTURE_2D texture. - gl_->ResetTextureCreationCount(); - video_frame = CreateTestStreamTextureHardwareVideoFrame(true); - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ((GLenum)GL_TEXTURE_2D, - resources.resources[0].mailbox_holder.texture_target); - EXPECT_EQ(1u, resources.release_callbacks.size()); - EXPECT_EQ(0, gl_->TextureCreationCount()); -} - -TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_TextureQuad) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - gl_->ResetTextureCreationCount(); - scoped_refptr<media::VideoFrame> video_frame = - CreateTestStreamTextureHardwareVideoFrame(false); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ((GLenum)GL_TEXTURE_EXTERNAL_OES, - resources.resources[0].mailbox_holder.texture_target); - EXPECT_EQ(1u, resources.release_callbacks.size()); - EXPECT_EQ(0, gl_->TextureCreationCount()); -} - -// Passthrough the sync token returned by the compositor if we don't have an -// existing release sync token. -TEST_F(VideoResourceUpdaterTest, PassReleaseSyncToken) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - - const gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x123), - 123); - - { - scoped_refptr<media::VideoFrame> video_frame = - CreateTestRGBAHardwareVideoFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - - ASSERT_EQ(resources.release_callbacks.size(), 1u); - std::move(resources.release_callbacks[0]).Run(sync_token, false); - } - - EXPECT_EQ(release_sync_token_, sync_token); -} - -// Generate new sync token because video frame has an existing sync token. -TEST_F(VideoResourceUpdaterTest, GenerateReleaseSyncToken) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - - const gpu::SyncToken sync_token1(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x123), - 123); - - const gpu::SyncToken sync_token2(gpu::CommandBufferNamespace::GPU_IO, - gpu::CommandBufferId::FromUnsafeValue(0x234), - 234); - - { - scoped_refptr<media::VideoFrame> video_frame = - CreateTestRGBAHardwareVideoFrame(); - - VideoFrameExternalResources resources1 = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - ASSERT_EQ(resources1.release_callbacks.size(), 1u); - std::move(resources1.release_callbacks[0]).Run(sync_token1, false); - - VideoFrameExternalResources resources2 = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - ASSERT_EQ(resources2.release_callbacks.size(), 1u); - std::move(resources2.release_callbacks[0]).Run(sync_token2, false); - } - - EXPECT_TRUE(release_sync_token_.HasData()); - EXPECT_NE(release_sync_token_, sync_token1); - EXPECT_NE(release_sync_token_, sync_token2); -} - -// Pass mailbox sync token as is if no GL operations are performed before frame -// resources are handed off to the compositor. -TEST_F(VideoResourceUpdaterTest, PassMailboxSyncToken) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - - scoped_refptr<media::VideoFrame> video_frame = - CreateTestRGBAHardwareVideoFrame(); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - - ASSERT_EQ(resources.resources.size(), 1u); - EXPECT_TRUE(resources.resources[0].mailbox_holder.sync_token.HasData()); - EXPECT_EQ(resources.resources[0].mailbox_holder.sync_token, - kMailboxSyncToken); -} - -// Generate new sync token for compositor when copying the texture. -TEST_F(VideoResourceUpdaterTest, GenerateSyncTokenOnTextureCopy) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - - scoped_refptr<media::VideoFrame> video_frame = - CreateTestStreamTextureHardwareVideoFrame(true /* needs_copy */); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - - ASSERT_EQ(resources.resources.size(), 1u); - EXPECT_TRUE(resources.resources[0].mailbox_holder.sync_token.HasData()); - EXPECT_NE(resources.resources[0].mailbox_holder.sync_token, - kMailboxSyncToken); -} - -// NV12 VideoFrames backed by a single native texture can be sampled out -// by GL as RGB. To use them as HW overlays we need to know the format -// of the underlying buffer, that is YUV_420_BIPLANAR. -TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_SingleNV12) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - gl_->ResetTextureCreationCount(); - scoped_refptr<media::VideoFrame> video_frame = CreateTestHardwareVideoFrame( - media::PIXEL_FORMAT_NV12, GL_TEXTURE_EXTERNAL_OES); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGB, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ((GLenum)GL_TEXTURE_EXTERNAL_OES, - resources.resources[0].mailbox_holder.texture_target); - EXPECT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR, - resources.resources[0].buffer_format); - - video_frame = CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_NV12, 1, - GL_TEXTURE_RECTANGLE_ARB); - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::RGB, resources.type); - EXPECT_EQ(1u, resources.resources.size()); - EXPECT_EQ((GLenum)GL_TEXTURE_RECTANGLE_ARB, - resources.resources[0].mailbox_holder.texture_target); - EXPECT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR, - resources.resources[0].buffer_format); - - EXPECT_EQ(0, gl_->TextureCreationCount()); -} - -TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_DualNV12) { - std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); - gl_->ResetTextureCreationCount(); - scoped_refptr<media::VideoFrame> video_frame = - CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_NV12, 2, - GL_TEXTURE_EXTERNAL_OES); - - VideoFrameExternalResources resources = - updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - EXPECT_EQ(2u, resources.resources.size()); - EXPECT_EQ(2u, resources.release_callbacks.size()); - EXPECT_EQ((GLenum)GL_TEXTURE_EXTERNAL_OES, - resources.resources[0].mailbox_holder.texture_target); - // |updater| doesn't set |buffer_format| in this case. - EXPECT_EQ(gfx::BufferFormat::RGBA_8888, resources.resources[0].buffer_format); - - video_frame = CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_NV12, 2, - GL_TEXTURE_RECTANGLE_ARB); - resources = updater->CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); - EXPECT_EQ(2u, resources.resources.size()); - EXPECT_EQ((GLenum)GL_TEXTURE_RECTANGLE_ARB, - resources.resources[0].mailbox_holder.texture_target); - EXPECT_EQ(gfx::BufferFormat::RGBA_8888, resources.resources[0].buffer_format); - // When TestWebGraphicsContext3D is used, createAndConsumeTextureCHROMIUM will - // call createTexture. But this is incorrect, so when this is converted to - // TestGLES2Interface, GenTextures will not being called. - EXPECT_EQ(0, gl_->TextureCreationCount()); -} - -} // namespace -} // namespace cc diff --git a/chromium/cc/scheduler/compositor_timing_history.cc b/chromium/cc/scheduler/compositor_timing_history.cc index dd4d8f8a958..d4ed618a8db 100644 --- a/chromium/cc/scheduler/compositor_timing_history.cc +++ b/chromium/cc/scheduler/compositor_timing_history.cc @@ -45,7 +45,6 @@ class CompositorTimingHistory::UMAReporter { virtual void AddActivateDuration(base::TimeDelta duration) = 0; virtual void AddDrawDuration(base::TimeDelta duration) = 0; virtual void AddSubmitToAckLatency(base::TimeDelta duration) = 0; - virtual void AddSubmitAckWasFast(bool was_fast) = 0; // crbug.com/758439: the following 3 functions are used to report timing in // certain conditions targeting blink / compositor animations. @@ -268,10 +267,6 @@ class RendererUMAReporter : public CompositorTimingHistory::UMAReporter { duration); } - void AddSubmitAckWasFast(bool was_fast) override { - UMA_HISTOGRAM_BOOLEAN("Scheduling.Renderer.SwapAckWasFast", was_fast); - } - void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override { UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED( "Scheduling.Renderer.MainAndImplFrameTimeDelta", delta); @@ -282,39 +277,26 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { public: ~BrowserUMAReporter() override = default; - void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override { - UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED( - "Scheduling.Browser.BeginMainFrameIntervalCritical", interval); - } + // BeginMainFrameIntervalCritical is not meaningful to measure on browser + // side because browser rendering fps is not at 60. + void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override {} void AddBeginMainFrameIntervalNotCritical(base::TimeDelta interval) override { - UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED( - "Scheduling.Browser.BeginMainFrameIntervalNotCritical", interval); } - void AddCommitInterval(base::TimeDelta interval) override { - UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED( - "Scheduling.Browser.CommitInterval", interval); - } + // CommitInterval is not meaningful to measure on browser side because + // browser rendering fps is not at 60. + void AddCommitInterval(base::TimeDelta interval) override {} - void AddDrawInterval(base::TimeDelta interval) override { - UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED("Scheduling.Browser.DrawInterval", - interval); - } + // DrawInterval is not meaningful to measure on browser side because + // browser rendering fps is not at 60. + void AddDrawInterval(base::TimeDelta interval) override {} void AddDrawIntervalWithCompositedAnimations( - base::TimeDelta interval) override { - // Still report, but the data is not meaningful. - UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED( - "Scheduling.Browser.DrawIntervalWithCompositedAnimations", interval); - } + base::TimeDelta interval) override {} void AddDrawIntervalWithMainThreadAnimations( - base::TimeDelta interval) override { - // Still report, but the data is not meaningful. - UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED( - "Scheduling.Browser.DrawIntervalWithMainThreadAnimations", interval); - } + base::TimeDelta interval) override {} void AddBeginImplFrameLatency(base::TimeDelta delta) override { UMA_HISTOGRAM_CUSTOM_TIMES_DURATION( @@ -328,10 +310,7 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { } void AddBeginMainFrameQueueDurationNotCriticalDuration( - base::TimeDelta duration) override { - UMA_HISTOGRAM_CUSTOM_TIMES_DURATION( - "Scheduling.Browser.BeginMainFrameQueueDurationNotCritical", duration); - } + base::TimeDelta duration) override {} void AddBeginMainFrameStartToCommitDuration( base::TimeDelta duration) override { @@ -357,15 +336,9 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { void AddReadyToActivateToWillActivateDuration( base::TimeDelta duration, bool pending_tree_is_impl_side) override { - if (pending_tree_is_impl_side) { - UMA_HISTOGRAM_CUSTOM_TIMES_DURATION_SUFFIX( - "Scheduling.Browser.ReadyToActivateToActivationDuration", ".Impl", - duration); - } else { - UMA_HISTOGRAM_CUSTOM_TIMES_DURATION_SUFFIX( - "Scheduling.Browser.ReadyToActivateToActivationDuration", ".Main", - duration); - } + UMA_HISTOGRAM_CUSTOM_TIMES_DURATION_SUFFIX( + "Scheduling.Browser.ReadyToActivateToActivationDuration", ".Main", + duration); } void AddPrepareTilesDuration(base::TimeDelta duration) override { @@ -388,10 +361,6 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { duration); } - void AddSubmitAckWasFast(bool was_fast) override { - UMA_HISTOGRAM_BOOLEAN("Scheduling.Browser.SwapAckWasFast", was_fast); - } - void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override { UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED( "Scheduling.Browser.MainAndImplFrameTimeDelta", delta); @@ -429,7 +398,6 @@ class NullUMAReporter : public CompositorTimingHistory::UMAReporter { void AddActivateDuration(base::TimeDelta duration) override {} void AddDrawDuration(base::TimeDelta duration) override {} void AddSubmitToAckLatency(base::TimeDelta duration) override {} - void AddSubmitAckWasFast(bool was_fast) override {} void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override {} }; @@ -614,7 +582,6 @@ void CompositorTimingHistory::WillBeginImplFrame( if (submit_ack_watchdog_enabled_) { base::TimeDelta submit_not_acked_time_ = now - submit_start_time_; if (submit_not_acked_time_ >= kSubmitAckWatchdogTimeout) { - uma_reporter_->AddSubmitAckWasFast(false); // Only record this UMA once per submitted CompositorFrame. submit_ack_watchdog_enabled_ = false; } @@ -953,11 +920,8 @@ void CompositorTimingHistory::DidReceiveCompositorFrameAck() { DCHECK_NE(base::TimeTicks(), submit_start_time_); base::TimeDelta submit_to_ack_duration = Now() - submit_start_time_; uma_reporter_->AddSubmitToAckLatency(submit_to_ack_duration); - if (submit_ack_watchdog_enabled_) { - bool was_fast = submit_to_ack_duration < kSubmitAckWatchdogTimeout; - uma_reporter_->AddSubmitAckWasFast(was_fast); + if (submit_ack_watchdog_enabled_) submit_ack_watchdog_enabled_ = false; - } submit_start_time_ = base::TimeTicks(); } @@ -965,9 +929,8 @@ void CompositorTimingHistory::SetTreePriority(TreePriority priority) { tree_priority_ = priority; } -void CompositorTimingHistory::ClearHistoryOnNavigation() { - TRACE_EVENT0("cc,benchmark", - "CompositorTimingHistory::ClearHistoryOnNavigation"); +void CompositorTimingHistory::ClearHistory() { + TRACE_EVENT0("cc,benchmark", "CompositorTimingHistory::ClearHistory"); begin_main_frame_queue_duration_history_.Clear(); begin_main_frame_queue_duration_critical_history_.Clear(); diff --git a/chromium/cc/scheduler/compositor_timing_history.h b/chromium/cc/scheduler/compositor_timing_history.h index 84aa1545eba..842583f1106 100644 --- a/chromium/cc/scheduler/compositor_timing_history.h +++ b/chromium/cc/scheduler/compositor_timing_history.h @@ -93,7 +93,7 @@ class CC_EXPORT CompositorTimingHistory { return begin_main_frame_sent_time_; } - void ClearHistoryOnNavigation(); + void ClearHistory(); size_t begin_main_frame_start_to_ready_to_commit_sample_count() const { return begin_main_frame_start_to_ready_to_commit_duration_history_ .sample_count(); @@ -126,7 +126,7 @@ class CC_EXPORT CompositorTimingHistory { base::TimeTicks draw_end_time_prev_; // If you add any history here, please remember to reset it in - // ClearHistoryOnNavigation. + // ClearHistory. RollingTimeDeltaHistory begin_main_frame_queue_duration_history_; RollingTimeDeltaHistory begin_main_frame_queue_duration_critical_history_; RollingTimeDeltaHistory begin_main_frame_queue_duration_not_critical_history_; diff --git a/chromium/cc/scheduler/compositor_timing_history_unittest.cc b/chromium/cc/scheduler/compositor_timing_history_unittest.cc index 564bc81c8bd..a5e8387d8b2 100644 --- a/chromium/cc/scheduler/compositor_timing_history_unittest.cc +++ b/chromium/cc/scheduler/compositor_timing_history_unittest.cc @@ -5,7 +5,7 @@ #include "cc/scheduler/compositor_timing_history.h" #include "base/macros.h" -#include "base/test/histogram_tester.h" +#include "base/test/metrics/histogram_tester.h" #include "cc/debug/rendering_stats_instrumentation.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/cc/scheduler/draw_result.h b/chromium/cc/scheduler/draw_result.h index 83fe4413f02..37ffb04c34a 100644 --- a/chromium/cc/scheduler/draw_result.h +++ b/chromium/cc/scheduler/draw_result.h @@ -7,6 +7,8 @@ namespace cc { +// Note that these values are reported in UMA. So entries should never be +// renumbered, and numeric values should never be reused. enum DrawResult { INVALID_RESULT, DRAW_SUCCESS, @@ -14,6 +16,8 @@ enum DrawResult { DRAW_ABORTED_MISSING_HIGH_RES_CONTENT, DRAW_ABORTED_CANT_DRAW, DRAW_ABORTED_DRAINING_PIPELINE, + // Magic constant used by the histogram macros. + kMaxValue = DRAW_ABORTED_DRAINING_PIPELINE, }; } // namespace cc diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index 87db539737f..184fae211c5 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -318,13 +318,15 @@ void Scheduler::SetVideoNeedsBeginFrames(bool video_needs_begin_frames) { ProcessScheduledActions(); } -void Scheduler::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) { +void Scheduler::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, + bool skip_draw) { DCHECK(settings_.using_synchronous_renderer_compositor); DCHECK_EQ(state_machine_.begin_impl_frame_state(), SchedulerStateMachine::BeginImplFrameState::IDLE); DCHECK(begin_impl_frame_deadline_task_.IsCancelled()); state_machine_.SetResourcelessSoftwareDraw(resourceless_software_draw); + state_machine_.SetSkipDraw(skip_draw); state_machine_.OnBeginImplFrameDeadline(); ProcessScheduledActions(); @@ -520,8 +522,7 @@ void Scheduler::SendBeginFrameAck(const viz::BeginFrameArgs& args, if (!did_submit) { DCHECK(!inside_scheduled_action_); base::AutoReset<bool> mark_inside(&inside_scheduled_action_, true); - client_->DidNotProduceFrame( - viz::BeginFrameAck(args.source_id, args.sequence_number, did_submit)); + client_->DidNotProduceFrame(viz::BeginFrameAck(args, did_submit)); } if (begin_frame_source_) @@ -774,7 +775,8 @@ void Scheduler::ProcessScheduledActions() { break; case SchedulerStateMachine::Action::INVALIDATE_LAYER_TREE_FRAME_SINK: { state_machine_.WillInvalidateLayerTreeFrameSink(); - client_->ScheduledActionInvalidateLayerTreeFrameSink(); + client_->ScheduledActionInvalidateLayerTreeFrameSink( + state_machine_.RedrawPending()); break; } } @@ -961,14 +963,13 @@ bool Scheduler::IsBeginMainFrameSentOrStarted() const { } viz::BeginFrameAck Scheduler::CurrentBeginFrameAckForActiveTree() const { - return viz::BeginFrameAck(begin_main_frame_args_.source_id, - begin_main_frame_args_.sequence_number, true); + return viz::BeginFrameAck(begin_main_frame_args_, true); } -void Scheduler::ClearHistoryOnNavigation() { +void Scheduler::ClearHistory() { // Ensure we reset decisions based on history from the previous navigation. state_machine_.SetSkipNextBeginMainFrameToReduceLatency(false); - compositor_timing_history_->ClearHistoryOnNavigation(); + compositor_timing_history_->ClearHistory(); ProcessScheduledActions(); } diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h index 79ec95b21d4..a9926c86687 100644 --- a/chromium/cc/scheduler/scheduler.h +++ b/chromium/cc/scheduler/scheduler.h @@ -44,7 +44,8 @@ class SchedulerClient { virtual void ScheduledActionActivateSyncTree() = 0; virtual void ScheduledActionBeginLayerTreeFrameSinkCreation() = 0; virtual void ScheduledActionPrepareTiles() = 0; - virtual void ScheduledActionInvalidateLayerTreeFrameSink() = 0; + virtual void ScheduledActionInvalidateLayerTreeFrameSink( + bool needs_redraw) = 0; virtual void ScheduledActionPerformImplSideInvalidation() = 0; virtual void DidFinishImplFrame() = 0; virtual void DidNotProduceFrame(const viz::BeginFrameAck& ack) = 0; @@ -79,7 +80,8 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { void OnBeginFrameSourcePausedChanged(bool paused) override; bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) override; - void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw); + void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, + bool skip_draw); const SchedulerSettings& settings() const { return settings_; } @@ -172,7 +174,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { viz::BeginFrameAck CurrentBeginFrameAckForActiveTree() const; - void ClearHistoryOnNavigation(); + void ClearHistory(); protected: // Virtual for testing. diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc index 8f5409fc010..a81baa256ea 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.cc +++ b/chromium/cc/scheduler/scheduler_state_machine.cc @@ -323,6 +323,10 @@ bool SchedulerStateMachine::ShouldDraw() const { if (did_draw_) return false; + // Don't draw if an early check determined the frame does not have damage. + if (skip_draw_) + return false; + // Don't draw if we are waiting on the first commit after a surface. if (layer_tree_frame_sink_state_ != LayerTreeFrameSinkState::ACTIVE) return false; @@ -1201,6 +1205,10 @@ void SchedulerStateMachine::SetCanDraw(bool can_draw) { can_draw_ = can_draw; } +void SchedulerStateMachine::SetSkipDraw(bool skip_draw) { + skip_draw_ = skip_draw; +} + void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; } diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h index 9fec8c62146..cd996142e4f 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.h +++ b/chromium/cc/scheduler/scheduler_state_machine.h @@ -246,7 +246,8 @@ class CC_EXPORT SchedulerStateMachine { // Indicates production should be skipped to recover latency. void SetSkipNextBeginMainFrameToReduceLatency(bool skip); - // Resourceless software draws are allowed even when invisible. + // For Android WebView, resourceless software draws are allowed even when + // invisible. void SetResourcelessSoftwareDraw(bool resourceless_draw); // Indicates whether drawing would, at this time, make sense. @@ -254,6 +255,10 @@ class CC_EXPORT SchedulerStateMachine { // when such behavior would be undesirable. void SetCanDraw(bool can); + // For Android WebView, indicates that the draw should be skipped because the + // frame sink is not ready to receive frames. + void SetSkipDraw(bool skip); + // Indicates that scheduled BeginMainFrame is started. void NotifyBeginMainFrameStarted(); @@ -386,6 +391,7 @@ class CC_EXPORT SchedulerStateMachine { bool begin_frame_source_paused_ = false; bool resourceless_draw_ = false; bool can_draw_ = false; + bool skip_draw_ = false; bool has_pending_tree_ = false; bool pending_tree_is_ready_for_activation_ = false; bool active_tree_needs_first_draw_ = false; diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index d1fcefb1c79..4d9c77ad14d 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -14,6 +14,7 @@ #include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" #include "base/run_loop.h" +#include "base/test/test_mock_time_task_runner.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "cc/test/scheduler_test_common.h" @@ -21,7 +22,6 @@ #include "components/viz/test/begin_frame_args_test.h" #include "components/viz/test/fake_delay_based_time_source.h" #include "components/viz/test/fake_external_begin_frame_source.h" -#include "components/viz/test/ordered_simple_task_runner.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -52,6 +52,7 @@ class FakeSchedulerClient : public SchedulerClient, states_.clear(); will_begin_impl_frame_causes_redraw_ = false; will_begin_impl_frame_requests_one_begin_impl_frame_ = false; + invalidate_needs_redraw_ = true; draw_will_happen_ = true; swap_will_happen_if_draw_happens_ = true; num_draws_ = 0; @@ -63,6 +64,7 @@ class FakeSchedulerClient : public SchedulerClient, bool needs_begin_frames() { return scheduler_->begin_frames_expected(); } int num_draws() const { return num_draws_; } + bool invalidate_needs_redraw() const { return invalidate_needs_redraw_; } const std::vector<std::string> Actions() const { return std::vector<std::string>(actions_.begin(), actions_.end()); } @@ -86,6 +88,9 @@ class FakeSchedulerClient : public SchedulerClient, void SetWillBeginImplFrameCausesRedraw(bool causes_redraw) { will_begin_impl_frame_causes_redraw_ = causes_redraw; } + void SetInvalidateNeedsRedraw(bool needs_redraw) { + invalidate_needs_redraw_ = needs_redraw; + } void SetDrawWillHappen(bool draw_will_happen) { draw_will_happen_ = draw_will_happen; } @@ -187,9 +192,10 @@ class FakeSchedulerClient : public SchedulerClient, scheduler_->WillPrepareTiles(); scheduler_->DidPrepareTiles(); } - void ScheduledActionInvalidateLayerTreeFrameSink() override { + void ScheduledActionInvalidateLayerTreeFrameSink(bool needs_redraw) override { EXPECT_FALSE(inside_action_); base::AutoReset<bool> mark_inside(&inside_action_, true); + invalidate_needs_redraw_ = needs_redraw; actions_.push_back("ScheduledActionInvalidateLayerTreeFrameSink"); states_.push_back(scheduler_->AsValue()); } @@ -256,6 +262,7 @@ class FakeSchedulerClient : public SchedulerClient, bool inside_begin_impl_frame_ = false; bool will_begin_impl_frame_causes_redraw_; bool will_begin_impl_frame_requests_one_begin_impl_frame_; + bool invalidate_needs_redraw_ = true; bool draw_will_happen_; bool swap_will_happen_if_draw_happens_; bool automatic_ack_ = true; @@ -276,20 +283,71 @@ enum BeginFrameSourceType { THROTTLED_BFS, }; +class SchedulerTestTaskRunner : public base::TestMockTimeTaskRunner { + public: + SchedulerTestTaskRunner() + : base::TestMockTimeTaskRunner( + base::TestMockTimeTaskRunner::Type::kStandalone) { + AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(110000)); + } + + void RunUntilTime(base::TimeTicks end_time) { + FastForwardBy(end_time - NowTicks()); + } + + // Runs all tasks posted before this call. + void RunPendingTasks() { + base::circular_deque<base::TestPendingTask> tasks = TakePendingTasks(); + while (!tasks.empty()) { + base::TestPendingTask task = std::move(tasks.front()); + tasks.pop_front(); + // Set clock to the beginning of task and run it. + AdvanceMockTickClock(task.GetTimeToRun() - NowTicks()); + std::move(task.task).Run(); + } + } + + // Runs tasks while condition is met. + // Condition is being checked when task exists and before it gets selected. + void RunTasksWhile(base::RepeatingCallback<bool()> condition) { + run_condition_ = condition; + FastForwardUntilNoTasksRemain(); + run_condition_ = base::nullopt; + // We've moved all the pending tasks away to break the execution loop, + // now we should restore them. + while (!tasks_to_requeue_.empty()) { + base::TestPendingTask task = std::move(tasks_to_requeue_.front()); + tasks_to_requeue_.pop_front(); + PostDelayedTask(task.location, std::move(task.task), + task.GetTimeToRun() - NowTicks()); + } + } + + protected: + void OnBeforeSelectingTask() override { + // Avoid potential infinite loops. + ASSERT_LT(++task_count_, 100u); + + if (run_condition_ && HasPendingTask() && !run_condition_->Run()) { + // Execution will not continue because we move all the pending tasks away. + tasks_to_requeue_ = TakePendingTasks(); + } + } + + private: + ~SchedulerTestTaskRunner() override = default; // Ref-counted. + + size_t task_count_ = 0u; + base::Optional<base::RepeatingCallback<bool()>> run_condition_; + base::circular_deque<base::TestPendingTask> tasks_to_requeue_; +}; + class SchedulerTest : public testing::Test { public: SchedulerTest() - : now_src_(new base::SimpleTestTickClock()), - task_runner_(new OrderedSimpleTaskRunner(now_src_.get(), true)), + : task_runner_(base::MakeRefCounted<SchedulerTestTaskRunner>()), fake_external_begin_frame_source_(nullptr), - fake_compositor_timing_history_(nullptr) { - now_src_->Advance(base::TimeDelta::FromMicroseconds(10000)); - // A bunch of tests require NowTicks() - // to be > viz::BeginFrameArgs::DefaultInterval() - now_src_->Advance(base::TimeDelta::FromMilliseconds(100)); - // Fail if we need to run 100 tasks in a row. - task_runner_->SetRunTaskLimit(100); - } + fake_compositor_timing_history_(nullptr) {} ~SchedulerTest() override = default; @@ -297,14 +355,14 @@ class SchedulerTest : public testing::Test { TestScheduler* CreateScheduler(BeginFrameSourceType bfs_type) { viz::BeginFrameSource* frame_source = nullptr; unthrottled_frame_source_.reset(new viz::BackToBackBeginFrameSource( - std::make_unique<viz::FakeDelayBasedTimeSource>(now_src_.get(), - task_runner_.get()))); + std::make_unique<viz::FakeDelayBasedTimeSource>( + task_runner_->GetMockTickClock(), task_runner_.get()))); fake_external_begin_frame_source_.reset( new viz::FakeExternalBeginFrameSource(1.0, false)); fake_external_begin_frame_source_->SetClient(client_.get()); synthetic_frame_source_ = std::make_unique<viz::DelayBasedBeginFrameSource>( - std::make_unique<viz::FakeDelayBasedTimeSource>(now_src_.get(), - task_runner_.get()), + std::make_unique<viz::FakeDelayBasedTimeSource>( + task_runner_->GetMockTickClock(), task_runner_.get()), viz::BeginFrameSource::kNotRestartableId); switch (bfs_type) { case EXTERNAL_BFS: @@ -325,7 +383,7 @@ class SchedulerTest : public testing::Test { fake_compositor_timing_history_ = fake_compositor_timing_history.get(); scheduler_.reset(new TestScheduler( - now_src_.get(), client_.get(), scheduler_settings_, 0, + task_runner_->GetMockTickClock(), client_.get(), scheduler_settings_, 0, task_runner_.get(), std::move(fake_compositor_timing_history))); client_->set_scheduler(scheduler_.get()); scheduler_->SetBeginFrameSource(frame_source); @@ -352,9 +410,6 @@ class SchedulerTest : public testing::Test { CreateScheduler(bfs_type); } - OrderedSimpleTaskRunner& task_runner() { return *task_runner_; } - base::SimpleTestTickClock* now_src() { return now_src_.get(); } - // As this function contains EXPECT macros, to allow debugging it should be // called inside EXPECT_SCOPED like so; // EXPECT_SCOPED( @@ -387,8 +442,8 @@ class SchedulerTest : public testing::Test { SCOPED_TRACE("Do first frame to commit after initialize."); AdvanceFrame(); - now_src_->Advance(base::TimeDelta::FromMilliseconds(1)); - scheduler_->NotifyBeginMainFrameStarted(now_src_->NowTicks()); + task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMilliseconds(1)); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); scheduler_->NotifyReadyToDraw(); @@ -398,7 +453,9 @@ class SchedulerTest : public testing::Test { if (scheduler_settings_.using_synchronous_renderer_compositor) { scheduler_->SetNeedsRedraw(); bool resourceless_software_draw = false; - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + bool skip_draw = false; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); } else { // Run the posted deadline task. EXPECT_TRUE(client_->IsInsideBeginImplFrame()); @@ -465,10 +522,10 @@ class SchedulerTest : public testing::Test { fake_external_begin_frame_source_.get()); // Creep the time forward so that any viz::BeginFrameArgs is not equal to // the last one otherwise we violate the viz::BeginFrameSource contract. - now_src_->Advance(viz::BeginFrameArgs::DefaultInterval()); + task_runner_->AdvanceMockTickClock(viz::BeginFrameArgs::DefaultInterval()); viz::BeginFrameArgs args = fake_external_begin_frame_source_->CreateBeginFrameArgs( - BEGINFRAME_FROM_HERE, now_src()); + BEGINFRAME_FROM_HERE, task_runner_->GetMockTickClock()); args.animate_only = animate_only; fake_external_begin_frame_source_->TestOnBeginFrame(args); return args; @@ -503,8 +560,7 @@ class SchedulerTest : public testing::Test { ScrollHandlerState scroll_handler_state, base::TimeDelta durations); - std::unique_ptr<base::SimpleTestTickClock> now_src_; - scoped_refptr<OrderedSimpleTaskRunner> task_runner_; + scoped_refptr<SchedulerTestTaskRunner> task_runner_; std::unique_ptr<viz::FakeExternalBeginFrameSource> fake_external_begin_frame_source_; std::unique_ptr<viz::SyntheticBeginFrameSource> synthetic_frame_source_; @@ -585,14 +641,14 @@ TEST_F(SchedulerTest, RequestCommit) { client_->Reset(); // If we don't swap on the deadline, we wait for the next BeginFrame. - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_NO_ACTION(); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); // NotifyReadyToCommit should trigger the commit. - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); EXPECT_TRUE(scheduler_->begin_frames_expected()); @@ -612,7 +668,7 @@ TEST_F(SchedulerTest, RequestCommit) { client_->Reset(); // BeginImplFrame deadline should draw. - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); @@ -625,7 +681,7 @@ TEST_F(SchedulerTest, RequestCommit) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("RemoveObserver(this)"); client_->Reset(); } @@ -639,7 +695,7 @@ TEST_F(SchedulerTest, RequestCommitAfterSetDeferCommit) { EXPECT_NO_ACTION(); client_->Reset(); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); // There are no pending tasks or actions. EXPECT_NO_ACTION(); EXPECT_FALSE(scheduler_->begin_frames_expected()); @@ -675,7 +731,7 @@ TEST_F(SchedulerTest, DeferCommitWithRedraw) { EXPECT_ACTIONS("WillBeginImplFrame"); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); @@ -707,7 +763,7 @@ TEST_F(SchedulerTest, RequestCommitAfterBeginMainFrameSent) { client_->Reset(); // Finish the first commit. - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); @@ -719,7 +775,7 @@ TEST_F(SchedulerTest, RequestCommitAfterBeginMainFrameSent) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); @@ -736,7 +792,7 @@ TEST_F(SchedulerTest, RequestCommitAfterBeginMainFrameSent) { // Finishing the commit before the deadline should post a new deadline task // to trigger the deadline early. - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); @@ -745,7 +801,7 @@ TEST_F(SchedulerTest, RequestCommitAfterBeginMainFrameSent) { EXPECT_ACTIONS("ScheduledActionActivateSyncTree"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); @@ -754,7 +810,7 @@ TEST_F(SchedulerTest, RequestCommitAfterBeginMainFrameSent) { // On the next BeginImplFrame, verify we go back to a quiescent state and // no longer request BeginImplFrames. EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_FALSE(scheduler_->begin_frames_expected()); client_->Reset(); } @@ -799,7 +855,7 @@ TEST_F(SchedulerTest, RequestRedrawInsideDraw) { EXPECT_EQ(0, client->num_draws()); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client->num_draws()); EXPECT_TRUE(scheduler_->RedrawPending()); EXPECT_TRUE(client->needs_begin_frames()); @@ -807,7 +863,7 @@ TEST_F(SchedulerTest, RequestRedrawInsideDraw) { client->SetRequestRedrawsInsideDraw(false); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(2, client_->num_draws()); EXPECT_FALSE(scheduler_->RedrawPending()); EXPECT_TRUE(client->needs_begin_frames()); @@ -815,7 +871,7 @@ TEST_F(SchedulerTest, RequestRedrawInsideDraw) { // We stop requesting BeginImplFrames after a BeginImplFrame where we don't // swap. EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(2, client->num_draws()); EXPECT_FALSE(scheduler_->RedrawPending()); EXPECT_FALSE(client->needs_begin_frames()); @@ -837,7 +893,7 @@ TEST_F(SchedulerTest, RequestRedrawInsideFailedDraw) { // Fail the draw. EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client->num_draws()); // We have a commit pending and the draw failed, and we didn't lose the redraw @@ -850,7 +906,7 @@ TEST_F(SchedulerTest, RequestRedrawInsideFailedDraw) { // Fail the draw again. EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(2, client->num_draws()); EXPECT_TRUE(scheduler_->CommitPending()); EXPECT_TRUE(scheduler_->RedrawPending()); @@ -859,7 +915,7 @@ TEST_F(SchedulerTest, RequestRedrawInsideFailedDraw) { // Draw successfully. client->SetDrawWillHappen(true); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(3, client->num_draws()); EXPECT_TRUE(scheduler_->CommitPending()); EXPECT_FALSE(scheduler_->RedrawPending()); @@ -910,16 +966,16 @@ TEST_F(SchedulerTest, RequestCommitInsideDraw) { client->SetNeedsBeginMainFrameOnNextDraw(); EXPECT_SCOPED(AdvanceFrame()); client->SetNeedsBeginMainFrameOnNextDraw(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client->num_draws()); EXPECT_TRUE(scheduler_->CommitPending()); EXPECT_TRUE(client->needs_begin_frames()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(2, client->num_draws()); EXPECT_FALSE(scheduler_->RedrawPending()); @@ -929,7 +985,7 @@ TEST_F(SchedulerTest, RequestCommitInsideDraw) { // We stop requesting BeginImplFrames after a BeginImplFrame where we don't // swap. EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(2, client->num_draws()); EXPECT_FALSE(scheduler_->RedrawPending()); EXPECT_FALSE(scheduler_->CommitPending()); @@ -951,7 +1007,7 @@ TEST_F(SchedulerTest, RequestCommitInsideFailedDraw) { // Fail the draw. EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client->num_draws()); // We have a commit pending and the draw failed, and we didn't lose the commit @@ -963,7 +1019,7 @@ TEST_F(SchedulerTest, RequestCommitInsideFailedDraw) { // Fail the draw again. EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(2, client->num_draws()); EXPECT_TRUE(scheduler_->CommitPending()); EXPECT_TRUE(scheduler_->RedrawPending()); @@ -972,7 +1028,7 @@ TEST_F(SchedulerTest, RequestCommitInsideFailedDraw) { // Draw successfully. client->SetDrawWillHappen(true); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(3, client->num_draws()); EXPECT_TRUE(scheduler_->CommitPending()); EXPECT_FALSE(scheduler_->RedrawPending()); @@ -992,7 +1048,7 @@ TEST_F(SchedulerTest, NoSwapWhenDrawFails) { // Draw successfully, this starts a new frame. client->SetNeedsBeginMainFrameOnNextDraw(); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client->num_draws()); scheduler_->SetNeedsRedraw(); @@ -1003,7 +1059,7 @@ TEST_F(SchedulerTest, NoSwapWhenDrawFails) { client->SetDrawWillHappen(false); client->SetNeedsBeginMainFrameOnNextDraw(); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(2, client->num_draws()); } @@ -1042,7 +1098,7 @@ TEST_F(SchedulerTest, PrepareTiles) { // On the deadline, the actions should have occured in the right order. client->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client->num_draws()); EXPECT_TRUE(client->HasAction("ScheduledActionDrawIfPossible")); EXPECT_TRUE(client->HasAction("ScheduledActionPrepareTiles")); @@ -1071,7 +1127,7 @@ TEST_F(SchedulerTest, PrepareTiles) { // then the PrepareTiles action will be triggered after the Draw. // Afterwards, neither a draw nor PrepareTiles are pending. client->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client->num_draws()); EXPECT_TRUE(client->HasAction("ScheduledActionDrawIfPossible")); EXPECT_TRUE(client->HasAction("ScheduledActionPrepareTiles")); @@ -1087,7 +1143,7 @@ TEST_F(SchedulerTest, PrepareTiles) { EXPECT_ACTIONS("WillBeginImplFrame"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("RemoveObserver(this)"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_EQ(0, client->num_draws()); @@ -1107,7 +1163,7 @@ TEST_F(SchedulerTest, PrepareTiles) { EXPECT_ACTIONS("WillBeginImplFrame"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(0, client->num_draws()); EXPECT_FALSE(client->HasAction("ScheduledActionDrawIfPossible")); EXPECT_TRUE(client->HasAction("ScheduledActionPrepareTiles")); @@ -1134,7 +1190,7 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { EXPECT_FALSE(scheduler_->PrepareTilesPending()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client_->num_draws()); EXPECT_TRUE(client_->HasAction("ScheduledActionDrawIfPossible")); EXPECT_FALSE(client_->HasAction("ScheduledActionPrepareTiles")); @@ -1151,7 +1207,7 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client_->num_draws()); EXPECT_TRUE(client_->HasAction("ScheduledActionDrawIfPossible")); EXPECT_TRUE(client_->HasAction("ScheduledActionPrepareTiles")); @@ -1175,7 +1231,7 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { EXPECT_TRUE(scheduler_->PrepareTilesPending()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client_->num_draws()); EXPECT_TRUE(client_->HasAction("ScheduledActionDrawIfPossible")); EXPECT_FALSE(client_->HasAction("ScheduledActionPrepareTiles")); @@ -1199,7 +1255,7 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { EXPECT_TRUE(scheduler_->PrepareTilesPending()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client_->num_draws()); EXPECT_TRUE(client_->HasAction("ScheduledActionDrawIfPossible")); EXPECT_FALSE(client_->HasAction("ScheduledActionPrepareTiles")); @@ -1215,7 +1271,7 @@ TEST_F(SchedulerTest, PrepareTilesOncePerFrame) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client_->num_draws()); EXPECT_TRUE(client_->HasAction("ScheduledActionDrawIfPossible")); EXPECT_TRUE(client_->HasAction("ScheduledActionPrepareTiles")); @@ -1241,7 +1297,7 @@ TEST_F(SchedulerTest, DidPrepareTilesPreventsPrepareTilesForOneFrame) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_ACTIONS("ScheduledActionDrawIfPossible", "ScheduledActionPrepareTiles"); @@ -1262,7 +1318,7 @@ TEST_F(SchedulerTest, DidPrepareTilesPreventsPrepareTilesForOneFrame) { // No scheduled prepare tiles because we've already counted a prepare tiles in // between frames. client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); @@ -1274,7 +1330,7 @@ TEST_F(SchedulerTest, DidPrepareTilesPreventsPrepareTilesForOneFrame) { // Resume scheduled prepare tiles. client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_ACTIONS("ScheduledActionDrawIfPossible", "ScheduledActionPrepareTiles"); @@ -1306,7 +1362,7 @@ TEST_F(SchedulerTest, WaitForReadyToDrawDoNotPostDeadline) { // Begin new frame. EXPECT_SCOPED(AdvanceFrame()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); client_->Reset(); @@ -1319,14 +1375,14 @@ TEST_F(SchedulerTest, WaitForReadyToDrawDoNotPostDeadline) { // Scheduler won't post deadline in the mode. client_->Reset(); - task_runner().RunPendingTasks(); // Try to run posted deadline. + task_runner_->RunPendingTasks(); // Try to run posted deadline. // There is no posted deadline. EXPECT_NO_ACTION(); // Scheduler received ready to draw signal, and posted deadline. scheduler_->NotifyReadyToDraw(); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(1, client_->num_draws()); EXPECT_TRUE(client_->HasAction("ScheduledActionDrawIfPossible")); } @@ -1344,7 +1400,7 @@ TEST_F(SchedulerTest, WaitForReadyToDrawCancelledWhenLostLayerTreeFrameSink) { // Begin new frame. EXPECT_SCOPED(AdvanceFrame()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); client_->Reset(); @@ -1357,7 +1413,7 @@ TEST_F(SchedulerTest, WaitForReadyToDrawCancelledWhenLostLayerTreeFrameSink) { // Scheduler won't post deadline in the mode. client_->Reset(); - task_runner().RunPendingTasks(); // Try to run posted deadline. + task_runner_->RunPendingTasks(); // Try to run posted deadline. // There is no posted deadline. EXPECT_NO_ACTION(); @@ -1366,7 +1422,7 @@ TEST_F(SchedulerTest, WaitForReadyToDrawCancelledWhenLostLayerTreeFrameSink) { client_->Reset(); scheduler_->DidLoseLayerTreeFrameSink(); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation", "RemoveObserver(this)"); } @@ -1377,9 +1433,9 @@ void SchedulerTest::AdvanceAndMissOneFrame() { EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); EXPECT_SCOPED(AdvanceFrame()); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", @@ -1397,7 +1453,7 @@ void SchedulerTest::CheckMainFrameSkippedAfterLateCommit( EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_EQ(expect_send_begin_main_frame, scheduler_->MainThreadMissedLastDeadline()); EXPECT_TRUE(client_->HasAction("WillBeginImplFrame")); @@ -1417,16 +1473,17 @@ TEST_F(SchedulerTest, MainFrameNotSkippedAfterLateBeginFrame) { scheduler_->SetNeedsBeginMainFrame(); // Advance frame and create a begin frame. - now_src_->Advance(viz::BeginFrameArgs::DefaultInterval()); + task_runner_->AdvanceMockTickClock(viz::BeginFrameArgs::DefaultInterval()); viz::BeginFrameArgs args = fake_external_begin_frame_source_->CreateBeginFrameArgs( - BEGINFRAME_FROM_HERE, now_src()); + BEGINFRAME_FROM_HERE, task_runner_->GetMockTickClock()); // Deliver this begin frame super late. - now_src_->Advance(viz::BeginFrameArgs::DefaultInterval() * 100); + task_runner_->AdvanceMockTickClock(viz::BeginFrameArgs::DefaultInterval() * + 100); fake_external_begin_frame_source_->TestOnBeginFrame(args); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_EQ(true, scheduler_->MainThreadMissedLastDeadline()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", "ScheduledActionDrawIfPossible"); @@ -1544,9 +1601,9 @@ TEST_F(SchedulerTest, MainFrameNotSkippedAfterLateBeginMainFrameAbort) { EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); // After aborting the frame, make sure we don't skip the @@ -1560,7 +1617,7 @@ TEST_F(SchedulerTest, MainFrameNotSkippedAfterLateBeginMainFrameAbort) { EXPECT_TRUE(client_->HasAction("WillBeginImplFrame")); EXPECT_TRUE(client_->HasAction("ScheduledActionSendBeginMainFrame")); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); } @@ -1579,9 +1636,9 @@ TEST_F(SchedulerTest, MainFrameNotSkippedAfterCanDrawChanges) { EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); // Make us abort the upcoming draw. @@ -1607,7 +1664,7 @@ TEST_F(SchedulerTest, MainFrameNotSkippedAfterCanDrawChanges) { EXPECT_TRUE(client_->HasAction("WillBeginImplFrame")); EXPECT_TRUE(client_->HasAction("ScheduledActionSendBeginMainFrame")); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); } @@ -1624,9 +1681,9 @@ TEST_F(SchedulerTest, MainFrameNotSkippedWhenNoTimingHistory) { EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); // Commit after the deadline. @@ -1640,7 +1697,7 @@ TEST_F(SchedulerTest, MainFrameNotSkippedWhenNoTimingHistory) { // have history from at least one frame. client_->Reset(); scheduler_->SetNeedsBeginMainFrame(); - scheduler_->ClearHistoryOnNavigation(); + scheduler_->ClearHistory(); EXPECT_SCOPED(AdvanceFrame()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); } @@ -1660,11 +1717,11 @@ void SchedulerTest::ImplFrameSkippedAfterLateAck( "ScheduledActionSendBeginMainFrame"); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionCommit", "ScheduledActionActivateSyncTree", "ScheduledActionDrawIfPossible"); @@ -1689,10 +1746,10 @@ void SchedulerTest::ImplFrameSkippedAfterLateAck( if (receive_ack_before_deadline) { // It shouldn't matter if the swap ack comes back before the deadline... scheduler_->DidReceiveCompositorFrameAck(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); } else { // ... or after the deadline. - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); scheduler_->DidReceiveCompositorFrameAck(); } EXPECT_NO_ACTION(); @@ -1705,10 +1762,10 @@ void SchedulerTest::ImplFrameSkippedAfterLateAck( EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionCommit", "ScheduledActionActivateSyncTree", "ScheduledActionDrawIfPossible"); } @@ -1782,7 +1839,7 @@ TEST_F(SchedulerTest, client_->Reset(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); // Verify we skip every other frame if the swap ack consistently @@ -1815,7 +1872,7 @@ TEST_F(SchedulerTest, client_->Reset(); // Deadline should be immediate. EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunUntilTime(now_src_->NowTicks()); + task_runner_->RunUntilTime(task_runner_->NowTicks()); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); } @@ -1834,11 +1891,11 @@ void SchedulerTest::ImplFrameNotSkippedAfterLateAck() { "ScheduledActionSendBeginMainFrame"); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionCommit", "ScheduledActionActivateSyncTree", "ScheduledActionDrawIfPossible"); @@ -1858,10 +1915,10 @@ void SchedulerTest::ImplFrameNotSkippedAfterLateAck() { client_->Reset(); scheduler_->DidReceiveCompositorFrameAck(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); // Verify that we don't skip the actions of the BeginImplFrame EXPECT_ACTIONS("ScheduledActionSendBeginMainFrame", "ScheduledActionCommit", @@ -1930,9 +1987,9 @@ TEST_F(SchedulerTest, MainFrameThenImplFrameSkippedAfterLateCommitAndLateAck) { EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); EXPECT_SCOPED(AdvanceFrame()); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); @@ -1947,8 +2004,8 @@ TEST_F(SchedulerTest, MainFrameThenImplFrameSkippedAfterLateCommitAndLateAck) { EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); @@ -1964,7 +2021,7 @@ TEST_F(SchedulerTest, MainFrameThenImplFrameSkippedAfterLateCommitAndLateAck) { EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("WillBeginImplFrame"); // Note: BeginMainFrame and swap are skipped here because of @@ -1988,7 +2045,7 @@ TEST_F(SchedulerTest, MainFrameThenImplFrameSkippedAfterLateCommitAndLateAck) { SendNextBeginFrame(); EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); scheduler_->DidReceiveCompositorFrameAck(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionDrawIfPossible"); @@ -1999,7 +2056,7 @@ TEST_F(SchedulerTest, MainFrameThenImplFrameSkippedAfterLateCommitAndLateAck) { EXPECT_TRUE(scheduler_->NeedsBeginMainFrame()); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); SendNextBeginFrame(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); scheduler_->DidReceiveCompositorFrameAck(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); @@ -2013,10 +2070,10 @@ TEST_F(SchedulerTest, MainFrameThenImplFrameSkippedAfterLateCommitAndLateAck) { EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); SendNextBeginFrame(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); scheduler_->DidReceiveCompositorFrameAck(); EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); @@ -2048,9 +2105,9 @@ TEST_F( EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(scheduler_->CommitPending()); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); EXPECT_FALSE(scheduler_->CommitPending()); @@ -2064,11 +2121,11 @@ TEST_F( EXPECT_FALSE(scheduler_->CommitPending()); scheduler_->SetNeedsBeginMainFrame(); EXPECT_SCOPED(AdvanceFrame()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", "ScheduledActionCommit"); @@ -2080,7 +2137,7 @@ TEST_F( scheduler_->SetNeedsBeginMainFrame(); EXPECT_SCOPED(AdvanceFrame()); EXPECT_FALSE(scheduler_->CommitPending()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("WillBeginImplFrame"); } @@ -2109,10 +2166,10 @@ TEST_F( EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(scheduler_->CommitPending()); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); scheduler_->DidReceiveCompositorFrameAck(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_FALSE(scheduler_->CommitPending()); EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", @@ -2127,10 +2184,10 @@ TEST_F( EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(scheduler_->CommitPending()); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); scheduler_->DidReceiveCompositorFrameAck(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", "ScheduledActionDrawIfPossible"); @@ -2168,7 +2225,7 @@ void SchedulerTest::BeginFramesNotFromClient(BeginFrameSourceType bfs_type) { client_->Reset(); // NotifyReadyToCommit should trigger the commit. - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); client_->Reset(); @@ -2186,7 +2243,7 @@ void SchedulerTest::BeginFramesNotFromClient(BeginFrameSourceType bfs_type) { // Make sure SetNeedsBeginFrame isn't called on the client // when the BeginFrame is no longer needed. - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_NO_ACTION(); client_->Reset(); } @@ -2222,7 +2279,7 @@ void SchedulerTest::BeginFramesNotFromClient_IsDrawThrottled( client_->Reset(); // NotifyReadyToCommit should trigger the pending commit. - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); client_->Reset(); @@ -2234,7 +2291,7 @@ void SchedulerTest::BeginFramesNotFromClient_IsDrawThrottled( // Swapping will put us into a swap throttled state. // Run posted deadline. - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2251,9 +2308,9 @@ void SchedulerTest::BeginFramesNotFromClient_IsDrawThrottled( base::TimeTicks before_deadline, after_deadline; // The deadline is set to the regular deadline. - before_deadline = now_src()->NowTicks(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); - after_deadline = now_src()->NowTicks(); + before_deadline = task_runner_->NowTicks(); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); + after_deadline = task_runner_->NowTicks(); // We can't do an equality comparison here because the scheduler uses a fudge // factor that's an internal implementation detail. EXPECT_GT(after_deadline, before_deadline); @@ -2274,9 +2331,9 @@ void SchedulerTest::BeginFramesNotFromClient_IsDrawThrottled( client_->Reset(); // The deadline is set to the regular deadline. - before_deadline = now_src()->NowTicks(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); - after_deadline = now_src()->NowTicks(); + before_deadline = task_runner_->NowTicks(); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); + after_deadline = task_runner_->NowTicks(); // We can't do an equality comparison here because the scheduler uses a fudge // factor that's an internal implementation detail. EXPECT_GT(after_deadline, before_deadline); @@ -2328,12 +2385,12 @@ TEST_F(SchedulerTest, DidLoseLayerTreeFrameSinkAfterBeginFrameStarted) { EXPECT_NO_ACTION(); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit", "ScheduledActionActivateSyncTree"); client_->Reset(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation", "RemoveObserver(this)"); } @@ -2359,7 +2416,7 @@ TEST_F(SchedulerTest, client_->Reset(); // Run posted deadline. EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); // OnBeginImplFrameDeadline didn't schedule LayerTreeFrameSink creation // because // main frame is not yet completed. @@ -2368,13 +2425,13 @@ TEST_F(SchedulerTest, // BeginImplFrame is not started. client_->Reset(); - task_runner().RunUntilTime(now_src()->NowTicks() + + task_runner_->RunUntilTime(task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(10)); EXPECT_NO_ACTION(); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit", "ScheduledActionActivateSyncTree", "ScheduledActionBeginLayerTreeFrameSinkCreation"); @@ -2393,7 +2450,7 @@ TEST_F(SchedulerTest, DidLoseLayerTreeFrameSinkAfterReadyToCommit) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); @@ -2404,7 +2461,7 @@ TEST_F(SchedulerTest, DidLoseLayerTreeFrameSinkAfterReadyToCommit) { // RemoveObserver(this) is not called until the end of the frame. client_->Reset(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation", "RemoveObserver(this)"); } @@ -2427,7 +2484,7 @@ TEST_F(SchedulerTest, DidLoseLayerTreeFrameSinkAfterSetNeedsPrepareTiles) { EXPECT_NO_ACTION(); client_->Reset(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionPrepareTiles", "ScheduledActionBeginLayerTreeFrameSinkCreation", "RemoveObserver(this)"); @@ -2449,7 +2506,7 @@ TEST_F(SchedulerTest, DidLoseLayerTreeFrameSinkWithDelayBasedBeginFrameSource) { // NotifyReadyToCommit should trigger the commit. client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); EXPECT_TRUE(scheduler_->begin_frames_expected()); @@ -2467,7 +2524,7 @@ TEST_F(SchedulerTest, DidLoseLayerTreeFrameSinkWithDelayBasedBeginFrameSource) { EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation"); EXPECT_FALSE(scheduler_->begin_frames_expected()); } @@ -2485,7 +2542,7 @@ TEST_F(SchedulerTest, DidLoseLayerTreeFrameSinkWhenIdle) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); @@ -2494,7 +2551,7 @@ TEST_F(SchedulerTest, DidLoseLayerTreeFrameSinkWhenIdle) { EXPECT_ACTIONS("ScheduledActionActivateSyncTree"); client_->Reset(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); // Idle time between BeginFrames. @@ -2517,14 +2574,14 @@ TEST_F(SchedulerTest, ScheduledActionActivateAfterBecomingInvisible) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); scheduler_->SetVisible(false); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. // Sync tree should be forced to activate. EXPECT_ACTIONS("ScheduledActionActivateSyncTree", "RemoveObserver(this)"); @@ -2543,14 +2600,14 @@ TEST_F(SchedulerTest, ScheduledActionActivateAfterBeginFrameSourcePaused) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); fake_external_begin_frame_source_->SetPaused(true); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. // Sync tree should be forced to activate. // Pausing the begin frame source aborts the draw. Then @@ -2573,7 +2630,7 @@ TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottled) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); scheduler_->SetNeedsRedraw(); @@ -2582,13 +2639,13 @@ TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottled) { client_->Reset(); // Unthrottled frame source will immediately begin a new frame. - task_runner().RunPendingTasks(); // Run posted BeginFrame. + task_runner_->RunPendingTasks(); // Run posted BeginFrame. EXPECT_ACTIONS("WillBeginImplFrame"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); // If we don't swap on the deadline, we wait for the next BeginFrame. - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2615,14 +2672,14 @@ TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottledBeforeDeadline) { EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible", // Unthrottled frame source will immediately begin a new frame. "WillBeginImplFrame"); scheduler_->SetNeedsRedraw(); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2637,12 +2694,12 @@ TEST_F(SchedulerTest, SwitchFrameSourceToThrottled) { EXPECT_NO_ACTION(); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted BeginFrame. + task_runner_->RunPendingTasks(); // Run posted BeginFrame. EXPECT_ACTIONS("WillBeginImplFrame"); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2653,7 +2710,7 @@ TEST_F(SchedulerTest, SwitchFrameSourceToThrottled) { // SetNeedsRedraw should begin the frame on the next BeginImplFrame. scheduler_->SetNeedsRedraw(); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_NO_ACTION(); client_->Reset(); @@ -2662,7 +2719,7 @@ TEST_F(SchedulerTest, SwitchFrameSourceToThrottled) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); } @@ -2683,7 +2740,7 @@ TEST_F(SchedulerTest, SwitchFrameSourceToNullInsideDeadline) { client_->Reset(); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(scheduler_->begin_frames_expected()); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); @@ -2711,7 +2768,7 @@ TEST_F(SchedulerTest, SwitchFrameSourceToNullInsideDeadline) { EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); } @@ -2727,7 +2784,7 @@ TEST_F(SchedulerTest, SwitchFrameSourceWhenNotObserving) { // Begin new frame. EXPECT_SCOPED(AdvanceFrame()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); client_->Reset(); @@ -2743,7 +2800,7 @@ TEST_F(SchedulerTest, SwitchFrameSourceWhenNotObserving) { client_->Reset(); scheduler_->DidLoseLayerTreeFrameSink(); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation", "RemoveObserver(this)"); @@ -2777,7 +2834,7 @@ TEST_F(SchedulerTest, ScheduledActionBeginMainFrameNotExpectedUntil) { EXPECT_SCOPED(AdvanceFrame()); scheduler_->SetMainThreadWantsBeginMainFrameNotExpected(true); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionBeginMainFrameNotExpectedUntil", "ScheduledActionDrawIfPossible"); @@ -2794,10 +2851,10 @@ TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon_Requested) { // Trigger a frame draw. EXPECT_SCOPED(AdvanceFrame()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", "ScheduledActionCommit", "ScheduledActionActivateSyncTree", "ScheduledActionDrawIfPossible"); @@ -2813,7 +2870,7 @@ TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon_Requested) { // The BeginImplFrame deadline should SetNeedsBeginFrame(false) and send a // SendBeginMainFrameNotExpectedSoon. - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("RemoveObserver(this)", "SendBeginMainFrameNotExpectedSoon"); client_->Reset(); } @@ -2830,10 +2887,10 @@ TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon_Unrequested) { // Trigger a frame draw. EXPECT_SCOPED(AdvanceFrame()); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", "ScheduledActionCommit", "ScheduledActionActivateSyncTree", "ScheduledActionDrawIfPossible"); @@ -2847,7 +2904,7 @@ TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon_Unrequested) { // The BeginImplFrame deadline should SetNeedsBeginFrame(false), but doesn't // send a SendBeginMainFrameNotExpectedSoon as it's not been requested by the // main thread. - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("RemoveObserver(this)"); client_->Reset(); @@ -2879,7 +2936,9 @@ TEST_F(SchedulerTest, SynchronousCompositorAnimation) { // Android onDraw. This doesn't consume the single begin frame request. scheduler_->SetNeedsRedraw(); bool resourceless_software_draw = false; - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + bool skip_draw = false; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2897,7 +2956,8 @@ TEST_F(SchedulerTest, SynchronousCompositorAnimation) { // Android onDraw. scheduler_->SetNeedsRedraw(); - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2915,7 +2975,9 @@ TEST_F(SchedulerTest, SynchronousCompositorOnDrawDuringIdle) { scheduler_->SetNeedsRedraw(); bool resourceless_software_draw = false; - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + bool skip_draw = false; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); EXPECT_ACTIONS("AddObserver(this)", "ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -2975,7 +3037,7 @@ TEST_F(SchedulerTest, AbortEarlyIfNoDamage) { EXPECT_ACTIONS("AddObserver(this)"); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. // Should not try to schedule a draw. (ScheduledActionDrawIfPossible should // not appear.) // When the frame is aborted, the scheduler does not ask for a proactive begin @@ -2986,10 +3048,56 @@ TEST_F(SchedulerTest, AbortEarlyIfNoDamage) { scheduler_->SetNeedsRedraw(); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_EQ(0, client_->num_draws()); } +TEST_F(SchedulerTest, SkipDraw) { + scheduler_settings_.using_synchronous_renderer_compositor = true; + SetUpScheduler(EXTERNAL_BFS); + + scheduler_->SetNeedsOneBeginImplFrame(); + EXPECT_ACTIONS("AddObserver(this)"); + client_->Reset(); + + client_->SetWillBeginImplFrameCausesRedraw(true); + + // Next vsync. + AdvanceFrame(); + EXPECT_ACTIONS("WillBeginImplFrame", + "ScheduledActionInvalidateLayerTreeFrameSink"); + EXPECT_TRUE(client_->invalidate_needs_redraw()); + client_->Reset(); + + // Android onDraw. This doesn't consume the single begin frame request. + scheduler_->SetNeedsPrepareTiles(); + bool resourceless_software_draw = false; + bool skip_draw = false; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); + EXPECT_ACTIONS("ScheduledActionDrawIfPossible", + "ScheduledActionPrepareTiles"); + client_->Reset(); + + // Next vsync. + scheduler_->SetNeedsPrepareTiles(); + AdvanceFrame(); + EXPECT_ACTIONS("WillBeginImplFrame", + "ScheduledActionInvalidateLayerTreeFrameSink"); + EXPECT_FALSE(client_->invalidate_needs_redraw()); + client_->Reset(); + + // Android onDraw. + scheduler_->SetNeedsRedraw(); + scheduler_->SetNeedsPrepareTiles(); + client_->SetInvalidateNeedsRedraw(false); + skip_draw = true; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); + EXPECT_ACTIONS("ScheduledActionPrepareTiles"); + client_->Reset(); +} + TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { scheduler_settings_.using_synchronous_renderer_compositor = true; SetUpScheduler(EXTERNAL_BFS); @@ -3004,12 +3112,11 @@ TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { EXPECT_FALSE(client_->IsInsideBeginImplFrame()); bool has_damage = false; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); EXPECT_NO_ACTION(); // Next vsync. @@ -3018,9 +3125,8 @@ TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { EXPECT_FALSE(client_->IsInsideBeginImplFrame()); has_damage = false; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); scheduler_->NotifyReadyToCommit(); @@ -3042,15 +3148,16 @@ TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { // filtering this ack (in CompositorExternalBeginFrameSource) and instead // forwarding the one attached to the later submitted CompositorFrame. has_damage = false; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); // Android onDraw. scheduler_->SetNeedsRedraw(); bool resourceless_software_draw = false; - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + bool skip_draw = false; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -3061,9 +3168,8 @@ TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { EXPECT_FALSE(client_->IsInsideBeginImplFrame()); has_damage = false; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); } @@ -3098,7 +3204,9 @@ TEST_F(SchedulerTest, SynchronousCompositorPrepareTilesOnDraw) { // Android onDraw. scheduler_->SetNeedsRedraw(); bool resourceless_software_draw = false; - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + bool skip_draw = false; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); EXPECT_ACTIONS("ScheduledActionDrawIfPossible", "ScheduledActionPrepareTiles"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); @@ -3107,7 +3215,8 @@ TEST_F(SchedulerTest, SynchronousCompositorPrepareTilesOnDraw) { // Android onDraw. scheduler_->SetNeedsRedraw(); - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); EXPECT_ACTIONS("ScheduledActionDrawIfPossible", "ScheduledActionPrepareTiles"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); @@ -3160,7 +3269,9 @@ TEST_F(SchedulerTest, SynchronousCompositorSendBeginMainFrameWhileIdle) { // Android onDraw. scheduler_->SetNeedsRedraw(); bool resourceless_software_draw = false; - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + bool skip_draw = false; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_FALSE(scheduler_->PrepareTilesPending()); @@ -3171,7 +3282,7 @@ TEST_F(SchedulerTest, SynchronousCompositorSendBeginMainFrameWhileIdle) { EXPECT_ACTIONS("ScheduledActionSendBeginMainFrame"); client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); client_->Reset(); @@ -3188,7 +3299,8 @@ TEST_F(SchedulerTest, SynchronousCompositorSendBeginMainFrameWhileIdle) { // Android onDraw. scheduler_->SetNeedsRedraw(); - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_FALSE(scheduler_->PrepareTilesPending()); @@ -3208,7 +3320,9 @@ TEST_F(SchedulerTest, SynchronousCompositorResourcelessOnDrawWhenInvisible) { scheduler_->SetNeedsRedraw(); bool resourceless_software_draw = true; - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + bool skip_draw = false; + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); // SynchronousCompositor has to draw regardless of visibility. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); @@ -3226,13 +3340,13 @@ TEST_F(SchedulerTest, AuthoritativeVSyncInterval) { EXPECT_EQ(initial_interval, scheduler_->BeginImplFrameInterval()); - scheduler_->NotifyBeginMainFrameStarted(now_src_->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); // Test changing the interval on the frame source external to the scheduler. - synthetic_frame_source_->OnUpdateVSyncParameters(now_src_->NowTicks(), + synthetic_frame_source_->OnUpdateVSyncParameters(task_runner_->NowTicks(), authoritative_interval); EXPECT_SCOPED(AdvanceFrame()); @@ -3302,7 +3416,7 @@ TEST_F(SchedulerTest, NoLayerTreeFrameSinkCreationWhileCommitPending) { // Abort the commit. client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->BeginMainFrameAborted( CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST); EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation"); @@ -3337,7 +3451,7 @@ TEST_F(SchedulerTest, ImplSideInvalidationsMergedWithCommit) { // actions since the impl-side invalidation request will be merged with the // commit. client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); EXPECT_FALSE(scheduler_->needs_impl_side_invalidation()); @@ -3360,7 +3474,7 @@ TEST_F(SchedulerTest, AbortedCommitsTriggerImplSideInvalidations) { // should not be blocked on the main frame. client_->Reset(); scheduler_->SetNeedsBeginMainFrame(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES); EXPECT_ACTIONS("ScheduledActionPerformImplSideInvalidation"); } @@ -3467,16 +3581,15 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) { EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); // Successful draw caused damage. bool has_damage = true; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); // Request another redraw, but fail it. Verify that a new ack is sent. @@ -3490,7 +3603,7 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) { client_->Reset(); client_->SetDrawWillHappen(false); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible", // Failed draw triggers SendBeginMainFrame. "ScheduledActionSendBeginMainFrame"); @@ -3499,9 +3612,8 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) { // Failed draw: no damage. has_damage = false; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); } @@ -3523,16 +3635,15 @@ TEST_F(SchedulerTest, BeginFrameAckForSkippedImplFrame) { EXPECT_TRUE(scheduler_->begin_frames_expected()); client_->Reset(); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner_->RunPendingTasks(); // Run posted deadline. EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); EXPECT_TRUE(scheduler_->begin_frames_expected()); // Successful draw caused damage. bool has_damage = true; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); // Request another redraw that will be skipped because the swap ack is still @@ -3547,9 +3658,8 @@ TEST_F(SchedulerTest, BeginFrameAckForSkippedImplFrame) { // Skipped draw: no damage. has_damage = false; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); } @@ -3578,9 +3688,8 @@ TEST_F(SchedulerTest, BeginFrameAckForBeginFrameBeforeLastDeadline) { // Latest ack should be for the dropped BeginFrame. bool has_damage = false; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); } @@ -3610,18 +3719,16 @@ TEST_F(SchedulerTest, BeginFrameAckForDroppedBeginFrame) { // Latest ack should be for the dropped BeginFrame. bool has_damage = false; - EXPECT_EQ(viz::BeginFrameAck(second_args.source_id, - second_args.sequence_number, has_damage), + EXPECT_EQ(viz::BeginFrameAck(second_args, has_damage), client_->last_begin_frame_ack()); client_->Reset(); - task_runner().RunPendingTasks(); // Run deadline of prior BeginFrame. + task_runner_->RunPendingTasks(); // Run deadline of prior BeginFrame. EXPECT_ACTIONS("RemoveObserver(this)"); // We'd expect an out-of-order ack for the prior BeginFrame. has_damage = false; - EXPECT_EQ(viz::BeginFrameAck(first_args.source_id, first_args.sequence_number, - has_damage), + EXPECT_EQ(viz::BeginFrameAck(first_args, has_damage), client_->last_begin_frame_ack()); client_->Reset(); } @@ -3633,13 +3740,13 @@ TEST_F(SchedulerTest, BeginFrameAckForLateMissedBeginFrame) { client_->Reset(); // Send a missed BeginFrame with a passed deadline. - now_src_->Advance(viz::BeginFrameArgs::DefaultInterval()); + task_runner_->AdvanceMockTickClock(viz::BeginFrameArgs::DefaultInterval()); viz::BeginFrameArgs args = fake_external_begin_frame_source_->CreateBeginFrameArgs( - BEGINFRAME_FROM_HERE, now_src()); + BEGINFRAME_FROM_HERE, task_runner_->GetMockTickClock()); args.type = viz::BeginFrameArgs::MISSED; - now_src_->Advance(viz::BeginFrameArgs::DefaultInterval()); - EXPECT_GT(now_src_->NowTicks(), args.deadline); + task_runner_->AdvanceMockTickClock(viz::BeginFrameArgs::DefaultInterval()); + EXPECT_GT(task_runner_->NowTicks(), args.deadline); fake_external_begin_frame_source_->TestOnBeginFrame(args); EXPECT_NO_ACTION(); @@ -3648,9 +3755,8 @@ TEST_F(SchedulerTest, BeginFrameAckForLateMissedBeginFrame) { // Latest ack should be for the missed BeginFrame that was too late: no // damage. bool has_damage = false; - EXPECT_EQ( - viz::BeginFrameAck(args.source_id, args.sequence_number, has_damage), - client_->last_begin_frame_ack()); + EXPECT_EQ(viz::BeginFrameAck(args, has_damage), + client_->last_begin_frame_ack()); client_->Reset(); } @@ -3669,14 +3775,15 @@ TEST_F(SchedulerTest, CriticalBeginMainFrameToActivateIsFast) { scheduler_->SetNeedsRedraw(); // An interval of 2ms makes sure that the main frame is considered slow. base::TimeDelta interval = base::TimeDelta::FromMilliseconds(2); - now_src_->Advance(interval); + task_runner_->AdvanceMockTickClock(interval); viz::BeginFrameArgs args = viz::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, 0u, 1u, now_src_->NowTicks(), - now_src_->NowTicks() + interval, interval, viz::BeginFrameArgs::NORMAL); + BEGINFRAME_FROM_HERE, 0u, 1u, task_runner_->NowTicks(), + task_runner_->NowTicks() + interval, interval, + viz::BeginFrameArgs::NORMAL); fake_external_begin_frame_source_->TestOnBeginFrame(args); EXPECT_TRUE(scheduler_->ImplLatencyTakesPriority()); - task_runner().RunPendingTasks(); // Run posted deadline to finish the frame. + task_runner_->RunPendingTasks(); // Run posted deadline to finish the frame. ASSERT_FALSE(client_->IsInsideBeginImplFrame()); // Set an interval of 10ms. The bmf_to_activate_interval should be 1*4 = 4ms, @@ -3685,14 +3792,15 @@ TEST_F(SchedulerTest, CriticalBeginMainFrameToActivateIsFast) { // the main frame to be activated is 8ms, so it should be considered fast. scheduler_->SetNeedsRedraw(); interval = base::TimeDelta::FromMilliseconds(10); - now_src_->Advance(interval); - args = viz::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, 0u, 2u, now_src_->NowTicks(), - now_src_->NowTicks() + interval, interval, viz::BeginFrameArgs::NORMAL); + task_runner_->AdvanceMockTickClock(interval); + args = viz::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, 0u, 2u, + task_runner_->NowTicks(), + task_runner_->NowTicks() + interval, + interval, viz::BeginFrameArgs::NORMAL); fake_external_begin_frame_source_->TestOnBeginFrame(args); EXPECT_FALSE(scheduler_->ImplLatencyTakesPriority()); - task_runner().RunPendingTasks(); // Run posted deadline to finish the frame. + task_runner_->RunPendingTasks(); // Run posted deadline to finish the frame. ASSERT_FALSE(client_->IsInsideBeginImplFrame()); // Increase the draw duration to decrease the time available for the main @@ -3700,10 +3808,11 @@ TEST_F(SchedulerTest, CriticalBeginMainFrameToActivateIsFast) { scheduler_->SetNeedsRedraw(); fake_compositor_timing_history_->SetDrawDurationEstimate( base::TimeDelta::FromMilliseconds(7)); - now_src_->Advance(interval); - args = viz::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, 0u, 3u, now_src_->NowTicks(), - now_src_->NowTicks() + interval, interval, viz::BeginFrameArgs::NORMAL); + task_runner_->AdvanceMockTickClock(interval); + args = viz::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, 0u, 3u, + task_runner_->NowTicks(), + task_runner_->NowTicks() + interval, + interval, viz::BeginFrameArgs::NORMAL); fake_external_begin_frame_source_->TestOnBeginFrame(args); EXPECT_TRUE(scheduler_->ImplLatencyTakesPriority()); } @@ -3726,10 +3835,11 @@ TEST_F(SchedulerTest, WaitForAllPipelineStagesSkipsMissedBeginFrames) { client_->Reset(); base::TimeDelta interval = base::TimeDelta::FromMilliseconds(16); - now_src_->Advance(interval); + task_runner_->AdvanceMockTickClock(interval); viz::BeginFrameArgs args = viz::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, 0u, 1u, now_src_->NowTicks(), - now_src_->NowTicks() + interval, interval, viz::BeginFrameArgs::MISSED); + BEGINFRAME_FROM_HERE, 0u, 1u, task_runner_->NowTicks(), + task_runner_->NowTicks() + interval, interval, + viz::BeginFrameArgs::MISSED); fake_external_begin_frame_source_->TestOnBeginFrame(args); EXPECT_FALSE(client_->IsInsideBeginImplFrame()); } @@ -3755,10 +3865,10 @@ TEST_F( // Uses MISSED BeginFrames even after the deadline has passed. base::TimeDelta interval = base::TimeDelta::FromMilliseconds(16); - now_src_->Advance(interval); - base::TimeTicks timestamp = now_src_->NowTicks(); + task_runner_->AdvanceMockTickClock(interval); + base::TimeTicks timestamp = task_runner_->NowTicks(); // Deadline should have passed after this. - now_src_->Advance(interval * 2); + task_runner_->AdvanceMockTickClock(interval * 2); viz::BeginFrameArgs args = viz::BeginFrameArgs::Create( BEGINFRAME_FROM_HERE, 0u, 1u, timestamp, timestamp + interval, interval, viz::BeginFrameArgs::MISSED); @@ -3792,8 +3902,8 @@ TEST_F(SchedulerTest, WaitForAllPipelineStagesAlwaysObservesBeginFrames) { // Scheduler begins a frame even if otherwise idle. base::TimeDelta interval = base::TimeDelta::FromMilliseconds(16); - now_src_->Advance(interval); - base::TimeTicks timestamp = now_src_->NowTicks(); + task_runner_->AdvanceMockTickClock(interval); + base::TimeTicks timestamp = task_runner_->NowTicks(); viz::BeginFrameArgs args = viz::BeginFrameArgs::Create( BEGINFRAME_FROM_HERE, 0u, 1u, timestamp, timestamp + interval, interval, viz::BeginFrameArgs::NORMAL); @@ -3803,7 +3913,7 @@ TEST_F(SchedulerTest, WaitForAllPipelineStagesAlwaysObservesBeginFrames) { client_->Reset(); // BeginFrame deadline is blocked because commits are deferred. - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_ACTIONS(); EXPECT_TRUE(client_->IsInsideBeginImplFrame()); client_->Reset(); @@ -3886,7 +3996,7 @@ TEST_F(SchedulerTest, SlowMainThreadButEstimatedFastTriggersInvalidations) { // Draw deadline. client_->Reset(); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_ACTIONS(); // Next frame. The invalidation should not be throttled. @@ -3910,13 +4020,13 @@ TEST_F(SchedulerTest, // Commit before deadline but not ready to activate. client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(now_src_->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("ScheduledActionCommit"); // Draw deadline. client_->Reset(); - task_runner().RunPendingTasks(); + task_runner_->RunPendingTasks(); EXPECT_ACTIONS(); // Next frame. The invalidation should still be throttled. @@ -3949,7 +4059,7 @@ TEST_F(SchedulerTest, DontSkipMainFrameAfterClearingHistory) { client_->Reset(); scheduler_->SetNeedsBeginMainFrame(); EXPECT_SCOPED(AdvanceFrame()); - task_runner().RunTasksWhile(client_->InsideBeginImplFrame(true)); + task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); @@ -3959,14 +4069,14 @@ TEST_F(SchedulerTest, DontSkipMainFrameAfterClearingHistory) { client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); scheduler_->SetNeedsBeginMainFrame(); - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); + scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); scheduler_->NotifyReadyToCommit(); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionCommit"); // But during the commit, the history is cleared. So the main frame should not // be skipped. client_->Reset(); - scheduler_->ClearHistoryOnNavigation(); + scheduler_->ClearHistory(); EXPECT_ACTIONS("ScheduledActionSendBeginMainFrame"); } diff --git a/chromium/cc/tiles/frame_viewer_instrumentation.cc b/chromium/cc/tiles/frame_viewer_instrumentation.cc index a40ad5ba365..09ac5b6857d 100644 --- a/chromium/cc/tiles/frame_viewer_instrumentation.cc +++ b/chromium/cc/tiles/frame_viewer_instrumentation.cc @@ -10,9 +10,8 @@ namespace cc { namespace frame_viewer_instrumentation { const char kCategoryLayerTree[] = - TRACE_DISABLED_BY_DEFAULT("cc.debug") "," - TRACE_DISABLED_BY_DEFAULT("cc.debug.quads") "," - TRACE_DISABLED_BY_DEFAULT("devtools.timeline.layers"); + TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT( + "viz.quads") "," TRACE_DISABLED_BY_DEFAULT("devtools.timeline.layers"); namespace { diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc index efac77afbf7..747f1a367a9 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache.cc @@ -111,17 +111,46 @@ int CalculateUploadScaleMipLevel(const DrawImage& draw_image) { // Calculates the scale factor which can be used to scale an image to a given // mip level. SkSize CalculateScaleFactorForMipLevel(const DrawImage& draw_image, - int mip_level) { + int upload_scale_mip_level) { gfx::Size base_size(draw_image.paint_image().width(), draw_image.paint_image().height()); - return MipMapUtil::GetScaleAdjustmentForLevel(base_size, mip_level); + return MipMapUtil::GetScaleAdjustmentForLevel(base_size, + upload_scale_mip_level); } // Calculates the size of a given mip level. -gfx::Size CalculateSizeForMipLevel(const DrawImage& draw_image, int mip_level) { +gfx::Size CalculateSizeForMipLevel(const DrawImage& draw_image, + int upload_scale_mip_level) { gfx::Size base_size(draw_image.paint_image().width(), draw_image.paint_image().height()); - return MipMapUtil::GetSizeForLevel(base_size, mip_level); + return MipMapUtil::GetSizeForLevel(base_size, upload_scale_mip_level); +} + +// Determines whether a draw image requires mips. +bool ShouldGenerateMips(const DrawImage& draw_image, + int upload_scale_mip_level) { + // If filter quality is less than medium, don't generate mips. + if (draw_image.filter_quality() < kMedium_SkFilterQuality) + return false; + + gfx::Size base_size(draw_image.paint_image().width(), + draw_image.paint_image().height()); + // Take the abs of the scale, as mipmap functions don't handle (and aren't + // impacted by) negative image dimensions. + gfx::SizeF scaled_size = gfx::ScaleSize( + gfx::SizeF(base_size), std::abs(draw_image.scale().width()), + std::abs(draw_image.scale().height())); + + // If our target size is smaller than our scaled size in both dimension, we + // need to generate mips. + gfx::SizeF target_size = + gfx::SizeF(CalculateSizeForMipLevel(draw_image, upload_scale_mip_level)); + if (scaled_size.width() < target_size.width() && + scaled_size.height() < target_size.height()) { + return true; + } + + return false; } // Draws and scales the provided |draw_image| into the |target_pixmap|. If the @@ -138,19 +167,32 @@ bool DrawAndScaleImage(const DrawImage& draw_image, SkPixmap* target_pixmap) { sk_sp<SkColorSpace> color_space = target_pixmap->info().refColorSpace(); const PaintImage& paint_image = draw_image.paint_image(); + const bool is_original_decode = + SkISize::Make(paint_image.width(), paint_image.height()) == + pixmap.bounds().size(); + const bool is_nearest_neighbor = + draw_image.filter_quality() == kNone_SkFilterQuality; + SkISize supported_size = paint_image.GetSupportedDecodeSize(pixmap.bounds().size()); - - if (supported_size == pixmap.bounds().size()) { + // We can directly decode into target pixmap if we are doing an original + // decode or we are decoding to scale without nearest neighbor filtering. + const bool can_directly_decode = is_original_decode || !is_nearest_neighbor; + if (supported_size == pixmap.bounds().size() && can_directly_decode) { SkImageInfo info = pixmap.info(); return paint_image.Decode(pixmap.writable_addr(), &info, color_space, draw_image.frame_index()); } // If we can't decode/scale directly, we will handle this in up to 3 steps. - // Step 1: Decode at the nearest (larger) directly supported size. - SkImageInfo decode_info = SkImageInfo::MakeN32Premul(supported_size.width(), - supported_size.height()); + // Step 1: Decode at the nearest (larger) directly supported size or the + // original size if nearest neighbor quality is requested. + SkISize decode_size = + is_nearest_neighbor + ? SkISize::Make(paint_image.width(), paint_image.height()) + : supported_size; + SkImageInfo decode_info = + SkImageInfo::MakeN32Premul(decode_size.width(), decode_size.height()); SkBitmap decode_bitmap; if (!decode_bitmap.tryAllocPixels(decode_info)) return false; @@ -185,21 +227,6 @@ bool DrawAndScaleImage(const DrawImage& draw_image, SkPixmap* target_pixmap) { return scaled_pixmap.readPixels(pixmap); } -// Returns the GL texture ID backing the given SkImage. -GrGLuint GlIdFromSkImage(SkImage* image) { - DCHECK(image->isTextureBacked()); - GrBackendTexture backend_texture = - image->getBackendTexture(true /* flushPendingGrContextIO */); - if (!backend_texture.isValid()) - return 0; - - GrGLTextureInfo info; - if (!backend_texture.getGLTextureInfo(&info)) - return 0; - - return info.fID; -} - // Takes ownership of the backing texture of an SkImage. This allows us to // delete this texture under Skia (via discardable). sk_sp<SkImage> TakeOwnershipOfSkImageBacking(GrContext* context, @@ -238,11 +265,49 @@ void DeleteSkImageAndPreventCaching(viz::RasterContextProvider* context, if (image_owned) { // Delete |original_image_owned| as Skia will not clean it up. We are // holding the context lock here, so we can delete immediately. - uint32_t texture_id = GlIdFromSkImage(image_owned.get()); + uint32_t texture_id = + GpuImageDecodeCache::GlIdFromSkImage(image_owned.get()); context->ContextGL()->DeleteTextures(1, &texture_id); } } +// TODO(ericrk): Replace calls to this with calls to SkImage::makeTextureImage, +// once that function handles colorspaces. https://crbug.com/834837 +sk_sp<SkImage> MakeTextureImage(viz::RasterContextProvider* context, + sk_sp<SkImage> source_image, + sk_sp<SkColorSpace> target_color_space, + GrMipMapped mip_mapped) { + // Step 1: Upload image and generate mips if necessary. If we will be applying + // a color-space conversion, don't generate mips yet, instead do it after + // conversion, in step 3. + bool add_mips_after_color_conversion = + (target_color_space && mip_mapped == GrMipMapped::kYes); + sk_sp<SkImage> uploaded_image = source_image->makeTextureImage( + context->GrContext(), nullptr, + add_mips_after_color_conversion ? GrMipMapped::kNo : mip_mapped); + + // Step 2: Apply a color-space conversion if necessary. + if (uploaded_image && target_color_space) { + sk_sp<SkImage> pre_converted_image = uploaded_image; + uploaded_image = uploaded_image->makeColorSpace(target_color_space); + + if (uploaded_image != pre_converted_image) + DeleteSkImageAndPreventCaching(context, std::move(pre_converted_image)); + } + + // Step 3: If we had a colorspace conversion, we couldn't mipmap in step 1, so + // add mips here. + if (uploaded_image && add_mips_after_color_conversion) { + sk_sp<SkImage> pre_mipped_image = uploaded_image; + uploaded_image = uploaded_image->makeTextureImage( + context->GrContext(), nullptr, GrMipMapped::kYes); + DCHECK_NE(pre_mipped_image, uploaded_image); + DeleteSkImageAndPreventCaching(context, std::move(pre_mipped_image)); + } + + return uploaded_image; +} + } // namespace // static @@ -255,13 +320,14 @@ GpuImageDecodeCache::InUseCacheKey::FromDrawImage(const DrawImage& draw_image) { // the |in_use_cache_|. GpuImageDecodeCache::InUseCacheKey::InUseCacheKey(const DrawImage& draw_image) : frame_key(draw_image.frame_key()), - mip_level(CalculateUploadScaleMipLevel(draw_image)), + upload_scale_mip_level(CalculateUploadScaleMipLevel(draw_image)), filter_quality(CalculateDesiredFilterQuality(draw_image)), target_color_space(draw_image.target_color_space()) {} bool GpuImageDecodeCache::InUseCacheKey::operator==( const InUseCacheKey& other) const { - return frame_key == other.frame_key && mip_level == other.mip_level && + return frame_key == other.frame_key && + upload_scale_mip_level == other.upload_scale_mip_level && filter_quality == other.filter_quality && target_color_space == other.target_color_space; } @@ -270,9 +336,9 @@ size_t GpuImageDecodeCache::InUseCacheKeyHash::operator()( const InUseCacheKey& cache_key) const { return base::HashInts( cache_key.target_color_space.GetHash(), - base::HashInts( - cache_key.frame_key.hash(), - base::HashInts(cache_key.mip_level, cache_key.filter_quality))); + base::HashInts(cache_key.frame_key.hash(), + base::HashInts(cache_key.upload_scale_mip_level, + cache_key.filter_quality))); } GpuImageDecodeCache::InUseCacheEntry::InUseCacheEntry( @@ -530,17 +596,21 @@ void GpuImageDecodeCache::UploadedImageData::ReportUsageStats() const { } GpuImageDecodeCache::ImageData::ImageData( + PaintImage::Id paint_image_id, DecodedDataMode mode, size_t size, const gfx::ColorSpace& target_color_space, SkFilterQuality quality, - int mip_level, + int upload_scale_mip_level, + bool needs_mips, bool is_bitmap_backed) - : mode(mode), + : paint_image_id(paint_image_id), + mode(mode), size(size), target_color_space(target_color_space), quality(quality), - mip_level(mip_level), + upload_scale_mip_level(upload_scale_mip_level), + needs_mips(needs_mips), is_bitmap_backed(is_bitmap_backed), decode(is_bitmap_backed) {} @@ -563,7 +633,7 @@ bool GpuImageDecodeCache::ImageData::IsGpuOrTransferCache() const { bool GpuImageDecodeCache::ImageData::HasUploadedData() const { switch (mode) { case DecodedDataMode::kGpu: - return upload.image(); + return !!upload.image(); case DecodedDataMode::kTransferCache: return !!upload.transfer_cache_id(); case DecodedDataMode::kCpu: @@ -578,6 +648,21 @@ void GpuImageDecodeCache::ImageData::ValidateBudgeted() const { DCHECK_GT(upload.ref_count, 0u); } +// static +GrGLuint GpuImageDecodeCache::GlIdFromSkImage(const SkImage* image) { + DCHECK(image->isTextureBacked()); + GrBackendTexture backend_texture = + image->getBackendTexture(true /* flushPendingGrContextIO */); + if (!backend_texture.isValid()) + return 0; + + GrGLTextureInfo info; + if (!backend_texture.getGLTextureInfo(&info)) + return 0; + + return info.fID; +} + GpuImageDecodeCache::GpuImageDecodeCache(viz::RasterContextProvider* context, bool use_transfer_cache, SkColorType color_type, @@ -613,6 +698,10 @@ GpuImageDecodeCache::~GpuImageDecodeCache() { // Unregister this component with memory_coordinator::ClientRegistry. base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this); + memory_pressure_listener_.reset( + new base::MemoryPressureListener(base::BindRepeating( + &GpuImageDecodeCache::OnMemoryPressure, base::Unretained(this)))); + // TODO(vmpstr): If we don't have a client name, it may cause problems in // unittests, since most tests don't set the name but some do. The UMA system // expects the name to be always the same. This assertion is violated in the @@ -651,8 +740,8 @@ ImageDecodeCache::TaskResult GpuImageDecodeCache::GetTaskForImageAndRefInternal( return TaskResult(false); base::AutoLock lock(lock_); - const PaintImage::FrameKey frame_key = draw_image.frame_key(); - ImageData* image_data = GetImageDataForDrawImage(draw_image); + const InUseCacheKey cache_key = InUseCacheKey::FromDrawImage(draw_image); + ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); scoped_refptr<ImageData> new_data; if (!image_data) { // We need an ImageData, create one now. @@ -665,13 +754,13 @@ ImageDecodeCache::TaskResult GpuImageDecodeCache::GetTaskForImageAndRefInternal( image_data->upload.task) { // We had an existing upload task, ref the image and return the task. image_data->ValidateBudgeted(); - RefImage(draw_image); + RefImage(draw_image, cache_key); return TaskResult(image_data->upload.task); } else if (task_type == DecodeTaskType::kStandAloneDecodeTask && image_data->decode.stand_alone_task) { // We had an existing out of raster task, ref the image and return the task. image_data->ValidateBudgeted(); - RefImage(draw_image); + RefImage(draw_image, cache_key); return TaskResult(image_data->decode.stand_alone_task); } @@ -685,11 +774,11 @@ ImageDecodeCache::TaskResult GpuImageDecodeCache::GetTaskForImageAndRefInternal( // If we had to create new image data, add it to our map now that we know it // will fit. if (new_data) - persistent_cache_.Put(frame_key, std::move(new_data)); + AddToPersistentCache(draw_image, std::move(new_data)); // Ref the image before creating a task - this ref is owned by the caller, and // it is their responsibility to release it by calling UnrefImage. - RefImage(draw_image); + RefImage(draw_image, cache_key); // If we already have an image and it is locked (or lock-able), just return // that. The image must be budgeted before we attempt to lock it. @@ -703,7 +792,7 @@ ImageDecodeCache::TaskResult GpuImageDecodeCache::GetTaskForImageAndRefInternal( if (task_type == DecodeTaskType::kPartOfUploadTask) { // Ref image and create a upload and decode tasks. We will release this ref // in UploadTaskCompleted. - RefImage(draw_image); + RefImage(draw_image, cache_key); task = base::MakeRefCounted<ImageUploadTaskImpl>( this, draw_image, GetImageDecodeTaskAndRef(draw_image, tracing_info, task_type), @@ -720,7 +809,15 @@ void GpuImageDecodeCache::UnrefImage(const DrawImage& draw_image) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::UnrefImage"); base::AutoLock lock(lock_); - UnrefImageInternal(draw_image); + UnrefImageInternal(draw_image, InUseCacheKey::FromDrawImage(draw_image)); +} + +bool GpuImageDecodeCache::UseCacheForDrawImage( + const DrawImage& draw_image) const { + if (draw_image.paint_image().GetSkImage()->isTextureBacked()) + return false; + + return true; } DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( @@ -736,20 +833,21 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( return DecodedDrawImage(); base::AutoLock lock(lock_); - ImageData* image_data = GetImageDataForDrawImage(draw_image); + const InUseCacheKey cache_key = InUseCacheKey::FromDrawImage(draw_image); + ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); if (!image_data) { // We didn't find the image, create a new entry. auto data = CreateImageData(draw_image); image_data = data.get(); - persistent_cache_.Put(draw_image.frame_key(), std::move(data)); + AddToPersistentCache(draw_image, std::move(data)); } // Ref the image and decode so that they stay alive while we are // decoding/uploading. // Note that refing the image will attempt to budget the image, if not already // done. - RefImage(draw_image); - RefImageDecode(draw_image); + RefImage(draw_image, cache_key); + RefImageDecode(draw_image, cache_key); // We may or may not need to decode and upload the image we've found, the // following functions early-out to if we already decoded. @@ -757,7 +855,7 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( UploadImageIfNecessary(draw_image, image_data); // Unref the image decode, but not the image. The image ref will be released // in DrawWithImageFinished. - UnrefImageDecode(draw_image); + UnrefImageDecode(draw_image, cache_key); if (image_data->mode == DecodedDataMode::kTransferCache) { DCHECK(use_transfer_cache_); @@ -766,11 +864,11 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( image_data->upload.mark_used(); DCHECK(id || image_data->decode.decode_failure); - SkSize scale_factor = - CalculateScaleFactorForMipLevel(draw_image, image_data->mip_level); + SkSize scale_factor = CalculateScaleFactorForMipLevel( + draw_image, image_data->upload_scale_mip_level); DecodedDrawImage decoded_draw_image( id, SkSize(), scale_factor, CalculateDesiredFilterQuality(draw_image), - image_data->is_budgeted); + image_data->needs_mips, image_data->is_budgeted); return decoded_draw_image; } else { DCHECK(!use_transfer_cache_); @@ -779,8 +877,8 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( image_data->upload.mark_used(); DCHECK(image || image_data->decode.decode_failure); - SkSize scale_factor = - CalculateScaleFactorForMipLevel(draw_image, image_data->mip_level); + SkSize scale_factor = CalculateScaleFactorForMipLevel( + draw_image, image_data->upload_scale_mip_level); DecodedDrawImage decoded_draw_image( std::move(image), SkSize(), scale_factor, CalculateDesiredFilterQuality(draw_image), image_data->is_budgeted); @@ -805,7 +903,7 @@ void GpuImageDecodeCache::DrawWithImageFinished( return; base::AutoLock lock(lock_); - UnrefImageInternal(draw_image); + UnrefImageInternal(draw_image, InUseCacheKey::FromDrawImage(draw_image)); // We are mid-draw and holding the context lock, ensure we clean up any // textures (especially at-raster), which may have just been marked for @@ -862,8 +960,16 @@ void GpuImageDecodeCache::ClearCache() { paint_image_entries_.clear(); } -GpuImageDecodeCache::PersistentCache::iterator -GpuImageDecodeCache::RemoveFromPersistentCache(PersistentCache::iterator it) { +void GpuImageDecodeCache::AddToPersistentCache(const DrawImage& draw_image, + scoped_refptr<ImageData> data) { + lock_.AssertAcquired(); + + WillAddCacheEntry(draw_image); + persistent_cache_.Put(draw_image.frame_key(), std::move(data)); +} + +template <typename Iterator> +Iterator GpuImageDecodeCache::RemoveFromPersistentCache(Iterator it) { lock_.AssertAcquired(); if (it->second->decode.ref_count != 0 || it->second->upload.ref_count != 0) { @@ -883,6 +989,15 @@ GpuImageDecodeCache::RemoveFromPersistentCache(PersistentCache::iterator it) { DeleteImage(it->second.get()); } + auto entries_it = paint_image_entries_.find(it->second->paint_image_id); + DCHECK(entries_it != paint_image_entries_.end()); + DCHECK_GT(entries_it->second.count, 0u); + + // If this is the last entry for this image, remove its tracking. + --entries_it->second.count; + if (entries_it->second.count == 0u) + paint_image_entries_.erase(entries_it); + return persistent_cache_.Erase(it); } @@ -981,7 +1096,8 @@ void GpuImageDecodeCache::DecodeImageInTask(const DrawImage& draw_image, TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::DecodeImage"); base::AutoLock lock(lock_); - ImageData* image_data = GetImageDataForDrawImage(draw_image); + ImageData* image_data = GetImageDataForDrawImage( + draw_image, InUseCacheKey::FromDrawImage(draw_image)); DCHECK(image_data); DCHECK(image_data->is_budgeted) << "Must budget an image for pre-decoding"; DecodeImageIfNecessary(draw_image, image_data, task_type); @@ -996,7 +1112,8 @@ void GpuImageDecodeCache::UploadImageInTask(const DrawImage& draw_image) { context_lock.emplace(context_); base::AutoLock lock(lock_); - ImageData* image_data = GetImageDataForDrawImage(draw_image); + ImageData* image_data = GetImageDataForDrawImage( + draw_image, InUseCacheKey::FromDrawImage(draw_image)); DCHECK(image_data); DCHECK(image_data->is_budgeted) << "Must budget an image for pre-decoding"; UploadImageIfNecessary(draw_image, image_data); @@ -1008,8 +1125,9 @@ void GpuImageDecodeCache::OnImageDecodeTaskCompleted( TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::OnImageDecodeTaskCompleted"); base::AutoLock lock(lock_); + auto cache_key = InUseCacheKey::FromDrawImage(draw_image); // Decode task is complete, remove our reference to it. - ImageData* image_data = GetImageDataForDrawImage(draw_image); + ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); DCHECK(image_data); if (task_type == DecodeTaskType::kPartOfUploadTask) { DCHECK(image_data->decode.task); @@ -1022,7 +1140,7 @@ void GpuImageDecodeCache::OnImageDecodeTaskCompleted( // While the decode task is active, we keep a ref on the decoded data. // Release that ref now. - UnrefImageDecode(draw_image); + UnrefImageDecode(draw_image, cache_key); } void GpuImageDecodeCache::OnImageUploadTaskCompleted( @@ -1031,7 +1149,8 @@ void GpuImageDecodeCache::OnImageUploadTaskCompleted( "GpuImageDecodeCache::OnImageUploadTaskCompleted"); base::AutoLock lock(lock_); // Upload task is complete, remove our reference to it. - ImageData* image_data = GetImageDataForDrawImage(draw_image); + InUseCacheKey cache_key = InUseCacheKey::FromDrawImage(draw_image); + ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); DCHECK(image_data); DCHECK(image_data->upload.task); image_data->upload.task = nullptr; @@ -1039,8 +1158,8 @@ void GpuImageDecodeCache::OnImageUploadTaskCompleted( // While the upload task is active, we keep a ref on both the image it will be // populating, as well as the decode it needs to populate it. Release these // refs now. - UnrefImageDecode(draw_image); - UnrefImageInternal(draw_image); + UnrefImageDecode(draw_image, cache_key); + UnrefImageInternal(draw_image, cache_key); } // Checks if an existing image decode exists. If not, returns a task to produce @@ -1053,12 +1172,14 @@ scoped_refptr<TileTask> GpuImageDecodeCache::GetImageDecodeTaskAndRef( "GpuImageDecodeCache::GetImageDecodeTaskAndRef"); lock_.AssertAcquired(); + auto cache_key = InUseCacheKey::FromDrawImage(draw_image); + // This ref is kept alive while an upload task may need this decode. We // release this ref in UploadTaskCompleted. if (task_type == DecodeTaskType::kPartOfUploadTask) - RefImageDecode(draw_image); + RefImageDecode(draw_image, cache_key); - ImageData* image_data = GetImageDataForDrawImage(draw_image); + ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); DCHECK(image_data); if (image_data->decode.is_locked()) { // We should never be creating a decode task for a not budgeted image. @@ -1076,29 +1197,31 @@ scoped_refptr<TileTask> GpuImageDecodeCache::GetImageDecodeTaskAndRef( if (!existing_task) { // Ref image decode and create a decode task. This ref will be released in // DecodeTaskCompleted. - RefImageDecode(draw_image); + RefImageDecode(draw_image, cache_key); existing_task = base::MakeRefCounted<GpuImageDecodeTaskImpl>( this, draw_image, tracing_info, task_type); } return existing_task; } -void GpuImageDecodeCache::RefImageDecode(const DrawImage& draw_image) { +void GpuImageDecodeCache::RefImageDecode(const DrawImage& draw_image, + const InUseCacheKey& cache_key) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::RefImageDecode"); lock_.AssertAcquired(); - auto found = in_use_cache_.find(InUseCacheKey::FromDrawImage(draw_image)); + auto found = in_use_cache_.find(cache_key); DCHECK(found != in_use_cache_.end()); ++found->second.ref_count; ++found->second.image_data->decode.ref_count; OwnershipChanged(draw_image, found->second.image_data.get()); } -void GpuImageDecodeCache::UnrefImageDecode(const DrawImage& draw_image) { +void GpuImageDecodeCache::UnrefImageDecode(const DrawImage& draw_image, + const InUseCacheKey& cache_key) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::UnrefImageDecode"); lock_.AssertAcquired(); - auto found = in_use_cache_.find(InUseCacheKey::FromDrawImage(draw_image)); + auto found = in_use_cache_.find(cache_key); DCHECK(found != in_use_cache_.end()); DCHECK_GT(found->second.image_data->decode.ref_count, 0u); DCHECK_GT(found->second.ref_count, 0u); @@ -1110,12 +1233,12 @@ void GpuImageDecodeCache::UnrefImageDecode(const DrawImage& draw_image) { } } -void GpuImageDecodeCache::RefImage(const DrawImage& draw_image) { +void GpuImageDecodeCache::RefImage(const DrawImage& draw_image, + const InUseCacheKey& cache_key) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::RefImage"); lock_.AssertAcquired(); - InUseCacheKey key = InUseCacheKey::FromDrawImage(draw_image); - auto found = in_use_cache_.find(key); + auto found = in_use_cache_.find(cache_key); // If no secondary cache entry was found for the given |draw_image|, then // the draw_image only exists in the |persistent_cache_|. Create an in-use @@ -1126,7 +1249,7 @@ void GpuImageDecodeCache::RefImage(const DrawImage& draw_image) { DCHECK(IsCompatible(found_image->second.get(), draw_image)); found = in_use_cache_ .insert(InUseCache::value_type( - key, InUseCacheEntry(found_image->second))) + cache_key, InUseCacheEntry(found_image->second))) .first; } @@ -1136,9 +1259,10 @@ void GpuImageDecodeCache::RefImage(const DrawImage& draw_image) { OwnershipChanged(draw_image, found->second.image_data.get()); } -void GpuImageDecodeCache::UnrefImageInternal(const DrawImage& draw_image) { +void GpuImageDecodeCache::UnrefImageInternal(const DrawImage& draw_image, + const InUseCacheKey& cache_key) { lock_.AssertAcquired(); - auto found = in_use_cache_.find(InUseCacheKey::FromDrawImage(draw_image)); + auto found = in_use_cache_.find(cache_key); DCHECK(found != in_use_cache_.end()); DCHECK_GT(found->second.image_data->upload.ref_count, 0u); DCHECK_GT(found->second.ref_count, 0u); @@ -1158,6 +1282,12 @@ void GpuImageDecodeCache::OwnershipChanged(const DrawImage& draw_image, bool has_any_refs = image_data->upload.ref_count > 0 || image_data->decode.ref_count > 0; + // If we have no image refs on an image, we should unbudget it. + if (!has_any_refs && image_data->is_budgeted) { + DCHECK_GE(working_set_bytes_, image_data->size); + working_set_bytes_ -= image_data->size; + image_data->is_budgeted = false; + } // Don't keep around completely empty images. This can happen if an image's // decode/upload tasks were both cancelled before completing. @@ -1168,7 +1298,7 @@ void GpuImageDecodeCache::OwnershipChanged(const DrawImage& draw_image, !image_data->is_orphaned) { auto found_persistent = persistent_cache_.Peek(draw_image.frame_key()); if (found_persistent != persistent_cache_.end()) - persistent_cache_.Erase(found_persistent); + RemoveFromPersistentCache(found_persistent); } // If we have no refs on an uploaded image, it should be unlocked. Do this @@ -1197,13 +1327,6 @@ void GpuImageDecodeCache::OwnershipChanged(const DrawImage& draw_image, image_data->is_budgeted = true; } - // If we have no image refs on an image, we should unbudget it. - if (!has_any_refs && image_data->is_budgeted) { - DCHECK_GE(working_set_bytes_, image_data->size); - working_set_bytes_ -= image_data->size; - image_data->is_budgeted = false; - } - // We should unlock the decoded image memory for the image in two cases: // 1) The image is no longer being used (no decode or upload refs). // 2) This is a non-CPU image that has already been uploaded and we have @@ -1256,18 +1379,7 @@ bool GpuImageDecodeCache::EnsureCapacity(size_t required_size) { continue; } - // Current entry has no refs. Ensure it is not locked. - DCHECK(!it->second->decode.is_locked()); - DCHECK(!it->second->upload.is_locked()); - - // Unlocked images must not be budgeted. - DCHECK(!it->second->is_budgeted); - - // Free the uploaded image if it exists. - if (it->second->HasUploadedData()) - DeleteImage(it->second.get()); - - it = persistent_cache_.Erase(it); + it = RemoveFromPersistentCache(it); } return CanFitInWorkingSet(required_size); @@ -1330,7 +1442,7 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, } TRACE_EVENT0("cc", "GpuImageDecodeCache::DecodeImage"); - RecordImageMipLevelUMA(image_data->mip_level); + RecordImageMipLevelUMA(image_data->upload_scale_mip_level); image_data->decode.ResetData(); std::unique_ptr<base::DiscardableMemory> backing_memory; @@ -1339,8 +1451,8 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, base::AutoUnlock unlock(lock_); backing_memory = base::DiscardableMemoryAllocator::GetInstance() ->AllocateLockedDiscardableMemory(image_data->size); - SkImageInfo image_info = - CreateImageInfoForDrawImage(draw_image, image_data->mip_level); + SkImageInfo image_info = CreateImageInfoForDrawImage( + draw_image, image_data->upload_scale_mip_level); SkPixmap pixmap(image_info, backing_memory->data(), image_info.minRowBytes()); @@ -1389,38 +1501,60 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, return; } - if (image_data->HasUploadedData() && - TryLockImage(HaveContextLock::kYes, draw_image, image_data)) { - // Someone has uploaded this image before us (at raster). + // If an upload already exists, try to lock it. If this fails, it will clear + // any uploaded data. + if (image_data->HasUploadedData()) + TryLockImage(HaveContextLock::kYes, draw_image, image_data); + + // Ensure the mip status is correct before returning the locked upload or + // preparing to upload a new image. + UpdateMipsIfNeeded(draw_image, image_data); + + // If we have uploaded data at this point, it is locked with correct mips, + // just return. + if (image_data->HasUploadedData()) return; - } TRACE_EVENT0("cc", "GpuImageDecodeCache::UploadImage"); DCHECK(image_data->decode.is_locked()); DCHECK_GT(image_data->decode.ref_count, 0u); DCHECK_GT(image_data->upload.ref_count, 0u); + sk_sp<SkColorSpace> target_color_space = + SupportsColorSpaceConversion() && + draw_image.target_color_space().IsValid() + ? draw_image.target_color_space().ToSkColorSpace() + : nullptr; + if (target_color_space && + SkColorSpace::Equals(target_color_space.get(), + image_data->decode.image()->colorSpace())) { + target_color_space = nullptr; + } + if (image_data->mode == DecodedDataMode::kTransferCache) { DCHECK(use_transfer_cache_); SkPixmap pixmap; if (!image_data->decode.image()->peekPixels(&pixmap)) return; - sk_sp<SkColorSpace> color_space = - SupportsColorSpaceConversion() - ? draw_image.target_color_space().ToSkColorSpace() - : nullptr; - ClientImageTransferCacheEntry image_entry(&pixmap, color_space.get()); + ClientImageTransferCacheEntry image_entry(&pixmap, target_color_space.get(), + image_data->needs_mips); size_t size = image_entry.SerializedSize(); void* data = context_->ContextSupport()->MapTransferCacheEntry(size); - // TODO(piman): handle error (failed to allocate/map shm) - DCHECK(data); - bool succeeded = image_entry.Serialize( - base::make_span(reinterpret_cast<uint8_t*>(data), size)); - DCHECK(succeeded); - context_->ContextSupport()->UnmapAndCreateTransferCacheEntry( - image_entry.UnsafeType(), image_entry.Id()); - image_data->upload.SetTransferCacheId(image_entry.Id()); + if (data) { + bool succeeded = image_entry.Serialize( + base::make_span(reinterpret_cast<uint8_t*>(data), size)); + DCHECK(succeeded); + context_->ContextSupport()->UnmapAndCreateTransferCacheEntry( + image_entry.UnsafeType(), image_entry.Id()); + image_data->upload.SetTransferCacheId(image_entry.Id()); + } else { + // Transfer cache entry can fail due to a lost gpu context or failure + // to allocate shared memory. Handle this gracefully. Mark this + // image as "decode failed" so that we do not try to handle it again. + // If this was a lost context, we'll recreate this image decode cache. + image_data->decode.decode_failure = true; + } return; } @@ -1437,50 +1571,40 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, if (image_data->mode == DecodedDataMode::kGpu) { DCHECK(!use_transfer_cache_); base::AutoUnlock unlock(lock_); - uploaded_image = - uploaded_image->makeTextureImage(context_->GrContext(), nullptr); - - if (uploaded_image && SupportsColorSpaceConversion() && - draw_image.target_color_space().IsValid()) { - TRACE_EVENT0("cc", "GpuImageDecodeCache::UploadImage - color conversion"); - sk_sp<SkImage> pre_converted_image = uploaded_image; - uploaded_image = uploaded_image->makeColorSpace( - draw_image.target_color_space().ToSkColorSpace(), - SkTransferFunctionBehavior::kIgnore); - - // If we created a new image while converting colorspace, we should - // destroy the previous image without caching it. - if (uploaded_image != pre_converted_image) - DeleteSkImageAndPreventCaching(context_, - std::move(pre_converted_image)); - } + uploaded_image = MakeTextureImage( + context_, std::move(uploaded_image), target_color_space, + image_data->needs_mips ? GrMipMapped::kYes : GrMipMapped::kNo); } // At-raster may have decoded this while we were unlocked. If so, ignore our // result. - if (!image_data->upload.image()) { - // Take ownership of any GL texture backing for the SkImage. This allows - // us to use the image with the discardable system. - if (uploaded_image) { - uploaded_image = TakeOwnershipOfSkImageBacking(context_->GrContext(), - std::move(uploaded_image)); - } + if (image_data->upload.image()) { + if (uploaded_image) + DeleteSkImageAndPreventCaching(context_, std::move(uploaded_image)); + return; + } - // TODO(crbug.com/740737): uploaded_image is sometimes null in certain - // context-lost situations. - if (!uploaded_image) - return; + // Take ownership of any GL texture backing for the SkImage. This allows + // us to use the image with the discardable system. + if (uploaded_image) { + uploaded_image = TakeOwnershipOfSkImageBacking(context_->GrContext(), + std::move(uploaded_image)); + } - image_data->upload.SetImage(std::move(uploaded_image)); + // TODO(crbug.com/740737): uploaded_image is sometimes null in certain + // context-lost situations. + if (!uploaded_image) + return; - // If we have a new GPU-backed image, initialize it for use in the GPU - // discardable system. - if (image_data->mode == DecodedDataMode::kGpu) { - // Notify the discardable system of this image so it will count against - // budgets. - context_->ContextGL()->InitializeDiscardableTextureCHROMIUM( - image_data->upload.gl_id()); - } + image_data->upload.SetImage(std::move(uploaded_image)); + + // If we have a new GPU-backed image, initialize it for use in the GPU + // discardable system. + if (image_data->mode == DecodedDataMode::kGpu) { + // Notify the discardable system of this image so it will count against + // budgets. + context_->ContextGL()->InitializeDiscardableTextureCHROMIUM( + image_data->upload.gl_id()); } } @@ -1490,9 +1614,10 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image) { "GpuImageDecodeCache::CreateImageData"); lock_.AssertAcquired(); - WillAddCacheEntry(draw_image); - int mip_level = CalculateUploadScaleMipLevel(draw_image); - SkImageInfo image_info = CreateImageInfoForDrawImage(draw_image, mip_level); + int upload_scale_mip_level = CalculateUploadScaleMipLevel(draw_image); + bool needs_mips = ShouldGenerateMips(draw_image, upload_scale_mip_level); + SkImageInfo image_info = + CreateImageInfoForDrawImage(draw_image, upload_scale_mip_level); DecodedDataMode mode; if (use_transfer_cache_) { @@ -1522,20 +1647,23 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image) { // However, if the image will be scaled or color converts on the cpu, we // consider it a lazy image and cache the scaled result in discardable memory. const bool is_bitmap_backed = !draw_image.paint_image().IsLazyGenerated() && - mip_level == 0 && + upload_scale_mip_level == 0 && !cache_color_conversion_on_cpu; - return base::WrapRefCounted(new ImageData( - mode, data_size, draw_image.target_color_space(), - CalculateDesiredFilterQuality(draw_image), mip_level, is_bitmap_backed)); + return base::WrapRefCounted( + new ImageData(draw_image.paint_image().stable_id(), mode, data_size, + draw_image.target_color_space(), + CalculateDesiredFilterQuality(draw_image), + upload_scale_mip_level, needs_mips, is_bitmap_backed)); } void GpuImageDecodeCache::WillAddCacheEntry(const DrawImage& draw_image) { - lock_.AssertAcquired(); - // Remove any old entries for this image. We keep at-most 2 ContentIds for a // PaintImage (pending and active tree). - auto& cached_content_ids = - paint_image_entries_[draw_image.paint_image().stable_id()].content_ids; + auto& cache_entries = + paint_image_entries_[draw_image.paint_image().stable_id()]; + cache_entries.count++; + + auto& cached_content_ids = cache_entries.content_ids; const PaintImage::ContentId new_content_id = draw_image.frame_key().content_id(); @@ -1568,6 +1696,12 @@ void GpuImageDecodeCache::WillAddCacheEntry(const DrawImage& draw_image) { } } + // Removing entries from the persistent cache should not erase the tracking + // for the current paint_image, since we have 2 different content ids for it + // and only one of them was erased above. + DCHECK_NE(paint_image_entries_.count(draw_image.paint_image().stable_id()), + 0u); + cached_content_ids[0] = content_id_to_keep; cached_content_ids[1] = new_content_id; } @@ -1592,6 +1726,12 @@ void GpuImageDecodeCache::UnlockImage(ImageData* image_data) { ids_pending_unlock_.push_back(*image_data->upload.transfer_cache_id()); } image_data->upload.OnUnlock(); + + // If we were holding onto an unmipped image for defering deletion, do it now + // it is guarenteed to have no-refs. + auto unmipped_image = image_data->upload.take_unmipped_image(); + if (unmipped_image) + images_pending_deletion_.push_back(std::move(unmipped_image)); } // We always run pending operations in the following order: @@ -1703,14 +1843,14 @@ bool GpuImageDecodeCache::TryLockImage(HaveContextLock have_context_lock, // |draw_image|. First looks for an exact entry in our |in_use_cache_|. If one // cannot be found, it looks for a compatible entry in our |persistent_cache_|. GpuImageDecodeCache::ImageData* GpuImageDecodeCache::GetImageDataForDrawImage( - const DrawImage& draw_image) { + const DrawImage& draw_image, + const InUseCacheKey& key) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::GetImageDataForDrawImage"); lock_.AssertAcquired(); - DCHECK(!draw_image.paint_image().GetSkImage()->isTextureBacked()); + DCHECK(UseCacheForDrawImage(draw_image)); - auto found_in_use = - in_use_cache_.find(InUseCacheKey::FromDrawImage(draw_image)); + auto found_in_use = in_use_cache_.find(key); if (found_in_use != in_use_cache_.end()) return found_in_use->second.image_data.get(); @@ -1720,12 +1860,7 @@ GpuImageDecodeCache::ImageData* GpuImageDecodeCache::GetImageDataForDrawImage( if (IsCompatible(image_data, draw_image)) { return image_data; } else { - found_persistent->second->is_orphaned = true; - // Call OwnershipChanged before erasing the orphaned task from the - // persistent cache. This ensures that if the orphaned task has 0 - // references, it is cleaned up safely before it is deleted. - OwnershipChanged(draw_image, image_data); - persistent_cache_.Erase(found_persistent); + RemoveFromPersistentCache(found_persistent); } } @@ -1738,9 +1873,9 @@ GpuImageDecodeCache::ImageData* GpuImageDecodeCache::GetImageDataForDrawImage( // the provided |draw_image|. bool GpuImageDecodeCache::IsCompatible(const ImageData* image_data, const DrawImage& draw_image) const { - bool is_scaled = image_data->mip_level != 0; - bool scale_is_compatible = - CalculateUploadScaleMipLevel(draw_image) >= image_data->mip_level; + bool is_scaled = image_data->upload_scale_mip_level != 0; + bool scale_is_compatible = CalculateUploadScaleMipLevel(draw_image) >= + image_data->upload_scale_mip_level; bool quality_is_compatible = CalculateDesiredFilterQuality(draw_image) <= image_data->quality; bool color_is_compatible = @@ -1809,6 +1944,18 @@ void GpuImageDecodeCache::OnPurgeMemory() { EnsureCapacity(0); } +void GpuImageDecodeCache::OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level) { + switch (level) { + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + break; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + OnPurgeMemory(); + break; + } +} + bool GpuImageDecodeCache::SupportsColorSpaceConversion() const { switch (color_type_) { case kRGBA_8888_SkColorType: @@ -1840,4 +1987,61 @@ void GpuImageDecodeCache::CheckContextLockAcquiredIfNecessary() { context_->GetLock()->AssertAcquired(); } +void GpuImageDecodeCache::UpdateMipsIfNeeded(const DrawImage& draw_image, + ImageData* image_data) { + CheckContextLockAcquiredIfNecessary(); + + // If we already have mips, nothing to do. + if (image_data->needs_mips) + return; + + bool needs_mips = + ShouldGenerateMips(draw_image, image_data->upload_scale_mip_level); + if (!needs_mips) + return; + + image_data->needs_mips = true; + + // If we have no uploaded image, nothing to do other than update needs_mips. + // Mips will be generated during later upload. + if (!image_data->HasUploadedData() || + image_data->mode != DecodedDataMode::kGpu) + return; + + // Need to generate mips. Take a reference on the image we're about to delete, + // delaying deletion. + sk_sp<SkImage> previous_image = image_data->upload.image(); + + // Generate a new image from the previous, adding mips. + sk_sp<SkImage> image_with_mips = previous_image->makeTextureImage( + context_->GrContext(), nullptr, GrMipMapped::kYes); + + // Handle lost context. + if (!image_with_mips) + return; + + // No need to do anything if mipping this image results in the same texture. + // Deleting it below will result in lifetime issues. + if (GlIdFromSkImage(image_with_mips.get()) == image_data->upload.gl_id()) + return; + + // Skia owns our new image, take ownership. + sk_sp<SkImage> image_with_mips_owned = TakeOwnershipOfSkImageBacking( + context_->GrContext(), std::move(image_with_mips)); + + // Handle lost context + if (!image_with_mips_owned) + return; + + // The previous image might be in the in-use cache, potentially held + // externally. We must defer deleting it until the entry is unlocked. + image_data->upload.set_unmipped_image(image_data->upload.image()); + + // Set the new image on the cache. + image_data->upload.Reset(); + image_data->upload.SetImage(std::move(image_with_mips_owned)); + context_->ContextGL()->InitializeDiscardableTextureCHROMIUM( + image_data->upload.gl_id()); +} + } // namespace cc diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h index 90374d7ab72..54cffb803b8 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.h +++ b/chromium/cc/tiles/gpu_image_decode_cache.h @@ -12,6 +12,7 @@ #include "base/containers/mru_cache.h" #include "base/memory/discardable_memory.h" #include "base/memory/memory_coordinator_client.h" +#include "base/memory/memory_pressure_listener.h" #include "base/synchronization/lock.h" #include "base/trace_event/memory_dump_provider.h" #include "cc/cc_export.h" @@ -109,6 +110,9 @@ class CC_EXPORT GpuImageDecodeCache int max_texture_size); ~GpuImageDecodeCache() override; + // Returns the GL texture ID backing the given SkImage. + static GrGLuint GlIdFromSkImage(const SkImage* image); + // ImageDecodeCache overrides. // Finds the existing uploaded image for the provided DrawImage. Creates an @@ -126,6 +130,7 @@ class CC_EXPORT GpuImageDecodeCache bool aggressively_free_resources) override; void ClearCache() override; size_t GetMaximumMemoryLimitBytes() const override; + bool UseCacheForDrawImage(const DrawImage& image) const override; // MemoryDumpProvider overrides. bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, @@ -135,6 +140,11 @@ class CC_EXPORT GpuImageDecodeCache void OnMemoryStateChange(base::MemoryState state) override; void OnPurgeMemory() override; + // TODO(gyuyoung): OnMemoryPressure is deprecated. So this should be removed + // when the memory coordinator is enabled by default. + void OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level); + // Called by Decode / Upload tasks. void DecodeImageInTask(const DrawImage& image, TaskType task_type); void UploadImageInTask(const DrawImage& image); @@ -161,6 +171,9 @@ class CC_EXPORT GpuImageDecodeCache bool IsInInUseCacheForTesting(const DrawImage& image) const; bool IsInPersistentCacheForTesting(const DrawImage& image) const; sk_sp<SkImage> GetSWImageDecodeForTesting(const DrawImage& image); + size_t paint_image_entries_count_for_testing() const { + return paint_image_entries_.size(); + } private: enum class DecodedDataMode { kGpu, kCpu, kTransferCache }; @@ -262,6 +275,14 @@ class CC_EXPORT GpuImageDecodeCache return transfer_cache_id_; } + void set_unmipped_image(sk_sp<SkImage> image) { + unmipped_image_ = std::move(image); + } + sk_sp<SkImage> take_unmipped_image() { + DCHECK(!is_locked_); + return std::move(unmipped_image_); + } + private: // Used for internal DCHECKs only. enum class Mode { @@ -281,25 +302,32 @@ class CC_EXPORT GpuImageDecodeCache // Used if |mode_| == kTransferCache. base::Optional<uint32_t> transfer_cache_id_; + + // The original un-mipped image, retained until it can be safely deleted. + sk_sp<SkImage> unmipped_image_; }; struct ImageData : public base::RefCountedThreadSafe<ImageData> { - ImageData(DecodedDataMode mode, + ImageData(PaintImage::Id paint_image_id, + DecodedDataMode mode, size_t size, const gfx::ColorSpace& target_color_space, SkFilterQuality quality, - int mip_level, + int upload_scale_mip_level, + bool needs_mips, bool is_bitmap_backed); bool IsGpuOrTransferCache() const; bool HasUploadedData() const; void ValidateBudgeted() const; + const PaintImage::Id paint_image_id; const DecodedDataMode mode; const size_t size; gfx::ColorSpace target_color_space; SkFilterQuality quality; - int mip_level; + int upload_scale_mip_level; + bool needs_mips = false; bool is_bitmap_backed; bool is_budgeted = false; @@ -339,7 +367,7 @@ class CC_EXPORT GpuImageDecodeCache explicit InUseCacheKey(const DrawImage& draw_image); PaintImage::FrameKey frame_key; - int mip_level; + int upload_scale_mip_level; SkFilterQuality filter_quality; gfx::ColorSpace target_color_space; }; @@ -364,10 +392,13 @@ class CC_EXPORT GpuImageDecodeCache const TracingInfo& tracing_info, DecodeTaskType task_type); - void RefImageDecode(const DrawImage& draw_image); - void UnrefImageDecode(const DrawImage& draw_image); - void RefImage(const DrawImage& draw_image); - void UnrefImageInternal(const DrawImage& draw_image); + void RefImageDecode(const DrawImage& draw_image, + const InUseCacheKey& cache_key); + void UnrefImageDecode(const DrawImage& draw_image, + const InUseCacheKey& cache_key); + void RefImage(const DrawImage& draw_image, const InUseCacheKey& cache_key); + void UnrefImageInternal(const DrawImage& draw_image, + const InUseCacheKey& cache_key); // Called any time the ownership of an object changed. This includes changes // to ref-count or to orphaned status. @@ -391,7 +422,8 @@ class CC_EXPORT GpuImageDecodeCache // Finds the ImageData that should be used for the given DrawImage. Looks // first in the |in_use_cache_|, and then in the |persistent_cache_|. - ImageData* GetImageDataForDrawImage(const DrawImage& image); + ImageData* GetImageDataForDrawImage(const DrawImage& image, + const InUseCacheKey& key); // Returns true if the given ImageData can be used to draw the specified // DrawImage. @@ -434,8 +466,13 @@ class CC_EXPORT GpuImageDecodeCache using PersistentCache = base::HashingMRUCache<PaintImage::FrameKey, scoped_refptr<ImageData>, PaintImage::FrameKeyHash>; - PersistentCache::iterator RemoveFromPersistentCache( - PersistentCache::iterator it); + void AddToPersistentCache(const DrawImage& draw_image, + scoped_refptr<ImageData> data); + template <typename Iterator> + Iterator RemoveFromPersistentCache(Iterator it); + + // Adds mips to an image if required. + void UpdateMipsIfNeeded(const DrawImage& draw_image, ImageData* image_data); const SkColorType color_type_; const bool use_transfer_cache_ = false; @@ -452,6 +489,10 @@ class CC_EXPORT GpuImageDecodeCache struct CacheEntries { PaintImage::ContentId content_ids[2] = {PaintImage::kInvalidContentId, PaintImage::kInvalidContentId}; + + // The number of cache entries for a PaintImage. Note that there can be + // multiple entries per content_id. + size_t count = 0u; }; // A map of PaintImage::Id to entries for this image in the // |persistent_cache_|. @@ -480,6 +521,8 @@ class CC_EXPORT GpuImageDecodeCache // Records the maximum number of items in the cache over the lifetime of the // cache. This is updated anytime we are requested to reduce cache usage. size_t lifetime_max_items_in_cache_ = 0u; + + std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; }; } // namespace cc diff --git a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc new file mode 100644 index 00000000000..fc4d9321ab9 --- /dev/null +++ b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc @@ -0,0 +1,183 @@ +// 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 <vector> + +#include "cc/base/lap_timer.h" +#include "cc/paint/draw_image.h" +#include "cc/paint/paint_image_builder.h" +#include "cc/raster/tile_task.h" +#include "cc/test/test_in_process_context_provider.h" +#include "cc/tiles/gpu_image_decode_cache.h" +#include "gpu/command_buffer/client/raster_interface.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/perf/perf_test.h" +#include "third_party/skia/include/core/SkSurface.h" + +namespace cc { +namespace { + +static const int kTimeLimitMillis = 2000; +static const int kWarmupRuns = 5; +static const int kTimeCheckInterval = 10; +static const int kCacheSize = 128 * 1024 * 1024; + +sk_sp<SkImage> CreateImage(int width, int height) { + SkBitmap bitmap; + bitmap.allocPixels(SkImageInfo::MakeS32(width, height, kPremul_SkAlphaType)); + return SkImage::MakeFromBitmap(bitmap); +} + +SkMatrix CreateMatrix(const SkSize& scale) { + SkMatrix matrix; + matrix.setScale(scale.width(), scale.height()); + return matrix; +} + +enum class TestMode { kGpu, kTransferCache, kSw }; + +class GpuImageDecodeCachePerfTest + : public testing::Test, + public testing::WithParamInterface<TestMode> { + public: + GpuImageDecodeCachePerfTest() + : timer_(kWarmupRuns, + base::TimeDelta::FromMilliseconds(kTimeLimitMillis), + kTimeCheckInterval), + context_provider_(base::MakeRefCounted<TestInProcessContextProvider>( + UseTransferCache(), + false /* support_locking */)), + cache_(context_provider_.get(), + UseTransferCache(), + kRGBA_8888_SkColorType, + kCacheSize, + MaxTextureSize()) {} + + protected: + size_t MaxTextureSize() const { + switch (GetParam()) { + case TestMode::kGpu: + case TestMode::kTransferCache: + return 4096; + case TestMode::kSw: + return 0; + } + } + + bool UseTransferCache() const { + return GetParam() == TestMode::kTransferCache; + } + + const char* ParamName() const { + switch (GetParam()) { + case TestMode::kGpu: + return "GPU"; + case TestMode::kTransferCache: + return "TransferCache"; + case TestMode::kSw: + return "SW"; + } + } + + LapTimer timer_; + scoped_refptr<TestInProcessContextProvider> context_provider_; + GpuImageDecodeCache cache_; +}; + +INSTANTIATE_TEST_CASE_P(P, + GpuImageDecodeCachePerfTest, + testing::Values(TestMode::kGpu, + TestMode::kTransferCache, + TestMode::kSw)); + +TEST_P(GpuImageDecodeCachePerfTest, DecodeWithColorConversion) { + timer_.Reset(); + do { + DrawImage image( + PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_image(CreateImage(1024, 2048), PaintImage::GetNextContentId()) + .TakePaintImage(), + SkIRect::MakeWH(1024, 2048), kMedium_SkFilterQuality, + CreateMatrix(SkSize::Make(1.0f, 1.0f)), 0u, + gfx::ColorSpace::CreateXYZD50()); + + DecodedDrawImage decoded_image = cache_.GetDecodedImageForDraw(image); + cache_.DrawWithImageFinished(image, decoded_image); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); + + perf_test::PrintResult("gpu_image_decode_cache_decode_with_color_conversion", + ParamName(), "result", timer_.LapsPerSecond(), + "runs/s", true); +} + +using GpuImageDecodeCachePerfTestNoSw = GpuImageDecodeCachePerfTest; +INSTANTIATE_TEST_CASE_P(P, + GpuImageDecodeCachePerfTestNoSw, + testing::Values(TestMode::kGpu, + TestMode::kTransferCache)); + +TEST_P(GpuImageDecodeCachePerfTestNoSw, DecodeWithMips) { + // Surface to render into. + auto surface = SkSurface::MakeRenderTarget( + context_provider_->GrContext(), SkBudgeted::kNo, + SkImageInfo::MakeN32Premul(2048, 2048)); + + timer_.Reset(); + do { + DrawImage image( + PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_image(CreateImage(1024, 2048), PaintImage::GetNextContentId()) + .TakePaintImage(), + SkIRect::MakeWH(1024, 2048), kMedium_SkFilterQuality, + CreateMatrix(SkSize::Make(0.6f, 0.6f)), 0u, gfx::ColorSpace()); + + DecodedDrawImage decoded_image = cache_.GetDecodedImageForDraw(image); + + if (GetParam() == TestMode::kGpu) { + SkPaint paint; + paint.setFilterQuality(kMedium_SkFilterQuality); + surface->getCanvas()->drawImageRect(decoded_image.image().get(), + SkRect::MakeWH(1024, 2048), + SkRect::MakeWH(614, 1229), &paint); + surface->prepareForExternalIO(); + } + + cache_.DrawWithImageFinished(image, decoded_image); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); + + perf_test::PrintResult("gpu_image_decode_cache_decode_with_mips", ParamName(), + "result", timer_.LapsPerSecond(), "runs/s", true); +} + +TEST_P(GpuImageDecodeCachePerfTest, AcquireExistingImages) { + timer_.Reset(); + DrawImage image( + PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_image(CreateImage(1024, 2048), PaintImage::GetNextContentId()) + .TakePaintImage(), + SkIRect::MakeWH(1024, 2048), kMedium_SkFilterQuality, + CreateMatrix(SkSize::Make(1.0f, 1.0f)), 0u, + gfx::ColorSpace::CreateXYZD50()); + + DecodedDrawImage decoded_image = cache_.GetDecodedImageForDraw(image); + cache_.DrawWithImageFinished(image, decoded_image); + + do { + DecodedDrawImage decoded_image = cache_.GetDecodedImageForDraw(image); + cache_.DrawWithImageFinished(image, decoded_image); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); + + perf_test::PrintResult("gpu_image_decode_cache_acquire_existing_images", + ParamName(), "result", timer_.LapsPerSecond(), + "runs/s", true); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc index 91fc94f17e2..5c208a7f605 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc @@ -15,10 +15,10 @@ #include "cc/test/transfer_cache_test_helper.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_gles2_interface.h" -#include "components/viz/test/test_web_graphics_context_3d.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkImageGenerator.h" #include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrContext.h" namespace cc { @@ -61,6 +61,16 @@ class FakeDiscardableManager { cached_textures_limit_ = limit; } + void ExpectLocked(GLuint texture_id) { + EXPECT_TRUE(textures_.end() != textures_.find(texture_id)); + + // Any value > kHandleLockedStart represents a locked texture. As we + // increment this value with each lock, we need the entire range and can't + // add additional values > kHandleLockedStart in the future. + EXPECT_GE(textures_[texture_id], kHandleLockedStart); + EXPECT_LE(textures_[texture_id], kHandleLockedEnd); + } + private: void EnforceLimit() { for (auto it = textures_.begin(); it != textures_.end(); ++it) { @@ -75,16 +85,6 @@ class FakeDiscardableManager { } } - void ExpectLocked(GLuint texture_id) { - EXPECT_TRUE(textures_.end() != textures_.find(texture_id)); - - // Any value > kHandleLockedStart represents a locked texture. As we - // increment this value with each lock, we need the entire range and can't - // add additional values > kHandleLockedStart in the future. - EXPECT_GE(textures_[texture_id], kHandleLockedStart); - EXPECT_LE(textures_[texture_id], kHandleLockedEnd); - } - const int32_t kHandleDeleted = 0; const int32_t kHandleUnlocked = 1; const int32_t kHandleLockedStart = 2; @@ -102,17 +102,17 @@ class FakeGPUImageDecodeTestGLES2Interface : public viz::TestGLES2Interface, explicit FakeGPUImageDecodeTestGLES2Interface( FakeDiscardableManager* discardable_manager, TransferCacheTestHelper* transfer_cache_helper) - : extension_string_("GL_EXT_texture_format_BGRA8888 GL_OES_rgb8_rgba8"), + : extension_string_( + "GL_EXT_texture_format_BGRA8888 GL_OES_rgb8_rgba8 " + "GL_OES_texture_npot"), discardable_manager_(discardable_manager), transfer_cache_helper_(transfer_cache_helper) {} ~FakeGPUImageDecodeTestGLES2Interface() override { // All textures / framebuffers / renderbuffers should be cleaned up. - if (test_context_) { - EXPECT_EQ(0u, test_context_->NumTextures()); - EXPECT_EQ(0u, test_context_->NumFramebuffers()); - EXPECT_EQ(0u, test_context_->NumRenderbuffers()); - } + EXPECT_EQ(0u, NumTextures()); + EXPECT_EQ(0u, NumFramebuffers()); + EXPECT_EQ(0u, NumRenderbuffers()); } void InitializeDiscardableTextureCHROMIUM(GLuint texture_id) override { @@ -190,6 +190,9 @@ class FakeGPUImageDecodeTestGLES2Interface : public viz::TestGLES2Interface, case GL_MAX_RENDERBUFFER_SIZE: *params = 2048; return; + case GL_MAX_VERTEX_ATTRIBS: + *params = 8; + return; default: break; } @@ -219,26 +222,33 @@ class GPUImageDecodeTestMockContextProvider : public viz::TestContextProvider { std::make_unique<FakeGPUImageDecodeTestGLES2Interface>( discardable_manager, transfer_cache_helper), std::make_unique<FakeGPUImageDecodeTestGLES2Interface>( - discardable_manager, transfer_cache_helper), - viz::TestWebGraphicsContext3D::Create()); + discardable_manager, transfer_cache_helper)); } private: ~GPUImageDecodeTestMockContextProvider() override = default; GPUImageDecodeTestMockContextProvider( std::unique_ptr<viz::TestContextSupport> support, - std::unique_ptr<viz::TestGLES2Interface> gl, - std::unique_ptr<viz::TestWebGraphicsContext3D> context) - : TestContextProvider(std::move(support), - std::move(gl), - std::move(context), - true) {} + std::unique_ptr<viz::TestGLES2Interface> gl) + : TestContextProvider(std::move(support), std::move(gl), true) {} }; gfx::ColorSpace DefaultColorSpace() { return gfx::ColorSpace::CreateSRGB(); } +SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { + SkMatrix matrix; + matrix.setScale(scale.width(), scale.height()); + + if (!is_decomposable) { + // Perspective is not decomposable, add it. + matrix[SkMatrix::kMPersp0] = 0.1f; + } + + return matrix; +} + size_t kGpuMemoryLimitBytes = 96 * 1024 * 1024; class GpuImageDecodeCacheTest @@ -248,7 +258,8 @@ class GpuImageDecodeCacheTest void SetUp() override { context_provider_ = GPUImageDecodeTestMockContextProvider::Create( &discardable_manager_, &transfer_cache_helper_); - discardable_manager_.SetGLES2Interface(context_provider_->TestContextGL()); + discardable_manager_.SetGLES2Interface( + context_provider_->UnboundTestContextGL()); context_provider_->BindToCurrentThread(); { viz::RasterContextProvider::ScopedRasterContextLock context_lock( @@ -288,12 +299,13 @@ class GpuImageDecodeCacheTest DecodedDrawImage EnsureImageBacked(DecodedDrawImage&& draw_image) { if (draw_image.transfer_cache_entry_id()) { EXPECT_TRUE(use_transfer_cache_); - sk_sp<SkImage> image = transfer_cache_helper_ - .GetEntryAs<ServiceImageTransferCacheEntry>( - *draw_image.transfer_cache_entry_id()) - ->image(); + auto* image_entry = + transfer_cache_helper_.GetEntryAs<ServiceImageTransferCacheEntry>( + *draw_image.transfer_cache_entry_id()); + if (draw_image.transfer_cache_entry_needs_mips()) + image_entry->EnsureMips(); DecodedDrawImage new_draw_image( - std::move(image), draw_image.src_rect_offset(), + image_entry->image(), draw_image.src_rect_offset(), draw_image.scale_adjustment(), draw_image.filter_quality(), draw_image.is_budgeted()); return new_draw_image; @@ -321,18 +333,6 @@ class GpuImageDecodeCacheTest int max_texture_size_ = 0; }; -SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { - SkMatrix matrix; - matrix.setScale(scale.width(), scale.height()); - - if (!is_decomposable) { - // Perspective is not decomposable, add it. - matrix[SkMatrix::kMPersp0] = 0.1f; - } - - return matrix; -} - TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { auto cache = CreateCache(); PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); @@ -2392,6 +2392,317 @@ TEST_P(GpuImageDecodeCacheTest, KeepOnlyLast2ContentIds) { for (int i = 0; i < 10; ++i) { cache->DrawWithImageFinished(draw_images[i], decoded_draw_images[i]); } + + // We have a single tracked entry, that gets cleared once we purge the cache. + EXPECT_EQ(cache->paint_image_entries_count_for_testing(), 1u); + cache->OnPurgeMemory(); + EXPECT_EQ(cache->paint_image_entries_count_for_testing(), 0u); +} + +TEST_P(GpuImageDecodeCacheTest, DecodeToScale) { + auto cache = CreateCache(); + bool is_decomposable = true; + SkFilterQuality quality = kMedium_SkFilterQuality; + + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + SkISize full_size = SkISize::Make(100, 100); + std::vector<SkISize> supported_sizes = {SkISize::Make(25, 25), + SkISize::Make(50, 50)}; + std::vector<FrameMetadata> frames = {FrameMetadata()}; + sk_sp<FakePaintImageGenerator> generator = + sk_make_sp<FakePaintImageGenerator>( + SkImageInfo::MakeN32Premul(full_size.width(), full_size.height(), + DefaultColorSpace().ToSkColorSpace()), + frames, true, supported_sizes); + PaintImage paint_image = PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_paint_image_generator(generator) + .set_frame_index(0u) + .TakePaintImage(); + + DrawImage draw_image1( + paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), + quality, CreateMatrix(SkSize::Make(0.5, 0.5), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DecodedDrawImage decoded_image1 = + EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image1)); + ASSERT_TRUE(decoded_image1.image()); + EXPECT_EQ(decoded_image1.image()->width(), 50); + EXPECT_EQ(decoded_image1.image()->height(), 50); + + // We should have requested a scaled decode from the generator. + ASSERT_EQ(generator->decode_infos().size(), 1u); + EXPECT_EQ(generator->decode_infos().at(0).width(), 50); + EXPECT_EQ(generator->decode_infos().at(0).height(), 50); + cache->DrawWithImageFinished(draw_image1, decoded_image1); +} + +TEST_P(GpuImageDecodeCacheTest, DecodeToScaleNoneQuality) { + auto cache = CreateCache(); + bool is_decomposable = true; + SkFilterQuality quality = kNone_SkFilterQuality; + + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + SkISize full_size = SkISize::Make(100, 100); + std::vector<SkISize> supported_sizes = {SkISize::Make(25, 25), + SkISize::Make(50, 50)}; + std::vector<FrameMetadata> frames = {FrameMetadata()}; + sk_sp<FakePaintImageGenerator> generator = + sk_make_sp<FakePaintImageGenerator>( + SkImageInfo::MakeN32Premul(full_size.width(), full_size.height(), + DefaultColorSpace().ToSkColorSpace()), + frames, true, supported_sizes); + PaintImage paint_image = PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_paint_image_generator(generator) + .set_frame_index(0u) + .TakePaintImage(); + + DrawImage draw_image( + paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), + quality, CreateMatrix(SkSize::Make(0.5, 0.5), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DecodedDrawImage decoded_image = + EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + ASSERT_TRUE(decoded_image.image()); + EXPECT_EQ(decoded_image.image()->width(), 50); + EXPECT_EQ(decoded_image.image()->height(), 50); + + // We should have requested the original decode from the generator. + ASSERT_EQ(generator->decode_infos().size(), 1u); + EXPECT_EQ(generator->decode_infos().at(0).width(), 100); + EXPECT_EQ(generator->decode_infos().at(0).height(), 100); + cache->DrawWithImageFinished(draw_image, decoded_image); +} + +TEST_P(GpuImageDecodeCacheTest, BasicMips) { + auto decode_and_check_mips = [this](SkFilterQuality filter_quality, + SkSize scale, gfx::ColorSpace color_space, + bool should_have_mips) { + auto cache = CreateCache(); + bool is_decomposable = true; + + PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + filter_quality, CreateMatrix(scale, is_decomposable), + PaintImage::kDefaultFrameIndex, color_space); + ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( + draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + EXPECT_TRUE(result.task); + + TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); + TestTileTaskRunner::ProcessTask(result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + DecodedDrawImage decoded_draw_image = + EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + + sk_sp<SkImage> image_with_mips = + decoded_draw_image.image()->makeTextureImage( + context_provider()->GrContext(), nullptr, GrMipMapped::kYes); + EXPECT_EQ(should_have_mips, image_with_mips == decoded_draw_image.image()); + + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); + }; + + // No scale == no mips. + decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(1.0f, 1.0f), + DefaultColorSpace(), false); + // Full mip level scale == no mips + decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(0.5f, 0.5f), + DefaultColorSpace(), false); + // Low filter quality == no mips + decode_and_check_mips(kLow_SkFilterQuality, SkSize::Make(0.6f, 0.6f), + DefaultColorSpace(), false); + // None filter quality == no mips + decode_and_check_mips(kNone_SkFilterQuality, SkSize::Make(0.6f, 0.6f), + DefaultColorSpace(), false); + // Medium filter quality == mips + decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(0.6f, 0.6f), + DefaultColorSpace(), true); + // High filter quality == mips + decode_and_check_mips(kHigh_SkFilterQuality, SkSize::Make(0.6f, 0.6f), + DefaultColorSpace(), true); + // Color conversion preserves mips + decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(0.6f, 0.6f), + gfx::ColorSpace::CreateXYZD50(), true); +} + +TEST_P(GpuImageDecodeCacheTest, MipsAddedSubsequentDraw) { + auto cache = CreateCache(); + bool is_decomposable = true; + auto filter_quality = kMedium_SkFilterQuality; + + PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); + + // Create an image with no scaling. It will not have mips. + { + DrawImage draw_image( + image, SkIRect::MakeWH(image.width(), image.height()), filter_quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( + draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + EXPECT_TRUE(result.task); + + TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); + TestTileTaskRunner::ProcessTask(result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + DecodedDrawImage decoded_draw_image = + EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + + // No mips should be generated + sk_sp<SkImage> image_with_mips = + decoded_draw_image.image()->makeTextureImage( + context_provider()->GrContext(), nullptr, GrMipMapped::kYes); + EXPECT_NE(image_with_mips, decoded_draw_image.image()); + + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); + } + + // Call ReduceCacheUsage to clean up. + cache->ReduceCacheUsage(); + + // Request the same image again, but this time with a scale. We should get + // no new task (re-uses the existing image), but mips should have been + // added. + { + DrawImage draw_image( + image, SkIRect::MakeWH(image.width(), image.height()), filter_quality, + CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( + draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + EXPECT_FALSE(result.task); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + DecodedDrawImage decoded_draw_image = + EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + + EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + + // Mips should be generated + sk_sp<SkImage> image_with_mips = + decoded_draw_image.image()->makeTextureImage( + context_provider()->GrContext(), nullptr, GrMipMapped::kYes); + EXPECT_EQ(image_with_mips, decoded_draw_image.image()); + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); + } +} + +TEST_P(GpuImageDecodeCacheTest, MipsAddedWhileOriginalInUse) { +#if defined(OS_WIN) + // TODO(ericrk): Mips are temporarily disabled to investigate a memory + // regression on Windows. https://crbug.com/867468 + return; +#endif // defined(OS_WIN) + + auto cache = CreateCache(); + bool is_decomposable = true; + auto filter_quality = kMedium_SkFilterQuality; + + PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); + + struct Decode { + DrawImage image; + DecodedDrawImage decoded_image; + }; + std::vector<Decode> images_to_unlock; + + // Create an image with no scaling. It will not have mips. + { + DrawImage draw_image( + image, SkIRect::MakeWH(image.width(), image.height()), filter_quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( + draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + EXPECT_TRUE(result.task); + + TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); + TestTileTaskRunner::ProcessTask(result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + DecodedDrawImage decoded_draw_image = + EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + + // No mips should be generated + sk_sp<SkImage> image_with_mips = + decoded_draw_image.image()->makeTextureImage( + context_provider()->GrContext(), nullptr, GrMipMapped::kYes); + EXPECT_NE(image_with_mips, decoded_draw_image.image()); + + images_to_unlock.push_back({draw_image, decoded_draw_image}); + } + + // Second decode with mips. + { + DrawImage draw_image( + image, SkIRect::MakeWH(image.width(), image.height()), filter_quality, + CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( + draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + EXPECT_FALSE(result.task); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + DecodedDrawImage decoded_draw_image = + EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + + EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + + // Mips should be generated + sk_sp<SkImage> image_with_mips = + decoded_draw_image.image()->makeTextureImage( + context_provider()->GrContext(), nullptr, GrMipMapped::kYes); + EXPECT_EQ(image_with_mips, decoded_draw_image.image()); + + images_to_unlock.push_back({draw_image, decoded_draw_image}); + } + + // Reduce cache usage to make sure anything marked for deletion is actually + // deleted. + cache->ReduceCacheUsage(); + + { + // All images which are currently ref-ed must have locked textures. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + for (const auto& decode : images_to_unlock) { + if (!use_transfer_cache_) { + discardable_manager_.ExpectLocked(GpuImageDecodeCache::GlIdFromSkImage( + decode.decoded_image.image().get())); + } + cache->DrawWithImageFinished(decode.image, decode.decoded_image); + cache->UnrefImage(decode.image); + } + } } INSTANTIATE_TEST_CASE_P( diff --git a/chromium/cc/tiles/image_decode_cache.h b/chromium/cc/tiles/image_decode_cache.h index a1653f4708d..7c75dd9d7c5 100644 --- a/chromium/cc/tiles/image_decode_cache.h +++ b/chromium/cc/tiles/image_decode_cache.h @@ -138,6 +138,11 @@ class CC_EXPORT ImageDecodeCache { // locked budget before creating a task. virtual size_t GetMaximumMemoryLimitBytes() const = 0; + // Returns true if the cache should be used for |image|. In certain cases the + // image can directly be used for raster (for instance bitmaps in a software + // draw). + virtual bool UseCacheForDrawImage(const DrawImage& image) const = 0; + protected: void RecordImageMipLevelUMA(int mip_level); }; diff --git a/chromium/cc/tiles/mipmap_util.cc b/chromium/cc/tiles/mipmap_util.cc index e20f9f0405a..8d018f6280a 100644 --- a/chromium/cc/tiles/mipmap_util.cc +++ b/chromium/cc/tiles/mipmap_util.cc @@ -4,13 +4,25 @@ #include "cc/tiles/mipmap_util.h" +#include "base/numerics/safe_math.h" + namespace cc { namespace { -// Calculates the size of |axis_base_size| at the given |mip_level| using -// OpenGL rounding rules. +// Calculates the size of |axis_base_size| at the given |mip_level|. Note that +// the calculation here rounds up for consistency with size calculations in the +// JPEG decoder. This allows us to decode images to the mip size directly. int ScaleAxisToMipLevel(int axis_base_size, int mip_level) { DCHECK_GE(mip_level, 0); DCHECK_LT(mip_level, 32); + + if (mip_level == 0) + return axis_base_size; + + // Increment the size by (2^mip_level - 1) so we round on when dividing it + // below. + base::CheckedNumeric<int> base_size = axis_base_size; + base_size += (1 << mip_level) - 1; + axis_base_size = base_size.ValueOrDefault(std::numeric_limits<int>::max()); return std::max(1, axis_base_size >> mip_level); } diff --git a/chromium/cc/tiles/mipmap_util_unittest.cc b/chromium/cc/tiles/mipmap_util_unittest.cc index ff1819c82f3..edbe87b5bed 100644 --- a/chromium/cc/tiles/mipmap_util_unittest.cc +++ b/chromium/cc/tiles/mipmap_util_unittest.cc @@ -85,8 +85,8 @@ TEST(MipMapUtilTest, NonSquare) { // Ensures that we handle rounding images correctly. TEST(MipMapUtilTest, Rounding) { const gfx::Size src_size(49, 49); - const gfx::Size target_size_larger(25, 25); - const gfx::Size target_size_smaller(24, 24); + const gfx::Size target_size_larger(26, 26); + const gfx::Size target_size_smaller(25, 25); const int target_level_larger = 0; const int target_level_smaller = 1; const SkSize expected_scale_larger = SkSize::Make(1, 1); @@ -116,5 +116,15 @@ TEST(MipMapUtilTest, Rounding) { MipMapUtil::GetScaleAdjustmentForSize(src_size, target_size_smaller)); } +// Ensures that we round up during mip calculation. +TEST(MipMapUtilTest, RoundUp) { + const gfx::Size src_sizes[] = {gfx::Size(3, 3), gfx::Size(5, 7), + gfx::Size(11, 14), gfx::Size(17, 31)}; + for (int i = 0; i < 4; ++i) { + EXPECT_EQ(MipMapUtil::GetSizeForLevel(src_sizes[i], i + 1), + gfx::Size(2, 2)); + } +} + } // namespace } // namespace cc diff --git a/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc b/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc index c1f7703c5f3..b18a67c20a2 100644 --- a/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc +++ b/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc @@ -7,12 +7,11 @@ #include <map> #include <vector> -#include "cc/resources/layer_tree_resource_provider.h" #include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_picture_layer_tiling_client.h" #include "cc/test/fake_raster_source.h" -#include "cc/test/fake_resource_provider.h" #include "cc/trees/layer_tree_settings.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/test/fake_output_surface.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/size_conversions.h" @@ -246,9 +245,8 @@ class PictureLayerTilingSetTestWithResources : public testing::Test { viz::TestContextProvider::Create(); ASSERT_EQ(context_provider->BindToCurrentThread(), gpu::ContextResult::kSuccess); - std::unique_ptr<LayerTreeResourceProvider> resource_provider = - FakeResourceProvider::CreateLayerTreeResourceProvider( - context_provider.get()); + std::unique_ptr<viz::ClientResourceProvider> resource_provider = + std::make_unique<viz::ClientResourceProvider>(true); FakePictureLayerTilingClient client(resource_provider.get(), context_provider.get()); diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc index 6a52e91c066..05038746066 100644 --- a/chromium/cc/tiles/software_image_decode_cache.cc +++ b/chromium/cc/tiles/software_image_decode_cache.cc @@ -24,23 +24,6 @@ using base::trace_event::MemoryDumpLevelOfDetail; namespace cc { namespace { -bool UseCacheForDrawImage(const DrawImage& draw_image) { - // Lazy generated images are have their decode cached. - sk_sp<SkImage> sk_image = draw_image.paint_image().GetSkImage(); - if (sk_image->isLazyGenerated()) - return true; - - // Cache images that need to be converted to a non-sRGB color space. - // TODO(ccameron): Consider caching when any color conversion is required. - // https://crbug.com/791828 - const gfx::ColorSpace& dst_color_space = draw_image.target_color_space(); - if (dst_color_space.IsValid() && - dst_color_space != gfx::ColorSpace::CreateSRGB()) { - return true; - } - - return false; -} // The number of entries to keep around in the cache. This limit can be breached // if more items are locked. That is, locked items ignore this limit. @@ -172,6 +155,9 @@ SoftwareImageDecodeCache::SoftwareImageDecodeCache( } // Register this component with base::MemoryCoordinatorClientRegistry. base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this); + memory_pressure_listener_.reset(new base::MemoryPressureListener( + base::BindRepeating(&SoftwareImageDecodeCache::OnMemoryPressure, + base::Unretained(this)))); } SoftwareImageDecodeCache::~SoftwareImageDecodeCache() { @@ -383,43 +369,37 @@ void SoftwareImageDecodeCache::DecodeImageIfNecessary( base::AutoUnlock release(lock_); local_cache_entry = Utils::DoDecodeImage(key, paint_image, color_type_); } else { - // Use the full image decode to generate a scaled/subrected decode. - // TODO(vmpstr): This part needs to handle decode to scale. - base::Optional<CacheKey> candidate_key; - auto image_keys_it = frame_key_to_image_keys_.find(key.frame_key()); - // We know that we must have at least our own |entry| in this list, so it - // won't be empty. - DCHECK(image_keys_it != frame_key_to_image_keys_.end()); - - auto& available_keys = image_keys_it->second; - std::sort(available_keys.begin(), available_keys.end(), - [](const CacheKey& one, const CacheKey& two) { - // Return true if |one| scale is less than |two| scale. - return one.target_size().width() < two.target_size().width() && - one.target_size().height() < two.target_size().height(); - }); - - for (auto& available_key : available_keys) { - // Only consider keys coming from the same src rect, since otherwise the - // resulting image was extracted using a different src. - if (available_key.src_rect() != key.src_rect()) - continue; - - // that are at least as big as the required |key|. - if (available_key.target_size().width() < key.target_size().width() || - available_key.target_size().height() < key.target_size().height()) { - continue; - } - auto image_it = decoded_images_.Peek(available_key); - DCHECK(image_it != decoded_images_.end()); - auto* available_entry = image_it->second.get(); - if (available_entry->is_locked || available_entry->Lock()) { - candidate_key.emplace(available_key); - break; - } + // Attempt to find a cached decode to generate a scaled/subrected decode + // from. + base::Optional<CacheKey> candidate_key = FindCachedCandidate(key); + + SkISize desired_size = gfx::SizeToSkISize(key.target_size()); + const bool should_decode_to_scale = + // Prefer scaling from a cached decode instead of performing another + // decode to the desired size. + !candidate_key && + // We need the original decode to subrect before scaling, if a subrect + // is requested. + key.src_rect() == + gfx::Rect(paint_image.width(), paint_image.height()) && + // Note that in the case where we can't decode to the exact desired + // size, but a size lower than the original, it would be better to + // decode to that size and then scale to the desired size. But this + // should be rare in practice, since we only decode to mip levels. + paint_image.GetSupportedDecodeSize(desired_size) == desired_size; + + // We don't scale and cache the result if nearest neighbor is requested, + // i.e., the processing type should be kOriginal or kSubrectOriginal. And + // requesting a subrect already vetoes decode to scale. + DCHECK(!should_decode_to_scale || !key.is_nearest_neighbor()); + if (should_decode_to_scale) { + base::AutoUnlock release(lock_); + local_cache_entry = Utils::DoDecodeImage(key, paint_image, color_type_); } - if (!candidate_key) { + // Couldn't decode to scale or find a cached candidate. Create the + // intermediate candidate key required for this decode. + if (!should_decode_to_scale && !candidate_key) { // IMPORTANT: There is a bit of a subtlety here. We would normally want to // generate a new candidate with the key.src_rect() as the src_rect. This // would ensure that when scaling we won't need to peek pixels, since it's @@ -445,25 +425,27 @@ void SoftwareImageDecodeCache::DecodeImageIfNecessary( candidate_key.emplace( CacheKey::FromDrawImage(candidate_draw_image, color_type_)); } - CHECK(*candidate_key != key) << key.ToString(); - auto decoded_draw_image = - GetDecodedImageForDrawInternal(*candidate_key, paint_image); - if (!decoded_draw_image.image()) { - local_cache_entry = nullptr; - } else { - base::AutoUnlock release(lock_); - // IMPORTANT: More subtleties: - // If the candidate could have used the original decode, that means we - // need to extractSubset from it. In all other cases, this would have - // already been done to generate the candidate. - local_cache_entry = Utils::GenerateCacheEntryFromCandidate( - key, decoded_draw_image, candidate_key->type() == CacheKey::kOriginal, - color_type_); - } + if (candidate_key) { + CHECK(*candidate_key != key) << key.ToString(); + auto decoded_draw_image = + GetDecodedImageForDrawInternal(*candidate_key, paint_image); + if (!decoded_draw_image.image()) { + local_cache_entry = nullptr; + } else { + base::AutoUnlock release(lock_); + // IMPORTANT: More subtleties: + // If the candidate could have used the original decode, that means we + // need to extractSubset from it. In all other cases, this would have + // already been done to generate the candidate. + local_cache_entry = Utils::GenerateCacheEntryFromCandidate( + key, decoded_draw_image, + candidate_key->type() == CacheKey::kOriginal, color_type_); + } - // Unref to balance the GetDecodedImageForDrawInternal() call. - UnrefImage(*candidate_key); + // Unref to balance the GetDecodedImageForDrawInternal() call. + UnrefImage(*candidate_key); + } } if (!local_cache_entry) { @@ -488,15 +470,70 @@ void SoftwareImageDecodeCache::DecodeImageIfNecessary( } } +base::Optional<SoftwareImageDecodeCache::CacheKey> +SoftwareImageDecodeCache::FindCachedCandidate(const CacheKey& key) { + auto image_keys_it = frame_key_to_image_keys_.find(key.frame_key()); + // We know that we must have at least our own |entry| in this list, so it + // won't be empty. + DCHECK(image_keys_it != frame_key_to_image_keys_.end()); + + auto& available_keys = image_keys_it->second; + std::sort(available_keys.begin(), available_keys.end(), + [](const CacheKey& one, const CacheKey& two) { + // Return true if |one| scale is less than |two| scale. + return one.target_size().width() < two.target_size().width() && + one.target_size().height() < two.target_size().height(); + }); + + for (auto& available_key : available_keys) { + // Only consider keys coming from the same src rect, since otherwise the + // resulting image was extracted using a different src. + if (available_key.src_rect() != key.src_rect()) + continue; + + // That are at least as big as the required |key|. + if (available_key.target_size().width() < key.target_size().width() || + available_key.target_size().height() < key.target_size().height()) { + continue; + } + auto image_it = decoded_images_.Peek(available_key); + DCHECK(image_it != decoded_images_.end()); + auto* available_entry = image_it->second.get(); + if (available_entry->is_locked || available_entry->Lock()) { + return available_key; + } + } + + return base::nullopt; +} + +bool SoftwareImageDecodeCache::UseCacheForDrawImage( + const DrawImage& draw_image) const { + sk_sp<SkImage> sk_image = draw_image.paint_image().GetSkImage(); + + // Software cache doesn't support using texture backed images. + if (sk_image->isTextureBacked()) + return false; + + // Lazy generated images need to have their decode cached. + if (sk_image->isLazyGenerated()) + return true; + + // Cache images that need to be converted to a non-sRGB color space. + // TODO(ccameron): Consider caching when any color conversion is required. + // https://crbug.com/791828 + const gfx::ColorSpace& dst_color_space = draw_image.target_color_space(); + if (dst_color_space.IsValid() && + dst_color_space != gfx::ColorSpace::CreateSRGB()) { + return true; + } + + return false; +} + DecodedDrawImage SoftwareImageDecodeCache::GetDecodedImageForDraw( const DrawImage& draw_image) { - // Non-cached images are be used for raster directly. - if (!UseCacheForDrawImage(draw_image)) { - return DecodedDrawImage(draw_image.paint_image().GetSkImage(), - SkSize::Make(0, 0), SkSize::Make(1.f, 1.f), - draw_image.filter_quality(), - true /* is_budgeted */); - } + DCHECK(UseCacheForDrawImage(draw_image)); base::AutoLock hold(lock_); return GetDecodedImageForDrawInternal( @@ -538,9 +575,7 @@ DecodedDrawImage SoftwareImageDecodeCache::GetDecodedImageForDrawInternal( void SoftwareImageDecodeCache::DrawWithImageFinished( const DrawImage& image, const DecodedDrawImage& decoded_image) { - if (!UseCacheForDrawImage(image)) - return; - + DCHECK(UseCacheForDrawImage(image)); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::DrawWithImageFinished", "key", CacheKey::FromDrawImage(image, color_type_).ToString()); @@ -670,6 +705,19 @@ void SoftwareImageDecodeCache::OnPurgeMemory() { ReduceCacheUsageUntilWithinLimit(0); } +void SoftwareImageDecodeCache::OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level) { + base::AutoLock lock(lock_); + switch (level) { + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + break; + case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + ReduceCacheUsageUntilWithinLimit(0); + break; + } +} + SoftwareImageDecodeCache::CacheEntry* SoftwareImageDecodeCache::AddCacheEntry( const CacheKey& key) { lock_.AssertAcquired(); diff --git a/chromium/cc/tiles/software_image_decode_cache.h b/chromium/cc/tiles/software_image_decode_cache.h index 0fa379c2b2f..e3e6b74f24f 100644 --- a/chromium/cc/tiles/software_image_decode_cache.h +++ b/chromium/cc/tiles/software_image_decode_cache.h @@ -12,6 +12,7 @@ #include "base/containers/mru_cache.h" #include "base/memory/memory_coordinator_client.h" +#include "base/memory/memory_pressure_listener.h" #include "base/memory/ref_counted.h" #include "base/numerics/safe_math.h" #include "base/trace_event/memory_dump_provider.h" @@ -52,6 +53,7 @@ class CC_EXPORT SoftwareImageDecodeCache bool aggressively_free_resources) override {} void ClearCache() override; size_t GetMaximumMemoryLimitBytes() const override; + bool UseCacheForDrawImage(const DrawImage& image) 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. @@ -115,6 +117,11 @@ class CC_EXPORT SoftwareImageDecodeCache void OnMemoryStateChange(base::MemoryState state) override; void OnPurgeMemory() override; + // TODO(gyuyoung): OnMemoryPressure is deprecated. So this should be removed + // when the memory coordinator is enabled by default. + void OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level); + // Helper method to get the different tasks. Note that this should be used as // if it was public (ie, all of the locks need to be properly acquired). TaskResult GetTaskForImageAndRefInternal(const DrawImage& image, @@ -128,6 +135,7 @@ class CC_EXPORT SoftwareImageDecodeCache CacheEntry* cache_entry); void AddBudgetForImage(const CacheKey& key, CacheEntry* entry); void RemoveBudgetForImage(const CacheKey& key, CacheEntry* entry); + base::Optional<CacheKey> FindCachedCandidate(const CacheKey& key); void UnrefImage(const CacheKey& key); @@ -140,6 +148,8 @@ class CC_EXPORT SoftwareImageDecodeCache // Decoded images and ref counts (predecode path). ImageMRUCache decoded_images_; + std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; + // A map of PaintImage::FrameKey to the ImageKeys for cached decodes of this // PaintImage. std::unordered_map<PaintImage::FrameKey, diff --git a/chromium/cc/tiles/software_image_decode_cache_unittest.cc b/chromium/cc/tiles/software_image_decode_cache_unittest.cc index c9f8c5a55e0..9c0c143d167 100644 --- a/chromium/cc/tiles/software_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/software_image_decode_cache_unittest.cc @@ -396,9 +396,9 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_1Scale) { draw_image, kN32_SkColorType); EXPECT_EQ(draw_image.frame_key(), key.frame_key()); EXPECT_EQ(key.type(), SoftwareImageDecodeCache::CacheKey::kSubrectAndScale); - EXPECT_EQ(62, key.target_size().width()); + EXPECT_EQ(63, key.target_size().width()); EXPECT_EQ(25, key.target_size().height()); - EXPECT_EQ(62u * 25u * 4u, key.locked_bytes()); + EXPECT_EQ(63u * 25u * 4u, key.locked_bytes()); } TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_01Scale) { @@ -415,9 +415,9 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_01Scale) { draw_image, kN32_SkColorType); EXPECT_EQ(draw_image.frame_key(), key.frame_key()); EXPECT_EQ(key.type(), SoftwareImageDecodeCache::CacheKey::kSubrectAndScale); - EXPECT_EQ(7, key.target_size().width()); - EXPECT_EQ(3, key.target_size().height()); - EXPECT_EQ(7u * 3u * 4u, key.locked_bytes()); + EXPECT_EQ(8, key.target_size().width()); + EXPECT_EQ(4, key.target_size().height()); + EXPECT_EQ(8u * 4u * 4u, key.locked_bytes()); } TEST(SoftwareImageDecodeCacheTest, @@ -477,9 +477,9 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToMediumIfTooLarge) { draw_image, kN32_SkColorType); EXPECT_EQ(draw_image.frame_key(), key.frame_key()); EXPECT_EQ(key.type(), SoftwareImageDecodeCache::CacheKey::kSubrectAndScale); - EXPECT_EQ(2277, key.target_size().width()); + EXPECT_EQ(2278, key.target_size().width()); EXPECT_EQ(1024, key.target_size().height()); - EXPECT_EQ(2277u * 1024u * 4u, key.locked_bytes()); + EXPECT_EQ(2278u * 1024u * 4u, key.locked_bytes()); } TEST(SoftwareImageDecodeCacheTest, @@ -1445,7 +1445,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_1ScaleIsHandled) { // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); - EXPECT_EQ(62, decoded_draw_image.image()->width()); + EXPECT_EQ(63, decoded_draw_image.image()->width()); EXPECT_EQ(25, decoded_draw_image.image()->height()); cache.DrawWithImageFinished(draw_image, decoded_draw_image); @@ -1476,8 +1476,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_01ScaleIsHandled) { // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); - EXPECT_EQ(7, decoded_draw_image.image()->width()); - EXPECT_EQ(3, decoded_draw_image.image()->height()); + EXPECT_EQ(8, decoded_draw_image.image()->width()); + EXPECT_EQ(4, decoded_draw_image.image()->height()); cache.DrawWithImageFinished(draw_image, decoded_draw_image); cache.UnrefImage(draw_image); @@ -1734,12 +1734,8 @@ TEST(SoftwareImageDecodeCacheTest, BitmapImageNotColorConverted) { quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); - DecodedDrawImage decoded_draw_image = - cache.GetDecodedImageForDraw(draw_image); - // Expect that we did not allocate a new image. - EXPECT_EQ(decoded_draw_image.image().get(), paint_image.GetSkImage().get()); - - cache.DrawWithImageFinished(draw_image, decoded_draw_image); + // The cache should not support this image. + EXPECT_FALSE(cache.UseCacheForDrawImage(draw_image)); } // TODO(ccameron): Re-enable this when the root cause of crashes is discovered. @@ -1777,5 +1773,138 @@ TEST(SoftwareImageDecodeCacheTest, DISABLED_ContentIdCaching) { } } +TEST(SoftwareImageDecodeCacheTest, DecodeToScale) { + TestSoftwareImageDecodeCache cache; + bool is_decomposable = true; + SkFilterQuality quality = kMedium_SkFilterQuality; + + SkISize full_size = SkISize::Make(100, 100); + std::vector<SkISize> supported_sizes = {SkISize::Make(25, 25), + SkISize::Make(50, 50)}; + std::vector<FrameMetadata> frames = {FrameMetadata()}; + sk_sp<FakePaintImageGenerator> generator = + sk_make_sp<FakePaintImageGenerator>( + SkImageInfo::MakeN32Premul(full_size.width(), full_size.height(), + DefaultColorSpace().ToSkColorSpace()), + frames, true, supported_sizes); + PaintImage paint_image = PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_paint_image_generator(generator) + .set_frame_index(0u) + .TakePaintImage(); + + // Scale to mip level 1, there should be a single entry in the cache from + // the direct decode. + DrawImage draw_image1( + paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), + quality, CreateMatrix(SkSize::Make(0.5, 0.5), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DecodedDrawImage decoded_image1 = cache.GetDecodedImageForDraw(draw_image1); + ASSERT_TRUE(decoded_image1.image()); + EXPECT_EQ(decoded_image1.image()->width(), 50); + EXPECT_EQ(decoded_image1.image()->height(), 50); + EXPECT_EQ(cache.GetNumCacheEntriesForTesting(), 1u); + + // We should have requested a scaled decode from the generator. + ASSERT_EQ(generator->decode_infos().size(), 1u); + EXPECT_EQ(generator->decode_infos().at(0).width(), 50); + EXPECT_EQ(generator->decode_infos().at(0).height(), 50); + + // Scale to mip level 2, we should be using the existing entry instead of + // re-decoding. + DrawImage draw_image2( + paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), + quality, CreateMatrix(SkSize::Make(0.25, 0.25), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DecodedDrawImage decoded_image2 = cache.GetDecodedImageForDraw(draw_image2); + ASSERT_TRUE(decoded_image2.image()); + EXPECT_EQ(decoded_image2.image()->width(), 25); + EXPECT_EQ(decoded_image2.image()->height(), 25); + EXPECT_EQ(cache.GetNumCacheEntriesForTesting(), 2u); + + // Since we scaled from the existing entry, no new decodes should be + // requested from the generator. + ASSERT_EQ(generator->decode_infos().size(), 1u); + EXPECT_EQ(generator->decode_infos().at(0).width(), 50); + EXPECT_EQ(generator->decode_infos().at(0).height(), 50); + + cache.DrawWithImageFinished(draw_image1, decoded_image1); + cache.DrawWithImageFinished(draw_image2, decoded_image2); +} + +TEST(SoftwareImageDecodeCacheTest, DecodeToScaleSubrect) { + TestSoftwareImageDecodeCache cache; + bool is_decomposable = true; + SkFilterQuality quality = kMedium_SkFilterQuality; + + SkISize full_size = SkISize::Make(100, 100); + std::vector<SkISize> supported_sizes = {SkISize::Make(25, 25), + SkISize::Make(50, 50)}; + std::vector<FrameMetadata> frames = {FrameMetadata()}; + sk_sp<FakePaintImageGenerator> generator = + sk_make_sp<FakePaintImageGenerator>( + SkImageInfo::MakeN32Premul(full_size.width(), full_size.height(), + DefaultColorSpace().ToSkColorSpace()), + frames, true, supported_sizes); + PaintImage paint_image = PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_paint_image_generator(generator) + .set_frame_index(0u) + .TakePaintImage(); + + // Scale to mip level 1, there should be 2 entries in the cache, since the + // subrect vetoes decode to scale. + DrawImage draw_image(paint_image, SkIRect::MakeWH(50, 50), quality, + CreateMatrix(SkSize::Make(0.5, 0.5), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DecodedDrawImage decoded_image = cache.GetDecodedImageForDraw(draw_image); + ASSERT_TRUE(decoded_image.image()); + EXPECT_EQ(decoded_image.image()->width(), 25); + EXPECT_EQ(decoded_image.image()->height(), 25); + EXPECT_EQ(cache.GetNumCacheEntriesForTesting(), 2u); + + // We should have requested the original decode from the generator. + ASSERT_EQ(generator->decode_infos().size(), 1u); + EXPECT_EQ(generator->decode_infos().at(0).width(), 100); + EXPECT_EQ(generator->decode_infos().at(0).height(), 100); + cache.DrawWithImageFinished(draw_image, decoded_image); +} + +TEST(SoftwareImageDecodeCacheTest, DecodeToScaleNoneQuality) { + TestSoftwareImageDecodeCache cache; + bool is_decomposable = true; + SkFilterQuality quality = kNone_SkFilterQuality; + + SkISize full_size = SkISize::Make(100, 100); + std::vector<SkISize> supported_sizes = {SkISize::Make(25, 25), + SkISize::Make(50, 50)}; + std::vector<FrameMetadata> frames = {FrameMetadata()}; + sk_sp<FakePaintImageGenerator> generator = + sk_make_sp<FakePaintImageGenerator>( + SkImageInfo::MakeN32Premul(full_size.width(), full_size.height(), + DefaultColorSpace().ToSkColorSpace()), + frames, true, supported_sizes); + PaintImage paint_image = PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_paint_image_generator(generator) + .set_frame_index(0u) + .TakePaintImage(); + + DrawImage draw_image( + paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), + quality, CreateMatrix(SkSize::Make(0.5, 0.5), is_decomposable), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DecodedDrawImage decoded_image = cache.GetDecodedImageForDraw(draw_image); + ASSERT_TRUE(decoded_image.image()); + EXPECT_EQ(decoded_image.image()->width(), 100); + EXPECT_EQ(decoded_image.image()->height(), 100); + + // We should have requested the original decode from the generator. + ASSERT_EQ(generator->decode_infos().size(), 1u); + EXPECT_EQ(generator->decode_infos().at(0).width(), 100); + EXPECT_EQ(generator->decode_infos().at(0).height(), 100); + cache.DrawWithImageFinished(draw_image, decoded_image); +} + } // namespace } // namespace cc diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.cc b/chromium/cc/tiles/software_image_decode_cache_utils.cc index 4b6016b4126..565a0ffbc45 100644 --- a/chromium/cc/tiles/software_image_decode_cache_utils.cc +++ b/chromium/cc/tiles/software_image_decode_cache_utils.cc @@ -71,7 +71,6 @@ SoftwareImageDecodeCacheUtils::DoDecodeImage(const CacheKey& key, TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCacheUtils::DoDecodeImage - " "decode"); - DCHECK_EQ(key.type(), CacheKey::kOriginal); bool result = paint_image.Decode(target_pixels->data(), &target_info, key.target_color_space().ToSkColorSpace(), key.frame_key().frame_index()); diff --git a/chromium/cc/tiles/tile_manager_perftest.cc b/chromium/cc/tiles/tile_manager_perftest.cc index 6bb3562ab13..8b30a30b947 100644 --- a/chromium/cc/tiles/tile_manager_perftest.cc +++ b/chromium/cc/tiles/tile_manager_perftest.cc @@ -45,9 +45,9 @@ class TileManagerPerfTest : public TestLayerTreeHostBase { base::TimeDelta::FromMilliseconds(kTimeLimitMillis), kTimeCheckInterval) {} - void InitializeRenderer() override { + void InitializeFrameSink() override { host_impl()->SetVisible(true); - host_impl()->InitializeRenderer(layer_tree_frame_sink()); + host_impl()->InitializeFrameSink(layer_tree_frame_sink()); tile_manager()->SetTileTaskManagerForTesting( std::make_unique<FakeTileTaskManagerImpl>()); } diff --git a/chromium/cc/trees/animation_options.h b/chromium/cc/trees/animation_options.h new file mode 100644 index 00000000000..bce98cda7dc --- /dev/null +++ b/chromium/cc/trees/animation_options.h @@ -0,0 +1,25 @@ +// Copyright 2018 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_TREES_ANIMATION_OPTIONS_H_ +#define CC_TREES_ANIMATION_OPTIONS_H_ + +#include <memory> + +#include "cc/cc_export.h" + +namespace cc { + +// This class acts as opaque handle in cc while the actual implementation lives +// in blink. It is meant to facilitate plumbing the options from blink main +// thread to worklet thread via cc animations machinery. +class CC_EXPORT AnimationOptions { + public: + virtual ~AnimationOptions() = default; + virtual std::unique_ptr<AnimationOptions> Clone() const = 0; +}; + +} // namespace cc + +#endif // CC_TREES_ANIMATION_OPTIONS_H_ diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index b888d085203..1628a53c0c6 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -13,6 +13,7 @@ #include "cc/layers/draw_properties.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/picture_layer.h" #include "cc/trees/clip_node.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" @@ -490,53 +491,6 @@ static void UpdateElasticOverscrollInternal( property_trees->transform_tree.set_needs_update(true); } -#if DCHECK_IS_ON() -static void ValidatePageScaleLayer(const Layer* page_scale_layer) { - DCHECK_EQ(page_scale_layer->position().ToString(), gfx::PointF().ToString()); - DCHECK_EQ(page_scale_layer->transform_origin().ToString(), - gfx::Point3F().ToString()); -} - -static void ValidatePageScaleLayer(const LayerImpl* page_scale_layer) {} -#endif - -template <typename LayerType> -static void UpdatePageScaleFactorInternal(PropertyTrees* property_trees, - const LayerType* page_scale_layer, - float page_scale_factor, - float device_scale_factor, - gfx::Transform device_transform) { - if (property_trees->transform_tree.page_scale_factor() == page_scale_factor || - !page_scale_layer) { - return; - } - - property_trees->transform_tree.set_page_scale_factor(page_scale_factor); - DCHECK_GE(page_scale_layer->transform_tree_index(), - TransformTree::kRootNodeId); - TransformNode* node = property_trees->transform_tree.Node( - page_scale_layer->transform_tree_index()); -// TODO(enne): property trees can't ask the layer these things, but -// the page scale layer should *just* be the page scale. -#if DCHECK_IS_ON() - ValidatePageScaleLayer(page_scale_layer); -#endif - - if (IsRootLayer(page_scale_layer)) { - // When the page scale layer is also the root layer, the node should also - // store the combined scale factor and not just the page scale factor. - float post_local_scale_factor = page_scale_factor * device_scale_factor; - node->post_local_scale_factor = post_local_scale_factor; - node->post_local = device_transform; - node->post_local.Scale(post_local_scale_factor, post_local_scale_factor); - } else { - node->post_local_scale_factor = page_scale_factor; - node->update_post_local_transform(gfx::PointF(), gfx::Point3F()); - } - node->needs_local_transform_update = true; - property_trees->transform_tree.set_needs_update(true); -} - static gfx::Rect LayerDrawableContentRect( const LayerImpl* layer, const gfx::Rect& layer_bounds_in_target_space, @@ -609,7 +563,7 @@ static gfx::Transform ScreenSpaceTransformInternal(LayerType* layer, layer->offset_to_transform_parent().y()); gfx::Transform ssxform = tree.ToScreen(layer->transform_tree_index()); xform.ConcatTransform(ssxform); - if (layer->should_flatten_transform_from_property_tree()) + if (layer->should_flatten_screen_space_transform_from_property_tree()) xform.FlattenTo2d(); return xform; } @@ -821,7 +775,7 @@ void FindLayersThatNeedUpdates(LayerTreeHost* layer_tree_host, // Append mask layers to the update layer list. They don't have valid // visible rects, so need to get added after the above calculation. - if (Layer* mask_layer = layer->mask_layer()) { + if (PictureLayer* mask_layer = layer->mask_layer()) { // Layers with empty bounds should never be painted, including masks. if (!mask_layer->bounds().IsEmpty()) update_layer_list->push_back(mask_layer); @@ -935,7 +889,7 @@ gfx::Transform DrawTransform(const LayerImpl* layer, transform_tree.property_trees()->GetToTarget( layer->transform_tree_index(), layer->render_target_effect_tree_index(), &xform); - if (layer->should_flatten_transform_from_property_tree()) + if (layer->should_flatten_screen_space_transform_from_property_tree()) xform.FlattenTo2d(); xform.Translate(layer->offset_to_transform_parent().x(), layer->offset_to_transform_parent().y()); @@ -1047,23 +1001,27 @@ void ComputeSurfaceDrawProperties(PropertyTrees* property_trees, } void UpdatePageScaleFactor(PropertyTrees* property_trees, - const LayerImpl* page_scale_layer, + TransformNode* page_scale_node, float page_scale_factor, float device_scale_factor, const gfx::Transform device_transform) { - UpdatePageScaleFactorInternal(property_trees, page_scale_layer, - page_scale_factor, device_scale_factor, - device_transform); -} + // TODO(wjmaclean): Once Issue #845097 is resolved, we can change the nullptr + // check below to a DCHECK. + if (property_trees->transform_tree.page_scale_factor() == page_scale_factor || + !page_scale_node) { + return; + } -void UpdatePageScaleFactor(PropertyTrees* property_trees, - const Layer* page_scale_layer, - float page_scale_factor, - float device_scale_factor, - const gfx::Transform device_transform) { - UpdatePageScaleFactorInternal(property_trees, page_scale_layer, - page_scale_factor, device_scale_factor, - device_transform); + property_trees->transform_tree.set_page_scale_factor(page_scale_factor); + + float post_local_scale_factor = page_scale_factor * device_scale_factor; + page_scale_node->post_local_scale_factor = post_local_scale_factor; + page_scale_node->post_local = device_transform; + page_scale_node->post_local.Scale(post_local_scale_factor, + post_local_scale_factor); + + page_scale_node->needs_local_transform_update = true; + property_trees->transform_tree.set_needs_update(true); } void UpdateElasticOverscroll(PropertyTrees* property_trees, diff --git a/chromium/cc/trees/draw_property_utils.h b/chromium/cc/trees/draw_property_utils.h index 6c843521df6..af88fbf17a4 100644 --- a/chromium/cc/trees/draw_property_utils.h +++ b/chromium/cc/trees/draw_property_utils.h @@ -24,6 +24,7 @@ class EffectTree; class TransformTree; class PropertyTrees; struct EffectNode; +struct TransformNode; namespace draw_property_utils { @@ -88,13 +89,7 @@ gfx::Transform CC_EXPORT ScreenSpaceTransform(const LayerImpl* layer, const TransformTree& tree); void CC_EXPORT UpdatePageScaleFactor(PropertyTrees* property_trees, - const LayerImpl* page_scale_layer, - float page_scale_factor, - float device_scale_factor, - const gfx::Transform device_transform); - -void CC_EXPORT UpdatePageScaleFactor(PropertyTrees* property_trees, - const Layer* page_scale_layer, + TransformNode* page_scale_node, float page_scale_factor, float device_scale_factor, const gfx::Transform device_transform); diff --git a/chromium/cc/trees/element_id.cc b/chromium/cc/trees/element_id.cc index fe33e57dfef..0603ba726c7 100644 --- a/chromium/cc/trees/element_id.cc +++ b/chromium/cc/trees/element_id.cc @@ -40,7 +40,7 @@ void ElementId::AddToTracedValue(base::trace_event::TracedValue* res) const { res->EndDictionary(); } -ElementIdType ElementId::ToInternalValue() const { +ElementIdType ElementId::GetInternalValue() const { return id_; } diff --git a/chromium/cc/trees/element_id.h b/chromium/cc/trees/element_id.h index 6f456b1163e..1b581ba881c 100644 --- a/chromium/cc/trees/element_id.h +++ b/chromium/cc/trees/element_id.h @@ -60,9 +60,7 @@ struct CC_EXPORT ElementId { void AddToTracedValue(base::trace_event::TracedValue* res) const; std::unique_ptr<base::Value> AsValue() const; - // Returns the element id as its underlying representation. - // TODO(wkorman): Remove or rename this method. http://crbug.com/752327 - ElementIdType ToInternalValue() const; + ElementIdType GetInternalValue() const; std::string ToString() const; diff --git a/chromium/cc/trees/frame_rate_counter.cc b/chromium/cc/trees/frame_rate_counter.cc index f801dbf640d..ac4d517f5d2 100644 --- a/chromium/cc/trees/frame_rate_counter.cc +++ b/chromium/cc/trees/frame_rate_counter.cc @@ -57,18 +57,6 @@ void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp, bool software) { base::TimeDelta frame_interval_seconds = RecentFrameInterval(ring_buffer_.BufferSize() - 1); - if (has_impl_thread_ && ring_buffer_.CurrentIndex() > 0) { - if (software) { - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Renderer4.SoftwareCompositorThreadImplDrawDelay", - frame_interval_seconds.InMilliseconds(), 1, 120, 60); - } else { - UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay", - frame_interval_seconds.InMilliseconds(), 1, - 120, 60); - } - } - if (!IsBadFrameInterval(frame_interval_seconds) && frame_interval_seconds.InSecondsF() > kDroppedFrameTime) dropped_frame_count_ += diff --git a/chromium/cc/trees/frame_token_allocator.cc b/chromium/cc/trees/frame_token_allocator.cc deleted file mode 100644 index 4f6ec1cf91c..00000000000 --- a/chromium/cc/trees/frame_token_allocator.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 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/trees/frame_token_allocator.h" - -namespace cc { - -uint32_t FrameTokenAllocator::GetOrAllocateFrameToken() { - if (frame_token_allocated_) - return frame_token_; - frame_token_allocated_ = true; - - // TODO(jonross) we will want to have a wrapping strategy to handle overflow. - // We will also want to confirm this looping behaviour in processes which - // receive the token. - return ++frame_token_; -} - -uint32_t FrameTokenAllocator::GetFrameTokenForSubmission() { - uint32_t result = frame_token_allocated_ ? frame_token_ : 0; - frame_token_allocated_ = false; - return result; -} - -} // namespace cc diff --git a/chromium/cc/trees/frame_token_allocator.h b/chromium/cc/trees/frame_token_allocator.h deleted file mode 100644 index 1bb978d631f..00000000000 --- a/chromium/cc/trees/frame_token_allocator.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018 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_TREES_FRAME_TOKEN_ALLOCATOR_H_ -#define CC_TREES_FRAME_TOKEN_ALLOCATOR_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "cc/cc_export.h" - -namespace cc { - -// For some compositor frame submissions, there is additional work which a frame -// embedder wishes to perform only after the frame has been processed by the -// display compositor. -// -// For this a FrameToken is sent both with the compositor frame submission, as -// well as with messages to the embedder. -// -// However for any given frame there are multiple possible sources which may -// wish to increment the FrameToken. FrameTokenAllocator is a shared source of -// these tokens, only ever increasing the token once during a given frame -// submission. -class CC_EXPORT FrameTokenAllocator { - public: - FrameTokenAllocator() = default; - virtual ~FrameTokenAllocator() = default; - - // During frame submission the first call to this allocates and returns a new - // frame token. All subsequent calls return the current frame token. - uint32_t GetOrAllocateFrameToken(); - - // Gets the token for the current frame submission, signifying the end of - // frame submission. Or 0 is no token was allocated. The next call to - // GetOrAllocateFrameToken will lead to the generation of a new frame token. - uint32_t GetFrameTokenForSubmission(); - - private: - // True if a frame token is allocated during the current frame submission. - bool frame_token_allocated_ = false; - - // The current frame token. - uint32_t frame_token_ = 0; - - DISALLOW_COPY_AND_ASSIGN(FrameTokenAllocator); -}; - -} // namespace cc - -#endif // CC_TREES_FRAME_TOKEN_ALLOCATOR_H_ diff --git a/chromium/cc/trees/latency_info_swap_promise.cc b/chromium/cc/trees/latency_info_swap_promise.cc index 9b376030ed5..208582c6e3e 100644 --- a/chromium/cc/trees/latency_info_swap_promise.cc +++ b/chromium/cc/trees/latency_info_swap_promise.cc @@ -9,23 +9,6 @@ #include "base/logging.h" #include "base/trace_event/trace_event.h" -namespace { -ui::LatencyComponentType DidNotSwapReasonToLatencyComponentType( - cc::SwapPromise::DidNotSwapReason reason) { - switch (reason) { - case cc::SwapPromise::ACTIVATION_FAILS: - case cc::SwapPromise::SWAP_FAILS: - return ui::INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT; - case cc::SwapPromise::COMMIT_FAILS: - return ui::INPUT_EVENT_LATENCY_TERMINATED_COMMIT_FAILED_COMPONENT; - case cc::SwapPromise::COMMIT_NO_UPDATE: - return ui::INPUT_EVENT_LATENCY_TERMINATED_COMMIT_NO_UPDATE_COMPONENT; - } - NOTREACHED() << "Unhandled DidNotSwapReason."; - return ui::INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT; -} -} // namespace - namespace cc { LatencyInfoSwapPromise::LatencyInfoSwapPromise(const ui::LatencyInfo& latency) @@ -33,22 +16,18 @@ LatencyInfoSwapPromise::LatencyInfoSwapPromise(const ui::LatencyInfo& latency) LatencyInfoSwapPromise::~LatencyInfoSwapPromise() = default; -void LatencyInfoSwapPromise::WillSwap( - viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator) { +void LatencyInfoSwapPromise::WillSwap(viz::CompositorFrameMetadata* metadata) { DCHECK(!latency_.terminated()); metadata->latency_info.push_back(latency_); } void LatencyInfoSwapPromise::DidSwap() {} -SwapPromise::DidNotSwapAction LatencyInfoSwapPromise::DidNotSwap( - DidNotSwapReason reason) { - latency_.AddLatencyNumber(DidNotSwapReasonToLatencyComponentType(reason), 0); +void LatencyInfoSwapPromise::DidNotSwap(DidNotSwapReason reason) { + latency_.Terminate(); // TODO(miletus): Turn this back on once per-event LatencyInfo tracking // is enabled in GPU side. // DCHECK(latency_.terminated); - return DidNotSwapAction::BREAK_PROMISE; } int64_t LatencyInfoSwapPromise::TraceId() const { diff --git a/chromium/cc/trees/latency_info_swap_promise.h b/chromium/cc/trees/latency_info_swap_promise.h index bdccb3eb546..2f2fdee234a 100644 --- a/chromium/cc/trees/latency_info_swap_promise.h +++ b/chromium/cc/trees/latency_info_swap_promise.h @@ -19,10 +19,9 @@ class CC_EXPORT LatencyInfoSwapPromise : public SwapPromise { ~LatencyInfoSwapPromise() override; void DidActivate() override {} - void WillSwap(viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator) override; + void WillSwap(viz::CompositorFrameMetadata* metadata) override; void DidSwap() override; - DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override; + void DidNotSwap(DidNotSwapReason reason) override; void OnCommit() override; int64_t TraceId() const override; diff --git a/chromium/cc/trees/latency_info_swap_promise_monitor.cc b/chromium/cc/trees/latency_info_swap_promise_monitor.cc index 99ec2c686a4..5da4b6895b3 100644 --- a/chromium/cc/trees/latency_info_swap_promise_monitor.cc +++ b/chromium/cc/trees/latency_info_swap_promise_monitor.cc @@ -19,19 +19,19 @@ bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info, ui::LatencyComponentType type = on_main ? ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT : ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT; - if (latency_info->FindLatency(type, 0, nullptr)) + if (latency_info->FindLatency(type, nullptr)) return false; - latency_info->AddLatencyNumber(type, 0); + latency_info->AddLatencyNumber(type); return true; } bool AddForwardingScrollUpdateToMainComponent(ui::LatencyInfo* latency_info) { if (latency_info->FindLatency( - ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT, 0, + ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT, nullptr)) return false; latency_info->AddLatencyNumber( - ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT, 0); + ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT); return true; } @@ -74,7 +74,7 @@ void LatencyInfoSwapPromiseMonitor::OnForwardScrollUpdateToMainThreadOnImpl() { *latency_, ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT); new_latency.AddLatencyNumberWithTraceName( - ui::LATENCY_BEGIN_SCROLL_LISTENER_UPDATE_MAIN_COMPONENT, 0, + ui::LATENCY_BEGIN_SCROLL_LISTENER_UPDATE_MAIN_COMPONENT, "ScrollUpdate"); std::unique_ptr<SwapPromise> swap_promise( new LatencyInfoSwapPromise(new_latency)); diff --git a/chromium/cc/trees/layer_tree_frame_sink.h b/chromium/cc/trees/layer_tree_frame_sink.h index 7f550dc9fe8..edbdd247f15 100644 --- a/chromium/cc/trees/layer_tree_frame_sink.h +++ b/chromium/cc/trees/layer_tree_frame_sink.h @@ -14,11 +14,11 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_checker.h" #include "cc/cc_export.h" +#include "components/viz/client/shared_bitmap_reporter.h" #include "components/viz/common/gpu/context_lost_observer.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/gpu/raster_context_provider.h" #include "components/viz/common/resources/returned_resource.h" -#include "components/viz/common/resources/shared_bitmap_reporter.h" #include "gpu/command_buffer/common/texture_in_use_response.h" #include "ui/gfx/color_space.h" @@ -44,21 +44,6 @@ class LayerTreeFrameSinkClient; class CC_EXPORT LayerTreeFrameSink : public viz::SharedBitmapReporter, public viz::ContextLostObserver { public: - struct Capabilities { - Capabilities() = default; - - // 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; - }; - // Constructor for GL-based and/or software resources. // // |compositor_task_runner| is used to post worker context lost callback and @@ -96,8 +81,6 @@ class CC_EXPORT LayerTreeFrameSink : public viz::SharedBitmapReporter, bool HasClient() { return !!client_; } - const Capabilities& capabilities() const { return capabilities_; } - // The viz::ContextProviders may be null if frames should be submitted with // software SharedMemory resources. viz::ContextProvider* context_provider() const { @@ -116,8 +99,9 @@ class CC_EXPORT LayerTreeFrameSink : public viz::SharedBitmapReporter, // Support for a pull-model where draws are requested by the implementation of // LayerTreeFrameSink. This is called by the compositor to notify that there's - // new content. - virtual void Invalidate() {} + // new content. Can be called when nothing needs to be drawn if tile + // priorities should be updated. + virtual void Invalidate(bool needs_draw) {} // For successful swaps, the implementation must call // DidReceiveCompositorFrameAck() asynchronously when the frame has been @@ -141,7 +125,6 @@ class CC_EXPORT LayerTreeFrameSink : public viz::SharedBitmapReporter, LayerTreeFrameSinkClient* client_ = nullptr; - struct LayerTreeFrameSink::Capabilities capabilities_; scoped_refptr<viz::ContextProvider> context_provider_; scoped_refptr<viz::RasterContextProvider> worker_context_provider_; scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_; diff --git a/chromium/cc/trees/layer_tree_frame_sink_client.h b/chromium/cc/trees/layer_tree_frame_sink_client.h index d774db56e35..87fc6b8b11e 100644 --- a/chromium/cc/trees/layer_tree_frame_sink_client.h +++ b/chromium/cc/trees/layer_tree_frame_sink_client.h @@ -15,6 +15,7 @@ #include "ui/gfx/geometry/rect.h" namespace gfx { +struct PresentationFeedback; class Transform; } @@ -58,12 +59,9 @@ class CC_EXPORT LayerTreeFrameSinkClient { // See ui/gfx/presentation_feedback.h for details on args. |time| is always // non-zero. - virtual void DidPresentCompositorFrame(uint32_t presentation_token, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) = 0; - - virtual void DidDiscardCompositorFrame(uint32_t presentation_token) = 0; + virtual void DidPresentCompositorFrame( + uint32_t presentation_token, + const gfx::PresentationFeedback& feedback) = 0; // The LayerTreeFrameSink is lost when the viz::ContextProviders held by it // encounter an error. In this case the LayerTreeFrameSink (and the @@ -74,7 +72,8 @@ class CC_EXPORT LayerTreeFrameSinkClient { // a new CompositorFrame synchronously. virtual void OnDraw(const gfx::Transform& transform, const gfx::Rect& viewport, - bool resourceless_software_draw) = 0; + bool resourceless_software_draw, + bool skip_draw) = 0; // For SynchronousCompositor (WebView) to set how much memory the compositor // can use without changing visibility. diff --git a/chromium/cc/trees/layer_tree_frame_sink_unittest.cc b/chromium/cc/trees/layer_tree_frame_sink_unittest.cc index 781effb7740..355b818c48b 100644 --- a/chromium/cc/trees/layer_tree_frame_sink_unittest.cc +++ b/chromium/cc/trees/layer_tree_frame_sink_unittest.cc @@ -9,7 +9,7 @@ #include "cc/test/fake_layer_tree_frame_sink_client.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/test/test_context_provider.h" -#include "components/viz/test/test_web_graphics_context_3d.h" +#include "components/viz/test/test_gles2_interface.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/raster_interface.h" #include "testing/gtest/include/gtest/gtest.h" @@ -66,7 +66,7 @@ TEST(LayerTreeFrameSinkTest, ContextLossFailsBind) { viz::TestContextProvider::CreateWorker(); // Lose the context so BindToClient fails. - context_provider->UnboundTestContext3d()->set_context_lost(true); + context_provider->UnboundTestContextGL()->set_context_lost(true); auto task_runner = base::MakeRefCounted<base::TestSimpleTaskRunner>(); StubLayerTreeFrameSink layer_tree_frame_sink(context_provider, @@ -112,7 +112,7 @@ TEST(LayerTreeFrameSinkTest, WorkerContextLossFailsBind) { viz::TestContextProvider::CreateWorker(); // Lose the context so BindToClient fails. - worker_provider->UnboundTestContext3d()->set_context_lost(true); + worker_provider->UnboundTestContextGL()->set_context_lost(true); auto task_runner = base::MakeRefCounted<base::TestSimpleTaskRunner>(); StubLayerTreeFrameSink layer_tree_frame_sink(context_provider, diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index b47182219c0..09f2fe5e574 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -16,6 +16,7 @@ #include "base/auto_reset.h" #include "base/bind.h" #include "base/command_line.h" +#include "base/json/json_writer.h" #include "base/location.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -61,6 +62,7 @@ #include "services/metrics/public/cpp/ukm_recorder.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" +#include "ui/gfx/presentation_feedback.h" namespace { static base::AtomicSequenceNumber s_layer_tree_host_sequence_number; @@ -265,10 +267,6 @@ void LayerTreeHost::BeginMainFrame(const viz::BeginFrameArgs& args) { client_->BeginMainFrame(args); } -void LayerTreeHost::DidStopFlinging() { - proxy_->MainThreadHasStoppedFlinging(); -} - const LayerTreeDebugState& LayerTreeHost::GetDebugState() const { return debug_state_; } @@ -299,32 +297,21 @@ void LayerTreeHost::FinishCommitOnImplThread( } sync_tree->set_source_frame_number(SourceFrameNumber()); - - // Set presentation token if any pending . - bool request_presentation_time = settings_.always_request_presentation_time; - if (!pending_presentation_time_callbacks_.empty()) { - request_presentation_time = true; - frame_to_presentation_time_callbacks_[SourceFrameNumber()] = - std::move(pending_presentation_time_callbacks_); - pending_presentation_time_callbacks_.clear(); - } else if (!frame_to_presentation_time_callbacks_.empty()) { - // There are pending callbacks. Keep requesting the presentation callback - // in case a previous frame was dropped (the callbacks run when the frame - // makes it to screen). - request_presentation_time = true; - } - sync_tree->set_request_presentation_time(request_presentation_time); + bool request_presentation_time = + settings_.always_request_presentation_time || + !pending_presentation_time_callbacks_.empty(); + if (request_presentation_time && pending_presentation_time_callbacks_.empty()) + pending_presentation_time_callbacks_.push_back(base::DoNothing()); + sync_tree->AddPresentationCallbacks( + std::move(pending_presentation_time_callbacks_)); if (needs_full_tree_sync_) TreeSynchronizer::SynchronizeTrees(root_layer(), 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) { - TRACE_EVENT0("cc,benchmark", "LayerTreeHost::DidNavigate"); - proxy_->ClearHistoryOnNavigation(); - host_impl->DidNavigate(); + if (clear_caches_on_next_commit_) { + clear_caches_on_next_commit_ = false; + proxy_->ClearHistory(); + host_impl->ClearCaches(); } { @@ -333,7 +320,7 @@ void LayerTreeHost::FinishCommitOnImplThread( PushPropertyTreesTo(sync_tree); sync_tree->lifecycle().AdvanceTo(LayerTreeLifecycle::kSyncedPropertyTrees); - PushSurfaceIdsTo(sync_tree); + PushSurfaceRangesTo(sync_tree); TreeSynchronizer::PushLayerProperties(this, sync_tree); sync_tree->lifecycle().AdvanceTo( LayerTreeLifecycle::kSyncedLayerProperties); @@ -546,6 +533,10 @@ void LayerTreeHost::SetNeedsCommit() { swap_promise_manager_.NotifySwapPromiseMonitorsOfSetNeedsCommit(); } +bool LayerTreeHost::RequestedMainFramePending() { + return proxy_->RequestedAnimatePending(); +} + void LayerTreeHost::SetNeedsRecalculateRasterScales() { next_commit_forces_recalculate_raster_scales_ = true; proxy_->SetNeedsCommit(); @@ -647,20 +638,6 @@ void LayerTreeHost::Composite(base::TimeTicks frame_begin_time, bool raster) { proxy->CompositeImmediately(frame_begin_time, raster); } -static int GetLayersUpdateTimeHistogramBucket(size_t numLayers) { - // We uses the following exponential (ratio 2) bucketization: - // [0, 10), [10, 30), [30, 70), [70, 150), [150, infinity) - if (numLayers < 10) - return 0; - if (numLayers < 30) - return 1; - if (numLayers < 70) - return 2; - if (numLayers < 150) - return 3; - return 4; -} - bool LayerTreeHost::UpdateLayers() { if (!root_layer()) { property_trees_.clear(); @@ -678,30 +655,18 @@ bool LayerTreeHost::UpdateLayers() { std::string histogram_name = base::StringPrintf("Compositing.%s.LayersUpdateTime", client_name); base::UmaHistogramCounts10M(histogram_name, elapsed); - - // Also add UpdateLayers metrics bucketed by the layer count. - base::StringAppendF(&histogram_name, ".%d", - GetLayersUpdateTimeHistogramBucket(NumLayers())); - base::UmaHistogramCounts10M(histogram_name, elapsed); } return result; } void LayerTreeHost::DidPresentCompositorFrame( - const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) { - for (int frame : source_frames) { - if (!frame_to_presentation_time_callbacks_.count(frame)) - continue; - - for (auto& callback : frame_to_presentation_time_callbacks_[frame]) - std::move(callback).Run(time, refresh, flags); - - frame_to_presentation_time_callbacks_.erase(frame); - } + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback) { + for (auto& callback : callbacks) + std::move(callback).Run(feedback); + client_->DidPresentCompositorFrame(frame_token, feedback); } void LayerTreeHost::DidCompletePageScaleAnimation() { @@ -803,23 +768,51 @@ bool LayerTreeHost::DoUpdateLayers(Layer* root_layer) { property_trees->AsTracedValue()); } +#if DCHECK_IS_ON() + // Ensure property tree nodes were created for all layers. When using layer + // lists, this can fail if blink doesn't setup layers or nodes correctly in + // |PaintArtifactCompositor|. When not using layer lists, this can fail if + // |PropertyTreeBuilder::BuildPropertyTrees| fails to create property tree + // nodes. + for (auto* layer : *this) { + DCHECK(property_trees_.effect_tree.Node(layer->effect_tree_index())); + DCHECK( + property_trees_.transform_tree.Node(layer->transform_tree_index())); + DCHECK(property_trees_.clip_tree.Node(layer->clip_tree_index())); + DCHECK(property_trees_.scroll_tree.Node(layer->scroll_tree_index())); + } +#endif + draw_property_utils::UpdatePropertyTrees(this, property_trees); draw_property_utils::FindLayersThatNeedUpdates(this, property_trees, &update_layer_list); + + // Dump property trees useful for debugging --blink-gen-property-trees + // flag. We care only about the renderer compositor. + if (VLOG_IS_ON(3) && GetClientNameForMetrics() == std::string("Renderer")) { + VLOG(3) << "CC Property Trees:"; + std::string out; + base::JSONWriter::WriteWithOptions( + *property_trees->AsTracedValue()->ToBaseValue(), + base::JSONWriter::OPTIONS_PRETTY_PRINT, &out); + VLOG(3) << out; + } } - bool content_has_slow_paths = false; - bool content_has_non_aa_paint = false; - bool did_paint_content = PaintContent( - update_layer_list, &content_has_slow_paths, &content_has_non_aa_paint); + bool painted_content_has_slow_paths = false; + bool painted_content_has_non_aa_paint = false; + bool did_paint_content = + PaintContent(update_layer_list, &painted_content_has_slow_paths, + &painted_content_has_non_aa_paint); - // |content_has_non_aa_paint| is a correctness (not performance) modifier, if - // it changes we immediately update. To prevent churn, this flag is sticky. - content_has_non_aa_paint_ |= content_has_non_aa_paint; + // |painted_content_has_non_aa_paint| is a correctness (not performance) + // modifier, if it changes we immediately update. To prevent churn, this flag + // is sticky. + content_has_non_aa_paint_ |= painted_content_has_non_aa_paint; // If no slow-path content has appeared for a required number of frames, // update the flag. - if (!content_has_slow_paths) { + if (!painted_content_has_slow_paths) { ++num_consecutive_frames_without_slow_paths_; if (num_consecutive_frames_without_slow_paths_ >= kNumFramesToConsiderBeforeRemovingSlowPathFlag) { @@ -848,7 +841,7 @@ void LayerTreeHost::ApplyViewportDeltas(ScrollAndScaleSet* info) { if (viewport_layers_.inner_viewport_scroll) { viewport_layers_.inner_viewport_scroll->SetScrollOffsetFromImplSide( gfx::ScrollOffsetWithDelta( - viewport_layers_.inner_viewport_scroll->scroll_offset(), + viewport_layers_.inner_viewport_scroll->CurrentScrollOffset(), inner_viewport_scroll_delta)); } @@ -889,7 +882,7 @@ void LayerTreeHost::ApplyScrollAndScale(ScrollAndScaleSet* info) { if (!layer) continue; layer->SetScrollOffsetFromImplSide(gfx::ScrollOffsetWithDelta( - layer->scroll_offset(), info->scrolls[i].scroll_delta)); + layer->CurrentScrollOffset(), info->scrolls[i].scroll_delta)); SetNeedsUpdateLayers(); } for (size_t i = 0; i < info->scrollbars.size(); ++i) { @@ -923,10 +916,18 @@ void LayerTreeHost::UpdateBrowserControlsState( } void LayerTreeHost::AnimateLayers(base::TimeTicks monotonic_time) { + // We do not need to animate on the main thread in this case because all + // relevant changes will be processed on the compositor thread, or proxy, + // and propagated back to the correct trees. + // TODO(majidvp): We should be able to eliminate this in the non- + // slimming path and will do so in a follow up. (762717) + if (IsUsingLayerLists()) + return; + std::unique_ptr<MutatorEvents> events = mutator_host_->CreateEvents(); if (mutator_host_->TickAnimations(monotonic_time, - property_trees()->scroll_tree)) + property_trees()->scroll_tree, true)) mutator_host_->UpdateAnimationState(true, events.get()); if (!events->IsEmpty()) @@ -1012,9 +1013,9 @@ void LayerTreeHost::RegisterViewportLayers(const ViewportLayers& layers) { // overscroll elasticity (optional) // page scale // inner viewport scroll - DCHECK(!layers.page_scale || + DCHECK(IsUsingLayerLists() || !layers.page_scale || layers.inner_viewport_scroll->parent() == layers.page_scale); - DCHECK(!layers.page_scale || + DCHECK(IsUsingLayerLists() || !layers.page_scale || layers.page_scale->parent() == layers.overscroll_elasticity || layers.page_scale->parent() == layers.inner_viewport_container); viewport_layers_.overscroll_elasticity = layers.overscroll_elasticity; @@ -1048,6 +1049,22 @@ void LayerTreeHost::SetEventListenerProperties( if (event_listener_properties_[index] == properties) return; + // TODO(sunxd): Remove NeedsFullTreeSync when computing mouse wheel event + // handler region is done. + // We only do full tree sync if the mouse wheel event listener property + // changes from kNone/kPassive to kBlocking/kBlockingAndPassive. + if (event_class == EventListenerClass::kMouseWheel && + !(event_listener_properties_[index] == + EventListenerProperties::kBlocking || + event_listener_properties_[index] == + EventListenerProperties::kBlockingAndPassive) && + (properties == EventListenerProperties::kBlocking || + properties == EventListenerProperties::kBlockingAndPassive)) { + if (root_layer()) + root_layer()->SetSubtreePropertyChanged(); + SetNeedsFullTreeSync(); + } + event_listener_properties_[index] = properties; SetNeedsCommit(); } @@ -1137,6 +1154,13 @@ void LayerTreeHost::SetPageScaleFactorAndLimits(float page_scale_factor, return; DCHECK_GE(page_scale_factor, min_page_scale_factor); DCHECK_LE(page_scale_factor, max_page_scale_factor); + // We should never process non-unit page_scale_delta for an OOPIF subframe. + // TODO(wjmaclean): Remove this check as a pre-condition to closing the bug. + // https://crbug.com/845097 + CHECK(!settings_.is_layer_tree_for_subframe || + page_scale_factor == page_scale_factor_) + << "Setting PSF in oopif subframe: old psf = " << page_scale_factor_ + << ", new psf = " << page_scale_factor; page_scale_factor_ = page_scale_factor; min_page_scale_factor_ = min_page_scale_factor; @@ -1176,22 +1200,35 @@ void LayerTreeHost::SetRasterColorSpace( } void LayerTreeHost::SetContentSourceId(uint32_t id) { - if (content_source_id_ == id) - return; content_source_id_ = id; - SetNeedsCommit(); } void LayerTreeHost::SetLocalSurfaceIdFromParent( const viz::LocalSurfaceId& local_surface_id_from_parent) { - if (local_surface_id_from_parent_ == local_surface_id_from_parent) + if (local_surface_id_from_parent_.parent_sequence_number() == + local_surface_id_from_parent.parent_sequence_number() && + local_surface_id_from_parent_.embed_token() == + local_surface_id_from_parent.embed_token()) { return; + } + + TRACE_EVENT_WITH_FLOW2( + TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"), + "LocalSurfaceId.Submission.Flow", + TRACE_ID_GLOBAL(local_surface_id_from_parent.submission_trace_id()), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step", + "SetLocalSurfaceIdFromParent", "local_surface_id", + local_surface_id_from_parent.ToString()); local_surface_id_from_parent_ = local_surface_id_from_parent; has_pushed_local_surface_id_from_parent_ = false; UpdateDeferCommitsInternal(); SetNeedsCommit(); } +void LayerTreeHost::ClearCachesOnNextCommit() { + clear_caches_on_next_commit_ = true; +} + void LayerTreeHost::RequestNewLocalSurfaceId() { // If surface synchronization is enabled, then we can still request a new // viz::LocalSurfaceId but that request will be deferred until we have a valid @@ -1236,10 +1273,6 @@ Layer* LayerTreeHost::LayerById(int id) const { return iter != layer_id_map_.end() ? iter->second : nullptr; } -size_t LayerTreeHost::NumLayers() const { - return layer_id_map_.size(); -} - bool LayerTreeHost::PaintContent(const LayerList& update_layer_list, bool* content_has_slow_paths, bool* content_has_non_aa_paint) { @@ -1253,27 +1286,27 @@ bool LayerTreeHost::PaintContent(const LayerList& update_layer_list, return did_paint_content; } -void LayerTreeHost::AddSurfaceLayerId(const viz::SurfaceId& surface_id) { - if (++surface_layer_ids_[surface_id] == 1) - needs_surface_ids_sync_ = true; +void LayerTreeHost::AddSurfaceRange(const viz::SurfaceRange& surface_range) { + if (++surface_ranges_[surface_range] == 1) + needs_surface_ranges_sync_ = true; } -void LayerTreeHost::RemoveSurfaceLayerId(const viz::SurfaceId& surface_id) { - auto iter = surface_layer_ids_.find(surface_id); - if (iter == surface_layer_ids_.end()) +void LayerTreeHost::RemoveSurfaceRange(const viz::SurfaceRange& surface_range) { + auto iter = surface_ranges_.find(surface_range); + if (iter == surface_ranges_.end()) return; if (--iter->second <= 0) { - surface_layer_ids_.erase(iter); - needs_surface_ids_sync_ = true; + surface_ranges_.erase(iter); + needs_surface_ranges_sync_ = true; } } -base::flat_set<viz::SurfaceId> LayerTreeHost::SurfaceLayerIds() const { - base::flat_set<viz::SurfaceId> ids; - for (auto& map_entry : surface_layer_ids_) - ids.insert(map_entry.first); - return ids; +base::flat_set<viz::SurfaceRange> LayerTreeHost::SurfaceRanges() const { + base::flat_set<viz::SurfaceRange> ranges; + for (auto& map_entry : surface_ranges_) + ranges.insert(map_entry.first); + return ranges; } void LayerTreeHost::AddLayerShouldPushProperties(Layer* layer) { @@ -1295,6 +1328,13 @@ bool LayerTreeHost::LayerNeedsPushPropertiesForTesting(Layer* layer) const { void LayerTreeHost::SetPageScaleFromImplSide(float page_scale) { DCHECK(CommitRequested()); + // We should never process non-unit page_scale_delta for an OOPIF subframe. + // TODO(wjmaclean): Remove this check as a pre-condition to closing the bug. + // https://crbug.com/845097 + DCHECK(!settings_.is_layer_tree_for_subframe || + page_scale == page_scale_factor_) + << "Setting PSF in oopif subframe: old psf = " << page_scale_factor_ + << ", new psf = " << page_scale; page_scale_factor_ = page_scale; SetPropertyTreesNeedRebuild(); } @@ -1353,7 +1393,7 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) { EventListenerClass::kTouchEndOrCancel, event_listener_properties(EventListenerClass::kTouchEndOrCancel)); - if (viewport_layers_.page_scale && viewport_layers_.inner_viewport_scroll) { + if (viewport_layers_.inner_viewport_scroll) { LayerTreeImpl::ViewportLayerIds ids; if (viewport_layers_.overscroll_elasticity) ids.overscroll_elasticity = viewport_layers_.overscroll_elasticity->id(); @@ -1411,12 +1451,12 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) { tree_impl->set_has_ever_been_drawn(false); } -void LayerTreeHost::PushSurfaceIdsTo(LayerTreeImpl* tree_impl) { - if (needs_surface_ids_sync()) { - tree_impl->ClearSurfaceLayerIds(); - tree_impl->SetSurfaceLayerIds(SurfaceLayerIds()); +void LayerTreeHost::PushSurfaceRangesTo(LayerTreeImpl* tree_impl) { + if (needs_surface_ranges_sync()) { + tree_impl->ClearSurfaceRanges(); + tree_impl->SetSurfaceRanges(SurfaceRanges()); // Reset for next update - set_needs_surface_ids_sync(false); + set_needs_surface_ranges_sync(false); } } @@ -1536,7 +1576,9 @@ void LayerTreeHost::SetElementTransformMutated( DCHECK(layer); layer->OnTransformAnimated(transform); - if (TransformNode* node = layer->GetTransformNode()) { + if (layer->has_transform_node()) { + TransformNode* node = + property_trees_.transform_tree.Node(layer->transform_tree_index()); if (node->local == transform) return; diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index 2fbf3e6c021..8cea9a0da23 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -48,6 +48,10 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/rect.h" +namespace gfx { +struct PresentationFeedback; +} + namespace cc { class HeadsUpDisplayLayer; class Layer; @@ -167,6 +171,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // synchronization. virtual void SetNeedsCommit(); + // Returns true after SetNeedsAnimate(), SetNeedsUpdateLayers() or + // SetNeedsCommit(), until it is satisfied. + bool RequestedMainFramePending(); + // Requests that the next frame re-chooses crisp raster scales for all layers. void SetNeedsRecalculateRasterScales(); @@ -177,6 +185,9 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // Enables/disables the compositor from requesting main frame updates from the // client. void SetDeferCommits(bool defer_commits); + // Returns the value last passed to SetDeferCommits(), though commits may be + // deferred also when the local_surface_id_from_parent() is not valid. + bool defer_commits() const { return defer_commits_; } // Synchronously performs a main frame update and layer updates. Used only in // single threaded mode when the compositor's internal scheduling is disabled. @@ -212,10 +223,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // the compositor thread. const base::WeakPtr<InputHandler>& GetInputHandler() const; - // Informs the compositor that an active fling gesture being processed on the - // main thread has been finished. - void DidStopFlinging(); - // Debugging and benchmarks --------------------------------- void SetDebugState(const LayerTreeDebugState& debug_state); const LayerTreeDebugState& GetDebugState() const; @@ -242,7 +249,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // to the screen (it's entirely possible some frames may be dropped between // the time this is called and the callback is run). using PresentationTimeCallback = - base::OnceCallback<void(base::TimeTicks, base::TimeDelta, uint32_t)>; + base::OnceCallback<void(const gfx::PresentationFeedback&)>; void RequestPresentationTimeForNextFrame(PresentationTimeCallback callback); void SetRootLayer(scoped_refptr<Layer> root_layer); @@ -336,6 +343,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { void SetContentSourceId(uint32_t); uint32_t content_source_id() const { return content_source_id_; } + // Clears image caches and resets the scheduling history for the content + // produced by this host so far. + void ClearCachesOnNextCommit(); + // If this LayerTreeHost needs a valid viz::LocalSurfaceId then commits will // be deferred until a valid viz::LocalSurfaceId is provided. void SetLocalSurfaceIdFromParent( @@ -371,8 +382,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { void UnregisterLayer(Layer* layer); Layer* LayerById(int id) const; - size_t NumLayers() const; - bool in_update_property_trees() const { return in_update_property_trees_; } bool PaintContent(const LayerList& update_layer_list, bool* content_has_slow_paths, @@ -382,9 +391,9 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { void SetHasCopyRequest(bool has_copy_request); bool has_copy_request() const { return has_copy_request_; } - void AddSurfaceLayerId(const viz::SurfaceId& surface_id); - void RemoveSurfaceLayerId(const viz::SurfaceId& surface_id); - base::flat_set<viz::SurfaceId> SurfaceLayerIds() const; + void AddSurfaceRange(const viz::SurfaceRange& surface_range); + void RemoveSurfaceRange(const viz::SurfaceRange& surface_range); + base::flat_set<viz::SurfaceRange> SurfaceRanges() const; void AddLayerShouldPushProperties(Layer* layer); void RemoveLayerShouldPushProperties(Layer* layer); @@ -401,16 +410,16 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { virtual void SetNeedsFullTreeSync(); bool needs_full_tree_sync() const { return needs_full_tree_sync_; } - bool needs_surface_ids_sync() const { return needs_surface_ids_sync_; } - void set_needs_surface_ids_sync(bool needs_surface_ids_sync) { - needs_surface_ids_sync_ = needs_surface_ids_sync; + bool needs_surface_ranges_sync() const { return needs_surface_ranges_sync_; } + void set_needs_surface_ranges_sync(bool needs_surface_ranges_sync) { + needs_surface_ranges_sync_ = needs_surface_ranges_sync; } void SetPropertyTreesNeedRebuild(); void PushPropertyTreesTo(LayerTreeImpl* tree_impl); void PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl); - void PushSurfaceIdsTo(LayerTreeImpl* tree_impl); + void PushSurfaceRangesTo(LayerTreeImpl* tree_impl); void PushLayerTreeHostPropertiesTo(LayerTreeHostImpl* host_impl); MutatorHost* mutator_host() const { return mutator_host_; } @@ -454,10 +463,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { client_->DidReceiveCompositorFrameAck(); } bool UpdateLayers(); - void DidPresentCompositorFrame(const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags); + void DidPresentCompositorFrame( + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback); // Called when the compositor completed page scale animation. void DidCompletePageScaleAnimation(); void ApplyScrollAndScale(ScrollAndScaleSet* info); @@ -647,6 +656,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { int raster_color_space_id_ = -1; gfx::ColorSpace raster_color_space_; + bool clear_caches_on_next_commit_ = false; uint32_t content_source_id_; viz::LocalSurfaceId local_surface_id_from_parent_; // Used to detect surface invariant violations. @@ -663,8 +673,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { gfx::Rect viewport_visible_rect_; bool have_scroll_event_handlers_ = false; - EventListenerProperties event_listener_properties_[static_cast<size_t>( - EventListenerClass::kNumClasses)]; + EventListenerProperties event_listener_properties_ + [static_cast<size_t>(EventListenerClass::kLast) + 1]; std::unique_ptr<PendingPageScaleAnimation> pending_page_scale_animation_; @@ -672,14 +682,15 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { bool needs_full_tree_sync_ = true; - bool needs_surface_ids_sync_ = false; + bool needs_surface_ranges_sync_ = false; gfx::Vector2dF elastic_overscroll_; scoped_refptr<HeadsUpDisplayLayer> hud_layer_; - // The number of SurfaceLayers that have fallback set to viz::SurfaceId. - base::flat_map<viz::SurfaceId, int> surface_layer_ids_; + // The number of SurfaceRanges that have (fallback,primary) set to + // viz::SurfaceRange. + base::flat_map<viz::SurfaceRange, int> surface_ranges_; // Set of layers that need to push properties. std::unordered_set<Layer*> layers_that_should_push_properties_; @@ -708,11 +719,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // added here. std::vector<PresentationTimeCallback> pending_presentation_time_callbacks_; - // Maps from the source frame presentation callbacks are requested for to - // the callbacks. - std::map<int, std::vector<PresentationTimeCallback>> - frame_to_presentation_time_callbacks_; - 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 9cfdfcd7ff2..1c2e7fff509 100644 --- a/chromium/cc/trees/layer_tree_host_client.h +++ b/chromium/cc/trees/layer_tree_host_client.h @@ -11,6 +11,7 @@ #include "base/time/time.h" namespace gfx { +struct PresentationFeedback; class Vector2dF; } @@ -89,10 +90,9 @@ class LayerTreeHostClient { virtual void DidCommitAndDrawFrame() = 0; virtual void DidReceiveCompositorFrameAck() = 0; virtual void DidCompletePageScaleAnimation() = 0; - // The only time a subframe ever gets its own LayerTree is when the subframe - // renders in a different process its ancestors; this returns true in - // that case. - virtual bool IsForSubframe() = 0; + virtual void DidPresentCompositorFrame( + uint32_t frame_token, + const gfx::PresentationFeedback& feedback) = 0; protected: virtual ~LayerTreeHostClient() {} diff --git a/chromium/cc/trees/layer_tree_host_common.cc b/chromium/cc/trees/layer_tree_host_common.cc index 215b52f341d..d556360f899 100644 --- a/chromium/cc/trees/layer_tree_host_common.cc +++ b/chromium/cc/trees/layer_tree_host_common.cc @@ -36,7 +36,8 @@ LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting:: float page_scale_factor, const Layer* page_scale_layer, const Layer* inner_viewport_scroll_layer, - const Layer* outer_viewport_scroll_layer) + const Layer* outer_viewport_scroll_layer, + TransformNode* page_scale_transform_node) : root_layer(root_layer), device_viewport_size(device_viewport_size), device_transform(device_transform), @@ -44,7 +45,8 @@ LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting:: page_scale_factor(page_scale_factor), page_scale_layer(page_scale_layer), inner_viewport_scroll_layer(inner_viewport_scroll_layer), - outer_viewport_scroll_layer(outer_viewport_scroll_layer) {} + outer_viewport_scroll_layer(outer_viewport_scroll_layer), + page_scale_transform_node(page_scale_transform_node) {} LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting:: CalcDrawPropsMainInputsForTesting(Layer* root_layer, @@ -57,6 +59,7 @@ LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting:: 1.f, nullptr, nullptr, + nullptr, nullptr) {} LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting:: @@ -80,7 +83,8 @@ LayerTreeHostCommon::CalcDrawPropsImplInputs::CalcDrawPropsImplInputs( int max_texture_size, bool can_adjust_raster_scales, RenderSurfaceList* render_surface_list, - PropertyTrees* property_trees) + PropertyTrees* property_trees, + TransformNode* page_scale_transform_node) : root_layer(root_layer), device_viewport_size(device_viewport_size), device_transform(device_transform), @@ -95,7 +99,8 @@ LayerTreeHostCommon::CalcDrawPropsImplInputs::CalcDrawPropsImplInputs( max_texture_size(max_texture_size), can_adjust_raster_scales(can_adjust_raster_scales), render_surface_list(render_surface_list), - property_trees(property_trees) {} + property_trees(property_trees), + page_scale_transform_node(page_scale_transform_node) {} LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: CalcDrawPropsImplInputsForTesting(LayerImpl* root_layer, @@ -116,7 +121,8 @@ LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: std::numeric_limits<int>::max() / 2, false, render_surface_list, - GetPropertyTrees(root_layer)) { + GetPropertyTrees(root_layer), + nullptr) { DCHECK(root_layer); DCHECK(render_surface_list); } @@ -541,10 +547,43 @@ void CalculateDrawPropertiesInternal( // on the active tree immediately affect the pending tree, so instead of // trying to update property trees whenever these values change, we // update property trees before using them. + + // When the page scale layer is also the root layer, the node should also + // store the combined scale factor and not just the page scale factor. + // TODO(bokan): Need to implement this behavior for + // BlinkGeneratedPropertyTrees. i.e. (no page scale layer). Ideally by + // not baking these into the page scale layer. + bool combine_dsf_and_psf = inputs->page_scale_layer == inputs->root_layer; + float device_scale_factor_for_page_scale_node = 1.f; + gfx::Transform device_transform_for_page_scale_node; + if (combine_dsf_and_psf) { + DCHECK( + !inputs->root_layer->layer_tree_impl()->settings().use_layer_lists); + device_transform_for_page_scale_node = inputs->device_transform; + device_scale_factor_for_page_scale_node = inputs->device_scale_factor; + } + + // We should never be setting a non-unit page scale factor on an oopif + // subframe ... if we attempt this log it and fail. + // TODO(wjmaclean): Remove as part of conditions for closing the bug. + // https://crbug.com/845097 + if (inputs->page_scale_factor != + inputs->property_trees->transform_tree.page_scale_factor() && + !inputs->page_scale_transform_node) { + LOG(ERROR) << "Setting PageScale on subframe: new psf = " + << inputs->page_scale_factor << ", old psf = " + << inputs->property_trees->transform_tree.page_scale_factor() + << ", in_oopif = " + << inputs->root_layer->layer_tree_impl() + ->settings() + .is_layer_tree_for_subframe; + CHECK(false); + } + draw_property_utils::UpdatePageScaleFactor( - inputs->property_trees, inputs->page_scale_layer, - inputs->page_scale_factor, inputs->device_scale_factor, - inputs->device_transform); + inputs->property_trees, inputs->page_scale_transform_node, + inputs->page_scale_factor, device_scale_factor_for_page_scale_node, + device_transform_for_page_scale_node); draw_property_utils::UpdateElasticOverscroll( inputs->property_trees, inputs->elastic_overscroll_application_layer, inputs->elastic_overscroll); @@ -554,9 +593,7 @@ void CalculateDrawPropertiesInternal( property_trees->clip_tree.SetViewportClip( gfx::RectF(gfx::SizeF(inputs->device_viewport_size))); float page_scale_factor_for_root = - inputs->page_scale_layer == inputs->root_layer - ? inputs->page_scale_factor - : 1.f; + combine_dsf_and_psf ? inputs->page_scale_factor : 1.f; property_trees->transform_tree.SetRootTransformsAndScales( inputs->device_scale_factor, page_scale_factor_for_root, inputs->device_transform, inputs->root_layer->position()); diff --git a/chromium/cc/trees/layer_tree_host_common.h b/chromium/cc/trees/layer_tree_host_common.h index 56799bf54ce..adbc352d802 100644 --- a/chromium/cc/trees/layer_tree_host_common.h +++ b/chromium/cc/trees/layer_tree_host_common.h @@ -17,6 +17,7 @@ #include "cc/layers/layer.h" #include "cc/layers/layer_collections.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/picture_layer.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/property_tree.h" @@ -42,7 +43,8 @@ class CC_EXPORT LayerTreeHostCommon { float page_scale_factor, const Layer* page_scale_layer, const Layer* inner_viewport_scroll_layer, - const Layer* outer_viewport_scroll_layer); + const Layer* outer_viewport_scroll_layer, + TransformNode* page_scale_transform_node); CalcDrawPropsMainInputsForTesting(Layer* root_layer, const gfx::Size& device_viewport_size, const gfx::Transform& device_transform); @@ -56,6 +58,7 @@ class CC_EXPORT LayerTreeHostCommon { const Layer* page_scale_layer; const Layer* inner_viewport_scroll_layer; const Layer* outer_viewport_scroll_layer; + TransformNode* page_scale_transform_node; }; struct CC_EXPORT CalcDrawPropsImplInputs { @@ -74,7 +77,8 @@ class CC_EXPORT LayerTreeHostCommon { int max_texture_size, bool can_adjust_raster_scales, RenderSurfaceList* render_surface_list, - PropertyTrees* property_trees); + PropertyTrees* property_trees, + TransformNode* page_scale_transform_node); LayerImpl* root_layer; gfx::Size device_viewport_size; @@ -90,6 +94,7 @@ class CC_EXPORT LayerTreeHostCommon { bool can_adjust_raster_scales; RenderSurfaceList* render_surface_list; PropertyTrees* property_trees; + TransformNode* page_scale_transform_node; }; struct CC_EXPORT CalcDrawPropsImplInputsForTesting @@ -181,7 +186,7 @@ void LayerTreeHostCommon::CallFunctionForEveryLayer(LayerTreeHost* host, const Function& function) { for (auto* layer : *host) { function(layer); - if (Layer* mask_layer = layer->mask_layer()) + if (PictureLayer* mask_layer = layer->mask_layer()) function(mask_layer); } } diff --git a/chromium/cc/trees/layer_tree_host_common_perftest.cc b/chromium/cc/trees/layer_tree_host_common_perftest.cc index e599a6b4c1d..ab5d5e600da 100644 --- a/chromium/cc/trees/layer_tree_host_common_perftest.cc +++ b/chromium/cc/trees/layer_tree_host_common_perftest.cc @@ -21,6 +21,7 @@ #include "cc/test/layer_tree_test.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_impl.h" +#include "cc/trees/transform_node.h" #include "components/viz/test/paths.h" #include "testing/perf/perf_test.h" @@ -111,7 +112,10 @@ class CalcDrawPropsTest : public LayerTreeHostCommonPerfTest { active_tree->elastic_overscroll()->Current(active_tree->IsActiveTree()), active_tree->OverscrollElasticityLayer(), max_texture_size, host_impl->settings().layer_transforms_should_scale_layer_contents, - &update_list, active_tree->property_trees()); + &update_list, active_tree->property_trees(), + active_tree->property_trees()->transform_tree.Node( + active_tree->InnerViewportContainerLayer() + ->transform_tree_index())); 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 329e9cd4b7e..f63d1a24987 100644 --- a/chromium/cc/trees/layer_tree_host_common_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_common_unittest.cc @@ -56,6 +56,13 @@ namespace cc { namespace { +bool LayerSubtreeHasCopyRequest(Layer* layer) { + LayerTreeHost* host = layer->layer_tree_host(); + int index = layer->effect_tree_index(); + auto* node = host->property_trees()->effect_tree.Node(index); + return node->subtree_has_copy_request; +} + class VerifyTreeCalcsLayerTreeSettings : public LayerTreeSettings { public: VerifyTreeCalcsLayerTreeSettings() = default; @@ -115,6 +122,12 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { inputs.page_scale_layer = page_scale_layer; inputs.inner_viewport_scroll_layer = inner_viewport_scroll_layer; inputs.outer_viewport_scroll_layer = outer_viewport_scroll_layer; + if (page_scale_layer) { + PropertyTrees* property_trees = + root_layer->layer_tree_host()->property_trees(); + inputs.page_scale_transform_node = property_trees->transform_tree.Node( + page_scale_layer->transform_tree_index()); + } LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); } @@ -149,6 +162,12 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { inputs.inner_viewport_scroll_layer = inner_viewport_scroll_layer; inputs.outer_viewport_scroll_layer = outer_viewport_scroll_layer; inputs.can_adjust_raster_scales = true; + if (page_scale_layer) { + PropertyTrees* property_trees = + root_layer->layer_tree_impl()->property_trees(); + inputs.page_scale_transform_node = property_trees->transform_tree.Node( + page_scale_layer->transform_tree_index()); + } LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); } @@ -298,16 +317,6 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { class LayerTreeHostCommonTest : public LayerTreeHostCommonTestBase, public testing::Test {}; -class LayerWithForcedDrawsContent : public Layer { - public: - LayerWithForcedDrawsContent() = default; - - bool DrawsContent() const override { return true; } - - private: - ~LayerWithForcedDrawsContent() override = default; -}; - class LayerTreeSettingsScaleContent : public VerifyTreeCalcsLayerTreeSettings { public: LayerTreeSettingsScaleContent() { @@ -1251,6 +1260,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { root, root->bounds(), translate, &render_surface_list_impl); inputs.page_scale_factor = page_scale_factor; inputs.page_scale_layer = root; + inputs.page_scale_transform_node = + inputs.property_trees->transform_tree.Node( + inputs.page_scale_layer->transform_tree_index()); inputs.property_trees->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); gfx::Transform page_scaled_translate = translate; @@ -2166,12 +2178,14 @@ TEST_F(LayerTreeHostCommonTest, LargeTransforms) { } static bool TransformIsAnimating(LayerImpl* layer) { - return layer->GetMutatorHost()->IsAnimatingTransformProperty( + MutatorHost* host = layer->layer_tree_impl()->mutator_host(); + return host->IsAnimatingTransformProperty( layer->element_id(), layer->GetElementTypeForAnimation()); } static bool HasPotentiallyRunningTransformAnimation(LayerImpl* layer) { - return layer->GetMutatorHost()->HasPotentiallyRunningTransformAnimation( + MutatorHost* host = layer->layer_tree_impl()->mutator_host(); + return host->HasPotentiallyRunningTransformAnimation( layer->element_id(), layer->GetElementTypeForAnimation()); } @@ -3046,7 +3060,7 @@ TEST_F(LayerTreeHostCommonTest, OcclusionBySiblingOfTarget) { root->test_properties()->AddChild(std::move(child)); host_impl.active_tree()->SetRootLayerForTesting(std::move(root)); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); @@ -3092,7 +3106,7 @@ TEST_F(LayerTreeHostCommonTest, TextureLayerSnapping) { root->test_properties()->AddChild(std::move(child)); host_impl.active_tree()->SetRootLayerForTesting(std::move(root)); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); @@ -3154,7 +3168,7 @@ TEST_F(LayerTreeHostCommonTest, root->test_properties()->AddChild(std::move(occluding_child)); host_impl.active_tree()->SetRootLayerForTesting(std::move(root)); host_impl.SetVisible(true); - host_impl.InitializeRenderer(layer_tree_frame_sink.get()); + host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); @@ -4364,7 +4378,8 @@ TEST_F(LayerTreeHostCommonTest, LayerSearch) { scoped_refptr<Layer> root = Layer::Create(); scoped_refptr<Layer> child = Layer::Create(); scoped_refptr<Layer> grand_child = Layer::Create(); - scoped_refptr<Layer> mask_layer = Layer::Create(); + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask_layer = PictureLayer::Create(&client); child->AddChild(grand_child.get()); child->SetMaskLayer(mask_layer.get()); @@ -7425,9 +7440,12 @@ TEST_F(LayerTreeHostCommonTest, MaximumAnimationScaleFactor) { EXPECT_EQ(0.f, GetStartingAnimationScale(child_raw)); EXPECT_EQ(0.f, GetStartingAnimationScale(grand_child_raw)); - grand_parent_animation->AbortKeyframeModels(TargetProperty::TRANSFORM, false); - parent_animation->AbortKeyframeModels(TargetProperty::TRANSFORM, false); - child_animation->AbortKeyframeModels(TargetProperty::TRANSFORM, false); + grand_parent_animation->AbortKeyframeModelsWithProperty( + TargetProperty::TRANSFORM, false); + parent_animation->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, + false); + child_animation->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, + false); TransformOperations perspective; perspective.AppendPerspective(10.f); @@ -7449,7 +7467,8 @@ TEST_F(LayerTreeHostCommonTest, MaximumAnimationScaleFactor) { EXPECT_EQ(0.f, GetStartingAnimationScale(child_raw)); EXPECT_EQ(0.f, GetStartingAnimationScale(grand_child_raw)); - child_animation->AbortKeyframeModels(TargetProperty::TRANSFORM, false); + child_animation->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, + false); gfx::Transform scale_matrix; scale_matrix.Scale(1.f, 2.f); @@ -7844,6 +7863,8 @@ TEST_F(LayerTreeHostCommonTest, DrawPropertyScales) { inputs.page_scale_factor = page_scale_factor; inputs.can_adjust_raster_scales = true; inputs.page_scale_layer = root_layer; + inputs.page_scale_transform_node = inputs.property_trees->transform_tree.Node( + inputs.page_scale_layer->transform_tree_index()); LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_FLOAT_EQ(3.f, root_layer->GetIdealContentsScale()); @@ -8299,8 +8320,9 @@ TEST_F(LayerTreeHostCommonTest, AnimatedOpacityCreatesRenderSurface) { } static bool FilterIsAnimating(LayerImpl* layer) { - return layer->GetMutatorHost()->IsAnimatingFilterProperty( - layer->element_id(), layer->GetElementTypeForAnimation()); + MutatorHost* host = layer->layer_tree_impl()->mutator_host(); + return host->IsAnimatingFilterProperty(layer->element_id(), + layer->GetElementTypeForAnimation()); } // Verify that having an animated filter (but no current filter, as these @@ -8332,7 +8354,8 @@ TEST_F(LayerTreeHostCommonTest, AnimatedFilterCreatesRenderSurface) { } bool HasPotentiallyRunningFilterAnimation(const LayerImpl& layer) { - return layer.GetMutatorHost()->HasPotentiallyRunningFilterAnimation( + MutatorHost* host = layer.layer_tree_impl()->mutator_host(); + return host->HasPotentiallyRunningFilterAnimation( layer.element_id(), layer.GetElementTypeForAnimation()); } @@ -8599,21 +8622,22 @@ TEST_F(LayerTreeHostCommonTest, HasCopyRequestsInTargetSubtree) { child2->SetOpacity(0.f); ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get()); - 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()); + EXPECT_TRUE(LayerSubtreeHasCopyRequest(root.get())); + EXPECT_TRUE(LayerSubtreeHasCopyRequest(child1.get())); + EXPECT_FALSE(LayerSubtreeHasCopyRequest(child2.get())); + EXPECT_TRUE(LayerSubtreeHasCopyRequest(grandchild.get())); + EXPECT_TRUE(LayerSubtreeHasCopyRequest(greatgrandchild.get())); } TEST_F(LayerTreeHostCommonTest, SkippingSubtreeMain) { - scoped_refptr<Layer> root = Layer::Create(); FakeContentLayerClient client; + + scoped_refptr<Layer> root = Layer::Create(); client.set_bounds(root->bounds()); - scoped_refptr<LayerWithForcedDrawsContent> child = - base::MakeRefCounted<LayerWithForcedDrawsContent>(); - scoped_refptr<LayerWithForcedDrawsContent> grandchild = - base::MakeRefCounted<LayerWithForcedDrawsContent>(); + scoped_refptr<Layer> child = Layer::Create(); + child->SetIsDrawable(true); + scoped_refptr<Layer> grandchild = Layer::Create(); + grandchild->SetIsDrawable(true); scoped_refptr<FakePictureLayer> greatgrandchild( FakePictureLayer::Create(&client)); @@ -9025,10 +9049,10 @@ TEST_F(LayerTreeHostCommonTest, LayerTreeRebuildTest) { child->RequestCopyOfOutput(viz::CopyOutputRequest::CreateStubForTesting()); ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get()); - EXPECT_TRUE(root->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(LayerSubtreeHasCopyRequest(root.get())); ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get()); - EXPECT_TRUE(root->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(LayerSubtreeHasCopyRequest(root.get())); } TEST_F(LayerTreeHostCommonTest, ResetPropertyTreeIndices) { @@ -9727,8 +9751,8 @@ TEST_F(LayerTreeHostCommonTest, LargeTransformTest) { TEST_F(LayerTreeHostCommonTest, PropertyTreesRebuildWithOpacityChanges) { scoped_refptr<Layer> root = Layer::Create(); - scoped_refptr<LayerWithForcedDrawsContent> child = - base::MakeRefCounted<LayerWithForcedDrawsContent>(); + scoped_refptr<Layer> child = Layer::Create(); + child->SetIsDrawable(true); root->AddChild(child); host()->SetRootLayer(root); @@ -9769,8 +9793,8 @@ TEST_F(LayerTreeHostCommonTest, PropertyTreesRebuildWithOpacityChanges) { TEST_F(LayerTreeHostCommonTest, OpacityAnimationsTrackingTest) { scoped_refptr<Layer> root = Layer::Create(); - scoped_refptr<LayerWithForcedDrawsContent> animated = - base::MakeRefCounted<LayerWithForcedDrawsContent>(); + scoped_refptr<Layer> animated = Layer::Create(); + animated->SetIsDrawable(true); root->AddChild(animated); host()->SetRootLayer(root); host()->SetElementIdsForTesting(); @@ -9810,8 +9834,8 @@ TEST_F(LayerTreeHostCommonTest, OpacityAnimationsTrackingTest) { EXPECT_TRUE(node->is_currently_animating_opacity); EXPECT_TRUE(node->has_potential_opacity_animation); - animation->AbortKeyframeModels(TargetProperty::OPACITY, - false /*needs_completion*/); + animation->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY, + false /*needs_completion*/); node = tree.Node(animated->effect_tree_index()); EXPECT_FALSE(node->is_currently_animating_opacity); EXPECT_FALSE(node->has_potential_opacity_animation); @@ -9819,8 +9843,8 @@ TEST_F(LayerTreeHostCommonTest, OpacityAnimationsTrackingTest) { TEST_F(LayerTreeHostCommonTest, TransformAnimationsTrackingTest) { scoped_refptr<Layer> root = Layer::Create(); - scoped_refptr<LayerWithForcedDrawsContent> animated = - base::MakeRefCounted<LayerWithForcedDrawsContent>(); + scoped_refptr<Layer> animated = Layer::Create(); + animated->SetIsDrawable(true); root->AddChild(animated); host()->SetRootLayer(root); host()->SetElementIdsForTesting(); @@ -9869,8 +9893,8 @@ TEST_F(LayerTreeHostCommonTest, TransformAnimationsTrackingTest) { EXPECT_TRUE(node->is_currently_animating); EXPECT_TRUE(node->has_potential_animation); - animation->AbortKeyframeModels(TargetProperty::TRANSFORM, - false /*needs_completion*/); + animation->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, + false /*needs_completion*/); node = tree.Node(animated->transform_tree_index()); EXPECT_FALSE(node->is_currently_animating); EXPECT_FALSE(node->has_potential_animation); diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index df0758ace5d..507ece40522 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -20,6 +20,7 @@ #include "base/containers/adapters.h" #include "base/containers/flat_map.h" #include "base/json/json_writer.h" +#include "base/memory/memory_coordinator_client_registry.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram.h" #include "base/numerics/safe_conversions.h" @@ -27,6 +28,7 @@ #include "base/strings/stringprintf.h" #include "base/sys_info.h" #include "base/trace_event/trace_event_argument.h" +#include "build/build_config.h" #include "cc/base/devtools_instrumentation.h" #include "cc/base/histograms.h" #include "cc/base/math_util.h" @@ -68,7 +70,6 @@ #include "cc/trees/draw_property_utils.h" #include "cc/trees/effect_node.h" #include "cc/trees/frame_rate_counter.h" -#include "cc/trees/frame_token_allocator.h" #include "cc/trees/image_animation_controller.h" #include "cc/trees/latency_info_swap_promise_monitor.h" #include "cc/trees/layer_tree_frame_sink.h" @@ -107,6 +108,7 @@ #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" +#include "ui/gfx/presentation_feedback.h" #include "ui/gfx/skia_util.h" namespace cc { @@ -163,9 +165,12 @@ viz::ResourceFormat TileRasterBufferFormat( // it and resets to the same location. class ViewportAnchor { public: - ViewportAnchor(LayerImpl* inner_scroll, LayerImpl* outer_scroll) - : inner_(inner_scroll), outer_(outer_scroll) { - viewport_in_content_coordinates_ = inner_->CurrentScrollOffset(); + ViewportAnchor(ScrollNode* inner_scroll, + LayerImpl* outer_scroll, + LayerTreeImpl* tree_impl) + : inner_(inner_scroll), outer_(outer_scroll), tree_impl_(tree_impl) { + viewport_in_content_coordinates_ = + scroll_tree().current_scroll_offset(inner_->element_id); if (outer_) viewport_in_content_coordinates_ += outer_->CurrentScrollOffset(); @@ -174,22 +179,28 @@ class ViewportAnchor { void ResetViewportToAnchoredPosition() { DCHECK(outer_); - inner_->ClampScrollToMaxScrollOffset(); + scroll_tree().ClampScrollToMaxScrollOffset(inner_, tree_impl_); outer_->ClampScrollToMaxScrollOffset(); gfx::ScrollOffset viewport_location = - inner_->CurrentScrollOffset() + outer_->CurrentScrollOffset(); + scroll_tree().current_scroll_offset(inner_->element_id) + + outer_->CurrentScrollOffset(); gfx::Vector2dF delta = viewport_in_content_coordinates_.DeltaFrom(viewport_location); - delta = inner_->ScrollBy(delta); + delta = scroll_tree().ScrollBy(inner_, delta, tree_impl_); outer_->ScrollBy(delta); } private: - LayerImpl* inner_; + ScrollTree& scroll_tree() { + return tree_impl_->property_trees()->scroll_tree; + } + + ScrollNode* inner_; LayerImpl* outer_; + LayerTreeImpl* tree_impl_; gfx::ScrollOffset viewport_in_content_coordinates_; }; @@ -221,6 +232,25 @@ void RecordCompositorSlowScrollMetric(InputHandler::ScrollInputType type, } } +ui::FrameMetricsSettings LTHI_FrameMetricsSettings( + const LayerTreeSettings& settings) { + ui::FrameMetricsSource source = + settings.commit_to_active_tree + ? ui::FrameMetricsSource::UiCompositor + : ui::FrameMetricsSource::RendererCompositor; + ui::FrameMetricsSourceThread source_thread = + settings.commit_to_active_tree + ? ui::FrameMetricsSourceThread::UiCompositor + : ui::FrameMetricsSourceThread::RendererCompositor; + ui::FrameMetricsCompileTarget compile_target = + settings.using_synchronous_renderer_compositor + ? ui::FrameMetricsCompileTarget::SynchronousCompositor + : settings.wait_for_all_pipeline_stages_before_draw + ? ui::FrameMetricsCompileTarget::Headless + : ui::FrameMetricsCompileTarget::Chromium; + return ui::FrameMetricsSettings(source, source_thread, compile_target); +} + } // namespace DEFINE_SCOPED_UMA_HISTOGRAM_TIMER(PendingTreeDurationHistogramTimer, @@ -264,23 +294,11 @@ LayerTreeHostImpl::LayerTreeHostImpl( : client_(client), task_runner_provider_(task_runner_provider), current_begin_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE), - need_update_gpu_rasterization_status_(false), - content_has_slow_paths_(false), - content_has_non_aa_paint_(false), - has_gpu_rasterization_trigger_(false), - use_gpu_rasterization_(false), - use_msaa_(false), - gpu_rasterization_status_(GpuRasterizationStatus::OFF_DEVICE), - input_handler_client_(nullptr), - did_lock_scrolling_layer_(false), - wheel_scrolling_(false), - scroll_affects_scroll_handler_(false), - tile_priorities_dirty_(false), settings_(settings), - visible_(false), - cached_managed_memory_policy_(settings.memory_policy), is_synchronous_single_threaded_(!task_runner_provider->HasImplThread() && - !settings.single_thread_proxy_scheduler), + !settings_.single_thread_proxy_scheduler), + resource_provider_(settings_.delegated_sync_points_required), + cached_managed_memory_policy_(settings.memory_policy), // Must be initialized after is_synchronous_single_threaded_ and // task_runner_provider_. tile_manager_(this, @@ -290,28 +308,17 @@ LayerTreeHostImpl::LayerTreeHostImpl( ? std::numeric_limits<size_t>::max() : settings.scheduled_raster_task_limit, settings.ToTileManagerSettings()), - pinch_gesture_active_(false), - pinch_gesture_end_should_clear_scrolling_node_(false), fps_counter_( FrameRateCounter::Create(task_runner_provider_->HasImplThread())), memory_history_(MemoryHistory::Create()), debug_rect_history_(DebugRectHistory::Create()), - max_memory_needed_bytes_(0), - resourceless_software_draw_(false), mutator_host_(std::move(mutator_host)), rendering_stats_instrumentation_(rendering_stats_instrumentation), micro_benchmark_controller_(this), task_graph_runner_(task_graph_runner), id_(id), - requires_high_res_to_draw_(false), - is_likely_to_require_a_draw_(false), - has_valid_layer_tree_frame_sink_(false), consecutive_frame_with_damage_count_(settings.damaged_frame_limit), scroll_animating_latched_element_id_(kInvalidElementId), - has_scrolled_by_wheel_(false), - has_scrolled_by_touch_(false), - touchpad_and_wheel_scroll_latching_enabled_(false), - impl_thread_phase_(ImplThreadPhase::IDLE), // It is safe to use base::Unretained here since we will outlive the // ImageAnimationController. image_animation_controller_( @@ -320,8 +327,9 @@ LayerTreeHostImpl::LayerTreeHostImpl( &LayerTreeHostImpl::RequestInvalidationForAnimatedImages, base::Unretained(this)), settings_.enable_image_animation_resync), - default_color_space_id_(gfx::ColorSpace::GetNextId()), - default_color_space_(gfx::ColorSpace::CreateSRGB()) { + frame_metrics_(LTHI_FrameMetricsSettings(settings_)), + skipped_frame_tracker_(&frame_metrics_), + is_animating_for_snap_(false) { DCHECK(mutator_host_); mutator_host_->SetMutatorHostClient(this); @@ -345,6 +353,7 @@ LayerTreeHostImpl::LayerTreeHostImpl( this, settings.top_controls_show_threshold, settings.top_controls_hide_threshold); + base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this); memory_pressure_listener_.reset( new base::MemoryPressureListener(base::BindRepeating( &LayerTreeHostImpl::OnMemoryPressure, base::Unretained(this)))); @@ -356,13 +365,12 @@ LayerTreeHostImpl::~LayerTreeHostImpl() { TRACE_EVENT_OBJECT_DELETED_WITH_ID(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::LayerTreeHostImpl", id_); - // It is released before shutdown. + // The frame sink is released before shutdown, which takes down + // all the resource and raster structures. DCHECK(!layer_tree_frame_sink_); - - DCHECK(!resource_provider_); DCHECK(!resource_pool_); - DCHECK(!single_thread_synchronous_task_graph_runner_); DCHECK(!image_decode_cache_); + DCHECK(!single_thread_synchronous_task_graph_runner_); if (input_handler_client_) { input_handler_client_->WillShutdown(); @@ -371,7 +379,9 @@ LayerTreeHostImpl::~LayerTreeHostImpl() { if (scroll_elasticity_helper_) scroll_elasticity_helper_.reset(); - // The layer trees must be destroyed before the layer tree host. + // The layer trees must be destroyed before the LayerTreeHost. Also, if they + // are holding onto any resources, destroying them will release them, before + // we mark any leftover resources as lost. if (recycle_tree_) recycle_tree_->Shutdown(); if (pending_tree_) @@ -381,6 +391,11 @@ LayerTreeHostImpl::~LayerTreeHostImpl() { pending_tree_ = nullptr; active_tree_ = nullptr; + base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this); + + // All resources should already be removed, so lose anything still exported. + resource_provider_.ShutdownAndReleaseAllResources(); + mutator_host_->ClearMutators(); mutator_host_->SetMutatorHostClient(nullptr); } @@ -617,7 +632,7 @@ void LayerTreeHostImpl::StartPageScaleAnimation( bool anchor_point, float page_scale, base::TimeDelta duration) { - if (!InnerViewportScrollLayer()) + if (!InnerViewportScrollNode()) return; gfx::ScrollOffset scroll_total = active_tree_->TotalScrollOffset(); @@ -770,6 +785,18 @@ LayerTreeHostImpl::EventListenerTypeForTouchStartOrMoveAt( : InputHandler::TouchStartOrMoveEventListenerType::HANDLER; } +bool LayerTreeHostImpl::HasWheelEventHandlerAt( + const gfx::Point& viewport_point) const { + gfx::PointF device_viewport_point = gfx::ScalePoint( + gfx::PointF(viewport_point), active_tree_->device_scale_factor()); + + LayerImpl* layer_impl_with_wheel_event_handler = + active_tree_->FindLayerThatIsHitByPointInWheelEventHandlerRegion( + device_viewport_point); + + return layer_impl_with_wheel_event_handler; +} + std::unique_ptr<SwapPromiseMonitor> LayerTreeHostImpl::CreateLatencyInfoSwapPromiseMonitor( ui::LatencyInfo* latency) { @@ -834,10 +861,10 @@ void LayerTreeHostImpl::FrameData::AsValueInto( value->SetBoolean("has_no_damage", has_no_damage); // Quad data can be quite large, so only dump render passes if we select - // cc.debug.quads. + // viz.quads. bool quads_enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED( - TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), &quads_enabled); + TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.quads"), + &quads_enabled); if (quads_enabled) { value->BeginArray("render_passes"); for (size_t i = 0; i < render_passes.size(); ++i) { @@ -920,6 +947,18 @@ bool LayerTreeHostImpl::HasDamage() const { if (!viewport_damage_rect_.IsEmpty()) return true; + // If the set of referenced surfaces has changed then we must submit a new + // CompositorFrame to update surface references. + if (last_draw_referenced_surfaces_ != active_tree()->SurfaceRanges()) + return true; + + // If we have a new LocalSurfaceId, we must always submit a CompositorFrame + // because the parent is blocking on us. + if (last_draw_local_surface_id_ != + child_local_surface_id_allocator_.GetCurrentLocalSurfaceId()) { + return true; + } + const LayerTreeImpl* active_tree = active_tree_.get(); // If the root render surface has no visible damage, then don't generate a @@ -929,18 +968,10 @@ bool LayerTreeHostImpl::HasDamage() const { root_surface->GetDamageRect().Intersects(root_surface->content_rect()); bool hud_wants_to_draw_ = active_tree->hud_layer() && active_tree->hud_layer()->IsAnimatingHUDContents(); - bool must_always_swap = - layer_tree_frame_sink_->capabilities().must_always_swap; - - // If we have a new LocalSurfaceId, we must always submit a CompositorFrame - // because the parent is blocking on us. - bool local_surface_id_changed = - last_draw_local_surface_id_ != - child_local_surface_id_allocator_.GetCurrentLocalSurfaceId(); return root_surface_has_visible_damage || active_tree_->property_trees()->effect_tree.HasCopyRequests() || - must_always_swap || hud_wants_to_draw_ || local_surface_id_changed; + hud_wants_to_draw_; } DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { @@ -1064,13 +1095,9 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { render_surface->AppendQuads(draw_mode, target_render_pass, &append_quads_data); } - } else if (it.state() == EffectTreeLayerListIterator::State::LAYER && - !it.current_layer()->visible_layer_rect().IsEmpty()) { + } else if (it.state() == EffectTreeLayerListIterator::State::LAYER) { LayerImpl* layer = it.current_layer(); - bool occluded = - layer->draw_properties().occlusion_in_content_space.IsOccluded( - layer->visible_layer_rect()); - if (!occluded && layer->WillDraw(draw_mode, resource_provider_.get())) { + if (layer->WillDraw(draw_mode, &resource_provider_)) { DCHECK_EQ(active_tree_.get(), layer->layer_tree_impl()); frame->will_draw_layers.push_back(layer); @@ -1222,12 +1249,6 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { return draw_result; } -void LayerTreeHostImpl::MainThreadHasStoppedFlinging() { - browser_controls_offset_manager_->MainThreadHasStoppedFlinging(); - if (input_handler_client_) - input_handler_client_->MainThreadHasStoppedFlinging(); -} - void LayerTreeHostImpl::DidAnimateScrollOffset() { client_->SetNeedsCommitOnImplThread(); client_->RenewTreePriority(); @@ -1239,8 +1260,12 @@ void LayerTreeHostImpl::SetViewportDamage(const gfx::Rect& damage_rect) { void LayerTreeHostImpl::InvalidateContentOnImplSide() { DCHECK(!pending_tree_); - // Invalidation should never be ran outside the impl frame. - DCHECK_EQ(impl_thread_phase_, ImplThreadPhase::INSIDE_IMPL_FRAME); + // Invalidation should never be ran outside the impl frame for non + // synchronous compositor mode. For devices that use synchronous compositor, + // e.g. Android Webview, the assertion is not guaranteed because it may ask + // for a frame at any time. + DCHECK(impl_thread_phase_ == ImplThreadPhase::INSIDE_IMPL_FRAME || + settings_.using_synchronous_renderer_compositor); if (!CommitToActiveTree()) CreatePendingTree(); @@ -1248,18 +1273,28 @@ void LayerTreeHostImpl::InvalidateContentOnImplSide() { UpdateSyncTreeAfterCommitOrImplSideInvalidation(); } -void LayerTreeHostImpl::InvalidateLayerTreeFrameSink() { +void LayerTreeHostImpl::InvalidateLayerTreeFrameSink(bool needs_redraw) { DCHECK(layer_tree_frame_sink()); - layer_tree_frame_sink()->Invalidate(); + + layer_tree_frame_sink()->Invalidate(needs_redraw); + skipped_frame_tracker_.DidProduceFrame(); } DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { TRACE_EVENT1("cc", "LayerTreeHostImpl::PrepareToDraw", "SourceFrameNumber", active_tree_->source_frame_number()); + TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline", + TRACE_ID_GLOBAL(CurrentBeginFrameArgs().trace_id), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, + "step", "GenerateRenderPass"); if (input_handler_client_) input_handler_client_->ReconcileElasticOverscrollAndRootScroll(); - if (const char* client_name = GetClientNameForMetrics()) { + // |client_name| is used for various UMA histograms below. + // GetClientNameForMetrics only returns one non-null value over the lifetime + // of the process, so the histogram names are runtime constant. + const char* client_name = GetClientNameForMetrics(); + if (client_name) { size_t total_memory_in_bytes = 0; size_t total_gpu_memory_for_tilings_in_bytes = 0; for (const PictureLayerImpl* layer : active_tree()->picture_layers()) { @@ -1267,16 +1302,12 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { total_gpu_memory_for_tilings_in_bytes += layer->GPUMemoryUsageInBytes(); } if (total_memory_in_bytes != 0) { - // GetClientNameForMetrics only returns one non-null value over the - // lifetime of the process, so this histogram name is runtime constant. UMA_HISTOGRAM_COUNTS( base::StringPrintf("Compositing.%s.PictureMemoryUsageKb", client_name), base::saturated_cast<int>(total_memory_in_bytes / 1024)); } - // GetClientNameForMetrics only returns one non-null value over the lifetime - // of the process, so these histogram names are runtime constant. UMA_HISTOGRAM_CUSTOM_COUNTS( base::StringPrintf("Compositing.%s.NumActiveLayers", client_name), base::saturated_cast<int>(active_tree_->NumLayers()), 1, 400, 20); @@ -1323,6 +1354,11 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { } DrawResult draw_result = CalculateRenderPasses(frame); + if (client_name) { + UMA_HISTOGRAM_ENUMERATION( + base::StringPrintf("Compositing.%s.DrawResult", client_name), + draw_result); + } if (draw_result != DRAW_SUCCESS) { DCHECK(!resourceless_software_draw_); return draw_result; @@ -1729,40 +1765,45 @@ void LayerTreeHostImpl::DidReceiveCompositorFrameAck() { client_->DidReceiveCompositorFrameAckOnImplThread(); } -void LayerTreeHostImpl::DidPresentCompositorFrame(uint32_t presentation_token, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) { - TRACE_EVENT_MARK_WITH_TIMESTAMP0("cc,benchmark", "FramePresented", time); - std::vector<int> source_frames; - auto iter = presentation_token_to_frame_.begin(); - for (; iter != presentation_token_to_frame_.end() && - iter->first <= presentation_token; - ++iter) { - source_frames.push_back(iter->second); - } - presentation_token_to_frame_.erase(presentation_token_to_frame_.begin(), - iter); - if (presentation_token_to_frame_.empty()) { - DCHECK_EQ(last_presentation_token_, presentation_token); - last_presentation_token_ = 0u; - } +LayerTreeHostImpl::FrameTokenInfo::FrameTokenInfo( + uint32_t token, + base::TimeTicks cc_frame_time, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks) + : token(token), + cc_frame_time(cc_frame_time), + callbacks(std::move(callbacks)) {} + +LayerTreeHostImpl::FrameTokenInfo::FrameTokenInfo(FrameTokenInfo&&) = default; +LayerTreeHostImpl::FrameTokenInfo::~FrameTokenInfo() = default; + +void LayerTreeHostImpl::DidPresentCompositorFrame( + uint32_t frame_token, + const gfx::PresentationFeedback& feedback) { + TRACE_EVENT_MARK_WITH_TIMESTAMP0("cc,benchmark", "FramePresented", + feedback.timestamp); + std::vector<LayerTreeHost::PresentationTimeCallback> all_callbacks; + while (!frame_token_infos_.empty()) { + auto info = frame_token_infos_.begin(); + if (viz::FrameTokenGT(info->token, frame_token)) + break; - client_->DidPresentCompositorFrameOnImplThread(source_frames, time, refresh, - flags); -} + // Update compositor frame latency and smoothness stats only for frames + // that caused on-screen damage. + if (info->token == frame_token) + frame_metrics_.AddFrameDisplayed(info->cc_frame_time, feedback.timestamp); -void LayerTreeHostImpl::DidDiscardCompositorFrame(uint32_t presentation_token) { + std::copy(std::make_move_iterator(info->callbacks.begin()), + std::make_move_iterator(info->callbacks.end()), + std::back_inserter(all_callbacks)); + frame_token_infos_.erase(info); + } + client_->DidPresentCompositorFrameOnImplThread( + frame_token, std::move(all_callbacks), feedback); } void LayerTreeHostImpl::ReclaimResources( const std::vector<viz::ReturnedResource>& resources) { - // TODO(piman): We may need to do some validation on this ack before - // processing it. - if (!resource_provider_) - return; - - resource_provider_->ReceiveReturnsFromParent(resources); + resource_provider_.ReceiveReturnsFromParent(resources); // In OOM, we now might be able to release more resources that were held // because they were exported. @@ -1792,8 +1833,15 @@ void LayerTreeHostImpl::ReclaimResources( void LayerTreeHostImpl::OnDraw(const gfx::Transform& transform, const gfx::Rect& viewport, - bool resourceless_software_draw) { + bool resourceless_software_draw, + bool skip_draw) { DCHECK(!resourceless_software_draw_); + + if (skip_draw) { + client_->OnDrawForLayerTreeFrameSink(resourceless_software_draw_, true); + return; + } + const bool transform_changed = external_transform_ != transform; const bool viewport_changed = external_viewport_ != viewport; @@ -1817,7 +1865,8 @@ void LayerTreeHostImpl::OnDraw(const gfx::Transform& transform, client_->OnCanDrawStateChanged(CanDraw()); } - client_->OnDrawForLayerTreeFrameSink(resourceless_software_draw_); + client_->OnDrawForLayerTreeFrameSink(resourceless_software_draw_, + skip_draw); } if (resourceless_software_draw) { @@ -1836,15 +1885,9 @@ void LayerTreeHostImpl::OnCanDrawStateChangedForTree() { viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { viz::CompositorFrameMetadata metadata; - - if (active_tree_->request_presentation_time()) { - metadata.presentation_token = ++last_presentation_token_; - // Assume there is never a constant stream of requests that triggers - // overflow. - CHECK_NE(0u, last_presentation_token_); - presentation_token_to_frame_[last_presentation_token_] = - active_tree_->source_frame_number(); - } + metadata.frame_token = next_frame_token_++; + if (!next_frame_token_) + next_frame_token_ = 1u; metadata.device_scale_factor = active_tree_->painted_device_scale_factor() * active_tree_->device_scale_factor(); @@ -1867,6 +1910,16 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { active_tree_->GetViewportSelection(&metadata.selection); + if (active_tree_->has_presentation_callbacks() || + settings_.always_request_presentation_time) { + metadata.request_presentation_feedback = true; + frame_token_infos_.emplace_back(metadata.frame_token, + CurrentBeginFrameArgs().frame_time, + active_tree_->TakePresentationCallbacks()); + + DCHECK_LE(frame_token_infos_.size(), 25u); + } + if (const auto* outer_viewport_scroll_node = OuterViewportScrollNode()) { metadata.root_overflow_y_hidden = !outer_viewport_scroll_node->user_scrollable_vertical; @@ -1877,8 +1930,13 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { IsActivelyScrolling() || mutator_host_->NeedsTickAnimations(); } - for (auto& surface_id : active_tree_->SurfaceLayerIds()) - metadata.referenced_surfaces.push_back(surface_id); + const base::flat_set<viz::SurfaceRange>& referenced_surfaces = + active_tree_->SurfaceRanges(); + for (auto& surface_range : referenced_surfaces) + metadata.referenced_surfaces.push_back(surface_range); + + if (last_draw_referenced_surfaces_ != referenced_surfaces) + last_draw_referenced_surfaces_ = referenced_surfaces; const auto* inner_viewport_scroll_node = InnerViewportScrollNode(); if (!inner_viewport_scroll_node) @@ -1894,15 +1952,21 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { return metadata; } -RenderFrameMetadata LayerTreeHostImpl::MakeRenderFrameMetadata() { +RenderFrameMetadata LayerTreeHostImpl::MakeRenderFrameMetadata( + FrameData* frame) { RenderFrameMetadata metadata; metadata.root_scroll_offset = gfx::ScrollOffsetToVector2dF(active_tree_->TotalScrollOffset()); + metadata.root_background_color = active_tree_->background_color(); metadata.is_scroll_offset_at_top = active_tree_->TotalScrollOffset().y() == 0; metadata.device_scale_factor = active_tree_->painted_device_scale_factor() * active_tree_->device_scale_factor(); + active_tree_->GetViewportSelection(&metadata.selection); + metadata.is_mobile_optimized = IsMobileOptimized(active_tree_.get()); metadata.viewport_size_in_pixels = device_viewport_size(); + +#if defined(OS_ANDROID) metadata.top_controls_height = browser_controls_offset_manager_->TopControlsHeight(); metadata.top_controls_shown_ratio = @@ -1911,8 +1975,28 @@ RenderFrameMetadata LayerTreeHostImpl::MakeRenderFrameMetadata() { browser_controls_offset_manager_->BottomControlsHeight(); metadata.bottom_controls_shown_ratio = browser_controls_offset_manager_->BottomControlsShownRatio(); + metadata.scrollable_viewport_size = active_tree_->ScrollableViewportSize(); + metadata.page_scale_factor = active_tree_->current_page_scale_factor(); + metadata.min_page_scale_factor = active_tree_->min_page_scale_factor(); + metadata.max_page_scale_factor = active_tree_->max_page_scale_factor(); + metadata.root_layer_size = active_tree_->ScrollableSize(); + if (const auto* outer_viewport_scroll_node = OuterViewportScrollNode()) { + metadata.root_overflow_y_hidden = + !outer_viewport_scroll_node->user_scrollable_vertical; + } + const auto* inner_viewport_scroll_node = InnerViewportScrollNode(); + if (inner_viewport_scroll_node) { + metadata.root_overflow_y_hidden |= + !inner_viewport_scroll_node->user_scrollable_vertical; + } + metadata.has_transparent_background = + frame->render_passes.back()->has_transparent_background; +#endif bool allocate_new_local_surface_id = +#if !defined(OS_ANDROID) + false; +#else last_draw_render_frame_metadata_ && (last_draw_render_frame_metadata_->top_controls_height != metadata.top_controls_height || @@ -1921,7 +2005,11 @@ RenderFrameMetadata LayerTreeHostImpl::MakeRenderFrameMetadata() { last_draw_render_frame_metadata_->bottom_controls_height != metadata.bottom_controls_height || last_draw_render_frame_metadata_->bottom_controls_shown_ratio != - metadata.bottom_controls_shown_ratio); + metadata.bottom_controls_shown_ratio || + last_draw_render_frame_metadata_->selection != metadata.selection || + last_draw_render_frame_metadata_->has_transparent_background != + metadata.has_transparent_background); +#endif viz::LocalSurfaceId local_surface_id = child_local_surface_id_allocator_.GetCurrentLocalSurfaceId(); @@ -1931,19 +2019,14 @@ RenderFrameMetadata LayerTreeHostImpl::MakeRenderFrameMetadata() { metadata.local_surface_id = local_surface_id; } - active_tree_->GetViewportSelection(&metadata.selection); - metadata.is_mobile_optimized = IsMobileOptimized(active_tree_.get()); - return metadata; } bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { DCHECK(CanDraw()); DCHECK_EQ(frame->has_no_damage, frame->render_passes.empty()); - - TRACE_EVENT0("cc,benchmark", "LayerTreeHostImpl::DrawLayers"); - ResetRequiresHighResToDraw(); + skipped_frame_tracker_.DidProduceFrame(); if (frame->has_no_damage) { DCHECK(!resourceless_software_draw_); @@ -1953,6 +2036,36 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { return false; } + auto compositor_frame = GenerateCompositorFrame(frame); + layer_tree_frame_sink_->SubmitCompositorFrame(std::move(compositor_frame)); + + // Clears the list of swap promises after calling DidSwap on each of them to + // signal that the swap is over. + active_tree()->ClearSwapPromises(); + + // The next frame should start by assuming nothing has changed, and changes + // 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_list->size(); i++) { + auto* surface = (*frame->render_surface_list)[i]; + surface->damage_tracker()->DidDrawDamagedArea(); + } + active_tree_->ResetAllChangeTracking(); + + active_tree_->set_has_ever_been_drawn(true); + devtools_instrumentation::DidDrawFrame(id_); + benchmark_instrumentation::IssueImplThreadRenderingStatsEvent( + rendering_stats_instrumentation_->TakeImplThreadRenderingStats()); + return true; +} + +viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( + FrameData* frame) { + TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline", + TRACE_ID_GLOBAL(CurrentBeginFrameArgs().trace_id), + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, + "step", "GenerateCompositorFrame"); base::TimeTicks frame_time = CurrentBeginFrameArgs().frame_time; fps_counter_->SaveTimeStamp(frame_time, !layer_tree_frame_sink_->context_provider()); @@ -1992,7 +2105,7 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { if (active_tree_->hud_layer()) { TRACE_EVENT0("cc", "DrawLayers.UpdateHudTexture"); active_tree_->hud_layer()->UpdateHudTexture( - draw_mode, layer_tree_frame_sink_, resource_provider_.get(), + draw_mode, layer_tree_frame_sink_, &resource_provider_, // The hud uses Gpu rasterization if the device is capable, not related // to the content of the web page. gpu_rasterization_status_ != GpuRasterizationStatus::OFF_DEVICE, @@ -2007,28 +2120,29 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { frame->use_default_lower_bound_deadline); metadata.activation_dependencies = std::move(frame->activation_dependencies); - active_tree()->FinishSwapPromises(&metadata, &frame_token_allocator_); + active_tree()->FinishSwapPromises(&metadata); + // The swap-promises should not change the frame-token. + DCHECK_EQ(metadata.frame_token + 1, next_frame_token_); if (render_frame_metadata_observer_) { - last_draw_render_frame_metadata_ = MakeRenderFrameMetadata(); + last_draw_render_frame_metadata_ = MakeRenderFrameMetadata(frame); render_frame_metadata_observer_->OnRenderFrameSubmission( - *last_draw_render_frame_metadata_); + *last_draw_render_frame_metadata_, &metadata); } metadata.latency_info.emplace_back(ui::SourceEventType::FRAME); ui::LatencyInfo& new_latency_info = metadata.latency_info.back(); if (CommitToActiveTree()) { new_latency_info.AddLatencyNumberWithTimestamp( - ui::LATENCY_BEGIN_FRAME_UI_COMPOSITOR_COMPONENT, 0, frame_time, 1); + ui::LATENCY_BEGIN_FRAME_UI_COMPOSITOR_COMPONENT, frame_time, 1); } else { new_latency_info.AddLatencyNumberWithTimestamp( - ui::LATENCY_BEGIN_FRAME_RENDERER_COMPOSITOR_COMPONENT, 0, frame_time, - 1); + ui::LATENCY_BEGIN_FRAME_RENDERER_COMPOSITOR_COMPONENT, frame_time, 1); base::TimeTicks draw_time = base::TimeTicks::Now(); for (auto& latency : metadata.latency_info) { latency.AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, draw_time, 1); + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, draw_time, 1); } } ui::LatencyInfo::TraceIntermediateFlowEvents(metadata.latency_info, @@ -2049,7 +2163,7 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { viz::CompositorFrame compositor_frame; compositor_frame.metadata = std::move(metadata); - resource_provider_->PrepareSendToParent( + resource_provider_.PrepareSendToParent( resources, &compositor_frame.resource_list, layer_tree_frame_sink_->context_provider()); compositor_frame.render_pass_list = std::move(frame->render_passes); @@ -2077,35 +2191,14 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { total_quad_count); } - compositor_frame.metadata.frame_token = - frame_token_allocator_.GetFrameTokenForSubmission(); - - layer_tree_frame_sink_->SubmitCompositorFrame(std::move(compositor_frame)); - - // Clears the list of swap promises after calling DidSwap on each of them to - // signal that the swap is over. - active_tree()->ClearSwapPromises(); - - // The next frame should start by assuming nothing has changed, and changes - // 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_list->size(); i++) { - auto* surface = (*frame->render_surface_list)[i]; - surface->damage_tracker()->DidDrawDamagedArea(); - } - active_tree_->ResetAllChangeTracking(); - - active_tree_->set_has_ever_been_drawn(true); - devtools_instrumentation::DidDrawFrame(id_); - benchmark_instrumentation::IssueImplThreadRenderingStatsEvent( - rendering_stats_instrumentation_->TakeImplThreadRenderingStats()); - return true; + return compositor_frame; } void LayerTreeHostImpl::DidDrawAllLayers(const FrameData& frame) { + // TODO(lethalantidote): LayerImpl::DidDraw can be removed when + // VideoLayerImpl is removed. for (size_t i = 0; i < frame.will_draw_layers.size(); ++i) - frame.will_draw_layers[i]->DidDraw(resource_provider_.get()); + frame.will_draw_layers[i]->DidDraw(&resource_provider_); for (auto* it : video_frame_controllers_) it->DidDrawFrame(); @@ -2293,6 +2386,7 @@ void LayerTreeHostImpl::UpdateTreeResourcesForGpuRasterizationIfNeeded() { } bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { + impl_thread_phase_ = ImplThreadPhase::INSIDE_IMPL_FRAME; current_begin_frame_tracker_.Start(args); if (is_likely_to_require_a_draw_) { @@ -2310,7 +2404,7 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { for (auto* it : video_frame_controllers_) it->OnBeginFrame(args); - impl_thread_phase_ = ImplThreadPhase::INSIDE_IMPL_FRAME; + skipped_frame_tracker_.BeginFrame(args.frame_time, args.interval); bool recent_frame_had_no_damage = consecutive_frame_with_damage_count_ < settings_.damaged_frame_limit; @@ -2335,6 +2429,7 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { } void LayerTreeHostImpl::DidFinishImplFrame() { + skipped_frame_tracker_.FinishFrame(); impl_thread_phase_ = ImplThreadPhase::IDLE; current_begin_frame_tracker_.Finish(); tile_manager_.decoded_image_tracker().NotifyFrameFinished(); @@ -2346,13 +2441,19 @@ void LayerTreeHostImpl::DidNotProduceFrame(const viz::BeginFrameAck& ack) { } void LayerTreeHostImpl::UpdateViewportContainerSizes() { + // TODO(bokan): Make URL-bar bounds deltas work with Blink-generated property + // trees. https://crbug.com/850135. + if (!InnerViewportScrollNode()) + return; + LayerImpl* inner_container = active_tree_->InnerViewportContainerLayer(); LayerImpl* outer_container = active_tree_->OuterViewportContainerLayer(); if (!inner_container) return; - ViewportAnchor anchor(InnerViewportScrollLayer(), OuterViewportScrollLayer()); + ViewportAnchor anchor(InnerViewportScrollNode(), OuterViewportScrollLayer(), + active_tree_.get()); float top_controls_layout_height = active_tree_->browser_controls_shrink_blink_size() @@ -2382,8 +2483,7 @@ void LayerTreeHostImpl::UpdateViewportContainerSizes() { gfx::Vector2dF amount_to_expand_scaled = gfx::ScaleVector2d( amount_to_expand, 1.f / active_tree_->min_page_scale_factor()); outer_container->SetViewportBoundsDelta(amount_to_expand_scaled); - active_tree_->InnerViewportScrollLayer()->SetViewportBoundsDelta( - amount_to_expand_scaled); + InnerViewportScrollLayer()->SetViewportBoundsDelta(amount_to_expand_scaled); anchor.ResetViewportToAnchoredPosition(); } @@ -2500,8 +2600,8 @@ void LayerTreeHostImpl::DidLoseLayerTreeFrameSink() { client_->DidLoseLayerTreeFrameSinkOnImplThread(); } -bool LayerTreeHostImpl::HaveRootScrollLayer() const { - return !!InnerViewportScrollLayer(); +bool LayerTreeHostImpl::HaveRootScrollNode() const { + return InnerViewportScrollNode(); } LayerImpl* LayerTreeHostImpl::InnerViewportContainerLayer() const { @@ -2513,11 +2613,7 @@ LayerImpl* LayerTreeHostImpl::InnerViewportScrollLayer() const { } ScrollNode* LayerTreeHostImpl::InnerViewportScrollNode() const { - const auto* inner_viewport_scroll_layer = InnerViewportScrollLayer(); - if (!inner_viewport_scroll_layer) - return nullptr; - ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree; - return scroll_tree.Node(inner_viewport_scroll_layer->scroll_tree_index()); + return active_tree_->InnerViewportScrollNode(); } LayerImpl* LayerTreeHostImpl::OuterViewportContainerLayer() const { @@ -2529,12 +2625,7 @@ LayerImpl* LayerTreeHostImpl::OuterViewportScrollLayer() const { } ScrollNode* LayerTreeHostImpl::OuterViewportScrollNode() const { - // TODO(pdr): Refactor this to work like InnerViewportScrollNode and access - // OuterViewportScrollLayer instead of MainScrollLayer. - if (!viewport()->MainScrollLayer()) - return nullptr; - ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree; - return scroll_tree.Node(viewport()->MainScrollLayer()->scroll_tree_index()); + return active_tree_->OuterViewportScrollNode(); } ScrollNode* LayerTreeHostImpl::CurrentlyScrollingNode() { @@ -2648,6 +2739,12 @@ void LayerTreeHostImpl::ActivateSyncTree() { DCHECK(!recycle_tree_); pending_tree_.swap(recycle_tree_); + // ScrollTimelines track a scroll source (i.e. a scroll node in the scroll + // tree), whose ElementId may change between the active and pending trees. + // Therefore we must inform all ScrollTimelines when the pending tree is + // promoted to active. + mutator_host_->PromoteScrollTimelinesPendingToActive(); + // If we commit to the active tree directly, this is already done during // commit. ActivateAnimations(); @@ -2702,6 +2799,23 @@ void LayerTreeHostImpl::ActivateStateForImages() { tile_manager_.DidActivateSyncTree(); } +void LayerTreeHostImpl::OnPurgeMemory() { + ReleaseTileResources(); + active_tree_->OnPurgeMemory(); + if (pending_tree_) + pending_tree_->OnPurgeMemory(); + if (recycle_tree_) + recycle_tree_->OnPurgeMemory(); + + EvictAllUIResources(); + if (image_decode_cache_) { + image_decode_cache_->SetShouldAggressivelyFreeResources(true); + image_decode_cache_->SetShouldAggressivelyFreeResources(false); + } + if (resource_pool_) + resource_pool_->OnPurgeMemory(); +} + void LayerTreeHostImpl::OnMemoryPressure( base::MemoryPressureListener::MemoryPressureLevel level) { // Only work for low-end devices for now. @@ -2713,15 +2827,7 @@ void LayerTreeHostImpl::OnMemoryPressure( case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: break; case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: - ReleaseTileResources(); - ReleaseTreeResources(); - ClearUIResources(); - if (image_decode_cache_) { - image_decode_cache_->SetShouldAggressivelyFreeResources(true); - image_decode_cache_->SetShouldAggressivelyFreeResources(false); - } - if (resource_pool_) - resource_pool_->OnPurgeMemory(); + OnPurgeMemory(); break; } } @@ -2769,6 +2875,7 @@ void LayerTreeHostImpl::SetNeedsOneBeginImplFrame() { void LayerTreeHostImpl::SetNeedsRedraw() { NotifySwapPromiseMonitorsOfSetNeedsRedraw(); client_->SetNeedsRedrawOnImplThread(); + skipped_frame_tracker_.WillProduceFrame(); } ManagedMemoryPolicy LayerTreeHostImpl::ActualManagedMemoryPolicy() const { @@ -2952,7 +3059,7 @@ LayerTreeHostImpl::TakeCompletedImageDecodeRequests() { return result; } -void LayerTreeHostImpl::DidNavigate() { +void LayerTreeHostImpl::ClearCaches() { // 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; @@ -3003,16 +3110,10 @@ void LayerTreeHostImpl::ReleaseLayerTreeFrameSink() { has_valid_layer_tree_frame_sink_ = false; - // Since we will create a new resource provider, we cannot continue to use - // the old resources (i.e. render_surfaces and texture IDs). Clear them - // before we destroy the old resource provider. ReleaseTreeResources(); - - // Note: ui resource cleanup uses the |resource_provider_|. CleanUpTileManagerResources(); resource_pool_ = nullptr; ClearUIResources(); - resource_provider_ = nullptr; if (layer_tree_frame_sink_->context_provider()) { auto* gl = layer_tree_frame_sink_->context_provider()->ContextGL(); @@ -3022,21 +3123,46 @@ void LayerTreeHostImpl::ReleaseLayerTreeFrameSink() { // Release any context visibility before we destroy the LayerTreeFrameSink. SetContextVisibility(false); + bool all_resources_are_lost = layer_tree_frame_sink_->context_provider(); + // Detach from the old LayerTreeFrameSink and reset |layer_tree_frame_sink_| // pointer as this surface is going to be destroyed independent of if binding // the new LayerTreeFrameSink succeeds or not. layer_tree_frame_sink_->DetachFromClient(); layer_tree_frame_sink_ = nullptr; + // If gpu compositing, then any resources created with the gpu context in the + // LayerTreeFrameSink were exported to the display compositor may be modified + // by it, and thus we would be unable to determine what state they are in, in + // order to reuse them, so they must be lost. In software compositing, the + // resources are not modified by the display compositor (there is no stateful + // metadata for shared memory), so we do not need to consider them lost. + // + // In both cases, the resources that are exported to the display compositor + // will have no means of being returned to this client without the + // LayerTreeFrameSink, so they should no longer be considered as exported. + // Do this *after* any interactions with the |layer_tree_frame_sink_| in case + // it tries to return resources during destruction. + // + // The assumption being made here is that the display compositor WILL NOT use + // any resources previously exported when the CompositorFrameSink is closed. + // This should be true as the connection is closed when the display compositor + // shuts down/crashes, or when it believes we are a malicious client in which + // case it will not display content from the previous CompositorFrameSink. If + // this assumption is violated, we may modify resources no longer considered + // as exported while the display compositor is still making use of them, + // leading to visual mistakes. + resource_provider_.ReleaseAllExportedResources(all_resources_are_lost); + // We don't know if the next LayerTreeFrameSink will support GPU // rasterization. Make sure to clear the flag so that we force a // re-computation. use_gpu_rasterization_ = false; } -bool LayerTreeHostImpl::InitializeRenderer( +bool LayerTreeHostImpl::InitializeFrameSink( LayerTreeFrameSink* layer_tree_frame_sink) { - TRACE_EVENT0("cc", "LayerTreeHostImpl::InitializeRenderer"); + TRACE_EVENT0("cc", "LayerTreeHostImpl::InitializeFrameSink"); ReleaseLayerTreeFrameSink(); if (!layer_tree_frame_sink->BindToClient(this)) { @@ -3058,25 +3184,17 @@ bool LayerTreeHostImpl::InitializeRenderer( max_texture_size_ = 16 * 1024; } - resource_provider_ = std::make_unique<LayerTreeResourceProvider>( - layer_tree_frame_sink_->context_provider(), - layer_tree_frame_sink_->capabilities().delegated_sync_points_required); resource_pool_ = std::make_unique<ResourcePool>( - resource_provider_.get(), layer_tree_frame_sink_->context_provider(), - GetTaskRunner(), ResourcePool::kDefaultExpirationDelay, + &resource_provider_, context_provider, GetTaskRunner(), + ResourcePool::kDefaultExpirationDelay, settings_.disallow_non_exact_resource_reuse); - // TODO(piman): Make oop raster always supported: http://crbug.com/786591 - use_oop_rasterization_ = settings_.enable_oop_rasterization; - if (use_oop_rasterization_) { - auto* context = layer_tree_frame_sink_->worker_context_provider(); - if (context) { - viz::RasterContextProvider::ScopedRasterContextLock hold(context); - use_oop_rasterization_ &= - context->ContextCapabilities().supports_oop_raster; - } else { - use_oop_rasterization_ = false; - } + auto* context = layer_tree_frame_sink_->worker_context_provider(); + if (context) { + viz::RasterContextProvider::ScopedRasterContextLock hold(context); + use_oop_rasterization_ = context->ContextCapabilities().supports_oop_raster; + } else { + use_oop_rasterization_ = false; } // Since the new context may be capable of MSAA, update status here. We don't @@ -3182,11 +3300,9 @@ float LayerTreeHostImpl::CurrentBrowserControlsShownRatio() const { return active_tree_->CurrentBrowserControlsShownRatio(); } -void LayerTreeHostImpl::BindToClient(InputHandlerClient* client, - bool wheel_scroll_latching_enabled) { +void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) { DCHECK(input_handler_client_ == nullptr); input_handler_client_ = client; - touchpad_and_wheel_scroll_latching_enabled_ = wheel_scroll_latching_enabled; } InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll( @@ -3363,6 +3479,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBeginImpl( } scroll_status.thread = SCROLL_ON_IMPL_THREAD; mutator_host_->ScrollAnimationAbort(); + is_animating_for_snap_ = false; browser_controls_offset_manager_->ScrollBegin(); @@ -3668,8 +3785,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated( // For the rest of the current scroll sequence, latch to the first node // that scrolled while it still exists. - if (touchpad_and_wheel_scroll_latching_enabled_ && - scroll_tree.FindNodeFromElementId( + if (scroll_tree.FindNodeFromElementId( scroll_animating_latched_element_id_) && scroll_node->element_id != scroll_animating_latched_element_id_) { continue; @@ -3693,6 +3809,8 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated( viewport()->ScrollAnimated(pending_delta, delayed_by); // Viewport::ScrollAnimated returns pending_delta as long as it starts // an animation. + did_scroll_x_for_scroll_gesture_ |= scrolled.x() != 0; + did_scroll_y_for_scroll_gesture_ |= scrolled.y() != 0; if (scrolled == pending_delta) { scroll_animating_latched_element_id_ = scroll_node->element_id; return scroll_status; @@ -3703,6 +3821,8 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated( gfx::Vector2dF scroll_delta = ComputeScrollDelta(*scroll_node, pending_delta); if (ScrollAnimationCreate(scroll_node, scroll_delta, delayed_by)) { + did_scroll_x_for_scroll_gesture_ |= scroll_delta.x() != 0; + did_scroll_y_for_scroll_gesture_ |= scroll_delta.y() != 0; scroll_animating_latched_element_id_ = scroll_node->element_id; return scroll_status; } @@ -4041,7 +4161,7 @@ void LayerTreeHostImpl::UpdateImageDecodingHints( void LayerTreeHostImpl::SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer) { render_frame_metadata_observer_ = std::move(observer); - render_frame_metadata_observer_->BindToCurrentThread(&frame_token_allocator_); + render_frame_metadata_observer_->BindToCurrentThread(); } InputHandlerScrollResult LayerTreeHostImpl::ScrollBy( @@ -4235,6 +4355,8 @@ bool LayerTreeHostImpl::SnapAtScrollEnd() { } else { ScrollAnimationCreate(scroll_node, delta, base::TimeDelta()); } + DCHECK(!is_animating_for_snap_); + is_animating_for_snap_ = true; return true; } @@ -4254,8 +4376,8 @@ gfx::ScrollOffset LayerTreeHostImpl::GetVisualScrollOffset( bool LayerTreeHostImpl::GetSnapFlingInfo( const gfx::Vector2dF& natural_displacement_in_viewport, - gfx::Vector2dF* initial_offset, - gfx::Vector2dF* target_offset) const { + gfx::Vector2dF* out_initial_offset, + gfx::Vector2dF* out_target_offset) const { const ScrollNode* scroll_node = CurrentlyScrollingNode(); if (!scroll_node || !scroll_node->snap_container_data.has_value()) return false; @@ -4265,7 +4387,7 @@ bool LayerTreeHostImpl::GetSnapFlingInfo( gfx::Vector2dF natural_displacement_in_content = gfx::ScaleVector2d(natural_displacement_in_viewport, 1.f / scale_factor); - *initial_offset = + *out_initial_offset = ScrollOffsetToVector2dF(GetVisualScrollOffset(*scroll_node)); bool did_scroll_x = did_scroll_x_for_scroll_gesture_ || @@ -4274,15 +4396,15 @@ bool LayerTreeHostImpl::GetSnapFlingInfo( natural_displacement_in_content.y() != 0; gfx::ScrollOffset snap_offset; - if (!data.FindSnapPosition( - gfx::ScrollOffset(*initial_offset + natural_displacement_in_content), - did_scroll_x, did_scroll_y, &snap_offset)) { + if (!data.FindSnapPosition(gfx::ScrollOffset(*out_initial_offset + + natural_displacement_in_content), + did_scroll_x, did_scroll_y, &snap_offset)) { return false; } - *target_offset = ScrollOffsetToVector2dF(snap_offset); - target_offset->Scale(scale_factor); - initial_offset->Scale(scale_factor); + *out_target_offset = ScrollOffsetToVector2dF(snap_offset); + out_target_offset->Scale(scale_factor); + out_initial_offset->Scale(scale_factor); return true; } @@ -4294,6 +4416,7 @@ void LayerTreeHostImpl::ClearCurrentlyScrollingNode() { accumulated_root_overscroll_ = gfx::Vector2dF(); did_scroll_x_for_scroll_gesture_ = false; did_scroll_y_for_scroll_gesture_ = false; + is_animating_for_snap_ = false; } void LayerTreeHostImpl::ScrollEndImpl(ScrollState* scroll_state) { @@ -4312,20 +4435,6 @@ void LayerTreeHostImpl::ScrollEnd(ScrollState* scroll_state, bool should_snap) { ScrollEndImpl(scroll_state); } -InputHandler::ScrollStatus LayerTreeHostImpl::FlingScrollBegin() { - InputHandler::ScrollStatus scroll_status; - scroll_status.main_thread_scrolling_reasons = - MainThreadScrollingReason::kNotScrollingOnMain; - if (!CurrentlyScrollingNode()) { - scroll_status.thread = SCROLL_IGNORED; - scroll_status.main_thread_scrolling_reasons = - MainThreadScrollingReason::kNoScrollingLayer; - } else { - scroll_status.thread = SCROLL_ON_IMPL_THREAD; - } - return scroll_status; -} - void LayerTreeHostImpl::MouseDown() { ScrollbarAnimationController* animation_controller = ScrollbarAnimationControllerForElementId( @@ -4378,8 +4487,8 @@ void LayerTreeHostImpl::MouseMoveAt(const gfx::Point& viewport_point) { scroll_element_id = scroll_node->element_id; // Scrollbars for the viewport are registered with the outer viewport layer. - if (InnerViewportScrollLayer() && OuterViewportScrollLayer() && - scroll_element_id == InnerViewportScrollLayer()->element_id()) + if (InnerViewportScrollNode() && OuterViewportScrollLayer() && + scroll_element_id == InnerViewportScrollNode()->element_id) scroll_element_id = OuterViewportScrollLayer()->element_id(); } @@ -4427,7 +4536,7 @@ void LayerTreeHostImpl::PinchGestureBegin() { void LayerTreeHostImpl::PinchGestureUpdate(float magnify_delta, const gfx::Point& anchor) { TRACE_EVENT0("cc", "LayerTreeHostImpl::PinchGestureUpdate"); - if (!InnerViewportScrollLayer()) + if (!InnerViewportScrollNode()) return; viewport()->PinchUpdate(magnify_delta, anchor); client_->SetNeedsCommitOnImplThread(); @@ -4455,27 +4564,23 @@ void LayerTreeHostImpl::PinchGestureEnd(const gfx::Point& anchor, SetNeedsRedraw(); } -static void CollectScrollDeltas(ScrollAndScaleSet* scroll_info, - LayerTreeImpl* tree_impl) { - if (tree_impl->LayerListIsEmpty()) +void LayerTreeHostImpl::CollectScrollDeltas( + ScrollAndScaleSet* scroll_info) const { + if (active_tree_->LayerListIsEmpty()) return; ElementId inner_viewport_scroll_element_id = - tree_impl->InnerViewportScrollLayer() - ? tree_impl->InnerViewportScrollLayer()->element_id() - : ElementId(); + InnerViewportScrollNode() ? InnerViewportScrollNode()->element_id + : ElementId(); - tree_impl->property_trees()->scroll_tree.CollectScrollDeltas( + active_tree_->property_trees()->scroll_tree.CollectScrollDeltas( scroll_info, inner_viewport_scroll_element_id); } -static void CollectScrollbarUpdates( - ScrollAndScaleSet* scroll_info, - std::unordered_map<ElementId, - std::unique_ptr<ScrollbarAnimationController>, - ElementIdHash>* controllers) { - scroll_info->scrollbars.reserve(controllers->size()); - for (auto& pair : *controllers) { +void LayerTreeHostImpl::CollectScrollbarUpdates( + ScrollAndScaleSet* scroll_info) const { + scroll_info->scrollbars.reserve(scrollbar_animation_controllers_.size()); + for (auto& pair : scrollbar_animation_controllers_) { scroll_info->scrollbars.push_back(LayerTreeHostCommon::ScrollbarsUpdateInfo( pair.first, pair.second->ScrollbarsHidden())); } @@ -4484,10 +4589,15 @@ static void CollectScrollbarUpdates( std::unique_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() { std::unique_ptr<ScrollAndScaleSet> scroll_info(new ScrollAndScaleSet()); - CollectScrollDeltas(scroll_info.get(), active_tree_.get()); - CollectScrollbarUpdates(scroll_info.get(), &scrollbar_animation_controllers_); + CollectScrollDeltas(scroll_info.get()); + CollectScrollbarUpdates(scroll_info.get()); scroll_info->page_scale_delta = active_tree_->page_scale_factor()->PullDeltaForMainThread(); + // We should never process non-unit page_scale_delta for an OOPIF subframe. + // TODO(wjmaclean): Remove this DCHECK as a pre-condition to closing the bug. + // https://crbug.com/845097 + DCHECK(!settings().is_layer_tree_for_subframe || + scroll_info->page_scale_delta == 1.f); scroll_info->top_controls_delta = active_tree()->top_controls_shown_ratio()->PullDeltaForMainThread(); scroll_info->elastic_overscroll_delta = @@ -4568,8 +4678,8 @@ bool LayerTreeHostImpl::AnimateLayers(base::TimeTicks monotonic_time, const ScrollTree& scroll_tree = is_active_tree ? active_tree_->property_trees()->scroll_tree : pending_tree_->property_trees()->scroll_tree; - const bool animated = - mutator_host_->TickAnimations(monotonic_time, scroll_tree); + const bool animated = mutator_host_->TickAnimations( + monotonic_time, scroll_tree, is_active_tree); // TODO(crbug.com/551134): Only do this if the animations are on the active // tree, or if they are on the pending tree waiting for some future time to @@ -4654,11 +4764,11 @@ LayerTreeHostImpl::ScrollbarAnimationControllerForElementId( // The viewport layers have only one set of scrollbars. On Android, these are // registered with the inner viewport, otherwise they're registered with the // outer viewport. If a controller for one exists, the other shouldn't. - if (InnerViewportScrollLayer() && OuterViewportScrollLayer()) { - if (scroll_element_id == InnerViewportScrollLayer()->element_id() || + if (InnerViewportScrollNode() && OuterViewportScrollLayer()) { + if (scroll_element_id == InnerViewportScrollNode()->element_id || scroll_element_id == OuterViewportScrollLayer()->element_id()) { auto itr = scrollbar_animation_controllers_.find( - InnerViewportScrollLayer()->element_id()); + InnerViewportScrollNode()->element_id); if (itr != scrollbar_animation_controllers_.end()) return itr->second.get(); @@ -4972,16 +5082,15 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, if (layer_tree_frame_sink_->context_provider()) { gpu::gles2::GLES2Interface* gl = layer_tree_frame_sink_->context_provider()->ContextGL(); - gpu::Mailbox mailbox = gpu::Mailbox::Generate(); + gpu::Mailbox mailbox; gl->ProduceTextureDirectCHROMIUM(texture_alloc.texture_id, mailbox.name); gpu::SyncToken sync_token = - LayerTreeResourceProvider::GenerateSyncTokenHelper(gl); + viz::ClientResourceProvider::GenerateSyncTokenHelper(gl); transferable = viz::TransferableResource::MakeGLOverlay( mailbox, GL_LINEAR, texture_alloc.texture_target, sync_token, upload_size, texture_alloc.overlay_candidate); transferable.format = format; - transferable.buffer_format = viz::BufferFormat(format); } else { mojo::ScopedSharedBufferHandle memory_handle = viz::bitmap_allocation::DuplicateAndCloseMappedBitmap( @@ -4992,7 +5101,7 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, upload_size, format); } transferable.color_space = color_space; - id = resource_provider_->ImportResource( + id = resource_provider_.ImportResource( transferable, // The OnUIResourceReleased method is bound with a WeakPtr, but the // resource backing will be deleted when the LayerTreeFrameSink is @@ -5019,11 +5128,12 @@ void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) { UIResourceData& data = it->second; viz::ResourceId id = data.resource_id_for_export; // Move the |data| to |deleted_ui_resources_| before removing it from the - // LayerTreeResourceProvider, so that the ReleaseCallback can see it there. + // viz::ClientResourceProvider, so that the ReleaseCallback can see it + // there. deleted_ui_resources_[uid] = std::move(data); ui_resource_map_.erase(it); - resource_provider_->RemoveImportedResource(id); + resource_provider_.RemoveImportedResource(id); } MarkUIResourceNotEvicted(uid); } @@ -5064,7 +5174,7 @@ void LayerTreeHostImpl::ClearUIResources() { for (auto& pair : ui_resource_map_) { UIResourceId uid = pair.first; UIResourceData& data = pair.second; - resource_provider_->RemoveImportedResource(data.resource_id_for_export); + resource_provider_.RemoveImportedResource(data.resource_id_for_export); // Immediately drop the backing instead of waiting for the resource to be // returned from the ResourceProvider, as this is called in cases where the // ability to clean up the backings will go away (context loss, shutdown). @@ -5174,18 +5284,11 @@ bool LayerTreeHostImpl::ScrollAnimationUpdateTarget( bool LayerTreeHostImpl::IsElementInList(ElementId element_id, ElementListType list_type) const { - if (list_type == ElementListType::ACTIVE) { - return active_tree() - ? active_tree()->LayerByElementId(element_id) != nullptr - : false; - } else { - if (pending_tree() && pending_tree()->LayerByElementId(element_id)) - return true; - if (recycle_tree() && recycle_tree()->LayerByElementId(element_id)) - return true; + if (list_type == ElementListType::ACTIVE) + return active_tree() && active_tree()->IsElementInLayerList(element_id); - return false; - } + return (pending_tree() && pending_tree()->IsElementInLayerList(element_id)) || + (recycle_tree() && recycle_tree()->IsElementInLayerList(element_id)); } void LayerTreeHostImpl::SetMutatorsNeedCommit() {} @@ -5288,7 +5391,7 @@ void LayerTreeHostImpl::ScrollOffsetAnimationFinished() { // TODO(majidvp): We should pass in the original starting scroll position here ScrollStateData scroll_state_data; ScrollState scroll_state(scroll_state_data); - ScrollEndImpl(&scroll_state); + ScrollEnd(&scroll_state, !is_animating_for_snap_); } gfx::ScrollOffset LayerTreeHostImpl::GetScrollOffsetForAnimation( diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index 3f93eb51e60..d2d9d750c8b 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -15,8 +15,10 @@ #include <vector> #include "base/callback.h" +#include "base/containers/circular_deque.h" #include "base/containers/flat_map.h" #include "base/macros.h" +#include "base/memory/memory_coordinator_client.h" #include "base/memory/memory_pressure_listener.h" #include "base/sequenced_task_runner.h" #include "base/time/time.h" @@ -27,7 +29,6 @@ #include "cc/input/input_handler.h" #include "cc/input/scrollbar_animation_controller.h" #include "cc/layers/layer_collections.h" -#include "cc/resources/layer_tree_resource_provider.h" #include "cc/resources/ui_resource_client.h" #include "cc/scheduler/begin_frame_tracker.h" #include "cc/scheduler/commit_earlyout_reason.h" @@ -36,8 +37,8 @@ #include "cc/tiles/decoded_image_tracker.h" #include "cc/tiles/image_decode_cache.h" #include "cc/tiles/tile_manager.h" -#include "cc/trees/frame_token_allocator.h" #include "cc/trees/layer_tree_frame_sink_client.h" +#include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_mutator.h" #include "cc/trees/layer_tree_settings.h" #include "cc/trees/managed_memory_policy.h" @@ -45,19 +46,23 @@ #include "cc/trees/render_frame_metadata.h" #include "cc/trees/task_runner_provider.h" #include "cc/trees/ukm_manager.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/gpu/context_cache_controller.h" #include "components/viz/common/quads/render_pass.h" #include "components/viz/common/surfaces/child_local_surface_id_allocator.h" #include "components/viz/common/surfaces/local_surface_id.h" #include "components/viz/common/surfaces/surface_id.h" +#include "components/viz/common/surfaces/surface_range.h" #include "ui/gfx/geometry/rect.h" +#include "ui/latency/frame_metrics.h" namespace gfx { class ScrollOffset; } namespace viz { +class CompositorFrame; class CompositorFrameMetadata; } @@ -135,7 +140,8 @@ class LayerTreeHostImplClient { virtual void DidCompletePageScaleAnimationOnImplThread() = 0; // Called when output surface asks for a draw. - virtual void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) = 0; + virtual void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, + bool skip_draw) = 0; virtual void NeedsImplSideInvalidation( bool needs_first_draw_on_activation) = 0; @@ -144,13 +150,12 @@ class LayerTreeHostImplClient { virtual void RequestBeginMainFrameNotExpected(bool new_state) = 0; - // Called when a presentation time is requested. |source_frames| identifies - // the frames that correspond to the request. + // Called when a presentation time is requested. |frame_token| identifies + // the frame that was presented. virtual void DidPresentCompositorFrameOnImplThread( - const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) = 0; + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback) = 0; protected: virtual ~LayerTreeHostImplClient() {} @@ -166,7 +171,8 @@ class CC_EXPORT LayerTreeHostImpl public ScrollbarAnimationControllerClient, public VideoFrameControllerClient, public MutatorHostClient, - public base::SupportsWeakPtr<LayerTreeHostImpl> { + public base::SupportsWeakPtr<LayerTreeHostImpl>, + public base::MemoryCoordinatorClient { public: // This structure is used to build all the state required for producing a // single CompositorFrame. The |render_passes| list becomes the set of @@ -230,8 +236,7 @@ class CC_EXPORT LayerTreeHostImpl ~LayerTreeHostImpl() override; // InputHandler implementation - void BindToClient(InputHandlerClient* client, - bool wheel_scroll_latching_enabled) override; + void BindToClient(InputHandlerClient* client) override; InputHandler::ScrollStatus ScrollBegin( ScrollState* scroll_state, InputHandler::ScrollInputType type) override; @@ -249,7 +254,6 @@ class CC_EXPORT LayerTreeHostImpl void SetSynchronousInputHandlerRootScrollOffset( const gfx::ScrollOffset& root_offset) override; void ScrollEnd(ScrollState* scroll_state, bool should_snap = false) override; - InputHandler::ScrollStatus FlingScrollBegin() override; void MouseDown() override; void MouseUp() override; @@ -275,6 +279,7 @@ class CC_EXPORT LayerTreeHostImpl EventListenerTypeForTouchStartOrMoveAt( const gfx::Point& viewport_port, TouchAction* out_touch_action) override; + bool HasWheelEventHandlerAt(const gfx::Point& viewport_point) const override; std::unique_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor( ui::LatencyInfo* latency) override; ScrollElasticityHelper* CreateScrollElasticityHelper() override; @@ -289,7 +294,7 @@ class CC_EXPORT LayerTreeHostImpl void SetCurrentBrowserControlsShownRatio(float offset) override; float CurrentBrowserControlsShownRatio() const override; void DidChangeBrowserControlsPosition() override; - bool HaveRootScrollLayer() const override; + bool HaveRootScrollNode() const override; void UpdateViewportContainerSizes(); @@ -309,7 +314,6 @@ class CC_EXPORT LayerTreeHostImpl void ActivateAnimations(); void Animate(); void AnimatePendingTreeAfterCommit(); - void MainThreadHasStoppedFlinging(); void DidAnimateScrollOffset(); void SetFullViewportDamage(); void SetViewportDamage(const gfx::Rect& damage_rect); @@ -318,7 +322,7 @@ class CC_EXPORT LayerTreeHostImpl // add impl-side invalidations to it. // virtual for testing. virtual void InvalidateContentOnImplSide(); - virtual void InvalidateLayerTreeFrameSink(); + virtual void InvalidateLayerTreeFrameSink(bool needs_redraw); void SetTreeLayerScrollOffsetMutated(ElementId element_id, LayerTreeImpl* tree, @@ -363,6 +367,7 @@ class CC_EXPORT LayerTreeHostImpl // called between the two. virtual DrawResult PrepareToDraw(FrameData* frame); virtual bool DrawLayers(FrameData* frame); + viz::CompositorFrame GenerateCompositorFrame(FrameData* frame); // Must be called if and only if PrepareToDraw was called. void DidDrawAllLayers(const FrameData& frame); @@ -430,18 +435,17 @@ class CC_EXPORT LayerTreeHostImpl base::Optional<viz::HitTestRegionList> BuildHitTestData() override; void DidLoseLayerTreeFrameSink() override; void DidReceiveCompositorFrameAck() override; - void DidPresentCompositorFrame(uint32_t presentation_token, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) override; - void DidDiscardCompositorFrame(uint32_t presentation_token) override; + void DidPresentCompositorFrame( + uint32_t frame_token, + const gfx::PresentationFeedback& feedback) override; void ReclaimResources( const std::vector<viz::ReturnedResource>& resources) override; void SetMemoryPolicy(const ManagedMemoryPolicy& policy) override; void SetTreeActivationCallback(const base::Closure& callback) override; void OnDraw(const gfx::Transform& transform, const gfx::Rect& viewport, - bool resourceless_software_draw) override; + bool resourceless_software_draw, + bool skip_draw) override; // Called from LayerTreeImpl. void OnCanDrawStateChangedForTree(); @@ -462,8 +466,7 @@ class CC_EXPORT LayerTreeHostImpl int RequestedMSAASampleCount() const; - // TODO(danakj): Rename this, there is no renderer. - virtual bool InitializeRenderer(LayerTreeFrameSink* layer_tree_frame_sink); + virtual bool InitializeFrameSink(LayerTreeFrameSink* layer_tree_frame_sink); TileManager* tile_manager() { return &tile_manager_; } void SetHasGpuRasterizationTrigger(bool flag); @@ -489,6 +492,8 @@ class CC_EXPORT LayerTreeHostImpl return &image_animation_controller_; } + uint32_t next_frame_token() const { return next_frame_token_; } + virtual bool WillBeginImplFrame(const viz::BeginFrameArgs& args); virtual void DidFinishImplFrame(); void DidNotProduceFrame(const viz::BeginFrameAck& ack); @@ -528,6 +533,10 @@ class CC_EXPORT LayerTreeHostImpl virtual void SetVisible(bool visible); bool visible() const { return visible_; } + bool is_animating_for_snap_for_testing() const { + return is_animating_for_snap_; + } + void SetNeedsCommit() { client_->SetNeedsCommitOnImplThread(); } void SetNeedsOneBeginImplFrame(); void SetNeedsRedraw(); @@ -548,8 +557,8 @@ class CC_EXPORT LayerTreeHostImpl FrameRateCounter* fps_counter() { return fps_counter_.get(); } MemoryHistory* memory_history() { return memory_history_.get(); } DebugRectHistory* debug_rect_history() { return debug_rect_history_.get(); } - LayerTreeResourceProvider* resource_provider() { - return resource_provider_.get(); + viz::ClientResourceProvider* resource_provider() { + return &resource_provider_; } BrowserControlsOffsetManager* browser_controls_manager() { return browser_controls_offset_manager_.get(); @@ -607,8 +616,8 @@ class CC_EXPORT LayerTreeHostImpl gfx::ScrollOffset GetVisualScrollOffset(const ScrollNode& scroll_node) const; bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement_in_viewport, - gfx::Vector2dF* initial_offset, - gfx::Vector2dF* target_offset) const override; + gfx::Vector2dF* out_initial_offset, + gfx::Vector2dF* out_target_offset) const override; // Returns the amount of delta that can be applied to scroll_node, taking // page scale into account. @@ -618,7 +627,7 @@ class CC_EXPORT LayerTreeHostImpl void ScheduleMicroBenchmark(std::unique_ptr<MicroBenchmarkImpl> benchmark); viz::CompositorFrameMetadata MakeCompositorFrameMetadata(); - RenderFrameMetadata MakeRenderFrameMetadata(); + RenderFrameMetadata MakeRenderFrameMetadata(FrameData* frame); // Viewport rectangle and clip in device space. These rects are used to // prioritize raster and determine what is submitted in a CompositorFrame. @@ -688,7 +697,7 @@ class CC_EXPORT LayerTreeHostImpl void QueueImageDecode(int request_id, const PaintImage& image); std::vector<std::pair<int, bool>> TakeCompletedImageDecodeRequests(); - void DidNavigate(); + void ClearCaches(); bool CanConsumeDelta(const ScrollNode& scroll_node, const ScrollState& scroll_state); @@ -727,12 +736,15 @@ class CC_EXPORT LayerTreeHostImpl // Removes empty or orphan RenderPasses from the frame. static void RemoveRenderPasses(FrameData* frame); - LayerTreeHostImplClient* client_; - TaskRunnerProvider* task_runner_provider_; + LayerTreeHostImplClient* const client_; + TaskRunnerProvider* const task_runner_provider_; BeginFrameTracker current_begin_frame_tracker_; private: + void CollectScrollDeltas(ScrollAndScaleSet* scroll_info) const; + void CollectScrollbarUpdates(ScrollAndScaleSet* scroll_info) const; + // Transforms viewport start point and scroll delta to local start point and // local delta, respectively. If the transformation of either the start or end // point of a scroll is clipped, the function returns false. @@ -861,9 +873,22 @@ class CC_EXPORT LayerTreeHostImpl // active tree. void ActivateStateForImages(); + // Overriden from base::MemoryCoordinatorClient. + void OnPurgeMemory() override; + + // TODO(gyuyoung): OnMemoryPressure is deprecated. So this should be removed + // when the memory coordinator is enabled by default. void OnMemoryPressure( base::MemoryPressureListener::MemoryPressureLevel level); + const LayerTreeSettings settings_; + const bool is_synchronous_single_threaded_; + + const int default_color_space_id_ = gfx::ColorSpace::GetNextId(); + const gfx::ColorSpace default_color_space_ = gfx::ColorSpace::CreateSRGB(); + + viz::ClientResourceProvider resource_provider_; + std::unordered_map<UIResourceId, UIResourceData> ui_resource_map_; // UIResources are held here once requested to be deleted until they are // released from the display compositor, then the backing can be deleted. @@ -892,15 +917,15 @@ class CC_EXPORT LayerTreeHostImpl std::unique_ptr<viz::ContextCacheController::ScopedVisibility> worker_context_visibility_; - std::unique_ptr<LayerTreeResourceProvider> resource_provider_; - bool need_update_gpu_rasterization_status_; - bool content_has_slow_paths_; - bool content_has_non_aa_paint_; - bool has_gpu_rasterization_trigger_; - bool use_gpu_rasterization_; - bool use_oop_rasterization_; - bool use_msaa_; - GpuRasterizationStatus gpu_rasterization_status_; + bool need_update_gpu_rasterization_status_ = false; + bool content_has_slow_paths_ = false; + bool content_has_non_aa_paint_ = false; + bool has_gpu_rasterization_trigger_ = false; + bool use_gpu_rasterization_ = false; + bool use_oop_rasterization_ = false; + bool use_msaa_ = false; + GpuRasterizationStatus gpu_rasterization_status_ = + GpuRasterizationStatus::OFF_DEVICE; std::unique_ptr<RasterBufferProvider> raster_buffer_provider_; std::unique_ptr<ResourcePool> resource_pool_; std::unique_ptr<ImageDecodeCache> image_decode_cache_; @@ -918,10 +943,10 @@ class CC_EXPORT LayerTreeHostImpl // by the next sync from the main thread. std::unique_ptr<LayerTreeImpl> recycle_tree_; - InputHandlerClient* input_handler_client_; - bool did_lock_scrolling_layer_; - bool wheel_scrolling_; - bool scroll_affects_scroll_handler_; + InputHandlerClient* input_handler_client_ = nullptr; + bool did_lock_scrolling_layer_ = false; + bool wheel_scrolling_ = false; + bool scroll_affects_scroll_handler_ = false; ElementId scroll_element_id_mouse_currently_over_; ElementId scroll_element_id_mouse_currently_captured_; @@ -932,14 +957,12 @@ class CC_EXPORT LayerTreeHostImpl // hold all state related to elasticity. May be NULL if never requested. std::unique_ptr<ScrollElasticityHelper> scroll_elasticity_helper_; - bool tile_priorities_dirty_; + bool tile_priorities_dirty_ = false; - const LayerTreeSettings settings_; LayerTreeDebugState debug_state_; - bool visible_; + bool visible_ = false; ManagedMemoryPolicy cached_managed_memory_policy_; - const bool is_synchronous_single_threaded_; TileManager tile_manager_; gfx::Vector2dF accumulated_root_overscroll_; @@ -949,8 +972,8 @@ class CC_EXPORT LayerTreeHostImpl bool did_scroll_x_for_scroll_gesture_; bool did_scroll_y_for_scroll_gesture_; - bool pinch_gesture_active_; - bool pinch_gesture_end_should_clear_scrolling_node_; + bool pinch_gesture_active_ = false; + bool pinch_gesture_end_should_clear_scrolling_node_ = false; std::unique_ptr<BrowserControlsOffsetManager> browser_controls_offset_manager_; @@ -963,7 +986,7 @@ class CC_EXPORT LayerTreeHostImpl // The maximum memory that would be used by the prioritized resource // manager, if there were no limit on memory usage. - size_t max_memory_needed_bytes_; + size_t max_memory_needed_bytes_ = 0; // Viewport size passed in from the main thread, in physical pixels. This // value is the default size for all concepts of physical viewport (draw @@ -985,7 +1008,7 @@ class CC_EXPORT LayerTreeHostImpl gfx::Transform external_transform_; gfx::Rect external_viewport_; gfx::Rect viewport_rect_for_tile_priority_; - bool resourceless_software_draw_; + bool resourceless_software_draw_ = false; gfx::Rect viewport_damage_rect_; @@ -1012,12 +1035,12 @@ class CC_EXPORT LayerTreeHostImpl std::set<SwapPromiseMonitor*> swap_promise_monitor_; - bool requires_high_res_to_draw_; - bool is_likely_to_require_a_draw_; + bool requires_high_res_to_draw_ = false; + bool is_likely_to_require_a_draw_ = false; // TODO(danakj): Delete the LayerTreeFrameSink and all resources when // it's lost instead of having this bool. - bool has_valid_layer_tree_frame_sink_; + bool has_valid_layer_tree_frame_sink_ = false; // If it is enabled in the LayerTreeSettings, we can check damage in // WillBeginImplFrame and abort early if there is no damage. We only check @@ -1046,12 +1069,10 @@ class CC_EXPORT LayerTreeHostImpl // These are used to transfer usage of touch and wheel scrolls to the main // thread. - bool has_scrolled_by_wheel_; - bool has_scrolled_by_touch_; - - bool touchpad_and_wheel_scroll_latching_enabled_; + bool has_scrolled_by_wheel_ = false; + bool has_scrolled_by_touch_ = false; - ImplThreadPhase impl_thread_phase_; + ImplThreadPhase impl_thread_phase_ = ImplThreadPhase::IDLE; ImageAnimationController image_animation_controller_; @@ -1060,26 +1081,42 @@ class CC_EXPORT LayerTreeHostImpl // Provides RenderFrameMetadata to the Browser process upon the submission of // each CompositorFrame. std::unique_ptr<RenderFrameMetadataObserver> render_frame_metadata_observer_; - FrameTokenAllocator frame_token_allocator_; - - // Maps from presentation_token set on CF to the source frame that requested - // it. Presentation tokens are requested if the active tree has - // request_presentation_time() set. - base::flat_map<uint32_t, int> presentation_token_to_frame_; - // If non-zero identifies the presentation-token added to the last CF. Reset - // to zero when no more presentation tokens are in flight. - uint32_t last_presentation_token_ = 0u; + uint32_t next_frame_token_ = 1u; viz::LocalSurfaceId last_draw_local_surface_id_; + base::flat_set<viz::SurfaceRange> last_draw_referenced_surfaces_; base::Optional<RenderFrameMetadata> last_draw_render_frame_metadata_; viz::ChildLocalSurfaceIdAllocator child_local_surface_id_allocator_; - const int default_color_space_id_; - const gfx::ColorSpace default_color_space_; - std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; + // Stores information needed once we get a response for a particular + // presentation token. + struct FrameTokenInfo { + FrameTokenInfo( + uint32_t token, + base::TimeTicks cc_frame_time, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks); + FrameTokenInfo(FrameTokenInfo&&); + ~FrameTokenInfo(); + + uint32_t token; + + // The compositor frame time used to produce the frame. + base::TimeTicks cc_frame_time; + + // The callbacks to send back to the main thread. + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks; + + DISALLOW_COPY_AND_ASSIGN(FrameTokenInfo); + }; + + base::circular_deque<FrameTokenInfo> frame_token_infos_; + ui::FrameMetrics frame_metrics_; + ui::SkippedFrameTracker skipped_frame_tracker_; + bool is_animating_for_snap_; + 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 b7fdefd87dc..e59987d0bda 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -18,7 +18,7 @@ #include "base/memory/ptr_util.h" #include "base/optional.h" #include "base/run_loop.h" -#include "base/test/histogram_tester.h" +#include "base/test/metrics/histogram_tester.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "cc/animation/animation_host.h" @@ -77,7 +77,6 @@ #include "components/viz/test/begin_frame_args_test.h" #include "components/viz/test/fake_output_surface.h" #include "components/viz/test/test_layer_tree_frame_sink.h" -#include "components/viz/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "media/base/media.h" #include "testing/gmock/include/gmock/gmock.h" @@ -100,7 +99,6 @@ using ::testing::AnyNumber; using ::testing::AtLeast; using ::testing::_; using media::VideoFrame; -using viz::TestWebGraphicsContext3D; namespace cc { namespace { @@ -116,7 +114,7 @@ viz::SurfaceId MakeSurfaceId(const viz::FrameSinkId& frame_sink_id, struct TestFrameData : public LayerTreeHostImpl::FrameData { TestFrameData() { // Set ack to something valid, so DCHECKs don't complain. - begin_frame_ack = viz::BeginFrameAck(0, 1, true); + begin_frame_ack = viz::BeginFrameAck::CreateManualAckWithDamage(); } }; @@ -193,7 +191,8 @@ class LayerTreeHostImplTest : public testing::Test, void DidCompletePageScaleAnimationOnImplThread() override { did_complete_page_scale_animation_ = true; } - void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) override { + void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, + bool skip_draw) override { std::unique_ptr<TestFrameData> frame(new TestFrameData); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(frame.get())); last_on_draw_render_passes_.clear(); @@ -209,10 +208,9 @@ class LayerTreeHostImplTest : public testing::Test, void NotifyImageDecodeRequestFinished() override {} void RequestBeginMainFrameNotExpected(bool new_state) override {} void DidPresentCompositorFrameOnImplThread( - const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) override {} + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback) override {} void set_reduce_memory_result(bool reduce_memory_result) { reduce_memory_result_ = reduce_memory_result; @@ -244,7 +242,7 @@ class LayerTreeHostImplTest : public testing::Test, image_worker_ ? image_worker_->task_runner() : nullptr); layer_tree_frame_sink_ = std::move(layer_tree_frame_sink); host_impl_->SetVisible(true); - bool init = host_impl_->InitializeRenderer(layer_tree_frame_sink_.get()); + bool init = host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get()); host_impl_->SetViewportSize(gfx::Size(10, 10)); host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f); host_impl_->active_tree()->SetLocalSurfaceIdFromParent( @@ -693,9 +691,9 @@ class LayerTreeHostImplTest : public testing::Test, SnapContainerData container_data( ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), - gfx::ScrollOffset(300, 300)); - SnapAreaData area_data(SnapAxis::kBoth, gfx::ScrollOffset(50, 50), - gfx::RectF(0, 0, 300, 300), false); + gfx::RectF(0, 0, 200, 200), gfx::ScrollOffset(300, 300)); + SnapAreaData area_data(ScrollSnapAlign(SnapAlignment::kStart), + gfx::RectF(50, 50, 100, 100), false); container_data.AddSnapAreaData(area_data); overflow->test_properties()->snap_container_data.emplace(container_data); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -807,7 +805,6 @@ class TestInputHandlerClient : public InputHandlerClient { // InputHandlerClient implementation. void WillShutdown() override {} void Animate(base::TimeTicks time) override {} - void MainThreadHasStoppedFlinging() override {} void ReconcileElasticOverscrollAndRootScroll() override {} void UpdateRootLayerStateForSynchronousInputHandler( const gfx::ScrollOffset& total_scroll_offset, @@ -900,7 +897,7 @@ TEST_F(LayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) { gfx::Transform identity; gfx::Rect viewport(100, 100); const bool resourceless_software_draw = true; - host_impl_->OnDraw(identity, viewport, resourceless_software_draw); + host_impl_->OnDraw(identity, viewport, resourceless_software_draw, false); ASSERT_EQ(fake_layer_tree_frame_sink->num_sent_frames(), 1u); EXPECT_EQ( gfx::SizeF(100.f, 100.f), @@ -1068,18 +1065,10 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) { status.main_thread_scrolling_reasons); } -class LostGLES2Interface : public viz::TestGLES2Interface { - public: - LostGLES2Interface() = default; - - void InitializeTestContext() override { - LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, - GL_INNOCENT_CONTEXT_RESET_ARB); - } -}; - TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) { - auto gl_owned = std::make_unique<LostGLES2Interface>(); + auto gl_owned = std::make_unique<viz::TestGLES2Interface>(); + gl_owned->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, + GL_INNOCENT_CONTEXT_RESET_ARB); // Initialization will fail. EXPECT_FALSE( @@ -1124,8 +1113,9 @@ TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) { } TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) { - SetupScrollAndContentsLayers(gfx::Size(100, 100)); + LayerImpl* scroll = SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->SetViewportSize(gfx::Size(50, 50)); + scroll->SetWheelEventHandlerRegion(Region(gfx::Rect(20, 20))); DrawFrame(); // Wheel handlers determine whether mouse events block scroll. @@ -1135,6 +1125,11 @@ TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) { EventListenerProperties::kBlocking, host_impl_->GetEventListenerProperties(EventListenerClass::kMouseWheel)); + // LTHI should know the wheel event handler region and only block mouse events + // in that region. + EXPECT_TRUE(host_impl_->HasWheelEventHandlerAt(gfx::Point(10, 10))); + EXPECT_FALSE(host_impl_->HasWheelEventHandlerAt(gfx::Point(30, 30))); + // But they don't influence the actual handling of the scroll gestures. InputHandler::ScrollStatus status = host_impl_->ScrollBegin( BeginState(gfx::Point()).get(), InputHandler::WHEEL); @@ -1198,81 +1193,6 @@ TEST_F(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) { EXPECT_EQ(kTouchActionPanX, touch_action); } -TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchscreen) { - SetupScrollAndContentsLayers(gfx::Size(100, 100)); - host_impl_->SetViewportSize(gfx::Size(50, 50)); - DrawFrame(); - - // Ignore the fling since no layer is being scrolled - InputHandler::ScrollStatus status = host_impl_->FlingScrollBegin(); - EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); - EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, - status.main_thread_scrolling_reasons); - - // Start scrolling a layer - 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); - - // Now the fling should go ahead since we've started scrolling a layer - status = host_impl_->FlingScrollBegin(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); - EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, - status.main_thread_scrolling_reasons); -} - -TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchpad) { - SetupScrollAndContentsLayers(gfx::Size(100, 100)); - host_impl_->SetViewportSize(gfx::Size(50, 50)); - DrawFrame(); - - // Ignore the fling since no layer is being scrolled - InputHandler::ScrollStatus status = host_impl_->FlingScrollBegin(); - EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); - EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, - status.main_thread_scrolling_reasons); - - // Start scrolling a layer - status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::WHEEL); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); - EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, - status.main_thread_scrolling_reasons); - - // Now the fling should go ahead since we've started scrolling a layer - status = host_impl_->FlingScrollBegin(); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); - EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, - status.main_thread_scrolling_reasons); -} - -TEST_F(LayerTreeHostImplTest, NoFlingWhenScrollingOnMain) { - SetupScrollAndContentsLayers(gfx::Size(100, 100)); - host_impl_->SetViewportSize(gfx::Size(50, 50)); - LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing(); - - root->set_main_thread_scrolling_reasons( - MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - - DrawFrame(); - - // Start scrolling a layer - InputHandler::ScrollStatus status = host_impl_->ScrollBegin( - BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN); - EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); - EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects, - status.main_thread_scrolling_reasons); - - // The fling should be ignored since there's no layer being scrolled impl-side - status = host_impl_->FlingScrollBegin(); - EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread); - EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, - status.main_thread_scrolling_reasons); -} - TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) { SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->SetViewportSize(gfx::Size(50, 50)); @@ -1553,19 +1473,21 @@ TEST_F(LayerTreeHostImplTest, ScrollByReturnsCorrectValue) { .did_scroll); } +// TODO(sunyunjia): Move scroll snap tests to a separate file. +// https://crbug.com/851690 TEST_F(LayerTreeHostImplTest, ScrollSnapOnX) { LayerImpl* overflow = CreateLayerForSnapping(); - gfx::Point scroll_position(10, 10); + gfx::Point pointer_position(10, 10); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); gfx::Vector2dF x_delta(20, 0); - host_impl_->ScrollBy(UpdateState(scroll_position, x_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, x_delta).get()); viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); @@ -1584,16 +1506,16 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnX) { TEST_F(LayerTreeHostImplTest, ScrollSnapOnY) { LayerImpl* overflow = CreateLayerForSnapping(); - gfx::Point scroll_position(10, 10); + gfx::Point pointer_position(10, 10); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); gfx::Vector2dF y_delta(0, 20); - host_impl_->ScrollBy(UpdateState(scroll_position, y_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, y_delta).get()); viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); @@ -1612,16 +1534,16 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnY) { TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) { LayerImpl* overflow = CreateLayerForSnapping(); - gfx::Point scroll_position(10, 10); + gfx::Point pointer_position(10, 10); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); gfx::Vector2dF delta(20, 20); - host_impl_->ScrollBy(UpdateState(scroll_position, delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, delta).get()); viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); @@ -1637,24 +1559,108 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) { EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), overflow->CurrentScrollOffset()); } +TEST_F(LayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) { + LayerImpl* overflow = CreateLayerForSnapping(); + + gfx::Point pointer_position(10, 10); + gfx::Vector2dF delta(20, 20); + + EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_->ScrollAnimated(pointer_position, delta).thread); + + EXPECT_EQ(overflow->scroll_tree_index(), + host_impl_->CurrentlyScrollingNode()->id); + + base::TimeTicks start_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); + viz::BeginFrameArgs begin_frame_args = + viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + BeginImplFrameAndAnimate(begin_frame_args, start_time); + + // Animating for the wheel scroll. + BeginImplFrameAndAnimate(begin_frame_args, + start_time + base::TimeDelta::FromMilliseconds(50)); + EXPECT_FALSE(host_impl_->is_animating_for_snap_for_testing()); + gfx::ScrollOffset current_offset = overflow->CurrentScrollOffset(); + EXPECT_LT(0, current_offset.x()); + EXPECT_GT(20, current_offset.x()); + EXPECT_LT(0, current_offset.y()); + EXPECT_GT(20, current_offset.y()); + + // Animating for the snap. + BeginImplFrameAndAnimate( + begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1000)); + EXPECT_TRUE(host_impl_->is_animating_for_snap_for_testing()); + + // Finish the animation. + BeginImplFrameAndAnimate( + begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1500)); + EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), overflow->CurrentScrollOffset()); + EXPECT_FALSE(host_impl_->is_animating_for_snap_for_testing()); +} + +TEST_F(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) { + LayerImpl* overflow = CreateLayerForSnapping(); + + gfx::Point pointer_position(10, 10); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) + .thread); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); + + gfx::Vector2dF x_delta(20, 0); + host_impl_->ScrollBy(UpdateState(pointer_position, x_delta).get()); + EXPECT_FALSE(host_impl_->is_animating_for_snap_for_testing()); + + viz::BeginFrameArgs begin_frame_args = + viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + host_impl_->ScrollEnd(EndState().get(), true); + base::TimeTicks start_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(100); + BeginImplFrameAndAnimate(begin_frame_args, start_time); + + // Animating for the snap. + BeginImplFrameAndAnimate(begin_frame_args, + start_time + base::TimeDelta::FromMilliseconds(100)); + EXPECT_TRUE(host_impl_->is_animating_for_snap_for_testing()); + gfx::ScrollOffset current_offset = overflow->CurrentScrollOffset(); + EXPECT_GT(50, current_offset.x()); + EXPECT_LT(20, current_offset.x()); + EXPECT_EQ(0, current_offset.y()); + + // Interrup the snap animation with ScrollBegin. + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) + .thread); + EXPECT_FALSE(host_impl_->is_animating_for_snap_for_testing()); + BeginImplFrameAndAnimate(begin_frame_args, + start_time + base::TimeDelta::FromMilliseconds(150)); + EXPECT_VECTOR_EQ(ScrollOffsetToVector2dF(current_offset), + overflow->CurrentScrollOffset()); +} + TEST_F(LayerTreeHostImplTest, GetSnapFlingInfoWhenZoomed) { LayerImpl* overflow = CreateLayerForSnapping(); // Scales the page to its 1/5. host_impl_->active_tree()->PushPageScaleFromMainThread(0.2f, 0.1f, 5.f); // Should be (10, 10) in the scroller's coordinate. - gfx::Point scroll_position(2, 2); + gfx::Point pointer_position(2, 2); EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); // Should be (20, 20) in the scroller's coordinate. gfx::Vector2dF delta(4, 4); InputHandlerScrollResult result = - host_impl_->ScrollBy(UpdateState(scroll_position, delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, delta).get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), overflow->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(4, 4), result.current_visual_offset); @@ -1684,13 +1690,13 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { scroll_layer->SetCurrentScrollOffset(gfx::ScrollOffset(30, 30)); DrawFrame(); - gfx::Point scroll_position(50, 50); + gfx::Point pointer_position(50, 50); // OverscrollBehaviorTypeAuto shouldn't prevent scroll propagation. EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(30, 30), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(), overflow->CurrentScrollOffset()); @@ -1698,7 +1704,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { gfx::Vector2dF x_delta(-10, 0); gfx::Vector2dF y_delta(0, -10); gfx::Vector2dF diagonal_delta(-10, -10); - host_impl_->ScrollBy(UpdateState(scroll_position, x_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, x_delta).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 30), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); @@ -1715,12 +1721,12 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 30), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); - host_impl_->ScrollBy(UpdateState(scroll_position, x_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, x_delta).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 30), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); @@ -1730,12 +1736,12 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 30), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); - host_impl_->ScrollBy(UpdateState(scroll_position, y_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, y_delta).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); @@ -1745,12 +1751,12 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); - host_impl_->ScrollBy(UpdateState(scroll_position, diagonal_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, diagonal_delta).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); @@ -1768,12 +1774,12 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); - host_impl_->ScrollBy(UpdateState(scroll_position, x_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, x_delta).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); @@ -1783,12 +1789,12 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); - host_impl_->ScrollBy(UpdateState(scroll_position, y_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, y_delta).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); @@ -1798,12 +1804,12 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); - host_impl_->ScrollBy(UpdateState(scroll_position, diagonal_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, diagonal_delta).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); @@ -1820,15 +1826,15 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { EXPECT_EQ( InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ - ->ScrollBegin(BeginState(scroll_position).get(), InputHandler::WHEEL) + ->ScrollBegin(BeginState(pointer_position).get(), InputHandler::WHEEL) .thread); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), overflow->CurrentScrollOffset()); - host_impl_->ScrollBy(UpdateState(scroll_position, x_delta).get()); - host_impl_->ScrollBy(UpdateState(scroll_position, -x_delta).get()); - host_impl_->ScrollBy(UpdateState(scroll_position, y_delta).get()); - host_impl_->ScrollBy(UpdateState(scroll_position, -y_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, x_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, -x_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, y_delta).get()); + host_impl_->ScrollBy(UpdateState(pointer_position, -y_delta).get()); host_impl_->ScrollEnd(EndState().get()); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), scroll_layer->CurrentScrollOffset()); EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset()); @@ -2602,7 +2608,7 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) { TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) { ui::LatencyInfo latency_info; latency_info.set_trace_id(5); - latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0); + latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT); std::unique_ptr<SwapPromise> swap_promise( new LatencyInfoSwapPromise(latency_info)); @@ -3524,7 +3530,7 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { host_impl_ = base::WrapUnique(host_impl_override_time); layer_tree_frame_sink_ = CreateLayerTreeFrameSink(); host_impl_->SetVisible(true); - host_impl_->InitializeRenderer(layer_tree_frame_sink_.get()); + host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get()); SetupScrollAndContentsLayers(content_size); host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 4.f); @@ -4404,12 +4410,15 @@ TEST_F(LayerTreeHostImplTest, ActivationDependenciesInMetadata) { root->test_properties()->AddChild(std::move(child)); } - base::flat_set<viz::SurfaceId> fallback_surfaces_set; - for (size_t i = 0; i < fallback_surfaces.size(); ++i) - fallback_surfaces_set.insert(fallback_surfaces[i]); + base::flat_set<viz::SurfaceRange> surfaces_set; + // |fallback_surfaces| and |primary_surfaces| should have same size + for (size_t i = 0; i < fallback_surfaces.size(); ++i) { + surfaces_set.insert( + viz::SurfaceRange(fallback_surfaces[i], primary_surfaces[i])); + } host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->active_tree()->SetSurfaceLayerIds(fallback_surfaces_set); + host_impl_->active_tree()->SetSurfaceRanges(std::move(surfaces_set)); host_impl_->SetFullViewportDamage(); DrawFrame(); @@ -4424,7 +4433,9 @@ TEST_F(LayerTreeHostImplTest, ActivationDependenciesInMetadata) { EXPECT_THAT( metadata.referenced_surfaces, testing::UnorderedElementsAre( - fallback_surfaces[0], fallback_surfaces[1], fallback_surfaces[2])); + viz::SurfaceRange(fallback_surfaces[0], primary_surfaces[0]), + viz::SurfaceRange(fallback_surfaces[1], primary_surfaces[1]), + viz::SurfaceRange(fallback_surfaces[2], primary_surfaces[2]))); EXPECT_EQ(2u, metadata.deadline.deadline_in_frames()); EXPECT_FALSE(metadata.deadline.use_default_lower_bound_deadline()); } @@ -4445,12 +4456,52 @@ TEST_F(LayerTreeHostImplTest, ActivationDependenciesInMetadata) { EXPECT_THAT( metadata.referenced_surfaces, testing::UnorderedElementsAre( - fallback_surfaces[0], fallback_surfaces[1], fallback_surfaces[2])); + viz::SurfaceRange(fallback_surfaces[0], primary_surfaces[0]), + viz::SurfaceRange(fallback_surfaces[1], primary_surfaces[1]), + viz::SurfaceRange(fallback_surfaces[2], primary_surfaces[2]))); EXPECT_EQ(0u, metadata.deadline.deadline_in_frames()); EXPECT_FALSE(metadata.deadline.use_default_lower_bound_deadline()); } } +// Verify that updating the set of referenced surfaces for the active tree +// causes a new CompositorFrame to be submitted, even if there is no other +// damage. +TEST_F(LayerTreeHostImplTest, SurfaceReferencesChangeCausesDamage) { + SetupScrollAndContentsLayers(gfx::Size(100, 100)); + host_impl_->SetViewportSize(gfx::Size(50, 50)); + auto* fake_layer_tree_frame_sink = + static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink()); + + // Submit an initial CompositorFrame with an empty set of referenced surfaces. + host_impl_->active_tree()->BuildPropertyTreesForTesting(); + host_impl_->active_tree()->SetSurfaceRanges({}); + host_impl_->SetFullViewportDamage(); + DrawFrame(); + + { + const viz::CompositorFrameMetadata& metadata = + fake_layer_tree_frame_sink->last_sent_frame()->metadata; + EXPECT_THAT(metadata.referenced_surfaces, testing::IsEmpty()); + } + + const viz::SurfaceId surface_id = MakeSurfaceId(viz::FrameSinkId(1, 1), 1); + + // Update the set of referenced surfaces to contain |surface_id| but don't + // make any other changes that would cause damage. This mimics updating the + // SurfaceLayer for an offscreen tab. + host_impl_->active_tree()->BuildPropertyTreesForTesting(); + host_impl_->active_tree()->SetSurfaceRanges({viz::SurfaceRange(surface_id)}); + DrawFrame(); + + { + const viz::CompositorFrameMetadata& metadata = + fake_layer_tree_frame_sink->last_sent_frame()->metadata; + EXPECT_THAT(metadata.referenced_surfaces, + testing::UnorderedElementsAre(viz::SurfaceRange(surface_id))); + } +} + TEST_F(LayerTreeHostImplTest, CompositorFrameMetadata) { SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->SetViewportSize(gfx::Size(50, 50)); @@ -4586,11 +4637,13 @@ class DidDrawCheckLayer : public LayerImpl { } bool WillDraw(DrawMode draw_mode, - LayerTreeResourceProvider* provider) override { - will_draw_called_ = true; + viz::ClientResourceProvider* provider) override { + if (!LayerImpl::WillDraw(draw_mode, provider)) + return false; if (will_draw_returns_false_) return false; - return LayerImpl::WillDraw(draw_mode, provider); + will_draw_returned_true_ = true; + return true; } void AppendQuads(viz::RenderPass* render_pass, @@ -4599,19 +4652,19 @@ class DidDrawCheckLayer : public LayerImpl { LayerImpl::AppendQuads(render_pass, append_quads_data); } - void DidDraw(LayerTreeResourceProvider* provider) override { + void DidDraw(viz::ClientResourceProvider* provider) override { did_draw_called_ = true; LayerImpl::DidDraw(provider); } - bool will_draw_called() const { return will_draw_called_; } + bool will_draw_returned_true() const { return will_draw_returned_true_; } bool append_quads_called() const { return append_quads_called_; } bool did_draw_called() const { return did_draw_called_; } void set_will_draw_returns_false() { will_draw_returns_false_ = true; } void ClearDidDrawCheck() { - will_draw_called_ = false; + will_draw_returned_true_ = false; append_quads_called_ = false; did_draw_called_ = false; } @@ -4625,7 +4678,7 @@ class DidDrawCheckLayer : public LayerImpl { DidDrawCheckLayer(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id), will_draw_returns_false_(false), - will_draw_called_(false), + will_draw_returned_true_(false), append_quads_called_(false), did_draw_called_(false) { SetBounds(gfx::Size(10, 10)); @@ -4635,7 +4688,7 @@ class DidDrawCheckLayer : public LayerImpl { private: bool will_draw_returns_false_; - bool will_draw_called_; + bool will_draw_returned_true_; bool append_quads_called_; bool did_draw_called_; }; @@ -4735,7 +4788,7 @@ TEST_F(LayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) { host_impl_->DrawLayers(&frame); host_impl_->DidDrawAllLayers(frame); - EXPECT_TRUE(layer->will_draw_called()); + EXPECT_TRUE(layer->will_draw_returned_true()); EXPECT_TRUE(layer->append_quads_called()); EXPECT_TRUE(layer->did_draw_called()); } @@ -4752,7 +4805,7 @@ TEST_F(LayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) { host_impl_->DrawLayers(&frame); host_impl_->DidDrawAllLayers(frame); - EXPECT_TRUE(layer->will_draw_called()); + EXPECT_FALSE(layer->will_draw_returned_true()); EXPECT_FALSE(layer->append_quads_called()); EXPECT_FALSE(layer->did_draw_called()); } @@ -4778,14 +4831,14 @@ TEST_F(LayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) { TestFrameData frame; - EXPECT_FALSE(layer->will_draw_called()); + EXPECT_FALSE(layer->will_draw_returned_true()); EXPECT_FALSE(layer->did_draw_called()); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); host_impl_->DrawLayers(&frame); host_impl_->DidDrawAllLayers(frame); - EXPECT_FALSE(layer->will_draw_called()); + EXPECT_FALSE(layer->will_draw_returned_true()); EXPECT_FALSE(layer->did_draw_called()); EXPECT_TRUE(layer->visible_layer_rect().IsEmpty()); @@ -4795,14 +4848,14 @@ TEST_F(LayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) { layer->NoteLayerPropertyChanged(); host_impl_->active_tree()->BuildPropertyTreesForTesting(); - EXPECT_FALSE(layer->will_draw_called()); + EXPECT_FALSE(layer->will_draw_returned_true()); EXPECT_FALSE(layer->did_draw_called()); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); host_impl_->DrawLayers(&frame); host_impl_->DidDrawAllLayers(frame); - EXPECT_TRUE(layer->will_draw_called()); + EXPECT_TRUE(layer->will_draw_returned_true()); EXPECT_TRUE(layer->did_draw_called()); EXPECT_FALSE(layer->visible_layer_rect().IsEmpty()); @@ -4835,18 +4888,18 @@ TEST_F(LayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) { TestFrameData frame; - EXPECT_FALSE(occluded_layer->will_draw_called()); + EXPECT_FALSE(occluded_layer->will_draw_returned_true()); EXPECT_FALSE(occluded_layer->did_draw_called()); - EXPECT_FALSE(top_layer->will_draw_called()); + EXPECT_FALSE(top_layer->will_draw_returned_true()); EXPECT_FALSE(top_layer->did_draw_called()); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); host_impl_->DrawLayers(&frame); host_impl_->DidDrawAllLayers(frame); - EXPECT_FALSE(occluded_layer->will_draw_called()); + EXPECT_FALSE(occluded_layer->will_draw_returned_true()); EXPECT_FALSE(occluded_layer->did_draw_called()); - EXPECT_TRUE(top_layer->will_draw_called()); + EXPECT_TRUE(top_layer->will_draw_returned_true()); EXPECT_TRUE(top_layer->did_draw_called()); } @@ -5110,7 +5163,7 @@ TEST_F(LayerTreeHostImplTest, host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); for (size_t i = 0; i < cases.size(); ++i) { const auto& testcase = cases[i]; @@ -5133,7 +5186,7 @@ TEST_F(LayerTreeHostImplTest, host_impl_->SetRequiresHighResToDraw(); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); } } @@ -6158,7 +6211,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, // Kick off an animation to show the browser controls. host_impl_->browser_controls_manager()->UpdateBrowserControlsState( - BOTH, SHOWN, true); + BrowserControlsState::kBoth, BrowserControlsState::kShown, true); base::TimeTicks start_time = base::TimeTicks::Now(); viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); @@ -6630,10 +6683,6 @@ TEST_F(LayerTreeHostImplTest, ScrollChildBeyondLimit) { } TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedLatchToChild) { - // Enable wheel scroll latching flag. - TestInputHandlerClient input_handler_client; - host_impl_->BindToClient(&input_handler_client, true); - // Scroll a child layer beyond its maximum scroll range and make sure the // parent layer isn't scrolled. gfx::Size surface_size(100, 100); @@ -6785,7 +6834,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); host_impl_->ScrollEnd(EndState().get()); @@ -6814,7 +6863,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, grand_child->scroll_tree_index()); @@ -6839,7 +6888,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, grand_child->scroll_tree_index()); @@ -6866,7 +6915,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(1, 1)).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); EXPECT_EQ(grand_child->scroll_tree_index(), host_impl_->CurrentlyScrollingNode()->id); @@ -7327,7 +7376,7 @@ TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) { scroll_layer->SetScrollable(gfx::Size(10, 20)); host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->BindToClient(&scroll_watcher, false); + host_impl_->BindToClient(&scroll_watcher); gfx::Vector2dF initial_scroll_delta(10.f, 10.f); scroll_layer->layer_tree_impl() @@ -7459,6 +7508,44 @@ TEST_F(LayerTreeHostImplTest, CheckLayerScrollDelta(scroll_layer, ScrollOffsetToVector2dF(scroll_offset)); } +TEST_F(LayerTreeHostImplTest, + ExternalRootLayerScrollOffsetPreventedByUserNotScrollable) { + host_impl_->SetViewportSize(gfx::Size(10, 20)); + LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); + LayerImpl* clip_layer = + scroll_layer->test_properties()->parent->test_properties()->parent; + clip_layer->SetBounds(gfx::Size(10, 20)); + scroll_layer->SetScrollable(gfx::Size(10, 20)); + scroll_layer->SetDrawsContent(true); + host_impl_->active_tree() + ->InnerViewportScrollLayer() + ->test_properties() + ->user_scrollable_vertical = false; + host_impl_->active_tree() + ->InnerViewportScrollLayer() + ->test_properties() + ->user_scrollable_horizontal = false; + host_impl_->active_tree()->BuildPropertyTreesForTesting(); + + // Draw first frame to clear any pending draws and check scroll. + DrawFrame(); + CheckLayerScrollDelta(scroll_layer, gfx::Vector2dF(0.f, 0.f)); + EXPECT_FALSE(host_impl_->active_tree()->needs_update_draw_properties()); + + // Set external scroll delta on delegate and notify LayerTreeHost. + gfx::ScrollOffset scroll_offset(10.f, 10.f); + host_impl_->SetSynchronousInputHandlerRootScrollOffset(scroll_offset); + host_impl_->active_tree()->BuildPropertyTreesForTesting(); + + TestFrameData frame; + EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); + host_impl_->DrawLayers(&frame); + host_impl_->DidDrawAllLayers(frame); + EXPECT_TRUE(frame.has_no_damage); + CheckLayerScrollDelta(scroll_layer, + ScrollOffsetToVector2dF(gfx::ScrollOffset())); +} + TEST_F(LayerTreeHostImplTest, OverscrollRoot) { InputHandlerScrollResult scroll_result; SetupScrollAndContentsLayers(gfx::Size(100, 100)); @@ -7638,7 +7725,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); scroll_result = host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); @@ -7653,7 +7740,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, grand_child_layer->scroll_tree_index()); @@ -7662,7 +7749,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); scroll_result = host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); @@ -7679,7 +7766,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(5, 5)).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, grand_child_layer->scroll_tree_index()); @@ -7799,10 +7886,8 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, host_impl_ ->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), - InputHandler::NON_BUBBLING_GESTURE) + InputHandler::TOUCHSCREEN) .thread); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); scroll_result = host_impl_->ScrollBy( UpdateState(gfx::Point(), gfx::Vector2dF(0, 20)).get()); EXPECT_TRUE(scroll_result.did_scroll); @@ -8366,15 +8451,16 @@ class BlendStateCheckLayer : public LayerImpl { public: BlendStateCheckLayer(LayerTreeImpl* tree_impl, int id, - LayerTreeResourceProvider* resource_provider) + viz::ClientResourceProvider* resource_provider) : LayerImpl(tree_impl, id), + resource_provider_(resource_provider), 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) { - resource_id_ = resource_provider->ImportResource( + resource_id_ = resource_provider_->ImportResource( viz::TransferableResource::MakeSoftware( viz::SharedBitmap::GenerateId(), gfx::Size(1, 1), viz::RGBA_8888), viz::SingleReleaseCallback::Create(base::DoNothing())); @@ -8382,6 +8468,10 @@ class BlendStateCheckLayer : public LayerImpl { SetDrawsContent(true); } + void ReleaseResources() override { + resource_provider_->RemoveImportedResource(resource_id_); + } + void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override { quads_appended_ = true; @@ -8428,6 +8518,7 @@ class BlendStateCheckLayer : public LayerImpl { } private: + viz::ClientResourceProvider* resource_provider_; bool blend_; bool has_render_surface_; LayerImpl* comparison_layer_; @@ -8831,7 +8922,7 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { gfx::Transform identity; gfx::Rect viewport(viewport_size_); bool resourceless_software_draw = true; - host_impl_->OnDraw(identity, viewport, resourceless_software_draw); + host_impl_->OnDraw(identity, viewport, resourceless_software_draw, false); VerifyEmptyLayerRenderPasses(last_on_draw_render_passes_); } @@ -8868,7 +8959,7 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { gfx::Transform identity; gfx::Rect viewport(viewport_size_); bool resourceless_software_draw = true; - host_impl_->OnDraw(identity, viewport, resourceless_software_draw); + host_impl_->OnDraw(identity, viewport, resourceless_software_draw, false); VerifyLayerInMiddleOfViewport(last_on_draw_render_passes_); } @@ -8905,7 +8996,7 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { gfx::Transform identity; gfx::Rect viewport(viewport_size_); bool resourceless_software_draw = true; - host_impl_->OnDraw(identity, viewport, resourceless_software_draw); + host_impl_->OnDraw(identity, viewport, resourceless_software_draw, false); VerifyLayerIsLargerThanViewport(last_on_draw_render_passes_); } @@ -9078,7 +9169,7 @@ TEST_F(LayerTreeHostImplTest, PartialSwapReceivesDamageRect) { &task_graph_runner_, AnimationHost::CreateForTesting(ThreadInstance::IMPL), 0, nullptr); layer_tree_host_impl->SetVisible(true); - layer_tree_host_impl->InitializeRenderer(layer_tree_frame_sink.get()); + layer_tree_host_impl->InitializeFrameSink(layer_tree_frame_sink.get()); layer_tree_host_impl->WillBeginImplFrame( viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2)); layer_tree_host_impl->SetViewportSize(gfx::Size(500, 500)); @@ -9514,7 +9605,7 @@ TEST_F(LayerTreeHostImplTest, host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); EXPECT_EQ(1u, last_on_draw_frame_->will_draw_layers.size()); EXPECT_EQ(host_impl_->active_tree()->root_layer_for_testing(), @@ -9549,7 +9640,7 @@ TEST_F(LayerTreeHostImplTest, MemoryLimits) { // Gpu compositing. layer_tree_frame_sink_ = FakeLayerTreeFrameSink::Create3d(); host_impl_->SetVisible(true); - host_impl_->InitializeRenderer(layer_tree_frame_sink_.get()); + host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get()); { const auto& state = host_impl_->global_tile_state(); EXPECT_EQ(kGpuByteLimit, state.hard_memory_limit_in_bytes); @@ -9577,7 +9668,7 @@ TEST_F(LayerTreeHostImplTest, MemoryLimits) { // Software compositing. host_impl_->ReleaseLayerTreeFrameSink(); layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware(); - host_impl_->InitializeRenderer(layer_tree_frame_sink_.get()); + host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get()); { const auto& state = host_impl_->global_tile_state(); EXPECT_EQ(kGpuByteLimit, state.hard_memory_limit_in_bytes); @@ -9694,7 +9785,7 @@ class LayerTreeHostImplTestPrepareTiles : public LayerTreeHostImplTest { host_impl_.reset(fake_host_impl_); layer_tree_frame_sink_ = CreateLayerTreeFrameSink(); host_impl_->SetVisible(true); - host_impl_->InitializeRenderer(layer_tree_frame_sink_.get()); + host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get()); host_impl_->SetViewportSize(gfx::Size(10, 10)); } @@ -9882,7 +9973,7 @@ TEST_F(LayerTreeHostImplTest, ShutdownReleasesContext) { // in a texture mailbox. ASSERT_TRUE(helper.unprocessed_result); EXPECT_FALSE(context_provider->HasOneRef()); - EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures()); + EXPECT_EQ(1u, context_provider->TestContextGL()->NumTextures()); host_impl_->ReleaseLayerTreeFrameSink(); host_impl_ = nullptr; @@ -9890,214 +9981,13 @@ TEST_F(LayerTreeHostImplTest, ShutdownReleasesContext) { // The texture release callback that was given to the CopyOutputResult has // been canceled, and the texture deleted. EXPECT_TRUE(context_provider->HasOneRef()); - EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures()); + EXPECT_EQ(0u, context_provider->TestContextGL()->NumTextures()); // When resetting the CopyOutputResult, it will run its texture release // callback. This should not cause a crash, etc. helper.unprocessed_result.reset(); } -TEST_F(LayerTreeHostImplTest, TouchFlingShouldNotBubble) { - // When flinging via touch, only the child should scroll (we should not - // 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(), kPageScaleLayerId); - std::unique_ptr<LayerImpl> root_clip = - LayerImpl::Create(host_impl_->active_tree(), kInnerViewportClipLayerId); - root_clip->test_properties()->force_render_surface = true; - - std::unique_ptr<LayerImpl> root_scroll = - CreateScrollableLayer(kInnerViewportScrollLayerId, content_size); - root_scroll->test_properties()->is_container_for_fixed_position_layers = true; - std::unique_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size); - - root_scroll->test_properties()->AddChild(std::move(child)); - ElementId root_id = root_scroll->element_id(); - root_clip->test_properties()->AddChild(std::move(root_scroll)); - root_ptr->test_properties()->AddChild(std::move(root_clip)); - - host_impl_->SetViewportSize(surface_size); - host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_ptr)); - 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(); - { - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::TOUCHSCREEN) - .thread); - - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - - gfx::Vector2d scroll_delta(0, 100); - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); - - host_impl_->ScrollEnd(EndState().get()); - - std::unique_ptr<ScrollAndScaleSet> scroll_info = - host_impl_->ProcessScrollDeltas(); - - // Only the child should have scrolled. - ASSERT_EQ(1u, scroll_info->scrolls.size()); - ExpectNone(*scroll_info.get(), root_id); - } -} - -TEST_F(LayerTreeHostImplTest, TouchFlingShouldContinueScrollingCurrentLayer) { - // Scroll a child layer beyond its maximum scroll range and make sure the - // the scroll doesn't bubble up to the parent layer. - gfx::Size surface_size(10, 10); - LayerImpl* root = - CreateBasicVirtualViewportLayers(surface_size, surface_size); - root->test_properties()->force_render_surface = true; - - std::unique_ptr<LayerImpl> root_scrolling_owned = - CreateScrollableLayer(12, surface_size); - auto* root_scrolling = root_scrolling_owned.get(); - root->test_properties()->AddChild(std::move(root_scrolling_owned)); - - std::unique_ptr<LayerImpl> child_owned = - CreateScrollableLayer(13, surface_size); - auto* child = child_owned.get(); - root_scrolling->test_properties()->AddChild(std::move(child_owned)); - - std::unique_ptr<LayerImpl> grand_child_owned = - CreateScrollableLayer(14, surface_size); - auto* grand_child = grand_child_owned.get(); - child->test_properties()->AddChild(std::move(grand_child_owned)); - - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->active_tree()->DidBecomeActive(); - - child->layer_tree_impl() - ->property_trees() - ->scroll_tree.UpdateScrollOffsetBaseForTesting(child->element_id(), - gfx::ScrollOffset(0, 4)); - grand_child->layer_tree_impl() - ->property_trees() - ->scroll_tree.UpdateScrollOffsetBaseForTesting(grand_child->element_id(), - gfx::ScrollOffset(0, 2)); - - host_impl_->SetViewportSize(surface_size); - DrawFrame(); - { - std::unique_ptr<ScrollAndScaleSet> scroll_info; - - gfx::Vector2d scroll_delta(0, -2); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::TOUCHSCREEN) - .thread); - EXPECT_TRUE( - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()) - .did_scroll); - - // The grand child should have scrolled up to its limit. - scroll_info = host_impl_->ProcessScrollDeltas(); - ASSERT_EQ(1u, scroll_info->scrolls.size()); - ElementId grand_child_scroll_id = - LayerIdToElementIdForTesting(grand_child->id()); - EXPECT_TRUE( - ScrollInfoContains(*scroll_info, grand_child_scroll_id, scroll_delta)); - EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, - grand_child->scroll_tree_index()); - - // The locked scrolling layer should remain set as the grand child. - EXPECT_FALSE( - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()) - .did_scroll); - scroll_info = host_impl_->ProcessScrollDeltas(); - ASSERT_EQ(1u, scroll_info->scrolls.size()); - EXPECT_TRUE( - ScrollInfoContains(*scroll_info, grand_child_scroll_id, scroll_delta)); - ExpectNone(*scroll_info, child->element_id()); - EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, - grand_child->scroll_tree_index()); - - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - EXPECT_FALSE( - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()) - .did_scroll); - EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, - grand_child->scroll_tree_index()); - - // The child should not have scrolled. - scroll_info = host_impl_->ProcessScrollDeltas(); - ASSERT_EQ(1u, scroll_info->scrolls.size()); - EXPECT_TRUE( - ScrollInfoContains(*scroll_info, grand_child_scroll_id, scroll_delta)); - ExpectNone(*scroll_info, child->element_id()); - - // As the locked layer is at it's limit, no further scrolling can occur. - EXPECT_FALSE( - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()) - .did_scroll); - EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, - grand_child->scroll_tree_index()); - host_impl_->ScrollEnd(EndState().get()); - } -} - -TEST_F(LayerTreeHostImplTest, WheelFlingShouldntBubble) { - // When flinging via wheel, we shouldn't bubble. - gfx::Size surface_size(10, 10); - gfx::Size content_size(20, 20); - LayerImpl* root_clip = - CreateBasicVirtualViewportLayers(surface_size, surface_size); - root_clip->test_properties()->force_render_surface = true; - std::unique_ptr<LayerImpl> root_scroll = - CreateScrollableLayer(11, content_size); - ElementId root_scroll_id = root_scroll->element_id(); - std::unique_ptr<LayerImpl> child = CreateScrollableLayer(12, content_size); - - root_scroll->test_properties()->AddChild(std::move(child)); - root_clip->test_properties()->AddChild(std::move(root_scroll)); - - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->active_tree()->DidBecomeActive(); - - host_impl_->SetViewportSize(surface_size); - DrawFrame(); - { - EXPECT_EQ( - InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::WHEEL) - .thread); - - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - - gfx::Vector2d scroll_delta(0, 100); - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); - - host_impl_->ScrollEnd(EndState().get()); - - std::unique_ptr<ScrollAndScaleSet> scroll_info = - host_impl_->ProcessScrollDeltas(); - - // The root shouldn't have scrolled. - ASSERT_EQ(1u, scroll_info->scrolls.size()); - ExpectNone(*scroll_info.get(), root_scroll_id); - } -} - TEST_F(LayerTreeHostImplTest, ScrollUnknownNotOnAncestorChain) { // If we ray cast a scroller that is not on the first layer's ancestor chain, // we should return SCROLL_UNKNOWN. @@ -10246,15 +10136,15 @@ TEST_F(LayerTreeHostImplLatencyInfoRendererTest, fake_layer_tree_frame_sink->last_sent_frame()->metadata.latency_info; EXPECT_EQ(1u, metadata_latency_after1.size()); EXPECT_TRUE(metadata_latency_after1[0].FindLatency( - ui::LATENCY_BEGIN_FRAME_RENDERER_COMPOSITOR_COMPONENT, 0, nullptr)); + ui::LATENCY_BEGIN_FRAME_RENDERER_COMPOSITOR_COMPONENT, nullptr)); EXPECT_TRUE(metadata_latency_after1[0].FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, nullptr)); + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, nullptr)); // The second frame should have the default BeginFrame component and the // component attached via LatencyInfoSwapPromise. ui::LatencyInfo latency_info; latency_info.set_trace_id(5); - latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0); + latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT); std::unique_ptr<SwapPromise> swap_promise( new LatencyInfoSwapPromise(latency_info)); host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise)); @@ -10270,15 +10160,15 @@ TEST_F(LayerTreeHostImplLatencyInfoRendererTest, fake_layer_tree_frame_sink->last_sent_frame()->metadata.latency_info; EXPECT_EQ(2u, metadata_latency_after2.size()); EXPECT_TRUE(metadata_latency_after2[0].FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); EXPECT_TRUE(metadata_latency_after2[1].FindLatency( - ui::LATENCY_BEGIN_FRAME_RENDERER_COMPOSITOR_COMPONENT, 0, nullptr)); + ui::LATENCY_BEGIN_FRAME_RENDERER_COMPOSITOR_COMPONENT, nullptr)); // Renderer should also record INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT. EXPECT_TRUE(metadata_latency_after2[0].FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, nullptr)); + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, nullptr)); EXPECT_TRUE(metadata_latency_after2[1].FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, nullptr)); + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, nullptr)); } // Make sure LatencyInfo are passed in viz::CompositorFrameMetadata properly in @@ -10301,15 +10191,15 @@ TEST_F(LayerTreeHostImplLatencyInfoUITest, fake_layer_tree_frame_sink->last_sent_frame()->metadata.latency_info; EXPECT_EQ(1u, metadata_latency_after1.size()); EXPECT_TRUE(metadata_latency_after1[0].FindLatency( - ui::LATENCY_BEGIN_FRAME_UI_COMPOSITOR_COMPONENT, 0, nullptr)); + ui::LATENCY_BEGIN_FRAME_UI_COMPOSITOR_COMPONENT, nullptr)); EXPECT_FALSE(metadata_latency_after1[0].FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, nullptr)); + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, nullptr)); // The second frame should have the default BeginFrame component and the // component attached via LatencyInfoSwapPromise. ui::LatencyInfo latency_info; latency_info.set_trace_id(5); - latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0); + latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT); std::unique_ptr<SwapPromise> swap_promise( new LatencyInfoSwapPromise(latency_info)); host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise)); @@ -10325,15 +10215,15 @@ TEST_F(LayerTreeHostImplLatencyInfoUITest, fake_layer_tree_frame_sink->last_sent_frame()->metadata.latency_info; EXPECT_EQ(2u, metadata_latency_after2.size()); EXPECT_TRUE(metadata_latency_after2[0].FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); EXPECT_TRUE(metadata_latency_after2[1].FindLatency( - ui::LATENCY_BEGIN_FRAME_UI_COMPOSITOR_COMPONENT, 0, nullptr)); + ui::LATENCY_BEGIN_FRAME_UI_COMPOSITOR_COMPONENT, nullptr)); // UI should not record INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT. EXPECT_FALSE(metadata_latency_after2[0].FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, nullptr)); + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, nullptr)); EXPECT_FALSE(metadata_latency_after2[1].FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, nullptr)); + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, nullptr)); } TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToCompositorFrameMetadata) { @@ -10636,7 +10526,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, host_impl_->SetViewportSize(gfx::Size(100, 100)); host_impl_->browser_controls_manager()->UpdateBrowserControlsState( - BOTH, SHOWN, false); + BrowserControlsState::kBoth, BrowserControlsState::kShown, false); DrawFrame(); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, @@ -10711,7 +10601,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, host_impl_->SetViewportSize(gfx::Size(50, 100)); host_impl_->active_tree()->set_browser_controls_shrink_blink_size(true); host_impl_->browser_controls_manager()->UpdateBrowserControlsState( - BOTH, SHOWN, false); + BrowserControlsState::kBoth, BrowserControlsState::kShown, false); DrawFrame(); LayerImpl* viewport_layer = host_impl_->InnerViewportScrollLayer(); @@ -10753,7 +10643,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, host_impl_->SetViewportSize(gfx::Size(100, 200)); host_impl_->browser_controls_manager()->UpdateBrowserControlsState( - BOTH, SHOWN, false); + BrowserControlsState::kBoth, BrowserControlsState::kShown, false); DrawFrame(); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, @@ -10833,7 +10723,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, host_impl_->SetViewportSize(gfx::Size(100, 100)); host_impl_->browser_controls_manager()->UpdateBrowserControlsState( - BOTH, SHOWN, false); + BrowserControlsState::kBoth, BrowserControlsState::kShown, false); float initial_scroll_offset = 50; scroll_layer->layer_tree_impl() ->property_trees() @@ -10905,84 +10795,6 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, } TEST_F(LayerTreeHostImplWithBrowserControlsTest, - BrowserControlsAnimationAfterMainThreadFlingStopped) { - LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 200)); - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - - host_impl_->SetViewportSize(gfx::Size(100, 100)); - host_impl_->browser_controls_manager()->UpdateBrowserControlsState( - BOTH, SHOWN, false); - float initial_scroll_offset = 50; - scroll_layer->layer_tree_impl() - ->property_trees() - ->scroll_tree.UpdateScrollOffsetBaseForTesting( - scroll_layer->element_id(), - gfx::ScrollOffset(0, initial_scroll_offset)); - DrawFrame(); - - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::TOUCHSCREEN) - .thread); - EXPECT_EQ(0, host_impl_->browser_controls_manager()->ControlsTopOffset()); - EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(), - scroll_layer->CurrentScrollOffset().ToString()); - - // Scroll the browser controls partially. - const float residue = 15; - float offset = top_controls_height_ - residue; - EXPECT_TRUE( - host_impl_ - ->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, offset)).get()) - .did_scroll); - EXPECT_FLOAT_EQ(-offset, - host_impl_->browser_controls_manager()->ControlsTopOffset()); - EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(), - scroll_layer->CurrentScrollOffset().ToString()); - - did_request_redraw_ = false; - did_request_next_frame_ = false; - did_request_commit_ = false; - - // End the fling while the controls are still offset from the limit. - host_impl_->MainThreadHasStoppedFlinging(); - ASSERT_TRUE(host_impl_->browser_controls_manager()->has_animation()); - EXPECT_TRUE(did_request_next_frame_); - EXPECT_TRUE(did_request_redraw_); - EXPECT_FALSE(did_request_commit_); - - // Animate the browser controls to the limit. - viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting( - BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now()); - while (did_request_next_frame_) { - did_request_redraw_ = false; - did_request_next_frame_ = false; - did_request_commit_ = false; - - float old_offset = - host_impl_->browser_controls_manager()->ControlsTopOffset(); - - begin_frame_args.frame_time += base::TimeDelta::FromMilliseconds(5); - begin_frame_args.sequence_number++; - host_impl_->WillBeginImplFrame(begin_frame_args); - host_impl_->Animate(); - - float new_offset = - host_impl_->browser_controls_manager()->ControlsTopOffset(); - - if (new_offset != old_offset) { - EXPECT_TRUE(did_request_redraw_); - EXPECT_TRUE(did_request_commit_); - } - host_impl_->DidFinishImplFrame(); - } - EXPECT_FALSE(host_impl_->browser_controls_manager()->has_animation()); - EXPECT_EQ(-top_controls_height_, - host_impl_->browser_controls_manager()->ControlsTopOffset()); -} - -TEST_F(LayerTreeHostImplWithBrowserControlsTest, BrowserControlsScrollDeltaInOverScroll) { // Verifies that the overscroll delta should not have accumulated in // the browser controls if we do a hide and show without releasing finger. @@ -10991,7 +10803,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest, host_impl_->SetViewportSize(gfx::Size(100, 100)); host_impl_->browser_controls_manager()->UpdateBrowserControlsState( - BOTH, SHOWN, false); + BrowserControlsState::kBoth, BrowserControlsState::kShown, false); DrawFrame(); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, @@ -11240,81 +11052,6 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, ScrollBothInnerAndOuterLayer) { } } -TEST_F(LayerTreeHostImplVirtualViewportTest, FlingScrollBubblesToInner) { - gfx::Size content_size = gfx::Size(200, 320); - gfx::Size outer_viewport = gfx::Size(100, 160); - gfx::Size inner_viewport = gfx::Size(50, 80); - - SetupVirtualViewportLayers(content_size, outer_viewport, inner_viewport); - - LayerImpl* outer_scroll = host_impl_->OuterViewportScrollLayer(); - LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer(); - - DrawFrame(); - { - gfx::Vector2dF inner_expected; - gfx::Vector2dF outer_expected; - EXPECT_VECTOR_EQ(inner_expected, inner_scroll->CurrentScrollOffset()); - EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset()); - - // Scrolling the viewport always sets the outer scroll layer as the - // currently scrolling layer. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::TOUCHSCREEN) - .thread); - EXPECT_EQ(outer_scroll->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - EXPECT_EQ(outer_scroll->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - - gfx::Vector2d scroll_delta(inner_viewport.width() / 2.f, - inner_viewport.height() / 2.f); - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); - inner_expected += gfx::Vector2dF(scroll_delta.x(), scroll_delta.y()); - EXPECT_EQ(outer_scroll->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - - host_impl_->ScrollEnd(EndState().get()); - EXPECT_EQ(nullptr, host_impl_->CurrentlyScrollingNode()); - - EXPECT_VECTOR_EQ(inner_expected, inner_scroll->CurrentScrollOffset()); - EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset()); - - // Fling past the inner viewport boundry, make sure outer viewport scrolls. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::TOUCHSCREEN) - .thread); - EXPECT_EQ(outer_scroll->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - EXPECT_EQ(outer_scroll->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); - inner_expected += gfx::Vector2dF(scroll_delta.x(), scroll_delta.y()); - EXPECT_EQ(outer_scroll->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); - outer_expected += gfx::Vector2dF(scroll_delta.x(), scroll_delta.y()); - EXPECT_EQ(outer_scroll->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - - host_impl_->ScrollEnd(EndState().get()); - EXPECT_EQ(nullptr, host_impl_->CurrentlyScrollingNode()); - - EXPECT_VECTOR_EQ(inner_expected, inner_scroll->CurrentScrollOffset()); - EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset()); - } -} - TEST_F(LayerTreeHostImplVirtualViewportTest, DiagonalScrollBubblesPerfectlyToInner) { gfx::Size content_size = gfx::Size(200, 320); @@ -11339,8 +11076,6 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN) .thread); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( gfx::Point(), InputHandler::TOUCHSCREEN)); @@ -11374,79 +11109,6 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, } TEST_F(LayerTreeHostImplVirtualViewportTest, - TouchFlingDoesntSwitchScrollingLayer) { - gfx::Size content_size = gfx::Size(100, 160); - gfx::Size outer_viewport = gfx::Size(50, 80); - gfx::Size inner_viewport = gfx::Size(25, 40); - - SetupVirtualViewportLayers(content_size, outer_viewport, inner_viewport); - - LayerImpl* outer_scroll = host_impl_->OuterViewportScrollLayer(); - LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer(); - - std::unique_ptr<LayerImpl> child = CreateScrollableLayer(10, outer_viewport); - LayerImpl* child_scroll = child.get(); - outer_scroll->test_properties()->children[0]->test_properties()->AddChild( - std::move(child)); - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - - ElementId child_scroll_id = LayerIdToElementIdForTesting(child_scroll->id()); - - DrawFrame(); - { - std::unique_ptr<ScrollAndScaleSet> scroll_info; - - gfx::Vector2d scroll_delta(0, inner_viewport.height()); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point()).get(), - InputHandler::TOUCHSCREEN) - .thread); - EXPECT_TRUE( - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()) - .did_scroll); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(), InputHandler::TOUCHSCREEN)); - - // The child should have scrolled up to its limit. - scroll_info = host_impl_->ProcessScrollDeltas(); - ASSERT_EQ(1u, scroll_info->scrolls.size()); - EXPECT_TRUE( - ScrollInfoContains(*scroll_info, child_scroll_id, scroll_delta)); - EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, - child_scroll->scroll_tree_index()); - - // The fling have no effect on the currently scrolling layer. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - EXPECT_FALSE( - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()) - .did_scroll); - EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, - child_scroll->scroll_tree_index()); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(), InputHandler::TOUCHSCREEN)); - - // The inner viewport shouldn't have scrolled. - scroll_info = host_impl_->ProcessScrollDeltas(); - ASSERT_EQ(1u, scroll_info->scrolls.size()); - EXPECT_TRUE( - ScrollInfoContains(*scroll_info, child_scroll_id, scroll_delta)); - ExpectNone(*scroll_info, inner_scroll->element_id()); - - // As the locked layer is at its limit, no further scrolling can occur. - EXPECT_FALSE( - host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()) - .did_scroll); - EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id, - child_scroll->scroll_tree_index()); - host_impl_->ScrollEnd(EndState().get()); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(), InputHandler::TOUCHSCREEN)); - } -} - -TEST_F(LayerTreeHostImplVirtualViewportTest, ScrollBeginEventThatTargetsViewportLayerSkipsHitTest) { gfx::Size content_size = gfx::Size(100, 160); gfx::Size outer_viewport = gfx::Size(50, 80); @@ -11546,7 +11208,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformReflectedInNextDraw) { host_impl_->SetExternalTilePriorityConstraints(external_viewport, external_transform); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); EXPECT_TRANSFORMATION_MATRIX_EQ( external_transform, layer->draw_properties().target_space_transform); @@ -11554,7 +11216,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformReflectedInNextDraw) { host_impl_->SetExternalTilePriorityConstraints(external_viewport, external_transform); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); EXPECT_TRANSFORMATION_MATRIX_EQ( external_transform, layer->draw_properties().target_space_transform); } @@ -11576,7 +11238,8 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) { // Clear any damage. host_impl_->SetExternalTilePriorityConstraints(viewport_for_tile_priority1, transform_for_tile_priority); - host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); last_on_draw_frame_.reset(); // Setting new constraints needs redraw. @@ -11584,7 +11247,8 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) { host_impl_->SetExternalTilePriorityConstraints(viewport_for_tile_priority2, transform_for_tile_priority); EXPECT_TRUE(did_request_redraw_); - host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); EXPECT_FALSE(last_on_draw_frame_->has_no_damage); } @@ -11601,213 +11265,49 @@ TEST_F(LayerTreeHostImplTest, OnDrawConstraintSetNeedsRedraw) { bool resourceless_software_draw = false; // Clear any damage. - host_impl_->OnDraw(draw_transform, draw_viewport1, - resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport1, resourceless_software_draw, + false); last_on_draw_frame_.reset(); // Same draw params does not swap. did_request_redraw_ = false; - host_impl_->OnDraw(draw_transform, draw_viewport1, - resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport1, resourceless_software_draw, + false); EXPECT_FALSE(did_request_redraw_); EXPECT_TRUE(last_on_draw_frame_->has_no_damage); last_on_draw_frame_.reset(); // Different draw params does swap. did_request_redraw_ = false; - host_impl_->OnDraw(draw_transform, draw_viewport2, - resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport2, resourceless_software_draw, + false); EXPECT_TRUE(did_request_redraw_); EXPECT_FALSE(last_on_draw_frame_->has_no_damage); } -// TODO(gyuyoung): OnMemoryPressure disabled on ASAN, TSAN, Android, windows -// due to the test failure. Will be handled on -// http://crbug.com/839687. -#if defined(OS_WIN) || defined(OS_ANDROID) || defined(ADDRESS_SANITIZER) || \ - defined(THREAD_SANITIZER) || defined(MEMORY_SANITIZER) || \ - defined(LEAK_SANITIZER) -#define MAYBE_OnMemoryPressure DISABLED_OnMemoryPressure -#else -#define MAYBE_OnMemoryPressure OnMemoryPressure -#endif - -TEST_F(LayerTreeHostImplTest, MAYBE_OnMemoryPressure) { +TEST_F(LayerTreeHostImplTest, OnMemoryPressure) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableLowEndDeviceMode); - const gfx::Size viewport_size(100, 100); - host_impl_->SetViewportSize(viewport_size); - host_impl_->CreatePendingTree(); - scoped_refptr<FakeRasterSource> raster_source( - FakeRasterSource::CreateFilled(viewport_size)); - std::unique_ptr<FakePictureLayerImpl> layer( - FakePictureLayerImpl::CreateWithRasterSource(host_impl_->pending_tree(), - 11, raster_source)); - layer->SetBounds(viewport_size); - layer->SetDrawsContent(true); - host_impl_->pending_tree()->SetRootLayerForTesting(std::move(layer)); - host_impl_->pending_tree()->BuildPropertyTreesForTesting(); - host_impl_->ActivateSyncTree(); - const gfx::Transform draw_transform; - host_impl_->OnDraw(draw_transform, gfx::Rect(viewport_size), false); + gfx::Size size(200, 200); + viz::ResourceFormat format = viz::RGBA_8888; + gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB(); + ResourcePool::InUsePoolResource resource = + host_impl_->resource_pool()->AcquireResource(size, format, color_space); + host_impl_->resource_pool()->ReleaseResource(std::move(resource)); - std::unique_ptr<base::ProcessMetrics> metric( - base::ProcessMetrics::CreateCurrentProcessMetrics()); - size_t current_memory_usage = metric->GetMallocUsage(); + size_t current_memory_usage = + host_impl_->resource_pool()->GetTotalMemoryUsageForTesting(); base::MemoryPressureListener::SimulatePressureNotification( base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); base::RunLoop().RunUntilIdle(); - size_t memory_usage_after_memory_pressure = metric->GetMallocUsage(); + size_t memory_usage_after_memory_pressure = + host_impl_->resource_pool()->GetTotalMemoryUsageForTesting(); // Memory usage after the memory pressure should be less than previous one. EXPECT_LT(memory_usage_after_memory_pressure, current_memory_usage); - EXPECT_FALSE(host_impl_->use_gpu_rasterization()); -} - -// We will force the touch event handler to be passive if we touch on a layer -// which is the current scrolling layer. -TEST_F(LayerTreeHostImplTest, TouchInsideFlingLayer) { - gfx::Size surface_size(100, 100); - gfx::Size content_size(200, 200); - - LayerImpl* root = - CreateBasicVirtualViewportLayers(surface_size, surface_size); - root->test_properties()->force_render_surface = true; - - // A div layer which has an event handler. - std::unique_ptr<LayerImpl> child = CreateScrollableLayer(26, surface_size); - LayerImpl* child_layer = child.get(); - - // The layer tree should create a layer for the div layer, which is the - // actual scrolling layer. - std::unique_ptr<LayerImpl> grand_child = - CreateScrollableLayer(27, content_size); - LayerImpl* grand_child_layer = grand_child.get(); - - child->test_properties()->AddChild(std::move(grand_child)); - root->test_properties()->AddChild(std::move(child)); - - host_impl_->SetViewportSize(surface_size); - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->active_tree()->DidBecomeActive(); - DrawFrame(); - - { - TouchAction touch_action; - // Touch on a layer which does not have a handler will return kNone. - EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER, - host_impl_->EventListenerTypeForTouchStartOrMoveAt( - gfx::Point(10, 10), &touch_action)); - TouchActionRegion touch_action_region; - touch_action_region.Union(kTouchActionNone, gfx::Rect(0, 0, 100, 100)); - child_layer->SetTouchActionRegion(touch_action_region); - EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType::HANDLER, - host_impl_->EventListenerTypeForTouchStartOrMoveAt( - gfx::Point(10, 10), &touch_action)); - // Flinging the grand_child layer. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point(60, 60)).get(), - InputHandler::TOUCHSCREEN) - .thread); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - EXPECT_EQ(grand_child_layer->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - // Touch on the grand_child layer, which is an active fling layer, the touch - // event handler will force to be passive. - EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType:: - HANDLER_ON_SCROLLING_LAYER, - host_impl_->EventListenerTypeForTouchStartOrMoveAt( - gfx::Point(70, 80), &touch_action)); - } -} - -// We will force the touch event handler to be passive if we touch on a layer -// which is a descendant of the current scrolling layer. If we touch on its -// ancestor, then the touch event handler will still be blocked. -TEST_F(LayerTreeHostImplTest, TouchInsideOrOutsideFlingLayer) { - gfx::Size surface_size(100, 100); - gfx::Size content_size(200, 200); - gfx::Size inner_size(50, 50); - - LayerImpl* root = - CreateBasicVirtualViewportLayers(surface_size, surface_size); - root->test_properties()->force_render_surface = true; - - // A div layer which has an event handler. - std::unique_ptr<LayerImpl> child = CreateScrollableLayer(26, surface_size); - LayerImpl* child_layer = child.get(); - - // The layer tree should create a layer for the div layer, which is the - // actual scrolling layer. - std::unique_ptr<LayerImpl> grand_child = - CreateScrollableLayer(27, content_size); - LayerImpl* grand_child_layer = grand_child.get(); - - // A child scrollable layer inside grand_child_layer. - std::unique_ptr<LayerImpl> great_grand_child = - CreateScrollableLayer(28, inner_size); - LayerImpl* great_grand_child_layer = great_grand_child.get(); - - grand_child->test_properties()->AddChild(std::move(great_grand_child)); - child->test_properties()->AddChild(std::move(grand_child)); - root->test_properties()->AddChild(std::move(child)); - - host_impl_->SetViewportSize(surface_size); - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->active_tree()->DidBecomeActive(); - DrawFrame(); - - { - TouchActionRegion touch_action_region; - touch_action_region.Union(kTouchActionNone, gfx::Rect(0, 0, 100, 100)); - child_layer->SetTouchActionRegion(std::move(touch_action_region)); - touch_action_region = TouchActionRegion(); - touch_action_region.Union(kTouchActionNone, gfx::Rect(0, 0, 50, 50)); - grand_child_layer->SetTouchActionRegion(std::move(touch_action_region)); - // Flinging the grand_child layer. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point(60, 60)).get(), - InputHandler::TOUCHSCREEN) - .thread); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - EXPECT_EQ(grand_child_layer->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - // Touch on the grand_child layer, which is an active fling layer, the touch - // event handler will force to be passive. - TouchAction touch_action; - EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType:: - HANDLER_ON_SCROLLING_LAYER, - host_impl_->EventListenerTypeForTouchStartOrMoveAt( - gfx::Point(70, 80), &touch_action)); - // Touch on the great_grand_child_layer layer, which is the child of the - // active fling layer, the touch event handler will force to be passive. - EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType:: - HANDLER_ON_SCROLLING_LAYER, - host_impl_->EventListenerTypeForTouchStartOrMoveAt( - gfx::Point(20, 30), &touch_action)); - - // Now flinging on the great_grand_child_layer. - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_ - ->ScrollBegin(BeginState(gfx::Point(10, 10)).get(), - InputHandler::TOUCHSCREEN) - .thread); - EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, - host_impl_->FlingScrollBegin().thread); - EXPECT_EQ(great_grand_child_layer->scroll_tree_index(), - host_impl_->CurrentlyScrollingNode()->id); - // Touch on the child layer, the touch event handler will be blocked. - EXPECT_EQ(InputHandler::TouchStartOrMoveEventListenerType::HANDLER, - host_impl_->EventListenerTypeForTouchStartOrMoveAt( - gfx::Point(60, 60), &touch_action)); - } } class ResourcelessSoftwareLayerTreeHostImplTest : public LayerTreeHostImplTest { @@ -11830,18 +11330,21 @@ TEST_F(ResourcelessSoftwareLayerTreeHostImplTest, bool resourceless_software_draw = false; // Clear any damage. - host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); last_on_draw_frame_.reset(); // Always swap even if same draw params. resourceless_software_draw = true; - host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); EXPECT_FALSE(last_on_draw_frame_->has_no_damage); last_on_draw_frame_.reset(); // Next hardware draw has damage. resourceless_software_draw = false; - host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); EXPECT_FALSE(last_on_draw_frame_->has_no_damage); } @@ -11869,7 +11372,8 @@ TEST_F(ResourcelessSoftwareLayerTreeHostImplTest, // Regular draw causes UpdateTiles. did_request_prepare_tiles_ = false; - host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw); + host_impl_->OnDraw(draw_transform, draw_viewport, resourceless_software_draw, + false); EXPECT_TRUE(did_request_prepare_tiles_); host_impl_->PrepareTiles(); @@ -11878,7 +11382,7 @@ TEST_F(ResourcelessSoftwareLayerTreeHostImplTest, resourceless_software_draw = true; did_request_prepare_tiles_ = false; host_impl_->OnDraw(draw_transform, new_draw_viewport, - resourceless_software_draw); + resourceless_software_draw, false); EXPECT_FALSE(did_request_prepare_tiles_); } @@ -11934,7 +11438,7 @@ TEST_F(LayerTreeHostImplTest, ExternalViewportAffectsVisibleRects) { host_impl_->SetExternalTilePriorityConstraints(external_viewport, external_transform); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); EXPECT_EQ(gfx::Rect(10, 20), content_layer->visible_layer_rect()); // Clear the external viewport. @@ -11943,7 +11447,7 @@ TEST_F(LayerTreeHostImplTest, ExternalViewportAffectsVisibleRects) { external_transform); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); EXPECT_EQ(gfx::Rect(90, 90), content_layer->visible_layer_rect()); } @@ -11970,7 +11474,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsVisibleRects) { // Visible rects should now be shifted and scaled because of the external // transform. host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); EXPECT_EQ(gfx::Rect(20, 20), content_layer->visible_layer_rect()); // Clear the external transform. @@ -11979,7 +11483,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsVisibleRects) { external_transform); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); EXPECT_EQ(gfx::Rect(50, 50), content_layer->visible_layer_rect()); } @@ -12018,7 +11522,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsSublayerScaleFactor) { // Transform node's sublayer scale should include the device transform scale. host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); node = host_impl_->active_tree()->property_trees()->effect_tree.Node( test_layer->effect_tree_index()); EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(2.f, 2.f)); @@ -12029,7 +11533,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsSublayerScaleFactor) { external_transform); host_impl_->OnDraw(external_transform, external_viewport, - resourceless_software_draw); + resourceless_software_draw, false); node = host_impl_->active_tree()->property_trees()->effect_tree.Node( test_layer->effect_tree_index()); EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(1.f, 1.f)); @@ -13228,12 +12732,12 @@ class MsaaIsSlowLayerTreeHostImplTest : public LayerTreeHostImplTest { settings.gpu_rasterization_msaa_sample_count = 4; auto frame_sink = FakeLayerTreeFrameSink::Builder() - .AllContexts(&TestWebGraphicsContext3D::SetMaxSamples, + .AllContexts(&viz::TestGLES2Interface::SetMaxSamples, settings.gpu_rasterization_msaa_sample_count) - .AllContexts(&TestWebGraphicsContext3D::set_msaa_is_slow, + .AllContexts(&viz::TestGLES2Interface::set_msaa_is_slow, msaa_is_slow) - .AllContexts(&TestWebGraphicsContext3D::set_gpu_rasterization, true) - .AllContexts(&TestWebGraphicsContext3D::set_avoid_stencil_buffers, + .AllContexts(&viz::TestGLES2Interface::set_gpu_rasterization, true) + .AllContexts(&viz::TestGLES2Interface::set_avoid_stencil_buffers, avoid_stencil_buffers) .Build(); EXPECT_TRUE(CreateHostImpl(settings, std::move(frame_sink))); @@ -13289,12 +12793,12 @@ class MsaaCompatibilityLayerTreeHostImplTest : public LayerTreeHostImplTest { settings.gpu_rasterization_msaa_sample_count = 4; auto frame_sink = FakeLayerTreeFrameSink::Builder() - .AllContexts(&TestWebGraphicsContext3D::SetMaxSamples, + .AllContexts(&viz::TestGLES2Interface::SetMaxSamples, settings.gpu_rasterization_msaa_sample_count) - .AllContexts(&TestWebGraphicsContext3D:: - set_support_multisample_compatibility, - support_multisample_compatibility) - .AllContexts(&TestWebGraphicsContext3D::set_gpu_rasterization, true) + .AllContexts( + &viz::TestGLES2Interface::set_support_multisample_compatibility, + support_multisample_compatibility) + .AllContexts(&viz::TestGLES2Interface::set_gpu_rasterization, true) .Build(); EXPECT_TRUE(CreateHostImpl(settings, std::move(frame_sink))); } @@ -13504,14 +13008,14 @@ TEST_F(LayerTreeHostImplTest, RecomputeGpuRasterOnLayerTreeFrameSinkChange) { AnimationHost::CreateForTesting(ThreadInstance::IMPL), 0, nullptr); host_impl_->SetVisible(true); - // InitializeRenderer with a gpu-raster enabled output surface. + // InitializeFrameSink with a gpu-raster enabled output surface. auto gpu_raster_layer_tree_frame_sink = FakeLayerTreeFrameSink::Create3d(); - host_impl_->InitializeRenderer(gpu_raster_layer_tree_frame_sink.get()); + host_impl_->InitializeFrameSink(gpu_raster_layer_tree_frame_sink.get()); EXPECT_TRUE(host_impl_->use_gpu_rasterization()); // Re-initialize with a software output surface. layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware(); - host_impl_->InitializeRenderer(layer_tree_frame_sink_.get()); + host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get()); EXPECT_FALSE(host_impl_->use_gpu_rasterization()); } @@ -14089,44 +13593,21 @@ TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest4) { WhiteListedTouchActionTestHelper(2.654f, 0.678f); } -// Test implementation of a SwapPromise which will always attempt to increment -// the frame token during WillSwap. -class FrameTokenAdvancingSwapPromise : public SwapPromise { - public: - FrameTokenAdvancingSwapPromise() {} - ~FrameTokenAdvancingSwapPromise() override {} - - void DidActivate() override {} - void WillSwap(viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator) override { - frame_token_allocator->GetOrAllocateFrameToken(); - } - void DidSwap() override {} - DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override { - return SwapPromise::DidNotSwapAction::BREAK_PROMISE; - } - int64_t TraceId() const override { return 42; } - - private: - DISALLOW_COPY_AND_ASSIGN(FrameTokenAdvancingSwapPromise); -}; - // Test implementation of RenderFrameMetadataObserver which can optionally -// increment the frame token during OnRenderFrameSubmission. +// request the frame-token to be sent to the embedder during frame submission. class TestRenderFrameMetadataObserver : public RenderFrameMetadataObserver { public: explicit TestRenderFrameMetadataObserver(bool increment_counter) : increment_counter_(increment_counter) {} ~TestRenderFrameMetadataObserver() override {} - void BindToCurrentThread( - FrameTokenAllocator* frame_token_allocator) override { - frame_token_allocator_ = frame_token_allocator; - } - void OnRenderFrameSubmission(RenderFrameMetadata metadata) override { + void BindToCurrentThread() override {} + void OnRenderFrameSubmission( + const RenderFrameMetadata& render_frame_metadata, + viz::CompositorFrameMetadata* compositor_frame_metadata) override { if (increment_counter_) - frame_token_allocator_->GetOrAllocateFrameToken(); - last_metadata_ = metadata; + compositor_frame_metadata->send_frame_token_to_embedder = true; + last_metadata_ = render_frame_metadata; } const base::Optional<RenderFrameMetadata>& last_metadata() const { @@ -14134,106 +13615,12 @@ class TestRenderFrameMetadataObserver : public RenderFrameMetadataObserver { } private: - FrameTokenAllocator* frame_token_allocator_; bool increment_counter_; base::Optional<RenderFrameMetadata> last_metadata_; DISALLOW_COPY_AND_ASSIGN(TestRenderFrameMetadataObserver); }; -// Tests that SwapPromises and RenderFrameMetadataObservers can increment the -// frame token when frames are being drawn. Furthermore verifies that when there -// are multiple attempts to increment the frame token during the same draw, that -// the token only ever increments by one. -TEST_F(LayerTreeHostImplTest, FrameTokenAllocation) { - SetupScrollAndContentsLayers(gfx::Size(100, 100)); - host_impl_->active_tree()->BuildPropertyTreesForTesting(); - host_impl_->SetViewportSize(gfx::Size(50, 50)); - - uint32_t expected_frame_token = 0u; - auto* fake_layer_tree_frame_sink = - static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink()); - - // No SwapPromise or RenderFrameMetadataObserver - { - DrawFrame(); - const viz::CompositorFrameMetadata& metadata = - fake_layer_tree_frame_sink->last_sent_frame()->metadata; - // With no token advancing we should receive the default of 0. - EXPECT_EQ(expected_frame_token, metadata.frame_token); - } - - // Just a SwapPromise no RenderFrameMetataObsever - { - std::vector<std::unique_ptr<SwapPromise>> promises; - promises.push_back(std::make_unique<FrameTokenAdvancingSwapPromise>()); - host_impl_->active_tree()->PassSwapPromises(std::move(promises)); - - host_impl_->SetViewportDamage(gfx::Rect(10, 10)); - DrawFrame(); - const viz::CompositorFrameMetadata& metadata = - fake_layer_tree_frame_sink->last_sent_frame()->metadata; - // SwapPromise should advance the token count by one. - EXPECT_EQ(++expected_frame_token, metadata.frame_token); - } - - // Just a RenderFrameMetadataObserver which does not advance the counter. - { - host_impl_->SetRenderFrameObserver( - std::make_unique<TestRenderFrameMetadataObserver>(false)); - host_impl_->SetViewportDamage(gfx::Rect(10, 10)); - DrawFrame(); - - const viz::CompositorFrameMetadata& metadata = - fake_layer_tree_frame_sink->last_sent_frame()->metadata; - // With no token advancing we should receive the default of 0. - EXPECT_EQ(0u, metadata.frame_token); - } - - // A SwapPromise which advances, and a RenderFrameMetadataObserver which does - // not advance the counter. - { - std::vector<std::unique_ptr<SwapPromise>> promises; - promises.push_back(std::make_unique<FrameTokenAdvancingSwapPromise>()); - host_impl_->active_tree()->PassSwapPromises(std::move(promises)); - - host_impl_->SetViewportDamage(gfx::Rect(10, 10)); - DrawFrame(); - const viz::CompositorFrameMetadata& metadata = - fake_layer_tree_frame_sink->last_sent_frame()->metadata; - // SwapPromise should advance the token count by one. - EXPECT_EQ(++expected_frame_token, metadata.frame_token); - } - - // Both a SwapPromise and a RenderFrameMetadataObserver which try to advance - // the counter. - { - std::vector<std::unique_ptr<SwapPromise>> promises; - promises.push_back(std::make_unique<FrameTokenAdvancingSwapPromise>()); - host_impl_->active_tree()->PassSwapPromises(std::move(promises)); - - host_impl_->SetRenderFrameObserver( - std::make_unique<TestRenderFrameMetadataObserver>(true)); - host_impl_->SetViewportDamage(gfx::Rect(10, 10)); - DrawFrame(); - const viz::CompositorFrameMetadata& metadata = - fake_layer_tree_frame_sink->last_sent_frame()->metadata; - // Even with both sources trying to advance the frame token, it should - // only increment by one. - EXPECT_EQ(++expected_frame_token, metadata.frame_token); - } - - // Just a RenderFrameMetadataObserver which advances the counter - { - host_impl_->SetViewportDamage(gfx::Rect(10, 10)); - DrawFrame(); - const viz::CompositorFrameMetadata& metadata = - fake_layer_tree_frame_sink->last_sent_frame()->metadata; - // The RenderFrameMetadataObserver should advance the counter by one. - EXPECT_EQ(++expected_frame_token, metadata.frame_token); - } -} - TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToRenderFrameMetadata) { const int root_layer_id = 1; std::unique_ptr<SolidColorLayerImpl> root = @@ -14505,5 +13892,51 @@ TEST_F(HitTestRegionListGeneratingLayerTreeHostImplTest, BuildHitTestData) { hit_test_region_list->regions[0].rect.ToString()); } +TEST_F(LayerTreeHostImplTest, ImplThreadPhaseUponImplSideInvalidation) { + LayerTreeSettings settings = DefaultSettings(); + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + // In general invalidation should never be ran outside the impl frame. + host_impl_->WillBeginImplFrame( + viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1)); + // Expect no crash because the operation is within an impl frame. + host_impl_->InvalidateContentOnImplSide(); + + // Once the impl frame is finished the impl thread phase is set to IDLE. + host_impl_->DidFinishImplFrame(); + + settings.using_synchronous_renderer_compositor = true; + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + // Expect no crash when using synchronous renderer compositor regardless the + // impl thread phase. + host_impl_->InvalidateContentOnImplSide(); + + // Test passes when there is no crash. +} + +TEST_F(LayerTreeHostImplTest, SkipOnDrawDoesNotUpdateDrawParams) { + EXPECT_TRUE(CreateHostImpl(DefaultSettings(), + FakeLayerTreeFrameSink::CreateSoftware())); + LayerImpl* layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); + layer->SetDrawsContent(true); + gfx::Transform transform; + transform.Translate(20, 20); + gfx::Rect viewport(0, 0, 50, 50); + const bool resourceless_software_draw = false; + + bool skip_draw = false; + host_impl_->OnDraw(transform, viewport, resourceless_software_draw, + skip_draw); + EXPECT_EQ(transform, host_impl_->DrawTransform()); + EXPECT_EQ(viewport, host_impl_->DeviceViewport()); + + skip_draw = true; + gfx::Transform new_transform; + gfx::Rect new_viewport; + host_impl_->OnDraw(new_transform, new_viewport, resourceless_software_draw, + skip_draw); + EXPECT_EQ(transform, host_impl_->DrawTransform()); + EXPECT_EQ(viewport, host_impl_->DeviceViewport()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_perftest.cc b/chromium/cc/trees/layer_tree_host_perftest.cc index 871e1a10a17..fe6ad7870da 100644 --- a/chromium/cc/trees/layer_tree_host_perftest.cc +++ b/chromium/cc/trees/layer_tree_host_perftest.cc @@ -261,7 +261,7 @@ class ScrollingLayerTreePerfTest : public LayerTreeHostPerfTestJsonReader { return; static const gfx::Vector2d delta = gfx::Vector2d(0, 10); scrollable_->SetScrollOffset( - gfx::ScrollOffsetWithDelta(scrollable_->scroll_offset(), delta)); + gfx::ScrollOffsetWithDelta(scrollable_->CurrentScrollOffset(), delta)); } private: diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc index b251df562ab..841a5eafc9e 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc @@ -137,7 +137,13 @@ TEST_F(LayerTreeHostScrollbarsPixelTest, TransformScale) { base::FilePath(FILE_PATH_LITERAL("spiral_double_scale.png"))); } -TEST_F(LayerTreeHostScrollbarsPixelTest, HugeTransformScale) { +// Disabled on TSan due to frequent timeouts. crbug.com/848994 +#if defined(THREAD_SANITIZER) +#define MAYBE_HugeTransformScale DISABLED_HugeTransformScale +#else +#define MAYBE_HugeTransformScale HugeTransformScale +#endif +TEST_F(LayerTreeHostScrollbarsPixelTest, MAYBE_HugeTransformScale) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(gfx::Rect(400, 400), SK_ColorWHITE); @@ -150,7 +156,8 @@ TEST_F(LayerTreeHostScrollbarsPixelTest, HugeTransformScale) { background->AddChild(layer); scoped_refptr<TestInProcessContextProvider> context( - new TestInProcessContextProvider(/*enable_oop_rasterization=*/false)); + new TestInProcessContextProvider(/*enable_oop_rasterization=*/false, + /*support_locking=*/false)); context->BindToCurrentThread(); int max_texture_size = 0; context->ContextGL()->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index e66bf23fa85..54ef9321ca6 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -67,7 +67,6 @@ #include "components/viz/test/fake_output_surface.h" #include "components/viz/test/test_gles2_interface.h" #include "components/viz/test/test_layer_tree_frame_sink.h" -#include "components/viz/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/khronos/GLES2/gl2.h" @@ -90,6 +89,13 @@ const char kCheckerboardArea[] = "CheckerboardedContentArea"; const char kCheckerboardAreaRatio[] = "CheckerboardedContentAreaRatio"; const char kMissingTiles[] = "NumMissingTiles"; +bool LayerSubtreeHasCopyRequest(Layer* layer) { + LayerTreeHost* host = layer->layer_tree_host(); + int index = layer->effect_tree_index(); + auto* node = host->property_trees()->effect_tree.Node(index); + return node->subtree_has_copy_request; +} + class LayerTreeHostTest : public LayerTreeTest {}; class LayerTreeHostTestHasImplThreadTest : public LayerTreeHostTest { @@ -210,6 +216,48 @@ class LayerTreeHostTestFrameOrdering : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestFrameOrdering); +class LayerTreeHostTestRequestedMainFrame : public LayerTreeHostTest { + public: + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void WillBeginMainFrame() override { + // Post NextStep() so it happens after the MainFrame completes. + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&LayerTreeHostTestRequestedMainFrame::NextStep, + base::Unretained(this))); + } + + void NextStep() { + // The MainFrame request is cleared once a MainFrame happens. + EXPECT_FALSE(layer_tree_host()->RequestedMainFramePending()); + switch (layer_tree_host()->SourceFrameNumber()) { + case 0: + ADD_FAILURE() + << "Case 0 is the initial commit used to send the test here"; + FALLTHROUGH; + case 1: + layer_tree_host()->SetNeedsAnimate(); + break; + case 2: + layer_tree_host()->SetNeedsUpdateLayers(); + break; + case 3: + layer_tree_host()->SetNeedsCommit(); + break; + case 4: + EndTest(); + return; + } + // SetNeeds{Animate,UpdateLayers,Commit}() will mean a MainFrame is pending. + EXPECT_TRUE(layer_tree_host()->RequestedMainFramePending()); + } + + void AfterTest() override {} +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestRequestedMainFrame); + class LayerTreeHostTestSetNeedsUpdateInsideLayout : public LayerTreeHostTest { protected: void BeginTest() override { PostSetNeedsCommitToMainThread(); } @@ -1274,6 +1322,100 @@ class LayerTreeHostTestEarlyDamageCheckStops : public LayerTreeHostTest { // multi-threaded compositor. MULTI_THREAD_TEST_F(LayerTreeHostTestEarlyDamageCheckStops); +// When settings->enable_early_damage_check is true, verifies that PrepareTiles +// need not cause a draw when there is no visible damage. Here, a child layer is +// translated outside of the viewport. After two draws, the early damage check +// should prevent further draws, but preventing further draws should not prevent +// PrepareTiles. +class LayerTreeHostTestPrepareTilesWithoutDraw : public LayerTreeHostTest { + void InitializeSettings(LayerTreeSettings* settings) override { + settings->using_synchronous_renderer_compositor = true; + settings->enable_early_damage_check = true; + } + + protected: + void SetupTree() override { + LayerTreeHostTest::SetupTree(); + child_layer_ = Layer::Create(); + layer_tree_host()->root_layer()->AddChild(child_layer_); + + layer_tree_host()->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f, + viz::LocalSurfaceId()); + + layer_tree_host()->root_layer()->SetBounds(gfx::Size(50, 50)); + child_layer_->SetBounds(gfx::Size(50, 50)); + + // Translate the child layer past the viewport. + gfx::Transform translation; + translation.Translate(100, 100); + child_layer_->SetTransform(translation); + child_layer_->SetBounds(gfx::Size(50, 50)); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void DidCommit() override { + int frame_number = layer_tree_host()->SourceFrameNumber(); + if (frame_number > 3) { + EndTest(); + return; + } + + // Modify the child layer each frame. + float new_opacity = 0.9f / (frame_number + 1); + child_layer_->SetOpacity(new_opacity); + PostSetNeedsCommitToMainThread(); + } + + void WillPrepareTilesOnThread(LayerTreeHostImpl* impl) override { + if (impl->active_tree()->source_frame_number() >= 0) + prepare_tiles_count_++; + + switch (impl->active_tree()->source_frame_number()) { + case 0: + EXPECT_EQ(1, prepare_tiles_count_); + break; + case 1: + EXPECT_EQ(2, prepare_tiles_count_); + break; + case 2: + EXPECT_EQ(3, prepare_tiles_count_); + break; + } + } + + void DrawLayersOnThread(LayerTreeHostImpl* impl) override { + draw_count_++; + + switch (impl->active_tree()->source_frame_number()) { + case 0: + // There is actual damage as the layers are set up. + EXPECT_EQ(1, draw_count_); + break; + case 1: + // There is no damage, but draw because the early damage check + // didn't occur. + EXPECT_EQ(2, draw_count_); + break; + default: + // After the first two draws, the early damage check should kick + // in and prevent further draws. + ADD_FAILURE(); + } + } + + void AfterTest() override { EXPECT_EQ(2, draw_count_); } + + private: + scoped_refptr<Layer> child_layer_; + int prepare_tiles_count_ = 0; + int draw_count_ = 0; +}; + +// This behavior is specific to Android WebView, which only uses +// multi-threaded compositor. +MULTI_THREAD_TEST_F(LayerTreeHostTestPrepareTilesWithoutDraw); + // Verify CanDraw() is false until first commit. class LayerTreeHostTestCantDrawBeforeCommit : public LayerTreeHostTest { protected: @@ -1898,8 +2040,11 @@ class LayerTreeHostTestTransformTreeDamageIsUpdated : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestTransformTreeDamageIsUpdated); -class UpdateCountingLayer : public Layer { +class UpdateCountingLayer : public PictureLayer { public: + explicit UpdateCountingLayer(ContentLayerClient* client) + : PictureLayer(client) {} + bool Update() override { update_count_++; return false; @@ -1921,9 +2066,9 @@ class LayerTreeHostTestSwitchMaskLayer : public LayerTreeHostTest { void SetupTree() override { scoped_refptr<Layer> root = Layer::Create(); root->SetBounds(gfx::Size(10, 10)); - child_layer_ = base::MakeRefCounted<UpdateCountingLayer>(); + child_layer_ = base::MakeRefCounted<UpdateCountingLayer>(&client_); child_layer_->SetBounds(gfx::Size(10, 10)); - mask_layer_ = base::MakeRefCounted<UpdateCountingLayer>(); + mask_layer_ = base::MakeRefCounted<UpdateCountingLayer>(&client_); mask_layer_->SetBounds(gfx::Size(10, 10)); child_layer_->SetMaskLayer(mask_layer_.get()); root->AddChild(child_layer_); @@ -1970,6 +2115,7 @@ class LayerTreeHostTestSwitchMaskLayer : public LayerTreeHostTest { void AfterTest() override {} + FakeContentLayerClient client_; scoped_refptr<UpdateCountingLayer> mask_layer_; scoped_refptr<UpdateCountingLayer> child_layer_; int index_; @@ -2414,24 +2560,24 @@ class LayerTreeHostTestRasterColorSpaceChange : public LayerTreeHostTest { PostSetNeedsCommitToMainThread(); break; case 2: - EXPECT_FALSE(child_layer_->NeedsDisplayForTesting()); + EXPECT_TRUE(child_layer_->update_rect().IsEmpty()); layer_tree_host()->SetRasterColorSpace(space2_); - EXPECT_TRUE(child_layer_->NeedsDisplayForTesting()); + EXPECT_FALSE(child_layer_->update_rect().IsEmpty()); break; case 3: // The redundant SetRasterColorSpace should cause no commit and no // damage. Force a commit for the test to continue. layer_tree_host()->SetRasterColorSpace(space2_); PostSetNeedsCommitToMainThread(); - EXPECT_FALSE(child_layer_->NeedsDisplayForTesting()); + EXPECT_TRUE(child_layer_->update_rect().IsEmpty()); break; case 4: - EXPECT_FALSE(child_layer_->NeedsDisplayForTesting()); + EXPECT_TRUE(child_layer_->update_rect().IsEmpty()); layer_tree_host()->SetRasterColorSpace(space1_); - EXPECT_TRUE(child_layer_->NeedsDisplayForTesting()); + EXPECT_FALSE(child_layer_->update_rect().IsEmpty()); break; case 5: - EXPECT_FALSE(child_layer_->NeedsDisplayForTesting()); + EXPECT_TRUE(child_layer_->update_rect().IsEmpty()); PostSetNeedsCommitToMainThread(); break; case 6: @@ -2932,7 +3078,7 @@ class LayerTreeHostTestStartPageScaleAnimation : public LayerTreeHostTest { const gfx::Vector2dF& elastic_overscroll_delta, float scale, float) override { - gfx::ScrollOffset offset = scroll_layer_->scroll_offset(); + gfx::ScrollOffset offset = scroll_layer_->CurrentScrollOffset(); scroll_layer_->SetScrollOffset(ScrollOffsetWithDelta(offset, scroll_delta)); layer_tree_host()->SetPageScaleFactorAndLimits(scale, 0.5f, 2.f); } @@ -3026,7 +3172,7 @@ class ViewportDeltasAppliedDuringPinch : public LayerTreeHostTest { EXPECT_EQ(2, scale_delta); auto* scroll_layer = layer_tree_host()->inner_viewport_scroll_layer(); - EXPECT_EQ(gfx::ScrollOffset(50, 50), scroll_layer->scroll_offset()); + EXPECT_EQ(gfx::ScrollOffset(50, 50), scroll_layer->CurrentScrollOffset()); EndTest(); } @@ -3829,12 +3975,12 @@ class OnDrawLayerTreeFrameSink : public viz::TestLayerTreeFrameSink { invalidate_callback_(std::move(invalidate_callback)) {} // TestLayerTreeFrameSink overrides. - void Invalidate() override { invalidate_callback_.Run(); } + void Invalidate(bool needs_draw) override { invalidate_callback_.Run(); } void OnDraw(bool resourceless_software_draw) { gfx::Transform identity; gfx::Rect empty_rect; - client_->OnDraw(identity, empty_rect, resourceless_software_draw); + client_->OnDraw(identity, empty_rect, resourceless_software_draw, false); } private: @@ -4005,7 +4151,7 @@ class LayerTreeHostTestUIResource : public LayerTreeHostTest { void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { auto* context = static_cast<viz::TestContextProvider*>( impl->layer_tree_frame_sink()->context_provider()) - ->TestContext3d(); + ->TestContextGL(); int frame = impl->active_tree()->source_frame_number(); switch (frame) { @@ -5324,8 +5470,7 @@ class TestSwapPromise : public SwapPromise { result_->did_activate_called = true; } - void WillSwap(viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator) override { + void WillSwap(viz::CompositorFrameMetadata* metadata) override { base::AutoLock lock(result_->lock); EXPECT_FALSE(result_->did_swap_called); EXPECT_FALSE(result_->did_not_swap_called); @@ -5334,7 +5479,7 @@ class TestSwapPromise : public SwapPromise { void DidSwap() override {} - DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override { + void DidNotSwap(DidNotSwapReason reason) override { base::AutoLock lock(result_->lock); EXPECT_FALSE(result_->did_swap_called); EXPECT_FALSE(result_->did_not_swap_called); @@ -5342,7 +5487,6 @@ class TestSwapPromise : public SwapPromise { reason != DidNotSwapReason::SWAP_FAILS); result_->did_not_swap_called = true; result_->reason = reason; - return DidNotSwapAction::BREAK_PROMISE; } int64_t TraceId() const override { return 0; } @@ -6052,16 +6196,16 @@ class LayerTreeHostWithGpuRasterizationTest : public LayerTreeHostTest { scoped_refptr<viz::RasterContextProvider> ignored_worker_context_provider) override { auto context_provider = viz::TestContextProvider::Create(); - context_provider->UnboundTestContext3d()->SetMaxSamples(4); - context_provider->UnboundTestContext3d() - ->set_support_multisample_compatibility(false); - context_provider->UnboundTestContext3d()->set_gpu_rasterization(true); + viz::TestGLES2Interface* gl = context_provider->UnboundTestContextGL(); + gl->SetMaxSamples(4); + gl->set_support_multisample_compatibility(false); + gl->set_gpu_rasterization(true); auto worker_context_provider = viz::TestContextProvider::CreateWorker(); - worker_context_provider->UnboundTestContext3d()->SetMaxSamples(4); - worker_context_provider->UnboundTestContext3d() - ->set_support_multisample_compatibility(false); - worker_context_provider->UnboundTestContext3d()->set_gpu_rasterization( - true); + viz::TestGLES2Interface* worker_gl = + worker_context_provider->UnboundTestContextGL(); + worker_gl->SetMaxSamples(4); + worker_gl->set_support_multisample_compatibility(false); + worker_gl->set_gpu_rasterization(true); return LayerTreeHostTest::CreateLayerTreeFrameSink( renderer_settings, refresh_rate, std::move(context_provider), std::move(worker_context_provider)); @@ -6907,11 +7051,11 @@ class LayerTreeHostTestCrispUpAfterPinchEndsWithOneCopy override { scoped_refptr<viz::TestContextProvider> display_context_provider = viz::TestContextProvider::Create(); - viz::TestWebGraphicsContext3D* context3d = - display_context_provider->UnboundTestContext3d(); - context3d->set_support_sync_query(true); + viz::TestGLES2Interface* gl = + display_context_provider->UnboundTestContextGL(); + gl->set_support_sync_query(true); #if defined(OS_MACOSX) - context3d->set_support_texture_rectangle(true); + gl->set_support_texture_rectangle(true); #endif display_context_provider->BindToCurrentThread(); return LayerTreeTest::CreateDisplayOutputSurfaceOnThread( @@ -7353,7 +7497,7 @@ class LayerTreeHostTestUpdateCopyRequests : public LayerTreeHostTest { void WillCommit() override { switch (layer_tree_host()->SourceFrameNumber()) { case 1: - EXPECT_TRUE(root->has_copy_requests_in_target_subtree()); + EXPECT_TRUE(LayerSubtreeHasCopyRequest(root.get())); break; } } @@ -7376,7 +7520,7 @@ class LayerTreeHostTestUpdateCopyRequests : public LayerTreeHostTest { child->SetTransform(transform); break; case 3: - EXPECT_FALSE(root->has_copy_requests_in_target_subtree()); + EXPECT_FALSE(LayerSubtreeHasCopyRequest(root.get())); EndTest(); break; } @@ -7523,7 +7667,7 @@ class LayerTreeHostTestPresentationTimeRequest : public LayerTreeHostTest { void DisplayReceivedCompositorFrameOnThread( const viz::CompositorFrame& frame) override { - EXPECT_NE(0u, frame.metadata.presentation_token); + EXPECT_TRUE(frame.metadata.request_presentation_feedback); EndTest(); } @@ -7531,6 +7675,55 @@ class LayerTreeHostTestPresentationTimeRequest : public LayerTreeHostTest { }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPresentationTimeRequest); +// A SwapPromise that turns on |request_presentation_feedback| during +// WillSwap(). +class RequestPresentationFeedbackSwapPromise : public SwapPromise { + public: + RequestPresentationFeedbackSwapPromise() = default; + ~RequestPresentationFeedbackSwapPromise() override = default; + + // SwapPromise: + void DidActivate() override {} + void WillSwap(viz::CompositorFrameMetadata* metadata) override { + metadata->request_presentation_feedback = true; + } + void DidSwap() override {} + void DidNotSwap(DidNotSwapReason reason) override {} + int64_t TraceId() const override { return 0; } +}; + +// Tests that a presentation-token can be requested during swap. +class LayerTreeHostTestPresentationTimeRequestDuringSwap + : public LayerTreeHostTest { + protected: + void BeginTest() override { + layer_tree_host()->QueueSwapPromise( + std::make_unique<RequestPresentationFeedbackSwapPromise>()); + PostSetNeedsCommitToMainThread(); + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + EXPECT_TRUE(frame.metadata.request_presentation_feedback); + frame_token_ = frame.metadata.frame_token; + } + + void DidReceivePresentationTimeOnThread( + LayerTreeHostImpl* host_impl, + uint32_t frame_token, + const gfx::PresentationFeedback& feedback) override { + EXPECT_EQ(frame_token_, frame_token); + EndTest(); + } + + void AfterTest() override { ASSERT_GT(frame_token_, 0u); } + + private: + uint32_t frame_token_ = 0; +}; +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostTestPresentationTimeRequestDuringSwap); + // Makes sure that viz::LocalSurfaceId is propagated to the LayerTreeFrameSink. class LayerTreeHostTestLocalSurfaceId : public LayerTreeHostTest { protected: @@ -7565,6 +7758,48 @@ class LayerTreeHostTestLocalSurfaceId : public LayerTreeHostTest { }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestLocalSurfaceId); +// Makes sure that LayerTreeHost does not pick up changes to +// viz::LocalSurfaceIds that only involve the child sequence number. +class LayerTreeHostTestLocalSurfaceIdSkipChildNum : public LayerTreeHostTest { + protected: + void InitializeSettings(LayerTreeSettings* settings) override { + settings->enable_surface_synchronization = true; + } + + void BeginTest() override { + expected_local_surface_id_ = allocator_.GetCurrentLocalSurfaceId(); + EXPECT_TRUE(child_allocator_.UpdateFromParent(expected_local_surface_id_)); + child_local_surface_id_ = child_allocator_.GenerateId(); + EXPECT_NE(expected_local_surface_id_, child_local_surface_id_); + PostSetLocalSurfaceIdToMainThread(expected_local_surface_id_); + PostSetLocalSurfaceIdToMainThread(child_local_surface_id_); + } + + DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + DrawResult draw_result) override { + EXPECT_EQ(DRAW_SUCCESS, draw_result); + // We should not be picking up the newer |child_local_surface_id_|. + EXPECT_EQ(expected_local_surface_id_, + host_impl->active_tree()->local_surface_id_from_parent()); + return draw_result; + } + + void DisplayReceivedLocalSurfaceIdOnThread( + const viz::LocalSurfaceId& local_surface_id) override { + EXPECT_EQ(expected_local_surface_id_, local_surface_id); + EndTest(); + } + + void AfterTest() override {} + + viz::LocalSurfaceId expected_local_surface_id_; + viz::LocalSurfaceId child_local_surface_id_; + viz::ParentLocalSurfaceIdAllocator allocator_; + viz::ChildLocalSurfaceIdAllocator child_allocator_; +}; +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestLocalSurfaceIdSkipChildNum); + // Makes sure that viz::LocalSurfaceId allocation requests propagate all the way // to LayerTreeFrameSink. class LayerTreeHostTestRequestNewLocalSurfaceId : public LayerTreeHostTest { @@ -7753,15 +7988,32 @@ class LayerTreeHostTestSubmitFrameResources : public LayerTreeHostTest { viz::RenderPass* child_pass = AddRenderPass(&frame->render_passes, 2, gfx::Rect(3, 3, 10, 10), gfx::Transform(), FilterOperations()); - AddOneOfEveryQuadType(child_pass, host_impl->resource_provider(), 0); + std::vector<viz::ResourceId> child_resources = + AddOneOfEveryQuadType(child_pass, host_impl->resource_provider(), 0); viz::RenderPass* pass = AddRenderPass(&frame->render_passes, 1, gfx::Rect(3, 3, 10, 10), gfx::Transform(), FilterOperations()); - AddOneOfEveryQuadType(pass, host_impl->resource_provider(), child_pass->id); + std::vector<viz::ResourceId> root_resources = AddOneOfEveryQuadType( + pass, host_impl->resource_provider(), child_pass->id); + + auto append = [](std::vector<viz::ResourceId>* to, + std::vector<viz::ResourceId> from) { + to->insert(to->end(), from.begin(), from.end()); + }; + append(&resources_, child_resources); + append(&resources_, root_resources); + return draw_result; } + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + // The resources have been submitted to the display compositor by now, we + // can remove them from cc now, so that they are not leaked. + for (viz::ResourceId id : resources_) + host_impl->resource_provider()->RemoveImportedResource(id); + } + void DisplayReceivedCompositorFrameOnThread( const viz::CompositorFrame& frame) override { EXPECT_EQ(2u, frame.render_pass_list.size()); @@ -7775,6 +8027,8 @@ class LayerTreeHostTestSubmitFrameResources : public LayerTreeHostTest { } void AfterTest() override {} + + std::vector<viz::ResourceId> resources_; }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSubmitFrameResources); @@ -7829,10 +8083,8 @@ class LayerTreeHostTestBeginFrameAcks : public LayerTreeHostTest { return; compositor_frame_submitted_ = true; - EXPECT_EQ( - viz::BeginFrameAck(current_begin_frame_args_.source_id, - current_begin_frame_args_.sequence_number, true), - frame.metadata.begin_frame_ack); + EXPECT_EQ(viz::BeginFrameAck(current_begin_frame_args_, true), + frame.metadata.begin_frame_ack); } void DrawLayersOnThread(LayerTreeHostImpl* impl) override { @@ -7842,10 +8094,8 @@ class LayerTreeHostTestBeginFrameAcks : public LayerTreeHostTest { EXPECT_TRUE(frame_data_); EXPECT_TRUE(compositor_frame_submitted_); - EXPECT_EQ( - viz::BeginFrameAck(current_begin_frame_args_.source_id, - current_begin_frame_args_.sequence_number, true), - frame_data_->begin_frame_ack); + EXPECT_EQ(viz::BeginFrameAck(current_begin_frame_args_, true), + frame_data_->begin_frame_ack); EndTest(); } @@ -8211,6 +8461,29 @@ class LayerTreeHostTestImageAnimationSynchronousScheduling MULTI_THREAD_TEST_F(LayerTreeHostTestImageAnimationSynchronousScheduling); +class LayerTreeHostTestImageAnimationSynchronousSchedulingSoftwareDraw + : public LayerTreeHostTestImageAnimationSynchronousScheduling { + public: + void InitializeSettings(LayerTreeSettings* settings) override { + LayerTreeHostTestImageAnimationSynchronousScheduling::InitializeSettings( + settings); + use_software_renderer_ = true; + } + + void AfterTest() override { + LayerTreeHostTestImageAnimationSynchronousScheduling::AfterTest(); + // 3 frames decoded twice, once during tile raster and once during raster + // for PictureDrawQuad. + for (size_t i = 0u; i < 3u; ++i) { + EXPECT_EQ(generator_->frames_decoded().find(i)->second, 2); + } + } +}; + +// TODO(crbug.com/851231): Disabled this test due to flakiness. +// MULTI_THREAD_TEST_F( +// LayerTreeHostTestImageAnimationSynchronousSchedulingSoftwareDraw); + class LayerTreeHostTestImageDecodingHints : public LayerTreeHostTest { public: void BeginTest() override { PostSetNeedsCommitToMainThread(); } diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index 0a1a2b73fc7..0b935c7adf9 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -29,6 +29,7 @@ #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/transform_node.h" +#include "components/viz/common/quads/compositor_frame.h" namespace cc { namespace { @@ -820,10 +821,10 @@ class LayerTreeHostAnimationTestScrollOffsetChangesArePropagated break; } default: - EXPECT_GE(scroll_layer_->scroll_offset().x(), 10); - EXPECT_GE(scroll_layer_->scroll_offset().y(), 20); - if (scroll_layer_->scroll_offset().x() > 10 && - scroll_layer_->scroll_offset().y() > 20) + EXPECT_GE(scroll_layer_->CurrentScrollOffset().x(), 10); + EXPECT_GE(scroll_layer_->CurrentScrollOffset().y(), 20); + if (scroll_layer_->CurrentScrollOffset().x() > 10 && + scroll_layer_->CurrentScrollOffset().y() > 20) EndTest(); } } @@ -1007,6 +1008,81 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestScrollOffsetAnimationAdjusted); +// Tests that presentation-time requested from the main-thread is attached to +// the correct frame (i.e. activation needs to happen before the +// presentation-request is attached to the frame). +class LayerTreeHostPresentationDuringAnimation + : public LayerTreeHostAnimationTest { + public: + void SetupTree() override { + LayerTreeHostAnimationTest::SetupTree(); + + scroll_layer_ = FakePictureLayer::Create(&client_); + scroll_layer_->SetScrollable(gfx::Size(100, 100)); + scroll_layer_->SetBounds(gfx::Size(10000, 10000)); + client_.set_bounds(scroll_layer_->bounds()); + scroll_layer_->SetScrollOffset(gfx::ScrollOffset(100.0, 200.0)); + layer_tree_host()->root_layer()->AddChild(scroll_layer_); + + std::unique_ptr<ScrollOffsetAnimationCurve> curve( + ScrollOffsetAnimationCurve::Create( + gfx::ScrollOffset(6500.f, 7500.f), + CubicBezierTimingFunction::CreatePreset( + CubicBezierTimingFunction::EaseType::EASE_IN_OUT))); + std::unique_ptr<KeyframeModel> keyframe_model(KeyframeModel::Create( + std::move(curve), 1, 0, TargetProperty::SCROLL_OFFSET)); + keyframe_model->set_needs_synchronized_start_time(true); + + AttachAnimationsToTimeline(); + animation_child_->AttachElement(scroll_layer_->element_id()); + animation_child_->AddKeyframeModel(std::move(keyframe_model)); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void BeginMainFrame(const viz::BeginFrameArgs& args) override { + PostSetNeedsCommitToMainThread(); + } + + void BeginCommitOnThread(LayerTreeHostImpl* host_impl) override { + if (host_impl->active_tree()->source_frame_number() == 1) { + request_token_ = host_impl->next_frame_token(); + layer_tree_host()->RequestPresentationTimeForNextFrame(base::BindOnce( + &LayerTreeHostPresentationDuringAnimation::OnPresentation, + base::Unretained(this))); + host_impl->BlockNotifyReadyToActivateForTesting(true); + } + } + + void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, + const viz::BeginFrameArgs& args) override { + if (host_impl->next_frame_token() >= 5) + host_impl->BlockNotifyReadyToActivateForTesting(false); + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + if (frame.metadata.request_presentation_feedback) + received_token_ = frame.metadata.frame_token; + } + + void AfterTest() override { + EXPECT_GT(request_token_, 0u); + EXPECT_GT(received_token_, request_token_); + EXPECT_GE(received_token_, 5u); + } + + private: + void OnPresentation(const gfx::PresentationFeedback& feedback) { EndTest(); } + + FakeContentLayerClient client_; + scoped_refptr<FakePictureLayer> scroll_layer_; + uint32_t request_token_ = 0; + uint32_t received_token_ = 0; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostPresentationDuringAnimation); + // Verifies that when the main thread removes a scroll animation and sets a new // scroll position, the active tree takes on exactly this new scroll position // after activation, and the main thread doesn't receive a spurious scroll @@ -1046,12 +1122,12 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval void BeginMainFrame(const viz::BeginFrameArgs& args) override { switch (layer_tree_host()->SourceFrameNumber()) { case 0: - EXPECT_EQ(scroll_layer_->scroll_offset().x(), 100); - EXPECT_EQ(scroll_layer_->scroll_offset().y(), 200); + EXPECT_EQ(scroll_layer_->CurrentScrollOffset().x(), 100); + EXPECT_EQ(scroll_layer_->CurrentScrollOffset().y(), 200); break; case 1: { - EXPECT_GE(scroll_layer_->scroll_offset().x(), 100); - EXPECT_GE(scroll_layer_->scroll_offset().y(), 200); + EXPECT_GE(scroll_layer_->CurrentScrollOffset().x(), 100); + EXPECT_GE(scroll_layer_->CurrentScrollOffset().y(), 200); KeyframeModel* keyframe_model = animation_child_->GetKeyframeModel(TargetProperty::SCROLL_OFFSET); animation_child_->RemoveKeyframeModel(keyframe_model->id()); @@ -1059,7 +1135,7 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval break; } default: - EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset()); + EXPECT_EQ(final_postion_, scroll_layer_->CurrentScrollOffset()); } } @@ -1092,7 +1168,7 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval } void AfterTest() override { - EXPECT_EQ(final_postion_, scroll_layer_->scroll_offset()); + EXPECT_EQ(final_postion_, scroll_layer_->CurrentScrollOffset()); } private: @@ -1220,9 +1296,12 @@ class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers EXPECT_EQ(KeyframeModel::RUNNING, child_keyframe_model->run_state()); EXPECT_EQ(root_keyframe_model->start_time(), child_keyframe_model->start_time()); - animation_impl->AbortKeyframeModels(TargetProperty::OPACITY, false); - animation_impl->AbortKeyframeModels(TargetProperty::TRANSFORM, false); - animation_child_impl->AbortKeyframeModels(TargetProperty::OPACITY, false); + animation_impl->AbortKeyframeModelsWithProperty(TargetProperty::OPACITY, + false); + animation_impl->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, + false); + animation_child_impl->AbortKeyframeModelsWithProperty( + TargetProperty::OPACITY, false); EndTest(); } @@ -1300,7 +1379,8 @@ class LayerTreeHostAnimationTestPendingTreeAnimatesFirstCommit // And the sync tree layer should know it is animating. EXPECT_TRUE(child->screen_space_transform_is_animating()); - animation_impl->AbortKeyframeModels(TargetProperty::TRANSFORM, false); + animation_impl->AbortKeyframeModelsWithProperty(TargetProperty::TRANSFORM, + false); EndTest(); } diff --git a/chromium/cc/trees/layer_tree_host_unittest_context.cc b/chromium/cc/trees/layer_tree_host_unittest_context.cc index f3ec645e85a..a57153ed21b 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_context.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_context.cc @@ -22,7 +22,6 @@ #include "cc/test/fake_painted_scrollbar_layer.h" #include "cc/test/fake_picture_layer.h" #include "cc/test/fake_picture_layer_impl.h" -#include "cc/test/fake_resource_provider.h" #include "cc/test/fake_scoped_ui_resource.h" #include "cc/test/fake_scrollbar.h" #include "cc/test/fake_video_frame_provider.h" @@ -32,12 +31,13 @@ #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" +#include "components/viz/client/client_resource_provider.h" #include "components/viz/common/resources/single_release_callback.h" +#include "components/viz/test/fake_output_surface.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_gles2_interface.h" #include "components/viz/test/test_layer_tree_frame_sink.h" #include "components/viz/test/test_shared_bitmap_manager.h" -#include "components/viz/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/raster_interface.h" #include "media/base/media.h" @@ -886,8 +886,7 @@ class LayerTreeHostContextTestDontUseLostResources CHECK_EQ(result, gpu::ContextResult::kSuccess); shared_bitmap_manager_ = std::make_unique<viz::TestSharedBitmapManager>(); child_resource_provider_ = - FakeResourceProvider::CreateLayerTreeResourceProvider( - child_context_provider_.get()); + std::make_unique<viz::ClientResourceProvider>(true); } static void EmptyReleaseCallback(const gpu::SyncToken& sync_token, @@ -896,8 +895,7 @@ class LayerTreeHostContextTestDontUseLostResources void SetupTree() override { gpu::gles2::GLES2Interface* gl = child_context_provider_->ContextGL(); - gpu::Mailbox mailbox; - gl->GenMailboxCHROMIUM(mailbox.name); + gpu::Mailbox mailbox = gpu::Mailbox::Generate(); gpu::SyncToken sync_token; gl->GenSyncTokenCHROMIUM(sync_token.GetData()); @@ -1042,7 +1040,7 @@ class LayerTreeHostContextTestDontUseLostResources scoped_refptr<viz::TestContextProvider> child_context_provider_; std::unique_ptr<viz::SharedBitmapManager> shared_bitmap_manager_; - std::unique_ptr<LayerTreeResourceProvider> child_resource_provider_; + std::unique_ptr<viz::ClientResourceProvider> child_resource_provider_; scoped_refptr<VideoFrame> color_video_frame_; scoped_refptr<VideoFrame> hw_video_frame_; @@ -1555,6 +1553,176 @@ class UIResourceLostEviction : public UIResourceLostTestSimple { SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostEviction); +class UIResourceFreedIfLostWhileExported : public LayerTreeHostContextTest { + protected: + void BeginTest() override { + // Make 1 UIResource, post it to the compositor thread, where it will be + // uploaded. + ui_resource_ = + FakeScopedUIResource::Create(layer_tree_host()->GetUIResourceManager()); + EXPECT_NE(0, ui_resource_->id()); + PostSetNeedsCommitToMainThread(); + } + + void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override { + switch (impl->active_tree()->source_frame_number()) { + case 0: + // The UIResource has been created and a gpu resource made for it. + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(1u, gl_->NumTextures()); + // Lose the LayerTreeFrameSink connection. The UI resource should + // be replaced and the old texture should be destroyed. + impl->DidLoseLayerTreeFrameSink(); + break; + case 1: + // The UIResource has been recreated, the old texture is not kept + // around. + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(1u, gl_->NumTextures()); + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce( + &UIResourceFreedIfLostWhileExported::DeleteAndEndTest, + base::Unretained(this))); + } + } + + void DeleteAndEndTest() { + ui_resource_->DeleteResource(); + EndTest(); + } + + void AfterTest() override {} + + std::unique_ptr<FakeScopedUIResource> ui_resource_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceFreedIfLostWhileExported); + +class TileResourceFreedIfLostWhileExported : public LayerTreeHostContextTest { + protected: + void SetupTree() override { + PaintFlags flags; + client_.set_fill_with_nonsolid_color(true); + + scoped_refptr<FakePictureLayer> picture_layer = + FakePictureLayer::Create(&client_); + picture_layer->SetBounds(gfx::Size(10, 20)); + client_.set_bounds(picture_layer->bounds()); + layer_tree_host()->SetRootLayer(std::move(picture_layer)); + + LayerTreeTest::SetupTree(); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void DrawLayersOnThread(LayerTreeHostImpl* impl) override { + switch (impl->active_tree()->source_frame_number()) { + case 0: + // The PicturLayer has a texture for a tile, that has been exported to + // the display compositor now. + EXPECT_EQ(1u, impl->resource_provider()->num_resources_for_testing()); + EXPECT_EQ(1u, impl->resource_pool()->resource_count()); + // Shows that the tile texture is allocated with the current context. + num_textures_ = gl_->NumTextures(); + EXPECT_GT(num_textures_, 0u); + + // Lose the LayerTreeFrameSink connection. The tile resource should + // be replaced and the old texture should be destroyed. + LoseContext(); + break; + case 1: + // The tile has been recreated, the old texture is not kept around in + // the pool indefinitely. It can be dropped as soon as the context is + // known to be lost. + EXPECT_EQ(1u, impl->resource_provider()->num_resources_for_testing()); + EXPECT_EQ(1u, impl->resource_pool()->resource_count()); + // Shows that the replacement tile texture is re-allocated with the + // current context, not just the previous one. + EXPECT_EQ(num_textures_, gl_->NumTextures()); + EndTest(); + } + } + + void AfterTest() override {} + + FakeContentLayerClient client_; + size_t num_textures_ = 0; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(TileResourceFreedIfLostWhileExported); + +class SoftwareTileResourceFreedIfLostWhileExported : public LayerTreeTest { + protected: + std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + const viz::RendererSettings& renderer_settings, + double refresh_rate, + scoped_refptr<viz::ContextProvider> compositor_context_provider, + scoped_refptr<viz::RasterContextProvider> worker_context_provider) + override { + // Induce software compositing in cc. + return LayerTreeTest::CreateLayerTreeFrameSink( + renderer_settings, refresh_rate, nullptr, nullptr); + } + + std::unique_ptr<viz::OutputSurface> CreateDisplayOutputSurfaceOnThread( + scoped_refptr<viz::ContextProvider> compositor_context_provider) + override { + // Induce software compositing in the display compositor. + return viz::FakeOutputSurface::CreateSoftware( + std::make_unique<viz::SoftwareOutputDevice>()); + } + + void SetupTree() override { + PaintFlags flags; + client_.set_fill_with_nonsolid_color(true); + + scoped_refptr<FakePictureLayer> picture_layer = + FakePictureLayer::Create(&client_); + picture_layer->SetBounds(gfx::Size(10, 20)); + client_.set_bounds(picture_layer->bounds()); + layer_tree_host()->SetRootLayer(std::move(picture_layer)); + + LayerTreeTest::SetupTree(); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void DrawLayersOnThread(LayerTreeHostImpl* impl) override { + switch (impl->active_tree()->source_frame_number()) { + case 0: { + // The PicturLayer has a bitmap for a tile, that has been exported to + // the display compositor now. + EXPECT_EQ(1u, impl->resource_provider()->num_resources_for_testing()); + EXPECT_EQ(1u, impl->resource_pool()->resource_count()); + + impl->DidLoseLayerTreeFrameSink(); + break; + } + case 1: { + // The tile did not need to be recreated, the same bitmap/resource + // should be used for it. + EXPECT_EQ(1u, impl->resource_provider()->num_resources_for_testing()); + EXPECT_EQ(1u, impl->resource_pool()->resource_count()); + + // TODO(danakj): It'd be possible to not destroy and recreate the + // software bitmap, however for simplicity we do the same for software + // and for gpu paths. If we didn't destroy it we could see the same + // bitmap on PictureLayerImpl's tile. + + EndTest(); + } + } + } + + void AfterTest() override {} + + FakeContentLayerClient client_; + viz::ResourceId exported_resource_id_ = 0; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTileResourceFreedIfLostWhileExported); + class LayerTreeHostContextTestLoseAfterSendingBeginMainFrame : public LayerTreeHostContextTest { protected: diff --git a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc index fcc95241448..83daa78f332 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc @@ -16,8 +16,8 @@ #include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/service/display/direct_renderer.h" #include "components/viz/test/fake_output_surface.h" +#include "components/viz/test/test_gles2_interface.h" #include "components/viz/test/test_layer_tree_frame_sink.h" -#include "components/viz/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" namespace cc { @@ -855,7 +855,7 @@ class LayerTreeHostCopyRequestTestDeleteTexture // releasing the copy output request should cause the texture in the request // to be destroyed by the compositor, so we should have 1 less by now. EXPECT_EQ(num_textures_after_readback_ - 1, - display_context_provider_->TestContext3d()->NumTextures()); + display_context_provider_->TestContextGL()->NumTextures()); // Drop the reference to the context provider on the compositor thread. display_context_provider_ = nullptr; @@ -869,7 +869,7 @@ class LayerTreeHostCopyRequestTestDeleteTexture // been allocated. EXPECT_FALSE(result_); num_textures_without_readback_ = - display_context_provider_->TestContext3d()->NumTextures(); + display_context_provider_->TestContextGL()->NumTextures(); // Request a copy of the layer. This will use another texture. MainThreadTaskRunner()->PostTask( @@ -886,7 +886,7 @@ class LayerTreeHostCopyRequestTestDeleteTexture case 2: // We did a readback, so there will be a readback texture around now. num_textures_after_readback_ = - display_context_provider_->TestContext3d()->NumTextures(); + display_context_provider_->TestContextGL()->NumTextures(); EXPECT_LT(num_textures_without_readback_, num_textures_after_readback_); // Now destroy the CopyOutputResult, releasing the texture inside back @@ -981,14 +981,14 @@ class LayerTreeHostCopyRequestTestCountTextures // The first frame has been drawn, so textures for drawing have been // allocated. num_textures_without_readback_ = - display_context_provider_->TestContext3d()->NumTextures(); + display_context_provider_->TestContextGL()->NumTextures(); break; case 1: // We did a readback, so there will be a readback texture around now. num_textures_with_readback_ = - display_context_provider_->TestContext3d()->NumTextures(); + display_context_provider_->TestContextGL()->NumTextures(); waited_sync_token_after_readback_ = - display_context_provider_->TestContext3d() + display_context_provider_->TestContextGL() ->last_waited_sync_token(); // End the test after main thread has a chance to hear about the diff --git a/chromium/cc/trees/layer_tree_host_unittest_occlusion.cc b/chromium/cc/trees/layer_tree_host_unittest_occlusion.cc index d979e0b46ea..41e9c4c0283 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_occlusion.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_occlusion.cc @@ -152,7 +152,7 @@ class LayerTreeHostOcclusionTestDrawPropertiesOnMask make_surface_bigger->SetIsDrawable(true); child_->AddChild(make_surface_bigger); - scoped_refptr<Layer> mask = PictureLayer::Create(&client_); + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client_); mask->SetBounds(gfx::Size(30, 40)); mask->SetIsDrawable(true); child_->SetMaskLayer(mask.get()); @@ -225,7 +225,7 @@ class LayerTreeHostOcclusionTestDrawPropertiesOnScaledMask grand_child->SetIsDrawable(true); child_->AddChild(grand_child); - scoped_refptr<Layer> mask = PictureLayer::Create(&client_); + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client_); mask->SetBounds(gfx::Size(30, 40)); mask->SetIsDrawable(true); child_->SetMaskLayer(mask.get()); diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index dc45563a2c7..d5fa8c48b57 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -108,11 +108,11 @@ class LayerTreeHostScrollTestScrollSimple : public LayerTreeHostScrollTest { LayerTreeHostClient::VisualStateUpdate requested_update) override { Layer* scroll_layer = layer_tree_host()->outer_viewport_scroll_layer(); if (!layer_tree_host()->SourceFrameNumber()) { - EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer->CurrentScrollOffset()); } else { - EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(initial_scroll_, - scroll_amount_), - scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ( + gfx::ScrollOffsetWithDelta(initial_scroll_, scroll_amount_), + scroll_layer->CurrentScrollOffset()); // Pretend like Javascript updated the scroll position itself. scroll_layer->SetScrollOffset(second_scroll_); @@ -180,14 +180,13 @@ class LayerTreeHostScrollTestScrollMultipleRedraw void BeginCommitOnThread(LayerTreeHostImpl* impl) override { switch (layer_tree_host()->SourceFrameNumber()) { case 0: - EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer_->scroll_offset()); + EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer_->CurrentScrollOffset()); break; case 1: case 2: - EXPECT_VECTOR_EQ( - gfx::ScrollOffsetWithDelta(initial_scroll_, - scroll_amount_ + scroll_amount_), - scroll_layer_->scroll_offset()); + EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta( + initial_scroll_, scroll_amount_ + scroll_amount_), + scroll_layer_->CurrentScrollOffset()); break; } } @@ -214,15 +213,14 @@ class LayerTreeHostScrollTestScrollMultipleRedraw EXPECT_VECTOR_EQ(scroll_amount_ + scroll_amount_, ScrollDelta(scroll_layer)); - EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer_->scroll_offset()); + EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer_->CurrentScrollOffset()); PostSetNeedsCommitToMainThread(); } else if (impl->active_tree()->source_frame_number() == 1) { // Third or later draw after second commit. EXPECT_GE(impl->SourceAnimationFrameNumberForTesting(), 3u); - EXPECT_VECTOR_EQ( - gfx::ScrollOffsetWithDelta(initial_scroll_, - scroll_amount_ + scroll_amount_), - scroll_layer_->scroll_offset()); + EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta( + initial_scroll_, scroll_amount_ + scroll_amount_), + scroll_layer_->CurrentScrollOffset()); EndTest(); } } @@ -284,7 +282,8 @@ class LayerTreeHostScrollTestScrollAbortedCommit // This will not be aborted because of the initial prop changes. EXPECT_EQ(0, num_impl_scrolls_); EXPECT_EQ(0, layer_tree_host()->SourceFrameNumber()); - EXPECT_VECTOR_EQ(initial_scroll_, root_scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ(initial_scroll_, + root_scroll_layer->CurrentScrollOffset()); EXPECT_EQ(1.f, layer_tree_host()->page_scale_factor()); break; case 2: @@ -294,7 +293,7 @@ class LayerTreeHostScrollTestScrollAbortedCommit EXPECT_EQ(1, layer_tree_host()->SourceFrameNumber()); EXPECT_VECTOR_EQ( gfx::ScrollOffsetWithDelta(initial_scroll_, impl_scroll_), - root_scroll_layer->scroll_offset()); + root_scroll_layer->CurrentScrollOffset()); EXPECT_EQ(impl_scale_, layer_tree_host()->page_scale_factor()); PostSetNeedsRedrawToMainThread(); break; @@ -303,14 +302,13 @@ class LayerTreeHostScrollTestScrollAbortedCommit EXPECT_EQ(2, num_impl_scrolls_); // The source frame number still increases even with the abort. EXPECT_EQ(2, layer_tree_host()->SourceFrameNumber()); - EXPECT_VECTOR_EQ( - gfx::ScrollOffsetWithDelta(initial_scroll_, - impl_scroll_ + impl_scroll_), - root_scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta( + initial_scroll_, impl_scroll_ + impl_scroll_), + root_scroll_layer->CurrentScrollOffset()); EXPECT_EQ(impl_scale_ * impl_scale_, layer_tree_host()->page_scale_factor()); root_scroll_layer->SetScrollOffset(gfx::ScrollOffsetWithDelta( - root_scroll_layer->scroll_offset(), second_main_scroll_)); + root_scroll_layer->CurrentScrollOffset(), second_main_scroll_)); break; case 4: // This commit will also be aborted. @@ -319,7 +317,7 @@ class LayerTreeHostScrollTestScrollAbortedCommit gfx::Vector2dF delta = impl_scroll_ + impl_scroll_ + impl_scroll_ + second_main_scroll_; EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(initial_scroll_, delta), - root_scroll_layer->scroll_offset()); + root_scroll_layer->CurrentScrollOffset()); // End the test by drawing to verify this commit is also aborted. PostSetNeedsRedrawToMainThread(); @@ -635,7 +633,7 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { } void DidScroll(const gfx::ScrollOffset& offset, const ElementId& element_id) { - final_scroll_offset_ = expected_scroll_layer_->scroll_offset(); + final_scroll_offset_ = expected_scroll_layer_->CurrentScrollOffset(); EXPECT_VECTOR_EQ(offset, final_scroll_offset_); EXPECT_EQ(element_id, expected_scroll_layer_->element_id()); } @@ -647,25 +645,25 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { void UpdateLayerTreeHost( LayerTreeHostClient::VisualStateUpdate requested_update) override { EXPECT_VECTOR_EQ(gfx::Vector2d(), - expected_no_scroll_layer_->scroll_offset()); + expected_no_scroll_layer_->CurrentScrollOffset()); switch (layer_tree_host()->SourceFrameNumber()) { case 0: EXPECT_VECTOR_EQ(initial_offset_, - expected_scroll_layer_->scroll_offset()); + expected_scroll_layer_->CurrentScrollOffset()); break; case 1: EXPECT_VECTOR_EQ( gfx::ScrollOffsetWithDelta(initial_offset_, scroll_amount_), - expected_scroll_layer_->scroll_offset()); + expected_scroll_layer_->CurrentScrollOffset()); // Pretend like Javascript updated the scroll position itself. expected_scroll_layer_->SetScrollOffset(javascript_scroll_); break; case 2: - EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(javascript_scroll_, - scroll_amount_), - expected_scroll_layer_->scroll_offset()); + EXPECT_VECTOR_EQ( + gfx::ScrollOffsetWithDelta(javascript_scroll_, scroll_amount_), + expected_scroll_layer_->CurrentScrollOffset()); break; } } @@ -852,10 +850,10 @@ class LayerTreeHostScrollTestSimple : public LayerTreeHostScrollTest { LayerTreeHostClient::VisualStateUpdate requested_update) override { Layer* scroll_layer = layer_tree_host()->outer_viewport_scroll_layer(); if (!layer_tree_host()->SourceFrameNumber()) { - EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer->CurrentScrollOffset()); } else { EXPECT_VECTOR_EQ( - scroll_layer->scroll_offset(), + scroll_layer->CurrentScrollOffset(), gfx::ScrollOffsetWithDelta(initial_scroll_, impl_thread_scroll1_)); // Pretend like Javascript updated the scroll position itself with a @@ -1301,12 +1299,6 @@ class ThreadCheckingInputHandlerClient : public InputHandlerClient { ADD_FAILURE() << "Animate called on wrong thread"; } - void MainThreadHasStoppedFlinging() override { - if (!task_runner_->BelongsToCurrentThread()) - ADD_FAILURE() << "MainThreadHasStoppedFlinging called on wrong thread"; - *received_stop_flinging_ = true; - } - void ReconcileElasticOverscrollAndRootScroll() override { if (!task_runner_->BelongsToCurrentThread()) { ADD_FAILURE() << "ReconcileElasticOverscrollAndRootScroll called on " @@ -1338,52 +1330,6 @@ class ThreadCheckingInputHandlerClient : public InputHandlerClient { bool* received_stop_flinging_; }; -void BindInputHandlerOnCompositorThread( - const base::WeakPtr<InputHandler>& input_handler, - ThreadCheckingInputHandlerClient* client) { - input_handler->BindToClient(client, false); -} - -TEST(LayerTreeHostFlingTest, DidStopFlingingThread) { - base::Thread impl_thread("cc"); - ASSERT_TRUE(impl_thread.Start()); - ASSERT_TRUE(impl_thread.task_runner()); - - bool received_stop_flinging = false; - LayerTreeSettings settings; - - StubLayerTreeHostClient layer_tree_host_client; - TestTaskGraphRunner task_graph_runner; - - auto animation_host = AnimationHost::CreateForTesting(ThreadInstance::MAIN); - - LayerTreeHost::InitParams params; - params.client = &layer_tree_host_client; - params.task_graph_runner = &task_graph_runner; - params.settings = &settings; - params.main_task_runner = base::ThreadTaskRunnerHandle::Get(); - params.mutator_host = animation_host.get(); - params.ukm_recorder_factory = std::make_unique<TestUkmRecorderFactory>(); - std::unique_ptr<LayerTreeHost> layer_tree_host = - LayerTreeHost::CreateThreaded(impl_thread.task_runner(), ¶ms); - - ThreadCheckingInputHandlerClient input_handler_client( - impl_thread.task_runner().get(), &received_stop_flinging); - impl_thread.task_runner()->PostTask( - FROM_HERE, base::BindOnce(&BindInputHandlerOnCompositorThread, - layer_tree_host->GetInputHandler(), - base::Unretained(&input_handler_client))); - - layer_tree_host->DidStopFlinging(); - - animation_host->SetMutatorHostClient(nullptr); - layer_tree_host = nullptr; - animation_host = nullptr; - - impl_thread.Stop(); - EXPECT_TRUE(received_stop_flinging); -} - class LayerTreeHostScrollTestLayerStructureChange : public LayerTreeHostScrollTest { public: @@ -1554,19 +1500,19 @@ class LayerTreeHostScrollTestScrollMFBA : public LayerTreeHostScrollTest { Layer* scroll_layer = layer_tree_host()->outer_viewport_scroll_layer(); switch (layer_tree_host()->SourceFrameNumber()) { case 0: - EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer->CurrentScrollOffset()); break; case 1: EXPECT_VECTOR_EQ( gfx::ScrollOffsetWithDelta(initial_scroll_, scroll_amount_), - scroll_layer->scroll_offset()); + scroll_layer->CurrentScrollOffset()); // Pretend like Javascript updated the scroll position itself. scroll_layer->SetScrollOffset(second_scroll_); break; case 2: // Third frame does not see a scroll delta because we only did one // scroll for the second and third frames. - EXPECT_VECTOR_EQ(second_scroll_, scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ(second_scroll_, scroll_layer->CurrentScrollOffset()); // Pretend like Javascript updated the scroll position itself. scroll_layer->SetScrollOffset(third_scroll_); break; @@ -1682,7 +1628,8 @@ class LayerTreeHostScrollTestScrollAbortedCommitMFBA // This will not be aborted because of the initial prop changes. EXPECT_EQ(0, num_impl_scrolls_); EXPECT_EQ(0, layer_tree_host()->SourceFrameNumber()); - EXPECT_VECTOR_EQ(initial_scroll_, root_scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ(initial_scroll_, + root_scroll_layer->CurrentScrollOffset()); break; case 2: // This commit will not be aborted because of the scroll change. @@ -1690,9 +1637,9 @@ class LayerTreeHostScrollTestScrollAbortedCommitMFBA EXPECT_EQ(1, layer_tree_host()->SourceFrameNumber()); EXPECT_VECTOR_EQ( gfx::ScrollOffsetWithDelta(initial_scroll_, impl_scroll_), - root_scroll_layer->scroll_offset()); + root_scroll_layer->CurrentScrollOffset()); root_scroll_layer->SetScrollOffset(gfx::ScrollOffsetWithDelta( - root_scroll_layer->scroll_offset(), second_main_scroll_)); + root_scroll_layer->CurrentScrollOffset(), second_main_scroll_)); break; case 3: { // This commit will be aborted. @@ -1702,7 +1649,7 @@ class LayerTreeHostScrollTestScrollAbortedCommitMFBA gfx::Vector2dF delta = impl_scroll_ + impl_scroll_ + second_main_scroll_; EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(initial_scroll_, delta), - root_scroll_layer->scroll_offset()); + root_scroll_layer->CurrentScrollOffset()); break; } case 4: { @@ -1712,7 +1659,7 @@ class LayerTreeHostScrollTestScrollAbortedCommitMFBA gfx::Vector2dF delta = impl_scroll_ + impl_scroll_ + impl_scroll_ + second_main_scroll_; EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(initial_scroll_, delta), - root_scroll_layer->scroll_offset()); + root_scroll_layer->CurrentScrollOffset()); break; } } @@ -1869,7 +1816,6 @@ class MockInputHandlerClient : public InputHandlerClient { void WillShutdown() override {} void Animate(base::TimeTicks) override {} - void MainThreadHasStoppedFlinging() override {} void UpdateRootLayerStateForSynchronousInputHandler( const gfx::ScrollOffset& total_scroll_offset, const gfx::ScrollOffset& max_scroll_offset, @@ -1905,7 +1851,7 @@ class LayerTreeHostScrollTestElasticOverscroll void BindInputHandler(base::WeakPtr<InputHandler> input_handler) { DCHECK(task_runner_provider()->IsImplThread()); - input_handler->BindToClient(&input_handler_client_, false); + input_handler->BindToClient(&input_handler_client_); scroll_elasticity_helper_ = input_handler->CreateScrollElasticityHelper(); DCHECK(scroll_elasticity_helper_); } @@ -2058,11 +2004,11 @@ class LayerTreeHostScrollTestPropertyTreeUpdate LayerTreeHostClient::VisualStateUpdate requested_update) override { Layer* scroll_layer = layer_tree_host()->inner_viewport_scroll_layer(); if (layer_tree_host()->SourceFrameNumber() == 0) { - EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer->scroll_offset()); + EXPECT_VECTOR_EQ(initial_scroll_, scroll_layer->CurrentScrollOffset()); } else { EXPECT_VECTOR_EQ( gfx::ScrollOffsetWithDelta(initial_scroll_, scroll_amount_), - scroll_layer->scroll_offset()); + scroll_layer->CurrentScrollOffset()); scroll_layer->SetScrollOffset(second_scroll_); scroll_layer->SetOpacity(0.5f); } @@ -2142,7 +2088,7 @@ class LayerTreeHostScrollTestImplSideInvalidation gfx::ScrollOffset delta_to_send = outer_viewport_offsets_[2] - outer_viewport_offsets_[1]; outer_viewport_layer->SetScrollOffset( - outer_viewport_layer->scroll_offset() + delta_to_send); + outer_viewport_layer->CurrentScrollOffset() + delta_to_send); } break; case 2: // Let the commit abort for the second set of deltas. diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index a6b06c59e26..cf474aceec6 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -90,7 +90,7 @@ LayerTreeImpl::LayerTreeImpl( needs_update_draw_properties_(true), scrollbar_geometries_need_update_(false), needs_full_tree_sync_(true), - needs_surface_ids_sync_(false), + needs_surface_ranges_sync_(false), next_activation_forces_redraw_(false), has_ever_been_drawn_(false), handle_visibility_changed_(false), @@ -136,6 +136,13 @@ void LayerTreeImpl::ReleaseResources() { } } +void LayerTreeImpl::OnPurgeMemory() { + if (!LayerListIsEmpty()) { + LayerTreeHostCommon::CallFunctionForEveryLayer( + this, [](LayerImpl* layer) { layer->OnPurgeMemory(); }); + } +} + void LayerTreeImpl::ReleaseTileResources() { if (!LayerListIsEmpty()) { LayerTreeHostCommon::CallFunctionForEveryLayer( @@ -228,9 +235,10 @@ void LayerTreeImpl::UpdateScrollbarGeometries() { scrolling_size = gfx::SizeF(scroll_node->bounds); } else { // Add offset and bounds contribution of inner viewport. - current_offset += InnerViewportScrollLayer()->CurrentScrollOffset(); - gfx::SizeF inner_viewport_bounds(scroll_tree.container_bounds( - InnerViewportScrollLayer()->scroll_tree_index())); + current_offset += scroll_tree.current_scroll_offset( + InnerViewportScrollNode()->element_id); + gfx::SizeF inner_viewport_bounds( + scroll_tree.container_bounds(InnerViewportScrollNode()->id)); viewport_bounds.SetToMin(inner_viewport_bounds); } viewport_bounds.Scale(1 / current_page_scale_factor()); @@ -249,7 +257,7 @@ void LayerTreeImpl::UpdateScrollbarGeometries() { } if (is_viewport_scrollbar) { scrollbar->SetVerticalAdjust( - InnerViewportContainerLayer()->ViewportBoundsDelta().y()); + property_trees_.inner_viewport_container_bounds_delta().y()); } } } @@ -339,9 +347,12 @@ bool LayerTreeImpl::IsRootLayer(const LayerImpl* layer) const { gfx::ScrollOffset LayerTreeImpl::TotalScrollOffset() const { gfx::ScrollOffset offset; + auto& scroll_tree = property_trees()->scroll_tree; - if (InnerViewportScrollLayer()) - offset += InnerViewportScrollLayer()->CurrentScrollOffset(); + if (InnerViewportScrollNode()) { + offset += scroll_tree.current_scroll_offset( + InnerViewportScrollNode()->element_id); + } if (OuterViewportScrollLayer()) offset += OuterViewportScrollLayer()->CurrentScrollOffset(); @@ -351,12 +362,13 @@ gfx::ScrollOffset LayerTreeImpl::TotalScrollOffset() const { gfx::ScrollOffset LayerTreeImpl::TotalMaxScrollOffset() const { gfx::ScrollOffset offset; + const ScrollTree& scroll_tree = property_trees()->scroll_tree; - if (InnerViewportScrollLayer()) - offset += InnerViewportScrollLayer()->MaxScrollOffset(); + if (auto* inner_node = InnerViewportScrollNode()) + offset += scroll_tree.MaxScrollOffset(inner_node->id); - if (OuterViewportScrollLayer()) - offset += OuterViewportScrollLayer()->MaxScrollOffset(); + if (auto* outer_node = OuterViewportScrollNode()) + offset += scroll_tree.MaxScrollOffset(outer_node->id); return offset; } @@ -420,12 +432,12 @@ void LayerTreeImpl::PushPropertyTreesTo(LayerTreeImpl* target_tree) { target_tree->SetCurrentlyScrollingNode(scrolling_node); } -void LayerTreeImpl::PushSurfaceIdsTo(LayerTreeImpl* target_tree) { - if (needs_surface_ids_sync()) { - target_tree->ClearSurfaceLayerIds(); - target_tree->SetSurfaceLayerIds(SurfaceLayerIds()); +void LayerTreeImpl::PushSurfaceRangesTo(LayerTreeImpl* target_tree) { + if (needs_surface_ranges_sync()) { + target_tree->ClearSurfaceRanges(); + target_tree->SetSurfaceRanges(SurfaceRanges()); // Reset for next update - set_needs_surface_ids_sync(false); + set_needs_surface_ranges_sync(false); } } @@ -434,7 +446,7 @@ 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); - PushSurfaceIdsTo(target_tree); + PushSurfaceRangesTo(target_tree); target_tree->property_trees()->scroll_tree.PushScrollUpdatesFromPendingTree( &property_trees_, target_tree); @@ -468,7 +480,6 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { target_tree->elastic_overscroll()->PushPendingToActive(); target_tree->set_content_source_id(content_source_id()); - target_tree->set_request_presentation_time(request_presentation_time()); if (TakeNewLocalSurfaceIdRequest()) target_tree->RequestNewLocalSurfaceId(); @@ -510,6 +521,7 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { // Note: this needs to happen after SetPropertyTrees. target_tree->HandleTickmarksVisibilityChange(); target_tree->HandleScrollbarShowRequestsFromMain(); + target_tree->AddPresentationCallbacks(std::move(presentation_callbacks_)); } void LayerTreeImpl::HandleTickmarksVisibilityChange() { @@ -596,52 +608,47 @@ LayerImplList::reverse_iterator LayerTreeImpl::rend() { return layer_list_.rend(); } -LayerImpl* LayerTreeImpl::LayerByElementId(ElementId element_id) const { - auto iter = element_layers_map_.find(element_id); - return (iter == element_layers_map_.end()) ? nullptr - : LayerById(iter->second); +bool LayerTreeImpl::IsElementInLayerList(ElementId element_id) const { + return elements_in_layer_list_.count(element_id); } ElementListType LayerTreeImpl::GetElementTypeForAnimation() const { return IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; } -void LayerTreeImpl::AddToElementMap(LayerImpl* layer) { - ElementId element_id = layer->element_id(); +void LayerTreeImpl::AddToElementLayerList(ElementId element_id) { if (!element_id) return; - TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("layer-element"), - "LayerTreeImpl::AddToElementMap", "element", - element_id.AsValue().release(), "layer_id", layer->id()); + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("layer-element"), + "LayerTreeImpl::AddToElementLayerList", "element", + element_id.AsValue().release()); #if DCHECK_IS_ON() - LayerImpl* existing_layer = LayerByElementId(element_id); bool element_id_collision_detected = - existing_layer && existing_layer != layer; + elements_in_layer_list_.count(element_id); DCHECK(!element_id_collision_detected); #endif - element_layers_map_[element_id] = layer->id(); + elements_in_layer_list_.insert(element_id); host_impl_->mutator_host()->RegisterElement(element_id, GetElementTypeForAnimation()); } -void LayerTreeImpl::RemoveFromElementMap(LayerImpl* layer) { - if (!layer->element_id()) +void LayerTreeImpl::RemoveFromElementLayerList(ElementId element_id) { + if (!element_id) return; - TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("layer-element"), - "LayerTreeImpl::RemoveFromElementMap", "element", - layer->element_id().AsValue().release(), "layer_id", - layer->id()); + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("layer-element"), + "LayerTreeImpl::RemoveFromElementLayerList", "element", + element_id.AsValue().release()); - host_impl_->mutator_host()->UnregisterElement(layer->element_id(), + host_impl_->mutator_host()->UnregisterElement(element_id, GetElementTypeForAnimation()); - element_layers_map_.erase(layer->element_id()); + elements_in_layer_list_.erase(element_id); } void LayerTreeImpl::SetTransformMutated(ElementId element_id, @@ -671,6 +678,20 @@ void LayerTreeImpl::SetFilterMutated(ElementId element_id, set_needs_update_draw_properties(); } +void LayerTreeImpl::AddPresentationCallbacks( + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks) { + std::copy(std::make_move_iterator(callbacks.begin()), + std::make_move_iterator(callbacks.end()), + std::back_inserter(presentation_callbacks_)); +} + +std::vector<LayerTreeHost::PresentationTimeCallback> +LayerTreeImpl::TakePresentationCallbacks() { + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks; + callbacks.swap(presentation_callbacks_); + return callbacks; +} + ScrollNode* LayerTreeImpl::CurrentlyScrollingNode() { DCHECK(IsActiveTree()); return property_trees_.scroll_tree.CurrentlyScrollingNode(); @@ -797,19 +818,47 @@ void LayerTreeImpl::UpdateTransformAnimation(ElementId element_id, } } +TransformNode* LayerTreeImpl::PageScaleTransformNode() { + auto* page_scale = PageScaleLayer(); + if (!page_scale) + return nullptr; + + return property_trees()->transform_tree.Node( + page_scale->transform_tree_index()); +} + +void LayerTreeImpl::UpdatePageScaleNode() { + if (!PageScaleTransformNode()) { + DCHECK(layer_list_.empty() || current_page_scale_factor() == 1); + return; + } + + // When the page scale layer is also the root layer (this happens in the UI + // compositor), the node should also store the combined scale factor and not + // just the page scale factor. + // TODO(bokan): Need to implement this behavior for + // BlinkGeneratedPropertyTrees. i.e. (no page scale layer). + float device_scale_factor_for_page_scale_layer = 1.f; + gfx::Transform device_transform_for_page_scale_layer; + if (IsRootLayer(PageScaleLayer())) { + DCHECK(!settings().use_layer_lists); + device_transform_for_page_scale_layer = host_impl_->DrawTransform(); + device_scale_factor_for_page_scale_layer = device_scale_factor(); + } + + draw_property_utils::UpdatePageScaleFactor( + property_trees(), PageScaleTransformNode(), current_page_scale_factor(), + device_scale_factor_for_page_scale_layer, + device_transform_for_page_scale_layer); +} + void LayerTreeImpl::SetPageScaleOnActiveTree(float active_page_scale) { DCHECK(IsActiveTree()); DCHECK(lifecycle().AllowsPropertyTreeAccess()); if (page_scale_factor()->SetCurrent( ClampPageScaleFactorToLimits(active_page_scale))) { DidUpdatePageScale(); - if (PageScaleLayer()) { - draw_property_utils::UpdatePageScaleFactor( - property_trees(), PageScaleLayer(), current_page_scale_factor(), - device_scale_factor(), host_impl_->DrawTransform()); - } else { - DCHECK(layer_list_.empty() || active_page_scale == 1); - } + UpdatePageScaleNode(); } } @@ -843,15 +892,8 @@ void LayerTreeImpl::PushPageScaleFactorAndLimits(const float* page_scale_factor, DidUpdatePageScale(); DCHECK(lifecycle().AllowsPropertyTreeAccess()); - if (page_scale_factor) { - if (PageScaleLayer()) { - draw_property_utils::UpdatePageScaleFactor( - property_trees(), PageScaleLayer(), current_page_scale_factor(), - device_scale_factor(), host_impl_->DrawTransform()); - } else { - DCHECK(layer_list_.empty() || *page_scale_factor == 1); - } - } + if (page_scale_factor) + UpdatePageScaleNode(); } void LayerTreeImpl::set_browser_controls_shrink_blink_size(bool shrink) { @@ -999,10 +1041,11 @@ const SyncedProperty<ScaleGroup>* LayerTreeImpl::page_scale_factor() const { } gfx::SizeF LayerTreeImpl::ScrollableViewportSize() const { - if (!InnerViewportContainerLayer()) + auto* inner_node = InnerViewportScrollNode(); + if (!inner_node) return gfx::SizeF(); - return gfx::ScaleSize(InnerViewportContainerLayer()->BoundsForScrolling(), + return gfx::ScaleSize(gfx::SizeF(inner_node->container_bounds), 1.0f / current_page_scale_factor()); } @@ -1051,6 +1094,21 @@ void LayerTreeImpl::ClearViewportLayers() { SetViewportLayersFromIds(ViewportLayerIds()); } +const ScrollNode* LayerTreeImpl::InnerViewportScrollNode() const { + auto* inner_scroll = InnerViewportScrollLayer(); + if (!inner_scroll) + return nullptr; + + return property_trees()->scroll_tree.Node(inner_scroll->scroll_tree_index()); +} + +const ScrollNode* LayerTreeImpl::OuterViewportScrollNode() const { + if (!OuterViewportScrollLayer()) + return nullptr; + return property_trees()->scroll_tree.Node( + OuterViewportScrollLayer()->scroll_tree_index()); +} + // For unit tests, we use the layer's id as its element id. static void SetElementIdForTesting(LayerImpl* layer) { layer->SetElementId(LayerIdToElementIdForTesting(layer->id())); @@ -1105,7 +1163,7 @@ bool LayerTreeImpl::UpdateDrawProperties( elastic_overscroll()->Current(IsActiveTree()), OverscrollElasticityLayer(), max_texture_size(), settings().layer_transforms_should_scale_layer_contents, - &render_surface_list_, &property_trees_); + &render_surface_list_, &property_trees_, PageScaleTransformNode()); LayerTreeHostCommon::CalculateDrawProperties(&inputs); if (const char* client_name = GetClientNameForMetrics()) { UMA_HISTOGRAM_COUNTS( @@ -1277,20 +1335,20 @@ LayerImpl* LayerTreeImpl::LayerById(int id) const { return iter != layer_id_map_.end() ? iter->second : nullptr; } -void LayerTreeImpl::SetSurfaceLayerIds( - const base::flat_set<viz::SurfaceId>& surface_layer_ids) { - DCHECK(surface_layer_ids_.empty()); - surface_layer_ids_ = surface_layer_ids; - needs_surface_ids_sync_ = true; +void LayerTreeImpl::SetSurfaceRanges( + const base::flat_set<viz::SurfaceRange> surface_ranges) { + DCHECK(surface_layer_ranges_.empty()); + surface_layer_ranges_ = std::move(surface_ranges); + needs_surface_ranges_sync_ = true; } -const base::flat_set<viz::SurfaceId>& LayerTreeImpl::SurfaceLayerIds() const { - return surface_layer_ids_; +const base::flat_set<viz::SurfaceRange>& LayerTreeImpl::SurfaceRanges() const { + return surface_layer_ranges_; } -void LayerTreeImpl::ClearSurfaceLayerIds() { - surface_layer_ids_.clear(); - needs_surface_ids_sync_ = true; +void LayerTreeImpl::ClearSurfaceRanges() { + surface_layer_ranges_.clear(); + needs_surface_ranges_sync_ = true; } void LayerTreeImpl::AddLayerShouldPushProperties(LayerImpl* layer) { @@ -1410,7 +1468,7 @@ viz::ContextProvider* LayerTreeImpl::context_provider() const { return host_impl_->layer_tree_frame_sink()->context_provider(); } -LayerTreeResourceProvider* LayerTreeImpl::resource_provider() const { +viz::ClientResourceProvider* LayerTreeImpl::resource_provider() const { return host_impl_->resource_provider(); } @@ -1588,19 +1646,30 @@ void LayerTreeImpl::AsValueInto(base::trace_event::TracedValue* state) const { } bool LayerTreeImpl::DistributeRootScrollOffset( - const gfx::ScrollOffset& root_offset) { - if (!InnerViewportScrollLayer() || !OuterViewportScrollLayer()) + const gfx::ScrollOffset& desired_root_offset) { + if (!InnerViewportScrollNode() || !OuterViewportScrollLayer()) return false; + gfx::ScrollOffset root_offset = desired_root_offset; + ScrollTree& scroll_tree = property_trees()->scroll_tree; + // If we get here, we have both inner/outer viewports, and need to distribute // the scroll offset between them. gfx::ScrollOffset inner_viewport_offset = - InnerViewportScrollLayer()->CurrentScrollOffset(); + scroll_tree.current_scroll_offset(InnerViewportScrollNode()->element_id); gfx::ScrollOffset outer_viewport_offset = OuterViewportScrollLayer()->CurrentScrollOffset(); + DCHECK(inner_viewport_offset + outer_viewport_offset == TotalScrollOffset()); + + // Setting the root scroll offset is driven by user actions so prevent + // it if it is not user scrollable in certain directions. + if (!InnerViewportScrollNode()->user_scrollable_horizontal) + root_offset.set_x(inner_viewport_offset.x() + outer_viewport_offset.x()); + + if (!InnerViewportScrollNode()->user_scrollable_vertical) + root_offset.set_y(inner_viewport_offset.y() + outer_viewport_offset.y()); // It may be nothing has changed. - DCHECK(inner_viewport_offset + outer_viewport_offset == TotalScrollOffset()); if (inner_viewport_offset + outer_viewport_offset == root_offset) return false; @@ -1613,7 +1682,9 @@ bool LayerTreeImpl::DistributeRootScrollOffset( OuterViewportScrollLayer()->SetCurrentScrollOffset(outer_viewport_offset); inner_viewport_offset = root_offset - outer_viewport_offset; - InnerViewportScrollLayer()->SetCurrentScrollOffset(inner_viewport_offset); + if (scroll_tree.SetScrollOffset(InnerViewportScrollNode()->element_id, + inner_viewport_offset)) + DidUpdateScrollOffset(InnerViewportScrollNode()->element_id); return true; } @@ -1632,15 +1703,8 @@ void LayerTreeImpl::QueuePinnedSwapPromise( void LayerTreeImpl::PassSwapPromises( std::vector<std::unique_ptr<SwapPromise>> new_swap_promises) { - for (auto& swap_promise : swap_promise_list_) { - if (swap_promise->DidNotSwap(SwapPromise::SWAP_FAILS) == - SwapPromise::DidNotSwapAction::KEEP_ACTIVE) { - // |swap_promise| must remain active, so place it in |new_swap_promises| - // in order to keep it alive and active. - new_swap_promises.push_back(std::move(swap_promise)); - } - } - swap_promise_list_.clear(); + for (auto& swap_promise : swap_promise_list_) + swap_promise->DidNotSwap(SwapPromise::SWAP_FAILS); swap_promise_list_.swap(new_swap_promises); } @@ -1651,13 +1715,11 @@ void LayerTreeImpl::AppendSwapPromises( new_swap_promises.clear(); } -void LayerTreeImpl::FinishSwapPromises( - viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator) { +void LayerTreeImpl::FinishSwapPromises(viz::CompositorFrameMetadata* metadata) { for (const auto& swap_promise : swap_promise_list_) - swap_promise->WillSwap(metadata, frame_token_allocator); + swap_promise->WillSwap(metadata); for (const auto& swap_promise : pinned_swap_promise_list_) - swap_promise->WillSwap(metadata, frame_token_allocator); + swap_promise->WillSwap(metadata); } void LayerTreeImpl::ClearSwapPromises() { @@ -1670,30 +1732,13 @@ void LayerTreeImpl::ClearSwapPromises() { } void LayerTreeImpl::BreakSwapPromises(SwapPromise::DidNotSwapReason reason) { - { - std::vector<std::unique_ptr<SwapPromise>> persistent_swap_promises; - for (auto& swap_promise : swap_promise_list_) { - if (swap_promise->DidNotSwap(reason) == - SwapPromise::DidNotSwapAction::KEEP_ACTIVE) { - persistent_swap_promises.push_back(std::move(swap_promise)); - } - } - // |persistent_swap_promises| must remain active even when swap fails. - swap_promise_list_ = std::move(persistent_swap_promises); - } - - { - std::vector<std::unique_ptr<SwapPromise>> persistent_swap_promises; - for (auto& swap_promise : pinned_swap_promise_list_) { - if (swap_promise->DidNotSwap(reason) == - SwapPromise::DidNotSwapAction::KEEP_ACTIVE) { - persistent_swap_promises.push_back(std::move(swap_promise)); - } - } + for (auto& swap_promise : swap_promise_list_) + swap_promise->DidNotSwap(reason); + swap_promise_list_.clear(); - // |persistent_swap_promises| must remain active even when swap fails. - pinned_swap_promise_list_ = std::move(persistent_swap_promises); - } + for (auto& swap_promise : pinned_swap_promise_list_) + swap_promise->DidNotSwap(reason); + pinned_swap_promise_list_.clear(); } void LayerTreeImpl::DidModifyTilePriorities() { @@ -1863,31 +1908,6 @@ static bool PointHitsRect( return true; } -static bool PointHitsRegion(const gfx::PointF& screen_space_point, - const gfx::Transform& screen_space_transform, - const Region& layer_space_region) { - // If the transform is not invertible, then assume that this point doesn't hit - // this region. - gfx::Transform inverse_screen_space_transform( - gfx::Transform::kSkipInitialization); - if (!screen_space_transform.GetInverse(&inverse_screen_space_transform)) - return false; - - // Transform the hit test point from screen space to the local space of the - // given region. - bool clipped = false; - gfx::PointF hit_test_point_in_layer_space = MathUtil::ProjectPoint( - inverse_screen_space_transform, screen_space_point, &clipped); - - // If ProjectPoint could not project to a valid value, then we assume that - // this point doesn't hit this region. - if (clipped) - return false; - - return layer_space_region.Contains( - gfx::ToRoundedPoint(hit_test_point_in_layer_space)); -} - static bool PointIsClippedByAncestorClipNode( const gfx::PointF& screen_space_point, const LayerImpl* layer) { @@ -1933,6 +1953,41 @@ static bool PointIsClippedBySurfaceOrClipRect( return PointIsClippedByAncestorClipNode(screen_space_point, layer); } +static bool PointHitsRegion(const gfx::PointF& screen_space_point, + const gfx::Transform& screen_space_transform, + const Region& layer_space_region, + const LayerImpl* layer_impl) { + if (layer_space_region.IsEmpty()) + return false; + + // If the transform is not invertible, then assume that this point doesn't hit + // this region. + gfx::Transform inverse_screen_space_transform( + gfx::Transform::kSkipInitialization); + if (!screen_space_transform.GetInverse(&inverse_screen_space_transform)) + return false; + + // Transform the hit test point from screen space to the local space of the + // given region. + bool clipped = false; + gfx::PointF hit_test_point_in_layer_space = MathUtil::ProjectPoint( + inverse_screen_space_transform, screen_space_point, &clipped); + + // If ProjectPoint could not project to a valid value, then we assume that + // this point doesn't hit this region. + if (clipped) + return false; + + // We need to walk up the parents to ensure that the layer is not clipped in + // such a way that it is impossible for the point to hit the layer. + if (layer_impl && + PointIsClippedBySurfaceOrClipRect(screen_space_point, layer_impl)) + return false; + + return layer_space_region.Contains( + gfx::ToRoundedPoint(hit_test_point_in_layer_space)); +} + static bool PointHitsLayer(const LayerImpl* layer, const gfx::PointF& screen_space_point, float* distance_to_intersection) { @@ -2043,44 +2098,49 @@ LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPoint( return state.closest_match; } -static bool LayerHasTouchEventHandlersAt(const gfx::PointF& screen_space_point, - LayerImpl* layer_impl) { - if (layer_impl->touch_action_region().region().IsEmpty()) - return false; - - if (!PointHitsRegion(screen_space_point, layer_impl->ScreenSpaceTransform(), - layer_impl->touch_action_region().region())) - return false; - - // At this point, we think the point does hit the touch event handler region - // on the layer, but we need to walk up the parents to ensure that the layer - // was not clipped in such a way that the hit point actually should not hit - // the layer. - if (PointIsClippedBySurfaceOrClipRect(screen_space_point, layer_impl)) - return false; - - return true; -} - struct FindTouchEventLayerFunctor { bool operator()(LayerImpl* layer) const { - return LayerHasTouchEventHandlersAt(screen_space_point, layer); + return PointHitsRegion(screen_space_point, layer->ScreenSpaceTransform(), + layer->touch_action_region().region(), layer); } const gfx::PointF screen_space_point; }; -LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPointInTouchHandlerRegion( - const gfx::PointF& screen_space_point) { +struct FindWheelEventHandlerLayerFunctor { + bool operator()(LayerImpl* layer) const { + return PointHitsRegion(screen_space_point, layer->ScreenSpaceTransform(), + layer->wheel_event_handler_region(), layer); + } + const gfx::PointF screen_space_point; +}; + +template <typename Functor> +LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPointInEventHandlerRegion( + const gfx::PointF& screen_space_point, + const Functor& func) { if (layer_list_.empty()) return nullptr; if (!UpdateDrawProperties()) return nullptr; - FindTouchEventLayerFunctor func = {screen_space_point}; FindClosestMatchingLayerState state; FindClosestMatchingLayer(screen_space_point, layer_list_[0], func, &state); return state.closest_match; } +LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPointInTouchHandlerRegion( + const gfx::PointF& screen_space_point) { + FindTouchEventLayerFunctor func = {screen_space_point}; + return FindLayerThatIsHitByPointInEventHandlerRegion(screen_space_point, + func); +} + +LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPointInWheelEventHandlerRegion( + const gfx::PointF& screen_space_point) { + FindWheelEventHandlerLayerFunctor func = {screen_space_point}; + return FindLayerThatIsHitByPointInEventHandlerRegion(screen_space_point, + func); +} + void LayerTreeImpl::RegisterSelection(const LayerSelection& selection) { if (selection_ == selection) return; diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index 11e406e78d4..0bbf294437e 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -20,6 +20,7 @@ #include "cc/layers/layer_impl.h" #include "cc/layers/layer_list_iterator.h" #include "cc/resources/ui_resource_client.h" +#include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/property_tree.h" #include "cc/trees/swap_promise.h" @@ -32,6 +33,7 @@ class TracedValue; } namespace viz { +class ClientResourceProvider; class ContextProvider; } @@ -44,7 +46,6 @@ class ImageDecodeCache; class LayerTreeDebugState; class LayerTreeImpl; class LayerTreeFrameSink; -class LayerTreeResourceProvider; class LayerTreeSettings; class MemoryHistory; class PictureLayerImpl; @@ -100,6 +101,7 @@ class CC_EXPORT LayerTreeImpl { void Shutdown(); void ReleaseResources(); + void OnPurgeMemory(); void ReleaseTileResources(); void RecreateTileResources(); @@ -110,7 +112,7 @@ class CC_EXPORT LayerTreeImpl { const LayerTreeSettings& settings() const; const LayerTreeDebugState& debug_state() const; viz::ContextProvider* context_provider() const; - LayerTreeResourceProvider* resource_provider() const; + viz::ClientResourceProvider* resource_provider() const; TileManager* tile_manager() const; ImageDecodeCache* image_decode_cache() const; ImageAnimationController* image_animation_controller() const; @@ -179,7 +181,7 @@ class CC_EXPORT LayerTreeImpl { void PushPropertyTreesTo(LayerTreeImpl* tree_impl); void PushPropertiesTo(LayerTreeImpl* tree_impl); - void PushSurfaceIdsTo(LayerTreeImpl* tree_impl); + void PushSurfaceRangesTo(LayerTreeImpl* tree_impl); void MoveChangeTrackingToLayers(); @@ -220,6 +222,14 @@ class CC_EXPORT LayerTreeImpl { gfx::ScrollOffset TotalScrollOffset() const; gfx::ScrollOffset TotalMaxScrollOffset() const; + void AddPresentationCallbacks( + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks); + std::vector<LayerTreeHost::PresentationTimeCallback> + TakePresentationCallbacks(); + bool has_presentation_callbacks() const { + return !presentation_callbacks_.empty(); + } + ScrollNode* CurrentlyScrollingNode(); const ScrollNode* CurrentlyScrollingNode() const; int LastScrolledScrollNodeIndex() const; @@ -264,6 +274,17 @@ class CC_EXPORT LayerTreeImpl { return LayerById(viewport_layer_ids_.outer_viewport_scroll); } + const ScrollNode* InnerViewportScrollNode() const; + ScrollNode* InnerViewportScrollNode() { + return const_cast<ScrollNode*>( + const_cast<const LayerTreeImpl*>(this)->InnerViewportScrollNode()); + } + const ScrollNode* OuterViewportScrollNode() const; + ScrollNode* OuterViewportScrollNode() { + return const_cast<ScrollNode*>( + const_cast<const LayerTreeImpl*>(this)->OuterViewportScrollNode()); + } + void ApplySentScrollAndScaleDeltasFromAbortedCommit(); SkColor background_color() const { return background_color_; } @@ -355,9 +376,9 @@ class CC_EXPORT LayerTreeImpl { void set_needs_full_tree_sync(bool needs) { needs_full_tree_sync_ = needs; } bool needs_full_tree_sync() const { return needs_full_tree_sync_; } - bool needs_surface_ids_sync() const { return needs_surface_ids_sync_; } - void set_needs_surface_ids_sync(bool needs_surface_ids_sync) { - needs_surface_ids_sync_ = needs_surface_ids_sync; + bool needs_surface_ranges_sync() const { return needs_surface_ranges_sync_; } + void set_needs_surface_ranges_sync(bool needs_surface_ranges_sync) { + needs_surface_ranges_sync_ = needs_surface_ranges_sync; } void ForceRedrawNextActivation() { next_activation_forces_redraw_ = true; } @@ -381,16 +402,13 @@ class CC_EXPORT LayerTreeImpl { LayerImpl* LayerById(int id) const; - // TODO(jaydasika): this is deprecated. It is used by - // scrolling animation to look up layers to mutate. - LayerImpl* LayerByElementId(ElementId element_id) const; - void AddToElementMap(LayerImpl* layer); - void RemoveFromElementMap(LayerImpl* layer); + bool IsElementInLayerList(ElementId element_id) const; + void AddToElementLayerList(ElementId element_id); + void RemoveFromElementLayerList(ElementId element_id); - void SetSurfaceLayerIds( - const base::flat_set<viz::SurfaceId>& surface_layer_ids); - const base::flat_set<viz::SurfaceId>& SurfaceLayerIds() const; - void ClearSurfaceLayerIds(); + void SetSurfaceRanges(const base::flat_set<viz::SurfaceRange> surface_ranges); + const base::flat_set<viz::SurfaceRange>& SurfaceRanges() const; + void ClearSurfaceRanges(); void AddLayerShouldPushProperties(LayerImpl* layer); void RemoveLayerShouldPushProperties(LayerImpl* layer); @@ -454,8 +472,7 @@ class CC_EXPORT LayerTreeImpl { std::vector<std::unique_ptr<SwapPromise>> new_swap_promises); void AppendSwapPromises( std::vector<std::unique_ptr<SwapPromise>> new_swap_promises); - void FinishSwapPromises(viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator); + void FinishSwapPromises(viz::CompositorFrameMetadata* metadata); void ClearSwapPromises(); void BreakSwapPromises(SwapPromise::DidNotSwapReason reason); @@ -484,6 +501,9 @@ class CC_EXPORT LayerTreeImpl { LayerImpl* FindLayerThatIsHitByPointInTouchHandlerRegion( const gfx::PointF& screen_space_point); + LayerImpl* FindLayerThatIsHitByPointInWheelEventHandlerRegion( + const gfx::PointF& screen_space_point); + void RegisterSelection(const LayerSelection& selection); bool HandleVisibilityChanged() const { return handle_visibility_changed_; } @@ -568,11 +588,6 @@ class CC_EXPORT LayerTreeImpl { LayerTreeLifecycle& lifecycle() { return lifecycle_; } - bool request_presentation_time() const { return request_presentation_time_; } - void set_request_presentation_time(bool value) { - request_presentation_time_ = value; - } - protected: float ClampPageScaleFactorToLimits(float page_scale_factor) const; void PushPageScaleFactorAndLimits(const float* page_scale_factor, @@ -585,8 +600,15 @@ class CC_EXPORT LayerTreeImpl { bool ClampBrowserControlsShownRatio(); private: + TransformNode* PageScaleTransformNode(); + void UpdatePageScaleNode(); + ElementListType GetElementTypeForAnimation() const; void UpdateTransformAnimation(ElementId element_id, int transform_node_index); + template <typename Functor> + LayerImpl* FindLayerThatIsHitByPointInEventHandlerRegion( + const gfx::PointF& screen_space_point, + const Functor& func); LayerTreeHostImpl* host_impl_; int source_frame_number_; @@ -623,7 +645,8 @@ class CC_EXPORT LayerTreeImpl { // Set of layers that need to push properties. std::unordered_set<LayerImpl*> layers_that_should_push_properties_; - std::unordered_map<ElementId, int, ElementIdHash> element_layers_map_; + // Set of ElementIds which are present in the |layer_list_|. + std::unordered_set<ElementId, ElementIdHash> elements_in_layer_list_; std::unordered_map<ElementId, float, ElementIdHash> element_id_to_opacity_animations_; @@ -643,7 +666,7 @@ class CC_EXPORT LayerTreeImpl { std::vector<PictureLayerImpl*> picture_layers_; - base::flat_set<viz::SurfaceId> surface_layer_ids_; + base::flat_set<viz::SurfaceRange> surface_layer_ranges_; // List of render surfaces for the most recently prepared frame. RenderSurfaceList render_surface_list_; @@ -662,7 +685,7 @@ class CC_EXPORT LayerTreeImpl { // structural differences relative to the active tree. bool needs_full_tree_sync_; - bool needs_surface_ids_sync_; + bool needs_surface_ranges_sync_; bool next_activation_forces_redraw_; @@ -676,8 +699,8 @@ class CC_EXPORT LayerTreeImpl { UIResourceRequestQueue ui_resource_request_queue_; bool have_scroll_event_handlers_; - EventListenerProperties event_listener_properties_[static_cast<size_t>( - EventListenerClass::kNumClasses)]; + EventListenerProperties event_listener_properties_ + [static_cast<size_t>(EventListenerClass::kLast) + 1]; // Whether or not Blink's viewport size was shrunk by the height of the top // controls at the time of the last layout. @@ -697,9 +720,7 @@ class CC_EXPORT LayerTreeImpl { // lifecycle states. See: |LayerTreeLifecycle|. LayerTreeLifecycle lifecycle_; - // If true LayerTreeHostImpl requests a presentation token for the current - // frame. - bool request_presentation_time_ = false; + std::vector<LayerTreeHost::PresentationTimeCallback> presentation_callbacks_; 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 3856dcd0c42..f4c3368e96a 100644 --- a/chromium/cc/trees/layer_tree_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_impl_unittest.cc @@ -2277,42 +2277,16 @@ TEST_F(LayerTreeImplTest, HitTestingCorrectLayerWheelListener) { namespace { -class PersistentSwapPromise - : public SwapPromise, - public base::SupportsWeakPtr<PersistentSwapPromise> { +class StubSwapPromise : public SwapPromise, + public base::SupportsWeakPtr<StubSwapPromise> { public: - PersistentSwapPromise() = default; - ~PersistentSwapPromise() override = default; + StubSwapPromise() = default; + ~StubSwapPromise() override = default; void DidActivate() override {} - MOCK_METHOD2(WillSwap, - void(viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator)); - MOCK_METHOD0(DidSwap, void()); - - DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override { - return DidNotSwapAction::KEEP_ACTIVE; - } - - void OnCommit() override {} - int64_t TraceId() const override { return 0; } -}; - -class NotPersistentSwapPromise - : public SwapPromise, - public base::SupportsWeakPtr<NotPersistentSwapPromise> { - public: - NotPersistentSwapPromise() = default; - ~NotPersistentSwapPromise() override = default; - - void DidActivate() override {} - void WillSwap(viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator) override {} + void WillSwap(viz::CompositorFrameMetadata* metadata) override {} void DidSwap() override {} - - DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override { - return DidNotSwapAction::BREAK_PROMISE; - } + void DidNotSwap(DidNotSwapReason reason) override {} void OnCommit() override {} int64_t TraceId() const override { return 0; } @@ -2320,64 +2294,31 @@ class NotPersistentSwapPromise } // namespace -TEST_F(LayerTreeImplTest, PersistentSwapPromisesAreKeptAlive) { - const size_t promises_count = 2; - - std::vector<base::WeakPtr<PersistentSwapPromise>> persistent_promises; - std::vector<std::unique_ptr<PersistentSwapPromise>> - persistent_promises_to_pass; - for (size_t i = 0; i < promises_count; ++i) { - persistent_promises_to_pass.push_back( - std::make_unique<PersistentSwapPromise>()); - } - - for (auto& promise : persistent_promises_to_pass) { - persistent_promises.push_back(promise->AsWeakPtr()); - host_impl().active_tree()->QueueSwapPromise(std::move(promise)); - } - - std::vector<std::unique_ptr<SwapPromise>> promises; - host_impl().active_tree()->PassSwapPromises(std::move(promises)); - host_impl().active_tree()->BreakSwapPromises( - SwapPromise::DidNotSwapReason::SWAP_FAILS); - - ASSERT_EQ(promises_count, persistent_promises.size()); - for (size_t i = 0; i < persistent_promises.size(); ++i) { - SCOPED_TRACE(testing::Message() << "While checking case #" << i); - ASSERT_TRUE(persistent_promises[i]); - EXPECT_CALL(*persistent_promises[i], WillSwap(testing::_, testing::_)); - } - host_impl().active_tree()->FinishSwapPromises(nullptr, nullptr); -} - -TEST_F(LayerTreeImplTest, NotPersistentSwapPromisesAreDroppedWhenSwapFails) { +TEST_F(LayerTreeImplTest, StubSwapPromisesAreDroppedWhenSwapFails) { const size_t promises_count = 2; - std::vector<base::WeakPtr<NotPersistentSwapPromise>> not_persistent_promises; - std::vector<std::unique_ptr<NotPersistentSwapPromise>> - not_persistent_promises_to_pass; + std::vector<base::WeakPtr<StubSwapPromise>> weak_swap_promises; + std::vector<std::unique_ptr<StubSwapPromise>> swap_promises_to_pass; for (size_t i = 0; i < promises_count; ++i) { - not_persistent_promises_to_pass.push_back( - std::make_unique<NotPersistentSwapPromise>()); + swap_promises_to_pass.push_back(std::make_unique<StubSwapPromise>()); } - for (auto& promise : not_persistent_promises_to_pass) { - not_persistent_promises.push_back(promise->AsWeakPtr()); + for (auto& promise : swap_promises_to_pass) { + weak_swap_promises.push_back(promise->AsWeakPtr()); host_impl().active_tree()->QueueSwapPromise(std::move(promise)); } std::vector<std::unique_ptr<SwapPromise>> promises; host_impl().active_tree()->PassSwapPromises(std::move(promises)); - ASSERT_EQ(promises_count, not_persistent_promises.size()); - for (size_t i = 0; i < not_persistent_promises.size(); ++i) { - EXPECT_FALSE(not_persistent_promises[i]) << "While checking case #" << i; + ASSERT_EQ(promises_count, weak_swap_promises.size()); + for (size_t i = 0; i < weak_swap_promises.size(); ++i) { + EXPECT_FALSE(weak_swap_promises[i]) << "While checking case #" << i; } - // Finally, check that not persistent promise doesn't survive + // Finally, check that the promises do not survive // |LayerTreeImpl::BreakSwapPromises|. { - std::unique_ptr<NotPersistentSwapPromise> promise( - new NotPersistentSwapPromise()); + std::unique_ptr<StubSwapPromise> promise(new StubSwapPromise()); auto weak_promise = promise->AsWeakPtr(); host_impl().active_tree()->QueueSwapPromise(std::move(promise)); host_impl().active_tree()->BreakSwapPromises( diff --git a/chromium/cc/trees/layer_tree_mutator.cc b/chromium/cc/trees/layer_tree_mutator.cc index bccd86c124e..addb4719ffe 100644 --- a/chromium/cc/trees/layer_tree_mutator.cc +++ b/chromium/cc/trees/layer_tree_mutator.cc @@ -4,12 +4,88 @@ #include "cc/trees/layer_tree_mutator.h" +#include <algorithm> + namespace cc { +AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState( + WorkletAnimationId worklet_animation_id, + std::string name, + double current_time, + std::unique_ptr<AnimationOptions> options) + : worklet_animation_id(worklet_animation_id), + name(name), + current_time(current_time), + options(std::move(options)) {} +AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState( + AddAndUpdateState&&) = default; +AnimationWorkletInput::AddAndUpdateState::~AddAndUpdateState() = default; + +#if DCHECK_IS_ON() +bool AnimationWorkletInput::ValidateScope(int scope_id) const { + return std::all_of(added_and_updated_animations.cbegin(), + added_and_updated_animations.cend(), + [scope_id](auto& it) { + return it.worklet_animation_id.scope_id == scope_id; + }) && + std::all_of(updated_animations.cbegin(), updated_animations.cend(), + [scope_id](auto& it) { + return it.worklet_animation_id.scope_id == scope_id; + }) && + std::all_of(removed_animations.cbegin(), removed_animations.cend(), + [scope_id](auto& it) { return it.scope_id == scope_id; }); +} +#endif + +AnimationWorkletInput::AnimationWorkletInput() = default; +AnimationWorkletInput::~AnimationWorkletInput() = default; + MutatorInputState::MutatorInputState() = default; MutatorInputState::~MutatorInputState() = default; -MutatorOutputState::MutatorOutputState() = default; -MutatorOutputState::~MutatorOutputState() = default; +bool MutatorInputState::IsEmpty() const { + // If there is an AnimationWorkletInput entry in the map then that entry is + // guranteed to be non-empty. So checking |inputs_| map emptiness is + // sufficient. + return inputs_.empty(); +} + +AnimationWorkletInput& MutatorInputState::EnsureWorkletEntry(int id) { + auto it = inputs_.find(id); + if (it == inputs_.end()) + it = inputs_.emplace_hint(it, id, new AnimationWorkletInput); + + return *it->second; +} + +void MutatorInputState::Add(AnimationWorkletInput::AddAndUpdateState&& state) { + AnimationWorkletInput& worklet_input = + EnsureWorkletEntry(state.worklet_animation_id.scope_id); + worklet_input.added_and_updated_animations.push_back(std::move(state)); +} +void MutatorInputState::Update(AnimationWorkletInput::UpdateState&& state) { + AnimationWorkletInput& worklet_input = + EnsureWorkletEntry(state.worklet_animation_id.scope_id); + worklet_input.updated_animations.push_back(std::move(state)); +} +void MutatorInputState::Remove(WorkletAnimationId worklet_animation_id) { + AnimationWorkletInput& worklet_input = + EnsureWorkletEntry(worklet_animation_id.scope_id); + worklet_input.removed_animations.push_back(worklet_animation_id); +} + +std::unique_ptr<AnimationWorkletInput> MutatorInputState::TakeWorkletState( + int scope_id) { + auto it = inputs_.find(scope_id); + if (it == inputs_.end()) + return nullptr; + + std::unique_ptr<AnimationWorkletInput> result = std::move(it->second); + inputs_.erase(it); + return result; +} + +AnimationWorkletOutput::AnimationWorkletOutput() = default; +AnimationWorkletOutput::~AnimationWorkletOutput() = default; } // namespace cc diff --git a/chromium/cc/trees/layer_tree_mutator.h b/chromium/cc/trees/layer_tree_mutator.h index 70cf3fc77bc..700549fa1d7 100644 --- a/chromium/cc/trees/layer_tree_mutator.h +++ b/chromium/cc/trees/layer_tree_mutator.h @@ -8,36 +8,100 @@ #include "base/callback_forward.h" #include "base/time/time.h" #include "cc/cc_export.h" +#include "cc/trees/animation_options.h" #include <memory> #include <string> +#include <unordered_map> #include <vector> namespace cc { -// TODO(majidvp): Currently the sync mechanism between cc and worklet is -// stateless meaning that it sends a new copy of the world every sync cycle. -// This has the benefit of keeping things very simple but we should revisit this -// and only send data relevant to particular phase of animator lifecycle e.g., -// name and options dictionary are used just for construction. -struct CC_EXPORT MutatorInputState { - struct CC_EXPORT AnimationState { - int animation_id = 0; +struct CC_EXPORT WorkletAnimationId { + // Uniquely identifies the animation worklet with which this animation is + // associated. + int scope_id; + // Uniquely identifies the animation within its animation worklet. Note that + // animation_id is only guaranteed to be unique per animation worklet. + int animation_id; + + inline bool operator==(const WorkletAnimationId& rhs) const { + return (this->scope_id == rhs.scope_id) && + (this->animation_id == rhs.animation_id); + } +}; + +struct CC_EXPORT AnimationWorkletInput { + struct CC_EXPORT AddAndUpdateState { + WorkletAnimationId worklet_animation_id; // Name associated with worklet animation. std::string name; // Worklet animation's current time, from its associated timeline. + double current_time; + std::unique_ptr<AnimationOptions> options; + + AddAndUpdateState(WorkletAnimationId worklet_animation_id, + std::string name, + double current_time, + std::unique_ptr<AnimationOptions> options); + + AddAndUpdateState(AddAndUpdateState&&); + ~AddAndUpdateState(); + }; + struct CC_EXPORT UpdateState { + WorkletAnimationId worklet_animation_id; + // Worklet animation's current time, from its associated timeline. double current_time = 0; }; + // Note: When adding any new fields please also update ValidateScope to + // reflect them if necessary. + std::vector<AddAndUpdateState> added_and_updated_animations; + std::vector<UpdateState> updated_animations; + std::vector<WorkletAnimationId> removed_animations; + + AnimationWorkletInput(); + ~AnimationWorkletInput(); + +#if DCHECK_IS_ON() + // Verifies all animation states have the expected scope id. + bool ValidateScope(int scope_id) const; +#endif + DISALLOW_COPY_AND_ASSIGN(AnimationWorkletInput); +}; + +class CC_EXPORT MutatorInputState { + public: MutatorInputState(); ~MutatorInputState(); - std::vector<AnimationState> animations; + bool IsEmpty() const; + void Add(AnimationWorkletInput::AddAndUpdateState&& state); + void Update(AnimationWorkletInput::UpdateState&& state); + void Remove(WorkletAnimationId worklet_animation_id); + + // Returns input for animation worklet with the given |scope_id| and nullptr + // if there is no input. + std::unique_ptr<AnimationWorkletInput> TakeWorkletState(int scope_id); + + private: + using InputMap = + std::unordered_map<int, std::unique_ptr<AnimationWorkletInput>>; + + // Maps a scope id to its associated AnimationWorkletInput instance. + // Only contains scope ids for which there is a non-empty input. + InputMap inputs_; + + // Returns iterator pointing to the entry in |inputs_| map whose key is id. It + // inserts a new entry if none exists. + AnimationWorkletInput& EnsureWorkletEntry(int id); + + DISALLOW_COPY_AND_ASSIGN(MutatorInputState); }; -struct CC_EXPORT MutatorOutputState { +struct CC_EXPORT AnimationWorkletOutput { struct CC_EXPORT AnimationState { - int animation_id = 0; + WorkletAnimationId worklet_animation_id; // The animator effect's local time. // TODO(majidvp): This assumes each animator has a single output effect // which does not hold once we state support group effects. @@ -45,12 +109,16 @@ struct CC_EXPORT MutatorOutputState { base::TimeDelta local_time; }; - MutatorOutputState(); - ~MutatorOutputState(); + AnimationWorkletOutput(); + ~AnimationWorkletOutput(); std::vector<AnimationState> animations; }; +// LayerTreeMutatorClient processes worklet outputs individually so we can +// define mutator output to be the same as animation worklet output. +using MutatorOutputState = AnimationWorkletOutput; + class LayerTreeMutatorClient { public: // Called when mutator needs to update its output. diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h index 50b8805bc7a..736df815a28 100644 --- a/chromium/cc/trees/layer_tree_settings.h +++ b/chromium/cc/trees/layer_tree_settings.h @@ -138,10 +138,6 @@ class CC_EXPORT LayerTreeSettings { // produces the active tree as its 'sync tree'. bool commit_to_active_tree = true; - // Whether to use out of process raster. If true, whenever gpu raster - // would have been used, out of process gpu raster will be used instead. - bool enable_oop_rasterization = false; - // Whether image animations can be reset to the beginning to avoid skipping // many frames. bool enable_image_animation_resync = true; @@ -160,6 +156,10 @@ class CC_EXPORT LayerTreeSettings { // Whether a HitTestRegionList should be built from the active layer tree when // submitting a CompositorFrame. bool build_hit_test_data = false; + + // When false, sync tokens are expected to be present, and are verified, + // before transfering gpu resources to the display compositor. + bool delegated_sync_points_required = true; }; } // namespace cc diff --git a/chromium/cc/trees/mutator_host.h b/chromium/cc/trees/mutator_host.h index 18b7fe5118c..586373cc8f2 100644 --- a/chromium/cc/trees/mutator_host.h +++ b/chromium/cc/trees/mutator_host.h @@ -61,12 +61,14 @@ class MutatorHost { // TODO(smcgruer): Once we only tick scroll-based animations on scroll, we // don't need to pass the scroll tree in here. virtual bool TickAnimations(base::TimeTicks monotonic_time, - const ScrollTree& scroll_tree) = 0; + const ScrollTree& scroll_tree, + bool is_active_tree) = 0; // Tick animations that depends on scroll offset. virtual void TickScrollAnimations(base::TimeTicks monotonic_time, const ScrollTree& scroll_tree) = 0; virtual bool UpdateAnimationState(bool start_ready_animations, MutatorEvents* events) = 0; + virtual void PromoteScrollTimelinesPendingToActive() = 0; virtual std::unique_ptr<MutatorEvents> CreateEvents() = 0; virtual void SetAnimationEvents(std::unique_ptr<MutatorEvents> events) = 0; diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc index 3553d91d01a..d94e2fe3c4f 100644 --- a/chromium/cc/trees/property_tree.cc +++ b/chromium/cc/trees/property_tree.cc @@ -982,12 +982,13 @@ void EffectTree::TakeCopyRequestsAndTransformToSurface( // copy of the exact source pixels. If the adjustment to the scale ratio // would produce out-of-range values, drop the copy request. if (request->is_scaled() || request->has_result_selection()) { - float scale_from_x = request->scale_from().x() * surface_rect.width(); - float scale_from_y = request->scale_from().y() * surface_rect.height(); - if (std::isnan(scale_from_x) || - !base::IsValueInRangeForNumericType<int>(scale_from_x) || - std::isnan(scale_from_y) || - !base::IsValueInRangeForNumericType<int>(scale_from_y)) { + float scale_from_x_f = request->scale_from().x() * surface_rect.width(); + float scale_from_y_f = + request->scale_from().y() * surface_rect.height(); + if (std::isnan(scale_from_x_f) || + !base::IsValueInRangeForNumericType<int>(scale_from_x_f) || + std::isnan(scale_from_y_f) || + !base::IsValueInRangeForNumericType<int>(scale_from_y_f)) { continue; } int scale_to_x = request->scale_to().x(); @@ -998,8 +999,15 @@ void EffectTree::TakeCopyRequestsAndTransformToSurface( .AssignIfValid(&scale_to_y)) { continue; } - request->SetScaleRatio(gfx::Vector2d(gfx::ToRoundedInt(scale_from_x), - gfx::ToRoundedInt(scale_from_y)), + int scale_from_x = gfx::ToRoundedInt(scale_from_x_f); + int scale_from_y = gfx::ToRoundedInt(scale_from_y_f); + if (scale_from_x <= 0 || scale_from_y <= 0 || scale_to_x <= 0 || + scale_to_y <= 0) { + // Transformed scaling ratio became illegal. Drop the request to + // provide an empty response. + continue; + } + request->SetScaleRatio(gfx::Vector2d(scale_from_x, scale_from_y), gfx::Vector2d(scale_to_x, scale_to_y)); } @@ -1381,6 +1389,18 @@ const SyncedScrollOffset* ScrollTree::GetSyncedScrollOffset( return it != synced_scroll_offset_map_.end() ? it->second.get() : nullptr; } +gfx::Vector2dF ScrollTree::ClampScrollToMaxScrollOffset( + ScrollNode* node, + LayerTreeImpl* layer_tree_impl) { + gfx::ScrollOffset old_offset = current_scroll_offset(node->element_id); + gfx::ScrollOffset clamped_offset = + ClampScrollOffsetToLimits(old_offset, *node); + gfx::Vector2dF delta = clamped_offset.DeltaFrom(old_offset); + if (!delta.IsZero()) + ScrollBy(node, delta, layer_tree_impl); + return delta; +} + const gfx::ScrollOffset ScrollTree::current_scroll_offset(ElementId id) const { if (property_trees()->is_main_thread) { ScrollOffsetMap::const_iterator it = scroll_offset_map_.find(id); diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h index 8cb5abca4aa..39dac923705 100644 --- a/chromium/cc/trees/property_tree.h +++ b/chromium/cc/trees/property_tree.h @@ -405,6 +405,8 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { void set_currently_scrolling_node(int scroll_node_id); gfx::Transform ScreenSpaceTransform(int scroll_node_id) const; + gfx::Vector2dF ClampScrollToMaxScrollOffset(ScrollNode* node, LayerTreeImpl*); + // Returns the current scroll offset. On the main thread this would return the // value for the LayerTree while on the impl thread this is the current value // on the active tree. diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc index 4aface2581b..a891ce20077 100644 --- a/chromium/cc/trees/property_tree_builder.cc +++ b/chromium/cc/trees/property_tree_builder.cc @@ -12,6 +12,7 @@ #include "cc/base/math_util.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/picture_layer.h" #include "cc/trees/clip_node.h" #include "cc/trees/draw_property_utils.h" #include "cc/trees/effect_node.h" @@ -59,6 +60,7 @@ class PropertyTreeBuilderContext { const gfx::Vector2dF& elastic_overscroll, float page_scale_factor, const gfx::Transform& device_transform, + MutatorHost* mutator_host, PropertyTrees* property_trees) : root_layer_(root_layer), page_scale_layer_(page_scale_layer), @@ -68,6 +70,7 @@ class PropertyTreeBuilderContext { elastic_overscroll_(elastic_overscroll), page_scale_factor_(page_scale_factor), device_transform_(device_transform), + mutator_host_(*mutator_host), property_trees_(*property_trees), transform_tree_(property_trees->transform_tree), clip_tree_(property_trees->clip_tree), @@ -117,6 +120,7 @@ class PropertyTreeBuilderContext { const gfx::Vector2dF elastic_overscroll_; float page_scale_factor_; const gfx::Transform& device_transform_; + MutatorHost& mutator_host_; PropertyTrees& property_trees_; TransformTree& transform_tree_; ClipTree& clip_tree_; @@ -155,7 +159,7 @@ static LayerImpl* LayerChildAt(LayerImpl* layer, int index) { } static Layer* LayerChildAt(Layer* layer, int index) { - return layer->child_at(index); + return layer->children()[index].get(); } static Layer* ScrollParent(Layer* layer) { @@ -182,7 +186,7 @@ static inline const FilterOperations& Filters(LayerImpl* layer) { return layer->test_properties()->filters; } -static Layer* MaskLayer(Layer* layer) { +static PictureLayer* MaskLayer(Layer* layer) { return layer->mask_layer(); } @@ -200,58 +204,61 @@ static const gfx::Transform& Transform(LayerImpl* layer) { // Methods to query state from the AnimationHost ---------------------- template <typename LayerType> -bool OpacityIsAnimating(LayerType* layer) { - return layer->GetMutatorHost()->IsAnimatingOpacityProperty( - layer->element_id(), layer->GetElementTypeForAnimation()); +bool OpacityIsAnimating(const MutatorHost& host, LayerType* layer) { + return host.IsAnimatingOpacityProperty(layer->element_id(), + layer->GetElementTypeForAnimation()); } template <typename LayerType> -bool HasPotentiallyRunningOpacityAnimation(LayerType* layer) { - return layer->GetMutatorHost()->HasPotentiallyRunningOpacityAnimation( +bool HasPotentiallyRunningOpacityAnimation(const MutatorHost& host, + LayerType* layer) { + return host.HasPotentiallyRunningOpacityAnimation( layer->element_id(), layer->GetElementTypeForAnimation()); } template <typename LayerType> -bool FilterIsAnimating(LayerType* layer) { - return layer->GetMutatorHost()->IsAnimatingFilterProperty( - layer->element_id(), layer->GetElementTypeForAnimation()); +bool FilterIsAnimating(const MutatorHost& host, LayerType* layer) { + return host.IsAnimatingFilterProperty(layer->element_id(), + layer->GetElementTypeForAnimation()); } template <typename LayerType> -bool HasPotentiallyRunningFilterAnimation(LayerType* layer) { - return layer->GetMutatorHost()->HasPotentiallyRunningFilterAnimation( +bool HasPotentiallyRunningFilterAnimation(const MutatorHost& host, + LayerType* layer) { + return host.HasPotentiallyRunningFilterAnimation( layer->element_id(), layer->GetElementTypeForAnimation()); } template <typename LayerType> -bool TransformIsAnimating(LayerType* layer) { - return layer->GetMutatorHost()->IsAnimatingTransformProperty( - layer->element_id(), layer->GetElementTypeForAnimation()); +bool TransformIsAnimating(const MutatorHost& host, LayerType* layer) { + return host.IsAnimatingTransformProperty(layer->element_id(), + layer->GetElementTypeForAnimation()); } template <typename LayerType> -bool HasPotentiallyRunningTransformAnimation(LayerType* layer) { - return layer->GetMutatorHost()->HasPotentiallyRunningTransformAnimation( +bool HasPotentiallyRunningTransformAnimation(const MutatorHost& host, + LayerType* layer) { + return host.HasPotentiallyRunningTransformAnimation( layer->element_id(), layer->GetElementTypeForAnimation()); } template <typename LayerType> -bool HasOnlyTranslationTransforms(LayerType* layer) { - return layer->GetMutatorHost()->HasOnlyTranslationTransforms( - layer->element_id(), layer->GetElementTypeForAnimation()); +bool HasOnlyTranslationTransforms(const MutatorHost& host, LayerType* layer) { + return host.HasOnlyTranslationTransforms(layer->element_id(), + layer->GetElementTypeForAnimation()); } template <typename LayerType> -bool AnimationsPreserveAxisAlignment(LayerType* layer) { - return layer->GetMutatorHost()->AnimationsPreserveAxisAlignment( - layer->element_id()); +bool AnimationsPreserveAxisAlignment(const MutatorHost& host, + LayerType* layer) { + return host.AnimationsPreserveAxisAlignment(layer->element_id()); } template <typename LayerType> -bool HasAnyAnimationTargetingProperty(LayerType* layer, +bool HasAnyAnimationTargetingProperty(const MutatorHost& host, + LayerType* layer, TargetProperty::Type property) { - return layer->GetMutatorHost()->HasAnyAnimationTargetingProperty( - layer->element_id(), property); + return host.HasAnyAnimationTargetingProperty(layer->element_id(), property); } // ------------------------------------------------------------------- @@ -295,7 +302,7 @@ static inline int SortingContextId(LayerImpl* layer) { } static inline bool Is3dSorted(Layer* layer) { - return layer->Is3dSorted(); + return layer->sorting_context_id() != 0; } static inline bool Is3dSorted(LayerImpl* layer) { @@ -393,20 +400,24 @@ bool PropertyTreeBuilderContext<LayerType>::AddTransformNodeIfNeeded( const bool is_scrollable = layer->scrollable(); const bool is_fixed = PositionConstraint(layer).is_fixed_position(); const bool is_sticky = StickyPositionConstraint(layer).is_sticky; - const bool is_snapped = layer->IsSnapped(); + // Scrolling a layer should not move it from being pixel-aligned to moving off + // the pixel grid and becoming fuzzy. So always snap scrollable things to the + // pixel grid. Layers may also request to be snapped as such. + const bool is_snapped = + is_scrollable || layer->IsSnappedToPixelGridInTarget(); const bool has_significant_transform = !Transform(layer).IsIdentityOr2DTranslation(); const bool has_potentially_animated_transform = - HasPotentiallyRunningTransformAnimation(layer); + HasPotentiallyRunningTransformAnimation(mutator_host_, layer); // A transform node is needed even for a finished animation, since differences // in the timing of animation state updates can mean that an animation that's // in the Finished state at tree-building time on the main thread is still in // the Running state right after commit on the compositor thread. - const bool has_any_transform_animation = - HasAnyAnimationTargetingProperty(layer, TargetProperty::TRANSFORM); + const bool has_any_transform_animation = HasAnyAnimationTargetingProperty( + mutator_host_, layer, TargetProperty::TRANSFORM); const bool has_surface = created_render_surface; @@ -433,6 +444,16 @@ bool PropertyTreeBuilderContext<LayerType>::AddTransformNodeIfNeeded( source_offset = LayerParent(layer)->offset_to_transform_parent(); } + // For a container of fixed position descendants, define for them their + // fixed-position transform parent as being this layer's transform node, or + // its transform parent's node if this layer won't have a node of its own. + // + // But if this layer is scrollable, then we point fixed position descendants + // to not be affected by this layer as it changes its scroll offset during + // a compositor thread scroll. We do this by pointing them to the direct + // parent of this layer, which acts as a proxy for this layer, without + // including scrolling, based on the assumption this layer has no transform + // itself when scrollable. if (IsContainerForFixedPositionLayers(layer) || is_root) { data_for_children->affected_by_outer_viewport_bounds_delta = layer->IsResizedByBrowserControls(); @@ -459,9 +480,9 @@ bool PropertyTreeBuilderContext<LayerType>::AddTransformNodeIfNeeded( &to_parent); source_to_parent = to_parent.To2dTranslation(); } - layer->set_offset_to_transform_parent(source_offset + source_to_parent + - local_offset); - layer->set_should_flatten_transform_from_property_tree( + layer->SetOffsetToTransformParent(source_offset + source_to_parent + + local_offset); + layer->SetShouldFlattenScreenSpaceTransformFromPropertyTree( data_from_ancestor.should_flatten); layer->SetTransformTreeIndex(parent_index); return false; @@ -496,10 +517,10 @@ bool PropertyTreeBuilderContext<LayerType>::AddTransformNodeIfNeeded( ShouldFlattenTransform(layer) || has_surface; node->has_potential_animation = has_potentially_animated_transform; - node->is_currently_animating = TransformIsAnimating(layer); + node->is_currently_animating = TransformIsAnimating(mutator_host_, layer); if (has_potentially_animated_transform) { node->has_only_translation_animations = - HasOnlyTranslationTransforms(layer); + HasOnlyTranslationTransforms(mutator_host_, layer); } float post_local_scale_factor = 1.0f; @@ -593,21 +614,23 @@ bool PropertyTreeBuilderContext<LayerType>::AddTransformNodeIfNeeded( node->needs_local_transform_update = true; transform_tree_.UpdateTransforms(node->id); - layer->set_offset_to_transform_parent(gfx::Vector2dF()); + layer->SetOffsetToTransformParent(gfx::Vector2dF()); // Flattening (if needed) will be handled by |node|. - layer->set_should_flatten_transform_from_property_tree(false); + layer->SetShouldFlattenScreenSpaceTransformFromPropertyTree(false); return true; } -static inline bool HasPotentialOpacityAnimation(Layer* layer) { - return HasPotentiallyRunningOpacityAnimation(layer) || +static inline bool HasPotentialOpacityAnimation(const MutatorHost& host, + Layer* layer) { + return HasPotentiallyRunningOpacityAnimation(host, layer) || layer->OpacityCanAnimateOnImplThread(); } -static inline bool HasPotentialOpacityAnimation(LayerImpl* layer) { - return HasPotentiallyRunningOpacityAnimation(layer) || +static inline bool HasPotentialOpacityAnimation(const MutatorHost& host, + LayerImpl* layer) { + return HasPotentiallyRunningOpacityAnimation(host, layer) || layer->test_properties()->opacity_can_animate; } @@ -749,7 +772,8 @@ static inline bool PropertyChanged(LayerImpl* layer) { } template <typename LayerType> -bool ShouldCreateRenderSurface(LayerType* layer, +bool ShouldCreateRenderSurface(const MutatorHost& mutator_host, + LayerType* layer, gfx::Transform current_transform, bool animation_axis_aligned) { const bool preserves_2d_axis_alignment = @@ -775,7 +799,7 @@ bool ShouldCreateRenderSurface(LayerType* layer, // If the layer will use a CSS filter. In this case, the animation // will start and add a filter to this layer, so it needs a surface. - if (HasPotentiallyRunningFilterAnimation(layer)) { + if (HasPotentiallyRunningFilterAnimation(mutator_host, layer)) { return true; } @@ -821,8 +845,9 @@ bool ShouldCreateRenderSurface(LayerType* layer, num_descendants_that_draw_content > 0 && (layer->DrawsContent() || num_descendants_that_draw_content > 1); - bool may_have_transparency = EffectiveOpacity(layer) != 1.f || - HasPotentiallyRunningOpacityAnimation(layer); + bool may_have_transparency = + EffectiveOpacity(layer) != 1.f || + HasPotentiallyRunningOpacityAnimation(mutator_host, layer); if (may_have_transparency && ShouldFlattenTransform(layer) && at_least_two_layers_in_subtree_draw_content) { TRACE_EVENT_INSTANT0( @@ -912,21 +937,22 @@ bool PropertyTreeBuilderContext<LayerType>::AddEffectNodeIfNeeded( const bool is_root = !LayerParent(layer); const bool has_transparency = EffectiveOpacity(layer) != 1.f; const bool has_potential_opacity_animation = - HasPotentialOpacityAnimation(layer); + HasPotentialOpacityAnimation(mutator_host_, layer); const bool has_potential_filter_animation = - HasPotentiallyRunningFilterAnimation(layer); + HasPotentiallyRunningFilterAnimation(mutator_host_, layer); data_for_children->animation_axis_aligned_since_render_target &= - AnimationsPreserveAxisAlignment(layer); + AnimationsPreserveAxisAlignment(mutator_host_, 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, + mutator_host_, 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) || + : !AnimationsPreserveAxisAlignment(mutator_host_, layer) || !Transform(layer).Preserves2dAxisAlignment(); // A non-axis aligned clip may need a render surface. So, we create an effect // node. @@ -963,8 +989,9 @@ bool PropertyTreeBuilderContext<LayerType>::AddEffectNodeIfNeeded( 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->is_currently_animating_opacity = + OpacityIsAnimating(mutator_host_, layer); + node->is_currently_animating_filter = FilterIsAnimating(mutator_host_, layer); node->effect_changed = PropertyChanged(layer); node->subtree_has_copy_request = SubtreeHasCopyRequest(layer); node->closest_ancestor_with_cached_render_surface_id = @@ -1118,7 +1145,8 @@ void PropertyTreeBuilderContext<LayerType>::AddScrollNodeIfNeeded( node.bounds = layer->bounds(); node.container_bounds = layer->scroll_container_bounds(); node.offset_to_transform_parent = layer->offset_to_transform_parent(); - node.should_flatten = layer->should_flatten_transform_from_property_tree(); + node.should_flatten = + layer->should_flatten_screen_space_transform_from_property_tree(); node.user_scrollable_horizontal = UserScrollableHorizontal(layer); node.user_scrollable_vertical = UserScrollableVertical(layer); node.element_id = layer->element_id(); @@ -1200,7 +1228,6 @@ void PropertyTreeBuilderContext<LayerType>::BuildPropertyTreesInternal( bool created_render_surface = AddEffectNodeIfNeeded(data_from_parent, layer, &data_for_children); - bool created_transform_node = AddTransformNodeIfNeeded( data_from_parent, layer, created_render_surface, &data_for_children); SetHasTransformNode(layer, created_transform_node); @@ -1215,7 +1242,7 @@ void PropertyTreeBuilderContext<LayerType>::BuildPropertyTreesInternal( bool not_axis_aligned_since_last_clip = data_from_parent.not_axis_aligned_since_last_clip ? true - : !AnimationsPreserveAxisAlignment(layer) || + : !AnimationsPreserveAxisAlignment(mutator_host_, layer) || !Transform(layer).Preserves2dAxisAlignment(); bool has_non_axis_aligned_clip = not_axis_aligned_since_last_clip && LayerClipsSubtree(layer); @@ -1244,7 +1271,7 @@ void PropertyTreeBuilderContext<LayerType>::BuildPropertyTreesInternal( if (MaskLayer(layer)) { MaskLayer(layer)->set_property_tree_sequence_number( property_trees_.sequence_number); - MaskLayer(layer)->set_offset_to_transform_parent( + MaskLayer(layer)->SetOffsetToTransformParent( layer->offset_to_transform_parent()); MaskLayer(layer)->SetTransformTreeIndex(layer->transform_tree_index()); MaskLayer(layer)->SetClipTreeIndex(layer->clip_tree_index()); @@ -1284,14 +1311,32 @@ void PropertyTreeBuilderContext<LayerType>::BuildPropertyTrees( const gfx::Rect& viewport, SkColor root_background_color) const { if (!property_trees_.needs_rebuild) { - draw_property_utils::UpdatePageScaleFactor( - &property_trees_, page_scale_layer_, page_scale_factor_, - device_scale_factor, device_transform_); + bool page_scale_is_root_layer = page_scale_layer_ == root_layer_; + if (page_scale_layer_) { + DCHECK_GE(page_scale_layer_->transform_tree_index(), + TransformTree::kRootNodeId); + TransformNode* node = property_trees_.transform_tree.Node( + page_scale_layer_->transform_tree_index()); + + // When the page scale layer is also the root layer, the node should also + // store the combined scale factor and not just the page scale factor. + float device_scale_factor_for_page_scale_node = 1.f; + gfx::Transform device_transform_for_page_scale_node; + if (page_scale_is_root_layer) { + device_transform_for_page_scale_node = device_transform_; + device_scale_factor_for_page_scale_node = device_scale_factor; + } + + draw_property_utils::UpdatePageScaleFactor( + &property_trees_, node, page_scale_factor_, + device_scale_factor_for_page_scale_node, + device_transform_for_page_scale_node); + } draw_property_utils::UpdateElasticOverscroll( &property_trees_, overscroll_elasticity_layer_, elastic_overscroll_); clip_tree_.SetViewportClip(gfx::RectF(viewport)); float page_scale_factor_for_root = - page_scale_layer_ == root_layer_ ? page_scale_factor_ : 1.f; + page_scale_is_root_layer ? page_scale_factor_ : 1.f; transform_tree_.SetRootTransformsAndScales( device_scale_factor, page_scale_factor_for_root, device_transform_, root_layer_->position()); @@ -1399,7 +1444,8 @@ void PropertyTreeBuilder::BuildPropertyTrees( PropertyTreeBuilderContext<Layer>( root_layer, page_scale_layer, inner_viewport_scroll_layer, outer_viewport_scroll_layer, overscroll_elasticity_layer, - elastic_overscroll, page_scale_factor, device_transform, property_trees) + elastic_overscroll, page_scale_factor, device_transform, + root_layer->layer_tree_host()->mutator_host(), property_trees) .BuildPropertyTrees(device_scale_factor, viewport, color); #if DCHECK_IS_ON() for (auto* layer : AllLayerRange(root_layer)) @@ -1438,7 +1484,8 @@ void PropertyTreeBuilder::BuildPropertyTrees( PropertyTreeBuilderContext<LayerImpl>( root_layer, page_scale_layer, inner_viewport_scroll_layer, outer_viewport_scroll_layer, overscroll_elasticity_layer, - elastic_overscroll, page_scale_factor, device_transform, property_trees) + elastic_overscroll, page_scale_factor, device_transform, + root_layer->layer_tree_impl()->mutator_host(), property_trees) .BuildPropertyTrees(device_scale_factor, viewport, color); property_trees->effect_tree.CreateOrReuseRenderSurfaces( &render_surfaces, root_layer->layer_tree_impl()); diff --git a/chromium/cc/trees/property_tree_unittest.cc b/chromium/cc/trees/property_tree_unittest.cc index 1c0c37692fc..7935b599b40 100644 --- a/chromium/cc/trees/property_tree_unittest.cc +++ b/chromium/cc/trees/property_tree_unittest.cc @@ -11,6 +11,7 @@ #include "cc/trees/effect_node.h" #include "cc/trees/scroll_node.h" #include "cc/trees/transform_node.h" +#include "components/viz/common/frame_sinks/copy_output_request.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -529,5 +530,138 @@ TEST(PropertyTreeTest, SingularTransformSnapTest) { EXPECT_NE(to_target, rounded); } +// Tests that CopyOutputRequests are transformed by the EffectTree, such that +// assumptions the original requestor made about coordinate spaces remains true +// after the EffectTree transforms the requests. +TEST(EffectTreeTest, CopyOutputRequestsAreTransformed) { + using viz::CopyOutputRequest; + + PropertyTrees property_trees; + + TransformTree& transform_tree = property_trees.transform_tree; + TransformNode contents_root; + contents_root.local.Scale(2, 2); + contents_root.source_node_id = 0; + contents_root.id = transform_tree.Insert(contents_root, 0); + transform_tree.UpdateTransforms(contents_root.id); + + EffectTree& effect_tree = property_trees.effect_tree; + EffectNode effect_node; + effect_node.has_render_surface = true; + effect_node.has_copy_request = true; + effect_node.transform_id = contents_root.id; + effect_node.id = effect_tree.Insert(effect_node, 0); + effect_tree.UpdateEffects(effect_node.id); + + // A CopyOutputRequest with only its area set should be transformed into one + // that is scaled by two. In this case, by specifying no result selection, the + // requestor has indicated they want all the pixels, regardless of size. Thus, + // the result selection and scale ratio should still be unset in the + // transformed request to carry-over those semantics. + auto request_in = CopyOutputRequest::CreateStubForTesting(); + request_in->set_area(gfx::Rect(10, 20, 30, 40)); + effect_tree.AddCopyRequest(effect_node.id, std::move(request_in)); + std::vector<std::unique_ptr<CopyOutputRequest>> requests_out; + effect_tree.TakeCopyRequestsAndTransformToSurface(effect_node.id, + &requests_out); + ASSERT_EQ(1u, requests_out.size()); + const CopyOutputRequest* request_out = requests_out.front().get(); + ASSERT_TRUE(request_out->has_area()); + EXPECT_EQ(gfx::Rect(20, 40, 60, 80), request_out->area()); + EXPECT_FALSE(request_out->has_result_selection()); + EXPECT_FALSE(request_out->is_scaled()); + + // A CopyOutputRequest with its area and result selection set, but no scaling + // specified, should be transformed into one that has its area scaled by two, + // but now also includes a scale ratio of 1/2. This is because the requestor + // had originally specified a result selection under old assumptions about the + // source coordinate system. + request_in = CopyOutputRequest::CreateStubForTesting(); + request_in->set_area(gfx::Rect(10, 20, 30, 40)); + request_in->set_result_selection(gfx::Rect(1, 2, 3, 4)); + effect_tree.AddCopyRequest(effect_node.id, std::move(request_in)); + requests_out.clear(); + effect_tree.TakeCopyRequestsAndTransformToSurface(effect_node.id, + &requests_out); + ASSERT_EQ(1u, requests_out.size()); + request_out = requests_out.front().get(); + ASSERT_TRUE(request_out->has_area()); + EXPECT_EQ(gfx::Rect(20, 40, 60, 80), request_out->area()); + ASSERT_TRUE(request_out->has_result_selection()); + EXPECT_EQ(gfx::Rect(1, 2, 3, 4), request_out->result_selection()); + ASSERT_TRUE(request_out->is_scaled()); + EXPECT_NEAR(0.5f, + static_cast<float>(request_out->scale_to().x()) / + request_out->scale_from().x(), + 0.000001); + EXPECT_NEAR(0.5f, + static_cast<float>(request_out->scale_to().y()) / + request_out->scale_from().y(), + 0.000001); + + // A CopyOutputRequest with all three of: area, result selection, and scale + // ratio; should be transformed into one with an updated area and combined + // scale ratio. + request_in = CopyOutputRequest::CreateStubForTesting(); + request_in->set_area(gfx::Rect(10, 20, 30, 40)); + request_in->set_result_selection(gfx::Rect(1, 2, 3, 4)); + // Request has a 3X scale in X, and 5X scale in Y. + request_in->SetScaleRatio(gfx::Vector2d(1, 1), gfx::Vector2d(3, 5)); + effect_tree.AddCopyRequest(effect_node.id, std::move(request_in)); + requests_out.clear(); + effect_tree.TakeCopyRequestsAndTransformToSurface(effect_node.id, + &requests_out); + ASSERT_EQ(1u, requests_out.size()); + request_out = requests_out.front().get(); + ASSERT_TRUE(request_out->has_area()); + EXPECT_EQ(gfx::Rect(20, 40, 60, 80), request_out->area()); + ASSERT_TRUE(request_out->has_result_selection()); + EXPECT_EQ(gfx::Rect(1, 2, 3, 4), request_out->result_selection()); + ASSERT_TRUE(request_out->is_scaled()); + EXPECT_NEAR(3.0f / 2.0f, + static_cast<float>(request_out->scale_to().x()) / + request_out->scale_from().x(), + 0.000001); + EXPECT_NEAR(5.0f / 2.0f, + static_cast<float>(request_out->scale_to().y()) / + request_out->scale_from().y(), + 0.000001); +} + +// Tests that a good CopyOutputRequest which becomes transformed into an invalid +// one is dropped (i.e., the requestor would get an "empty response" in its +// result callback). The scaling transform in this test is so extreme that it +// would result in an illegal adjustment to the CopyOutputRequest's scale ratio. +TEST(EffectTreeTest, CopyOutputRequestsThatBecomeIllegalAreDropped) { + using viz::CopyOutputRequest; + + PropertyTrees property_trees; + + TransformTree& transform_tree = property_trees.transform_tree; + TransformNode contents_root; + contents_root.local.Scale(1.0f / 1.0e9f, 1.0f / 1.0e9f); + contents_root.source_node_id = 0; + contents_root.id = transform_tree.Insert(contents_root, 0); + transform_tree.UpdateTransforms(contents_root.id); + + EffectTree& effect_tree = property_trees.effect_tree; + EffectNode effect_node; + effect_node.has_render_surface = true; + effect_node.has_copy_request = true; + effect_node.transform_id = contents_root.id; + effect_node.id = effect_tree.Insert(effect_node, 0); + effect_tree.UpdateEffects(effect_node.id); + + auto request_in = CopyOutputRequest::CreateStubForTesting(); + request_in->set_area(gfx::Rect(10, 20, 30, 40)); + request_in->set_result_selection(gfx::Rect(1, 2, 3, 4)); + request_in->SetScaleRatio(gfx::Vector2d(1, 1), gfx::Vector2d(3, 5)); + effect_tree.AddCopyRequest(effect_node.id, std::move(request_in)); + std::vector<std::unique_ptr<CopyOutputRequest>> requests_out; + effect_tree.TakeCopyRequestsAndTransformToSurface(effect_node.id, + &requests_out); + EXPECT_TRUE(requests_out.empty()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h index 5ae47500340..42d4fd2975c 100644 --- a/chromium/cc/trees/proxy.h +++ b/chromium/cc/trees/proxy.h @@ -50,14 +50,16 @@ class CC_EXPORT Proxy { virtual void SetNeedsRedraw(const gfx::Rect& damage_rect) = 0; virtual void SetNextCommitWaitsForActivation() = 0; + // Returns true if an animate or commit has been requested, and hasn't + // completed yet. + virtual bool RequestedAnimatePending() = 0; + virtual void NotifyInputThrottledUntilCommit() = 0; // Defers commits until it is reset. It is only supported when using a // scheduler. virtual void SetDeferCommits(bool defer_commits) = 0; - virtual void MainThreadHasStoppedFlinging() = 0; - virtual bool CommitRequested() const = 0; // Must be called before using the proxy. @@ -80,7 +82,7 @@ class CC_EXPORT Proxy { virtual void SetURLForUkm(const GURL& url) = 0; - virtual void ClearHistoryOnNavigation() = 0; + virtual void ClearHistory() = 0; virtual void SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer) = 0; diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index 06b051455ed..f750ec063f8 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -27,6 +27,7 @@ #include "components/viz/common/gpu/context_provider.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "services/metrics/public/cpp/ukm_recorder.h" +#include "ui/gfx/presentation_feedback.h" namespace cc { @@ -136,7 +137,7 @@ void ProxyImpl::InitializeLayerTreeFrameSinkOnImpl( proxy_main_frame_sink_bound_weak_ptr_ = proxy_main_frame_sink_bound_weak_ptr; LayerTreeHostImpl* host_impl = host_impl_.get(); - bool success = host_impl->InitializeRenderer(layer_tree_frame_sink); + bool success = host_impl->InitializeFrameSink(layer_tree_frame_sink); MainThreadTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&ProxyMain::DidInitializeLayerTreeFrameSink, proxy_main_weak_ptr_, success)); @@ -144,11 +145,6 @@ void ProxyImpl::InitializeLayerTreeFrameSinkOnImpl( scheduler_->DidCreateAndInitializeLayerTreeFrameSink(); } -void ProxyImpl::MainThreadHasStoppedFlingingOnImpl() { - DCHECK(IsImplThread()); - host_impl_->MainThreadHasStoppedFlinging(); -} - void ProxyImpl::SetInputThrottledUntilCommitOnImpl(bool is_throttled) { DCHECK(IsImplThread()); if (is_throttled == input_throttled_until_commit_) @@ -463,9 +459,11 @@ void ProxyImpl::DidCompletePageScaleAnimationOnImplThread() { proxy_main_weak_ptr_)); } -void ProxyImpl::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) { +void ProxyImpl::OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, + bool skip_draw) { DCHECK(IsImplThread()); - scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); + scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw, + skip_draw); } void ProxyImpl::NeedsImplSideInvalidation(bool needs_first_draw_on_activation) { @@ -479,14 +477,13 @@ void ProxyImpl::NotifyImageDecodeRequestFinished() { } void ProxyImpl::DidPresentCompositorFrameOnImplThread( - const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) { + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback) { MainThreadTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&ProxyMain::DidPresentCompositorFrame, - proxy_main_weak_ptr_, source_frames, time, - refresh, flags)); + proxy_main_weak_ptr_, frame_token, + std::move(callbacks), feedback)); } bool ProxyImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { @@ -611,10 +608,10 @@ void ProxyImpl::ScheduledActionPrepareTiles() { host_impl_->PrepareTiles(); } -void ProxyImpl::ScheduledActionInvalidateLayerTreeFrameSink() { +void ProxyImpl::ScheduledActionInvalidateLayerTreeFrameSink(bool needs_redraw) { TRACE_EVENT0("cc", "ProxyImpl::ScheduledActionInvalidateLayerTreeFrameSink"); DCHECK(IsImplThread()); - host_impl_->InvalidateLayerTreeFrameSink(); + host_impl_->InvalidateLayerTreeFrameSink(needs_redraw); } void ProxyImpl::ScheduledActionPerformImplSideInvalidation() { @@ -727,10 +724,10 @@ void ProxyImpl::SetURLForUkm(const GURL& url) { host_impl_->ukm_manager()->SetSourceURL(url); } -void ProxyImpl::ClearHistoryOnNavigation() { +void ProxyImpl::ClearHistory() { DCHECK(IsImplThread()); DCHECK(IsMainThreadBlocked()); - scheduler_->ClearHistoryOnNavigation(); + scheduler_->ClearHistory(); } void ProxyImpl::SetRenderFrameObserver( diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h index 467f0e616d8..a6b9fa279b5 100644 --- a/chromium/cc/trees/proxy_impl.h +++ b/chromium/cc/trees/proxy_impl.h @@ -38,7 +38,6 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, LayerTreeFrameSink* layer_tree_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); void SetDeferCommitsOnImpl(bool defer_commits) const; void SetNeedsRedrawOnImpl(const gfx::Rect& damage_rect); @@ -55,7 +54,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, base::TimeTicks main_thread_start_time, bool hold_commit_for_activation); void SetURLForUkm(const GURL& url); - void ClearHistoryOnNavigation(); + void ClearHistory(); void SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer); @@ -97,14 +96,14 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, void WillPrepareTiles() override; void DidPrepareTiles() override; void DidCompletePageScaleAnimationOnImplThread() override; - void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) override; + void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, + bool skip_draw) override; void NeedsImplSideInvalidation(bool needs_first_draw_on_activation) override; void NotifyImageDecodeRequestFinished() override; void DidPresentCompositorFrameOnImplThread( - const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) override; + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback) override; // SchedulerClient implementation bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override; @@ -118,7 +117,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, void ScheduledActionActivateSyncTree() override; void ScheduledActionBeginLayerTreeFrameSinkCreation() override; void ScheduledActionPrepareTiles() override; - void ScheduledActionInvalidateLayerTreeFrameSink() override; + void ScheduledActionInvalidateLayerTreeFrameSink(bool needs_redraw) override; void ScheduledActionPerformImplSideInvalidation() override; void SendBeginMainFrameNotExpectedSoon() override; void ScheduledActionBeginMainFrameNotExpectedUntil( diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 668e674e30d..334c323e297 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -300,7 +300,7 @@ void ProxyMain::BeginMainFrame( // commit. ui::LatencyInfo new_latency_info(ui::SourceEventType::FRAME); new_latency_info.AddLatencyNumberWithTimestamp( - ui::LATENCY_BEGIN_FRAME_RENDERER_MAIN_COMPONENT, 0, + ui::LATENCY_BEGIN_FRAME_RENDERER_MAIN_COMPONENT, begin_main_frame_state->begin_frame_args.frame_time, 1); layer_tree_host_->QueueSwapPromise( std::make_unique<LatencyInfoSwapPromise>(new_latency_info)); @@ -331,12 +331,12 @@ void ProxyMain::BeginMainFrame( layer_tree_host_->DidBeginMainFrame(); } -void ProxyMain::DidPresentCompositorFrame(const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) { - layer_tree_host_->DidPresentCompositorFrame(source_frames, time, refresh, - flags); +void ProxyMain::DidPresentCompositorFrame( + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback) { + layer_tree_host_->DidPresentCompositorFrame(frame_token, std::move(callbacks), + feedback); } bool ProxyMain::IsStarted() const { @@ -418,6 +418,10 @@ void ProxyMain::SetNextCommitWaitsForActivation() { commit_waits_for_activation_ = true; } +bool ProxyMain::RequestedAnimatePending() { + return max_requested_pipeline_stage_ >= ANIMATE_PIPELINE_STAGE; +} + void ProxyMain::NotifyInputThrottledUntilCommit() { DCHECK(IsMainThread()); ImplThreadTaskRunner()->PostTask( @@ -450,13 +454,6 @@ bool ProxyMain::CommitRequested() const { max_requested_pipeline_stage_ >= COMMIT_PIPELINE_STAGE; } -void ProxyMain::MainThreadHasStoppedFlinging() { - DCHECK(IsMainThread()); - ImplThreadTaskRunner()->PostTask( - FROM_HERE, base::BindOnce(&ProxyImpl::MainThreadHasStoppedFlingingOnImpl, - base::Unretained(proxy_impl_.get()))); -} - void ProxyMain::Start() { DCHECK(IsMainThread()); DCHECK(layer_tree_host_->IsThreaded()); @@ -597,11 +594,11 @@ void ProxyMain::SetURLForUkm(const GURL& url) { base::Unretained(proxy_impl_.get()), url)); } -void ProxyMain::ClearHistoryOnNavigation() { +void ProxyMain::ClearHistory() { // Must only be called from the impl thread during commit. DCHECK(task_runner_provider_->IsImplThread()); DCHECK(task_runner_provider_->IsMainThreadBlocked()); - proxy_impl_->ClearHistoryOnNavigation(); + proxy_impl_->ClearHistory(); } void ProxyMain::SetRenderFrameObserver( diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h index b280ca948ae..9e32ce2d311 100644 --- a/chromium/cc/trees/proxy_main.h +++ b/chromium/cc/trees/proxy_main.h @@ -52,10 +52,10 @@ class CC_EXPORT ProxyMain : public Proxy { void DidCompletePageScaleAnimation(); void BeginMainFrame( std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state); - void DidPresentCompositorFrame(const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags); + void DidPresentCompositorFrame( + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback); CommitPipelineStage max_requested_pipeline_stage() const { return max_requested_pipeline_stage_; @@ -79,10 +79,10 @@ class CC_EXPORT ProxyMain : public Proxy { void SetNeedsCommit() override; void SetNeedsRedraw(const gfx::Rect& damage_rect) override; void SetNextCommitWaitsForActivation() override; + bool RequestedAnimatePending() override; void NotifyInputThrottledUntilCommit() override; void SetDeferCommits(bool defer_commits) override; bool CommitRequested() const override; - void MainThreadHasStoppedFlinging() override; void Start() override; void Stop() override; bool SupportsImplScrolling() const override; @@ -94,7 +94,7 @@ class CC_EXPORT ProxyMain : public Proxy { bool animate) override; void RequestBeginMainFrameNotExpected(bool new_state) override; void SetURLForUkm(const GURL& url) override; - void ClearHistoryOnNavigation() override; + void ClearHistory() override; void SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer) override; diff --git a/chromium/cc/trees/render_frame_metadata.cc b/chromium/cc/trees/render_frame_metadata.cc index 5678f5c8987..82340bb7fff 100644 --- a/chromium/cc/trees/render_frame_metadata.cc +++ b/chromium/cc/trees/render_frame_metadata.cc @@ -4,6 +4,8 @@ #include "cc/trees/render_frame_metadata.h" +#include "build/build_config.h" + namespace cc { RenderFrameMetadata::RenderFrameMetadata() = default; @@ -15,23 +17,6 @@ RenderFrameMetadata::RenderFrameMetadata(RenderFrameMetadata&& other) = default; RenderFrameMetadata::~RenderFrameMetadata() {} -// static -bool RenderFrameMetadata::HasAlwaysUpdateMetadataChanged( - const RenderFrameMetadata& rfm1, - const RenderFrameMetadata& rfm2) { - return rfm1.root_background_color != rfm2.root_background_color || - rfm1.is_scroll_offset_at_top != rfm2.is_scroll_offset_at_top || - rfm1.selection != rfm2.selection || - rfm1.is_mobile_optimized != rfm2.is_mobile_optimized || - rfm1.device_scale_factor != rfm2.device_scale_factor || - rfm1.viewport_size_in_pixels != rfm2.viewport_size_in_pixels || - rfm1.local_surface_id != rfm2.local_surface_id || - rfm1.top_controls_height != rfm2.top_controls_height || - rfm1.top_controls_shown_ratio != rfm2.top_controls_shown_ratio || - rfm1.bottom_controls_height != rfm2.bottom_controls_height || - rfm1.bottom_controls_shown_ratio != rfm2.bottom_controls_shown_ratio; -} - RenderFrameMetadata& RenderFrameMetadata::operator=( const RenderFrameMetadata&) = default; @@ -46,11 +31,20 @@ bool RenderFrameMetadata::operator==(const RenderFrameMetadata& other) const { is_mobile_optimized == other.is_mobile_optimized && device_scale_factor == other.device_scale_factor && viewport_size_in_pixels == other.viewport_size_in_pixels && - local_surface_id == other.local_surface_id && + page_scale_factor == other.page_scale_factor && +#if defined(OS_ANDROID) top_controls_height == other.top_controls_height && top_controls_shown_ratio == other.top_controls_shown_ratio && bottom_controls_height == other.bottom_controls_height && - bottom_controls_shown_ratio == other.bottom_controls_shown_ratio; + bottom_controls_shown_ratio == other.bottom_controls_shown_ratio && + min_page_scale_factor == other.min_page_scale_factor && + max_page_scale_factor == other.max_page_scale_factor && + root_overflow_y_hidden == other.root_overflow_y_hidden && + scrollable_viewport_size == other.scrollable_viewport_size && + root_layer_size == other.root_layer_size && + has_transparent_background == other.has_transparent_background && +#endif + local_surface_id == other.local_surface_id; } bool RenderFrameMetadata::operator!=(const RenderFrameMetadata& other) const { diff --git a/chromium/cc/trees/render_frame_metadata.h b/chromium/cc/trees/render_frame_metadata.h index d07d7d4bfc6..e86ff1efe9c 100644 --- a/chromium/cc/trees/render_frame_metadata.h +++ b/chromium/cc/trees/render_frame_metadata.h @@ -6,11 +6,13 @@ #define CC_TREES_RENDER_FRAME_METADATA_H_ #include "base/optional.h" +#include "build/build_config.h" #include "cc/cc_export.h" #include "components/viz/common/quads/selection.h" #include "components/viz/common/surfaces/local_surface_id.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_f.h" #include "ui/gfx/geometry/vector2d_f.h" #include "ui/gfx/selection_bound.h" @@ -23,12 +25,6 @@ class CC_EXPORT RenderFrameMetadata { RenderFrameMetadata(RenderFrameMetadata&& other); ~RenderFrameMetadata(); - // Certain fields should always have their changes reported. This will return - // true when there is a difference between |rfm1| and |rfm2| for those fields. - // These fields have a low frequency rate of change. - static bool HasAlwaysUpdateMetadataChanged(const RenderFrameMetadata& rfm1, - const RenderFrameMetadata& rfm2); - RenderFrameMetadata& operator=(const RenderFrameMetadata&); RenderFrameMetadata& operator=(RenderFrameMetadata&& other); bool operator==(const RenderFrameMetadata& other) const; @@ -67,6 +63,9 @@ class CC_EXPORT RenderFrameMetadata { // The last viz::LocalSurfaceId used to submit a CompositorFrame. base::Optional<viz::LocalSurfaceId> local_surface_id; + float page_scale_factor = 1.f; + +#if defined(OS_ANDROID) // Used to position the Android location top bar and page content, whose // precise position is computed by the renderer compositor. float top_controls_height = 0.f; @@ -76,6 +75,20 @@ class CC_EXPORT RenderFrameMetadata { // renderer compositor. float bottom_controls_height = 0.f; float bottom_controls_shown_ratio = 0.f; + + // These limits can be used together with the scroll/scale fields above to + // determine if scrolling/scaling in a particular direction is possible. + float min_page_scale_factor = 0.f; + float max_page_scale_factor = 0.f; + bool root_overflow_y_hidden = false; + + gfx::SizeF scrollable_viewport_size; + gfx::SizeF root_layer_size; + + // Returns whether the root RenderPass of the CompositorFrame has a + // transparent background color. + bool has_transparent_background = false; +#endif }; } // namespace cc diff --git a/chromium/cc/trees/render_frame_metadata_observer.h b/chromium/cc/trees/render_frame_metadata_observer.h index 90491d70ab9..69037c5ef5b 100644 --- a/chromium/cc/trees/render_frame_metadata_observer.h +++ b/chromium/cc/trees/render_frame_metadata_observer.h @@ -9,9 +9,11 @@ #include "cc/cc_export.h" #include "cc/trees/render_frame_metadata.h" -namespace cc { +namespace viz { +class CompositorFrameMetadata; +} -class FrameTokenAllocator; +namespace cc { // Observes RenderFrameMetadata associated with the submission of a frame. // LayerTreeHostImpl will create the metadata when submitting a CompositorFrame. @@ -24,12 +26,13 @@ class CC_EXPORT RenderFrameMetadataObserver { // Binds on the current thread. This should only be called from the compositor // thread. - virtual void BindToCurrentThread( - FrameTokenAllocator* frame_token_allocator) = 0; + virtual void BindToCurrentThread() = 0; // Notification of the RendarFrameMetadata for the frame being submitted to // the display compositor. - virtual void OnRenderFrameSubmission(RenderFrameMetadata metadata) = 0; + virtual void OnRenderFrameSubmission( + const RenderFrameMetadata& render_frame_metadata, + viz::CompositorFrameMetadata* compositor_frame_metadata) = 0; private: DISALLOW_COPY_AND_ASSIGN(RenderFrameMetadataObserver); diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index 28c45b6196b..1d199d2f6d4 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -139,7 +139,7 @@ void SingleThreadProxy::SetLayerTreeFrameSink( { DebugScopedSetMainThreadBlocked main_thread_blocked(task_runner_provider_); DebugScopedSetImplThread impl(task_runner_provider_); - success = host_impl_->InitializeRenderer(layer_tree_frame_sink); + success = host_impl_->InitializeFrameSink(layer_tree_frame_sink); } if (success) { @@ -257,6 +257,10 @@ void SingleThreadProxy::SetNextCommitWaitsForActivation() { DCHECK(task_runner_provider_->IsMainThread()); } +bool SingleThreadProxy::RequestedAnimatePending() { + return animate_requested_ || commit_requested_; +} + void SingleThreadProxy::SetDeferCommits(bool defer_commits) { DCHECK(task_runner_provider_->IsMainThread()); // Deferring commits only makes sense if there's a scheduler. @@ -447,7 +451,8 @@ void SingleThreadProxy::DidReceiveCompositorFrameAckOnImplThread() { } void SingleThreadProxy::OnDrawForLayerTreeFrameSink( - bool resourceless_software_draw) { + bool resourceless_software_draw, + bool skip_draw) { NOTREACHED() << "Implemented by ThreadProxy for synchronous compositor."; } @@ -473,12 +478,11 @@ void SingleThreadProxy::NotifyImageDecodeRequestFinished() { } void SingleThreadProxy::DidPresentCompositorFrameOnImplThread( - const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) { - layer_tree_host_->DidPresentCompositorFrame(source_frames, time, refresh, - flags); + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback) { + layer_tree_host_->DidPresentCompositorFrame(frame_token, std::move(callbacks), + feedback); } void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) { @@ -521,7 +525,13 @@ void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time, #if DCHECK_IS_ON() DCHECK(inside_impl_frame_); #endif + animate_requested_ = false; + // Prevent new commits from being requested inside DoBeginMainFrame. + // Note: We do not want to prevent SetNeedsAnimate from requesting + // a commit here. + commit_requested_ = true; DoBeginMainFrame(begin_frame_args); + commit_requested_ = false; DoPainting(); DoCommit(); @@ -545,8 +555,7 @@ void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time, if (raster) { LayerTreeHostImpl::FrameData frame; - frame.begin_frame_ack = viz::BeginFrameAck( - begin_frame_args.source_id, begin_frame_args.sequence_number, true); + frame.begin_frame_ack = viz::BeginFrameAck(begin_frame_args, true); DoComposite(&frame); } @@ -638,10 +647,10 @@ bool SingleThreadProxy::MainFrameWillHappenForTesting() { return scheduler_on_impl_thread_->MainFrameForTestingWillHappen(); } -void SingleThreadProxy::ClearHistoryOnNavigation() { +void SingleThreadProxy::ClearHistory() { DCHECK(task_runner_provider_->IsImplThread()); if (scheduler_on_impl_thread_) - scheduler_on_impl_thread_->ClearHistoryOnNavigation(); + scheduler_on_impl_thread_->ClearHistory(); } void SingleThreadProxy::SetRenderFrameObserver( @@ -744,7 +753,7 @@ void SingleThreadProxy::BeginMainFrame( // know we will commit since QueueSwapPromise itself requests a commit. ui::LatencyInfo new_latency_info(ui::SourceEventType::FRAME); new_latency_info.AddLatencyNumberWithTimestamp( - ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT, 0, begin_frame_args.frame_time, + ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT, begin_frame_args.frame_time, 1); layer_tree_host_->QueueSwapPromise( std::make_unique<LatencyInfoSwapPromise>(new_latency_info)); @@ -834,7 +843,8 @@ void SingleThreadProxy::ScheduledActionPrepareTiles() { host_impl_->PrepareTiles(); } -void SingleThreadProxy::ScheduledActionInvalidateLayerTreeFrameSink() { +void SingleThreadProxy::ScheduledActionInvalidateLayerTreeFrameSink( + bool needs_redraw) { // This is an Android WebView codepath, which only uses multi-thread // compositor. So this should not occur in single-thread mode. NOTREACHED() << "Android Webview use-case, so multi-thread only"; diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h index f505320da40..0ff0f79de6e 100644 --- a/chromium/cc/trees/single_thread_proxy.h +++ b/chromium/cc/trees/single_thread_proxy.h @@ -49,10 +49,10 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void SetNeedsCommit() override; void SetNeedsRedraw(const gfx::Rect& damage_rect) override; void SetNextCommitWaitsForActivation() override; + bool RequestedAnimatePending() override; void NotifyInputThrottledUntilCommit() override {} void SetDeferCommits(bool defer_commits) override; bool CommitRequested() const override; - void MainThreadHasStoppedFlinging() override {} void Start() override; void Stop() override; void SetMutator(std::unique_ptr<LayerTreeMutator> mutator) override; @@ -63,7 +63,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, // layout tests. This will still get called in the latter case, but we don't // need to record UKM in that case. } - void ClearHistoryOnNavigation() override; + void ClearHistory() override; void SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer) override; @@ -85,7 +85,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void ScheduledActionActivateSyncTree() override; void ScheduledActionBeginLayerTreeFrameSinkCreation() override; void ScheduledActionPrepareTiles() override; - void ScheduledActionInvalidateLayerTreeFrameSink() override; + void ScheduledActionInvalidateLayerTreeFrameSink(bool needs_redraw) override; void ScheduledActionPerformImplSideInvalidation() override; void SendBeginMainFrameNotExpectedSoon() override; void ScheduledActionBeginMainFrameNotExpectedUntil( @@ -117,15 +117,15 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void WillPrepareTiles() override; void DidPrepareTiles() override; void DidCompletePageScaleAnimationOnImplThread() override; - void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) override; + void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw, + bool skip_draw) override; void NeedsImplSideInvalidation(bool needs_first_draw_on_activation) override; void RequestBeginMainFrameNotExpected(bool new_state) override; void NotifyImageDecodeRequestFinished() override; void DidPresentCompositorFrameOnImplThread( - const std::vector<int>& source_frames, - base::TimeTicks time, - base::TimeDelta refresh, - uint32_t flags) override; + uint32_t frame_token, + std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + const gfx::PresentationFeedback& feedback) override; void RequestNewLayerTreeFrameSink(); diff --git a/chromium/cc/trees/swap_promise.h b/chromium/cc/trees/swap_promise.h index c56b07155b3..b8663a61aa5 100644 --- a/chromium/cc/trees/swap_promise.h +++ b/chromium/cc/trees/swap_promise.h @@ -12,8 +12,6 @@ namespace cc { -class FrameTokenAllocator; - // When a change to the compositor's state/invalidation/whatever happens, a // Swap Promise can be inserted into LayerTreeHost/LayerTreeImpl, to track // whether the compositor's reply to the new state/invaliadtion/whatever is @@ -42,9 +40,9 @@ class FrameTokenAllocator; // that the promise can be broken at either main or impl thread, e.g. commit // fails on main thread, new frame data has no actual damage so // LayerTreeHostImpl::SwapBuffers() bails out early on impl thread, so don't -// assume that Did*() methods are called at a particular thread. It is better -// to let the subclass carry thread-safe member data and operate on that -// member data in Did*(). +// assume that DidNotSwap() method is called at a particular thread. It is +// better to let the subclass carry thread-safe member data and operate on that +// member data in DidNotSwap(). class CC_EXPORT SwapPromise { public: enum DidNotSwapReason { @@ -54,21 +52,13 @@ class CC_EXPORT SwapPromise { ACTIVATION_FAILS, }; - enum class DidNotSwapAction { - BREAK_PROMISE, - KEEP_ACTIVE, - }; - SwapPromise() {} virtual ~SwapPromise() {} virtual void DidActivate() = 0; - virtual void WillSwap(viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator) = 0; + virtual void WillSwap(viz::CompositorFrameMetadata* metadata) = 0; virtual void DidSwap() = 0; - // Return |KEEP_ACTIVE| if this promise should remain active (should not be - // broken by the owner). - virtual DidNotSwapAction DidNotSwap(DidNotSwapReason reason) = 0; + virtual void DidNotSwap(DidNotSwapReason reason) = 0; // This is called when the main thread starts a (blocking) commit virtual void OnCommit() {} diff --git a/chromium/cc/trees/swap_promise_manager_unittest.cc b/chromium/cc/trees/swap_promise_manager_unittest.cc index 3acf5aa1f4d..d88818e90e2 100644 --- a/chromium/cc/trees/swap_promise_manager_unittest.cc +++ b/chromium/cc/trees/swap_promise_manager_unittest.cc @@ -30,12 +30,9 @@ class MockSwapPromise : public SwapPromise { ~MockSwapPromise() override = default; void DidActivate() override {} - void WillSwap(viz::CompositorFrameMetadata* metadata, - FrameTokenAllocator* frame_token_allocator) override {} + void WillSwap(viz::CompositorFrameMetadata* metadata) override {} void DidSwap() override {} - DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override { - return DidNotSwapAction::BREAK_PROMISE; - } + void DidNotSwap(DidNotSwapReason reason) override {} MOCK_METHOD0(OnCommit, void()); int64_t TraceId() const override { return 0; } }; diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc index 98f3b049e4b..46e83a1b3b7 100644 --- a/chromium/cc/trees/tree_synchronizer_unittest.cc +++ b/chromium/cc/trees/tree_synchronizer_unittest.cc @@ -17,7 +17,9 @@ #include "cc/animation/animation_host.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/picture_layer.h" #include "cc/test/animation_test_common.h" +#include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_impl_task_runner_provider.h" #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_rendering_stats_instrumentation.h" @@ -451,7 +453,8 @@ TEST_F(TreeSynchronizerTest, SyncMaskLayer) { layer_tree_root->AddChild(Layer::Create()); // First child gets a mask layer. - scoped_refptr<Layer> mask_layer = Layer::Create(); + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask_layer = PictureLayer::Create(&client); layer_tree_root->children()[0]->SetMaskLayer(mask_layer.get()); host_->SetRootLayer(layer_tree_root); @@ -571,7 +574,7 @@ TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) { // the pending base and active base must be the same at this stage. scoped_refptr<SyncedScrollOffset> scroll_layer_offset = new SyncedScrollOffset; - scroll_layer_offset->PushMainToPending(scroll_layer->scroll_offset()); + scroll_layer_offset->PushMainToPending(scroll_layer->CurrentScrollOffset()); scroll_layer_offset->PushPendingToActive(); EXPECT_TRUE(AreScrollOffsetsEqual( scroll_layer_offset.get(), @@ -582,7 +585,7 @@ TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) { scoped_refptr<SyncedScrollOffset> transient_scroll_layer_offset = new SyncedScrollOffset; transient_scroll_layer_offset->PushMainToPending( - transient_scroll_layer->scroll_offset()); + transient_scroll_layer->CurrentScrollOffset()); transient_scroll_layer_offset->PushPendingToActive(); EXPECT_TRUE( AreScrollOffsetsEqual(transient_scroll_layer_offset.get(), @@ -604,7 +607,7 @@ TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) { scroll_tree.CollectScrollDeltas(scroll_info.get(), ElementId()); host_->proxy()->SetNeedsCommit(); host_->ApplyScrollAndScale(scroll_info.get()); - EXPECT_EQ(gfx::ScrollOffset(20, 30), scroll_layer->scroll_offset()); + EXPECT_EQ(gfx::ScrollOffset(20, 30), scroll_layer->CurrentScrollOffset()); scroll_layer->SetScrollOffset(gfx::ScrollOffset(100, 100)); // More update to ScrollOffset active delta: gfx::ScrollOffset(20, 20) |