diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/cc | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/cc')
246 files changed, 7865 insertions, 2534 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index 30d71bb6528..19828e3374a 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -175,6 +175,8 @@ cc_component("cc") { "raster/tile_task.h", "raster/zero_copy_raster_buffer_provider.cc", "raster/zero_copy_raster_buffer_provider.h", + "resources/cross_thread_shared_bitmap.cc", + "resources/cross_thread_shared_bitmap.h", "resources/display_resource_provider.cc", "resources/display_resource_provider.h", "resources/layer_tree_resource_provider.cc", @@ -186,9 +188,10 @@ cc_component("cc") { "resources/resource_pool.h", "resources/resource_provider.cc", "resources/resource_provider.h", - "resources/resource_util.h", "resources/scoped_ui_resource.cc", "resources/scoped_ui_resource.h", + "resources/shared_bitmap_id_registrar.cc", + "resources/shared_bitmap_id_registrar.h", "resources/ui_resource_bitmap.cc", "resources/ui_resource_bitmap.h", "resources/ui_resource_client.h", @@ -277,6 +280,8 @@ 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", @@ -372,7 +377,7 @@ cc_component("cc") { "//gpu/command_buffer/client:raster_interface", "//gpu/ipc:gl_in_process_context", "//gpu/skia_bindings:skia_bindings", - "//gpu/vulkan:features", + "//gpu/vulkan:buildflags", "//media", "//mojo/public/cpp/bindings:struct_traits", "//services/metrics/public/cpp:ukm_builders", @@ -589,6 +594,7 @@ cc_test("cc_unittests") { "layers/nine_patch_generator_unittest.cc", "layers/nine_patch_layer_impl_unittest.cc", "layers/nine_patch_layer_unittest.cc", + "layers/painted_overlay_scrollbar_layer_unittest.cc", "layers/painted_scrollbar_layer_impl_unittest.cc", "layers/painted_scrollbar_layer_unittest.cc", "layers/picture_image_layer_unittest.cc", @@ -614,6 +620,7 @@ cc_test("cc_unittests") { "paint/display_item_list_unittest.cc", "paint/filter_operations_unittest.cc", "paint/oop_pixeltest.cc", + "paint/paint_filter_unittest.cc", "paint/paint_image_unittest.cc", "paint/paint_op_buffer_unittest.cc", "paint/paint_op_helper_unittest.cc", @@ -631,6 +638,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/display_resource_provider_unittest.cc", "resources/layer_tree_resource_provider_unittest.cc", "resources/resource_pool_unittest.cc", "resources/resource_provider_unittest.cc", @@ -685,8 +693,8 @@ cc_test("cc_unittests") { # Animation test files. "animation/animation_host_unittest.cc", - "animation/animation_player_unittest.cc", "animation/animation_timeline_unittest.cc", + "animation/animation_unittest.cc", "animation/element_animations_unittest.cc", "animation/keyframe_model_unittest.cc", "animation/keyframed_animation_curve_unittest.cc", @@ -736,7 +744,7 @@ cc_test("cc_unittests") { "//gpu/ipc:gl_in_process_context", "//gpu/skia_bindings", "//media", - "//mojo/edk/system", + "//mojo/edk", "//mojo/public/cpp/bindings", "//testing/gmock", "//testing/gtest", @@ -788,7 +796,7 @@ cc_test("cc_perftests") { "//gpu/command_buffer/client:raster", "//gpu/ipc/common:struct_traits", "//media", - "//mojo/edk/system", + "//mojo/edk", "//mojo/public/cpp/bindings", "//services/viz/public/interfaces", "//skia", diff --git a/chromium/cc/PRESUBMIT.py b/chromium/cc/PRESUBMIT.py index 26909c0b294..3ddbde82ec0 100644 --- a/chromium/cc/PRESUBMIT.py +++ b/chromium/cc/PRESUBMIT.py @@ -318,6 +318,6 @@ def PostUploadHook(cl, change, output_api): cl, [ 'master.tryserver.blink:linux_trusty_blink_rel', - 'master.tryserver.chromium.android:android_optional_gpu_tests_rel', + 'luci.chromium.try:android_optional_gpu_tests_rel', ], 'Automatically added Blink and Android GPU trybots for CQ.') diff --git a/chromium/cc/animation/README.md b/chromium/cc/animation/README.md index 7f68611486b..12359aad369 100644 --- a/chromium/cc/animation/README.md +++ b/chromium/cc/animation/README.md @@ -157,5 +157,5 @@ base. [Smooth Scrolling in Chromium](https://goo.gl/XXwAwk) provides an overview of smooth scrolling. There is further class header documentation in Blink's -[platform/scroll](https://codesearch.chromium.org/chromium/src/third_party/WebKit/Source/platform/scroll/) +[platform/scroll](https://codesearch.chromium.org/chromium/src/third_party/blink/renderer/platform/scroll/) directory. diff --git a/chromium/cc/animation/animation_host.cc b/chromium/cc/animation/animation_host.cc index f6efdcceaec..0b17d50a78e 100644 --- a/chromium/cc/animation/animation_host.cc +++ b/chromium/cc/animation/animation_host.cc @@ -9,6 +9,7 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "cc/animation/animation.h" @@ -659,7 +660,7 @@ void AnimationHost::SetAnimationCounts( // If an animation is being run on the compositor, it will have a ticking // Animation (which will have a corresponding impl-thread version). Therefore // to find the count of main-only animations, we can simply subtract the - // number of ticking players from the total count. + // number of ticking animations from the total count. size_t ticking_animations_count = ticking_animations_.size(); if (main_thread_animations_count_ != total_animations_count - ticking_animations_count) { diff --git a/chromium/cc/animation/animation_host.h b/chromium/cc/animation/animation_host.h index 23f5500a06a..6a639b15e60 100644 --- a/chromium/cc/animation/animation_host.h +++ b/chromium/cc/animation/animation_host.h @@ -10,7 +10,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "cc/animation/animation_export.h" diff --git a/chromium/cc/animation/animation_player_unittest.cc b/chromium/cc/animation/animation_unittest.cc index 6bef965ac93..6bef965ac93 100644 --- a/chromium/cc/animation/animation_player_unittest.cc +++ b/chromium/cc/animation/animation_unittest.cc diff --git a/chromium/cc/animation/element_animations.cc b/chromium/cc/animation/element_animations.cc index ddb9625f83a..cea0c141feb 100644 --- a/chromium/cc/animation/element_animations.cc +++ b/chromium/cc/animation/element_animations.cc @@ -9,7 +9,6 @@ #include <algorithm> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/numerics/ranges.h" #include "cc/animation/animation_delegate.h" #include "cc/animation/animation_events.h" diff --git a/chromium/cc/animation/keyframe_model_unittest.cc b/chromium/cc/animation/keyframe_model_unittest.cc index df583614674..ac13921648b 100644 --- a/chromium/cc/animation/keyframe_model_unittest.cc +++ b/chromium/cc/animation/keyframe_model_unittest.cc @@ -4,7 +4,6 @@ #include "cc/animation/keyframe_model.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "cc/test/animation_test_common.h" #include "cc/trees/target_property.h" diff --git a/chromium/cc/animation/keyframed_animation_curve_unittest.cc b/chromium/cc/animation/keyframed_animation_curve_unittest.cc index f714b28341f..7a13c743e4e 100644 --- a/chromium/cc/animation/keyframed_animation_curve_unittest.cc +++ b/chromium/cc/animation/keyframed_animation_curve_unittest.cc @@ -5,6 +5,7 @@ #include "cc/animation/keyframed_animation_curve.h" #include "cc/animation/transform_operations.h" +#include "cc/test/geometry_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/animation/tween.h" @@ -304,6 +305,102 @@ TEST(KeyframedAnimationCurveTest, RepeatedTransformKeyTimes) { ExpectTranslateX(6.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); } +// Tests that a discrete transform animation (e.g. where one or more keyframes +// is a non-invertible matrix) works as expected. +TEST(KeyframedAnimationCurveTest, DiscreteLinearTransformAnimation) { + gfx::Transform non_invertible_matrix(0, 0, 0, 0, 0, 0); + gfx::Transform identity_matrix; + + std::unique_ptr<KeyframedTransformAnimationCurve> curve( + KeyframedTransformAnimationCurve::Create()); + TransformOperations operations1; + operations1.AppendMatrix(non_invertible_matrix); + TransformOperations operations2; + operations2.AppendMatrix(identity_matrix); + TransformOperations operations3; + operations3.AppendMatrix(non_invertible_matrix); + + curve->AddKeyframe( + TransformKeyframe::Create(base::TimeDelta(), operations1, nullptr)); + curve->AddKeyframe(TransformKeyframe::Create( + base::TimeDelta::FromSecondsD(1.0), operations2, nullptr)); + curve->AddKeyframe(TransformKeyframe::Create( + base::TimeDelta::FromSecondsD(2.0), operations3, nullptr)); + + TransformOperations result; + + // Between 0 and 0.5 seconds, the first keyframe should be returned. + result = curve->GetValue(base::TimeDelta::FromSecondsD(0.01f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); + + result = curve->GetValue(base::TimeDelta::FromSecondsD(0.49f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); + + // Between 0.5 and 1.5 seconds, the middle keyframe should be returned. + result = curve->GetValue(base::TimeDelta::FromSecondsD(0.5f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply()); + + result = curve->GetValue(base::TimeDelta::FromSecondsD(1.49f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply()); + + // Between 1.5 and 2.0 seconds, the last keyframe should be returned. + result = curve->GetValue(base::TimeDelta::FromSecondsD(1.5f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); + + result = curve->GetValue(base::TimeDelta::FromSecondsD(2.0f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); +} + +TEST(KeyframedAnimationCurveTest, DiscreteCubicBezierTransformAnimation) { + gfx::Transform non_invertible_matrix(0, 0, 0, 0, 0, 0); + gfx::Transform identity_matrix; + + std::unique_ptr<KeyframedTransformAnimationCurve> curve( + KeyframedTransformAnimationCurve::Create()); + TransformOperations operations1; + operations1.AppendMatrix(non_invertible_matrix); + TransformOperations operations2; + operations2.AppendMatrix(identity_matrix); + TransformOperations operations3; + operations3.AppendMatrix(non_invertible_matrix); + + // The cubic-bezier here is a nice fairly strong ease-in curve, where 50% + // progression is at approximately 85% of the time. + curve->AddKeyframe(TransformKeyframe::Create( + base::TimeDelta(), operations1, + CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f))); + curve->AddKeyframe(TransformKeyframe::Create( + base::TimeDelta::FromSecondsD(1.0), operations2, + CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f))); + curve->AddKeyframe(TransformKeyframe::Create( + base::TimeDelta::FromSecondsD(2.0), operations3, + CubicBezierTimingFunction::Create(0.75f, 0.25f, 0.9f, 0.4f))); + + TransformOperations result; + + // Due to the cubic-bezier, the first keyframe is returned almost all the way + // to 1 second. + result = curve->GetValue(base::TimeDelta::FromSecondsD(0.01f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); + + result = curve->GetValue(base::TimeDelta::FromSecondsD(0.8f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); + + // Between ~0.85 and ~1.85 seconds, the middle keyframe should be returned. + result = curve->GetValue(base::TimeDelta::FromSecondsD(0.85f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply()); + + result = curve->GetValue(base::TimeDelta::FromSecondsD(1.8f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(identity_matrix, result.Apply()); + + // Finally the last keyframe only takes effect after ~1.85 seconds. + result = curve->GetValue(base::TimeDelta::FromSecondsD(1.85f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); + + result = curve->GetValue(base::TimeDelta::FromSecondsD(2.0f)); + EXPECT_TRANSFORMATION_MATRIX_EQ(non_invertible_matrix, result.Apply()); +} + // Tests that a filter animation with one keyframe works as expected. TEST(KeyframedAnimationCurveTest, OneFilterKeyframe) { std::unique_ptr<KeyframedFilterAnimationCurve> curve( diff --git a/chromium/cc/animation/scroll_offset_animations_impl.h b/chromium/cc/animation/scroll_offset_animations_impl.h index 69341f89fc2..e64456eb9f0 100644 --- a/chromium/cc/animation/scroll_offset_animations_impl.h +++ b/chromium/cc/animation/scroll_offset_animations_impl.h @@ -6,7 +6,6 @@ #define CC_ANIMATION_SCROLL_OFFSET_ANIMATIONS_IMPL_H_ #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "cc/animation/animation_delegate.h" #include "cc/animation/scroll_offset_animation_curve.h" diff --git a/chromium/cc/animation/transform_operations.cc b/chromium/cc/animation/transform_operations.cc index 5e4e8070d25..9b456185030 100644 --- a/chromium/cc/animation/transform_operations.cc +++ b/chromium/cc/animation/transform_operations.cc @@ -53,7 +53,11 @@ gfx::Transform TransformOperations::Apply() const { TransformOperations TransformOperations::Blend(const TransformOperations& from, SkMScalar progress) const { TransformOperations to_return; - BlendInternal(from, progress, &to_return); + if (!BlendInternal(from, progress, &to_return)) { + // If the matrices cannot be blended, fallback to discrete animation logic. + // See https://drafts.csswg.org/css-transforms/#matrix-interpolation + to_return = progress < 0.5 ? from : *this; + } return to_return; } diff --git a/chromium/cc/animation/transform_operations.h b/chromium/cc/animation/transform_operations.h index b74ddacf65b..081da5932c9 100644 --- a/chromium/cc/animation/transform_operations.h +++ b/chromium/cc/animation/transform_operations.h @@ -48,6 +48,9 @@ class CC_ANIMATION_EXPORT TransformOperations { // transforms are baked to matrices (using apply), and the matrices are // then decomposed and interpolated. For more information, see // http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition. + // + // If either of the matrices are non-decomposable for the blend, Blend applies + // discrete interpolation between them based on the progress value. TransformOperations Blend(const TransformOperations& from, SkMScalar progress) const; diff --git a/chromium/cc/animation/transform_operations_unittest.cc b/chromium/cc/animation/transform_operations_unittest.cc index ca73a5a417a..ac7e5289d9f 100644 --- a/chromium/cc/animation/transform_operations_unittest.cc +++ b/chromium/cc/animation/transform_operations_unittest.cc @@ -9,7 +9,6 @@ #include <limits> #include <vector> -#include "base/memory/ptr_util.h" #include "cc/test/geometry_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/animation/tween.h" @@ -857,6 +856,32 @@ TEST(TransformOperationTest, ExtrapolateMatrixBlending) { operations1.Blend(operations2, -0.5).Apply()); } +TEST(TransformOperationTest, NonDecomposableBlend) { + TransformOperations non_decomposible_transform; + gfx::Transform non_decomposible_matrix(0, 0, 0, 0, 0, 0); + non_decomposible_transform.AppendMatrix(non_decomposible_matrix); + + TransformOperations identity_transform; + gfx::Transform identity_matrix; + identity_transform.AppendMatrix(identity_matrix); + + // Before the half-way point, we should return the 'from' matrix. + EXPECT_TRANSFORMATION_MATRIX_EQ( + non_decomposible_matrix, + identity_transform.Blend(non_decomposible_transform, 0.0f).Apply()); + EXPECT_TRANSFORMATION_MATRIX_EQ( + non_decomposible_matrix, + identity_transform.Blend(non_decomposible_transform, 0.49f).Apply()); + + // After the half-way point, we should return the 'to' matrix. + EXPECT_TRANSFORMATION_MATRIX_EQ( + identity_matrix, + identity_transform.Blend(non_decomposible_transform, 0.5f).Apply()); + EXPECT_TRANSFORMATION_MATRIX_EQ( + identity_matrix, + identity_transform.Blend(non_decomposible_transform, 1.0f).Apply()); +} + TEST(TransformOperationTest, BlendedBoundsWhenTypesDoNotMatch) { TransformOperations operations_from; operations_from.AppendScale(2.0, 4.0, 8.0); diff --git a/chromium/cc/animation/worklet_animation.cc b/chromium/cc/animation/worklet_animation.cc index 8ad26c2ae6f..5db8231c413 100644 --- a/chromium/cc/animation/worklet_animation.cc +++ b/chromium/cc/animation/worklet_animation.cc @@ -4,7 +4,6 @@ #include "cc/animation/worklet_animation.h" -#include "base/memory/ptr_util.h" #include "cc/animation/scroll_timeline.h" namespace cc { diff --git a/chromium/cc/animation/worklet_animation_player_unittest.cc b/chromium/cc/animation/worklet_animation_player_unittest.cc new file mode 100644 index 00000000000..d77e0a7bb5d --- /dev/null +++ b/chromium/cc/animation/worklet_animation_player_unittest.cc @@ -0,0 +1,135 @@ +// 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/animation/worklet_animation_player.h" + +#include "cc/animation/scroll_timeline.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 "cc/trees/property_tree.h" +#include "testing/gmock/include/gmock/gmock.h" + +using ::testing::Mock; +using ::testing::Return; +using ::testing::_; + +namespace cc { + +namespace { + +class WorkletAnimationPlayerTest : public AnimationTimelinesTest { + public: + WorkletAnimationPlayerTest() = default; + ~WorkletAnimationPlayerTest() override = default; + + int worklet_player_id_ = 11; +}; + +class MockScrollTimeline : public ScrollTimeline { + public: + MockScrollTimeline() + : ScrollTimeline(ElementId(), ScrollTimeline::Vertical, 0) {} + MOCK_CONST_METHOD1(CurrentTime, double(const ScrollTree&)); +}; + +TEST_F(WorkletAnimationPlayerTest, LocalTimeIsUsedWithAnimations) { + client_.RegisterElement(element_id_, ElementListType::ACTIVE); + client_impl_.RegisterElement(element_id_, ElementListType::PENDING); + client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE); + + 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; + + scoped_refptr<WorkletAnimationPlayer> worklet_player_ = + WorkletAnimationPlayer::Create(worklet_player_id_, "test_name", nullptr); + + worklet_player_->AttachElement(element_id_); + host_->AddAnimationTimeline(timeline_); + timeline_->AttachPlayer(worklet_player_); + + AddOpacityTransitionToPlayer(worklet_player_.get(), duration, start_opacity, + end_opacity, true); + + host_->PushPropertiesTo(host_impl_); + host_impl_->ActivateAnimations(); + + // TODO(majidvp): At the moment the player does not use the local time when + // it is starting. This is because KeyframeModel::ConvertToActiveTime always + // returns the time_offset when starting. We need to change this. + base::TimeTicks time; + time += base::TimeDelta::FromSecondsD(0.1); + TickAnimationsTransferEvents(time, 1u); + + base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2); + worklet_player_->SetLocalTime(local_time); + + host_->PushPropertiesTo(host_impl_); + + TickAnimationsTransferEvents(time, 0u); + + client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE, + expected_opacity); + + 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(WorkletAnimationPlayerTest, + LayerTreeMutatorsIsMutatedWithCorrectInputState) { + MockLayerTreeMutator* mock_mutator = new MockLayerTreeMutator(); + host_impl_->SetLayerTreeMutator( + base::WrapUnique<LayerTreeMutator>(mock_mutator)); + + client_.RegisterElement(element_id_, ElementListType::ACTIVE); + client_impl_.RegisterElement(element_id_, ElementListType::PENDING); + client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE); + + const float start_opacity = .7f; + const float end_opacity = .3f; + const double duration = 1.; + + scoped_refptr<WorkletAnimationPlayer> worklet_player_ = + WorkletAnimationPlayer::Create(worklet_player_id_, "test_name", nullptr); + + worklet_player_->AttachElement(element_id_); + host_->AddAnimationTimeline(timeline_); + timeline_->AttachPlayer(worklet_player_); + + AddOpacityTransitionToPlayer(worklet_player_.get(), duration, start_opacity, + end_opacity, true); + + host_->PushPropertiesTo(host_impl_); + host_impl_->ActivateAnimations(); + + EXPECT_CALL(*mock_mutator, MutateRef(_)); + + base::TimeTicks time; + time += base::TimeDelta::FromSecondsD(0.1); + TickAnimationsTransferEvents(time, 1u); + + Mock::VerifyAndClearExpectations(mock_mutator); +} + +TEST_F(WorkletAnimationPlayerTest, CurrentTimeCorrectlyUsesScrolltimeline) { + auto scroll_timeline = std::make_unique<MockScrollTimeline>(); + EXPECT_CALL(*scroll_timeline, CurrentTime(_)).WillOnce(Return(1234)); + scoped_refptr<WorkletAnimationPlayer> worklet_player = + WorkletAnimationPlayer::Create(worklet_player_id_, "test_name", + std::move(scroll_timeline)); + + ScrollTree scroll_tree; + EXPECT_EQ(1234, + worklet_player->CurrentTime(base::TimeTicks::Now(), scroll_tree)); +} + +} // namespace + +} // namespace cc diff --git a/chromium/cc/animation/worklet_animation_unittest.cc b/chromium/cc/animation/worklet_animation_unittest.cc index 36e65fc0f01..ef89d6fb11f 100644 --- a/chromium/cc/animation/worklet_animation_unittest.cc +++ b/chromium/cc/animation/worklet_animation_unittest.cc @@ -4,6 +4,7 @@ #include "cc/animation/worklet_animation.h" +#include "base/memory/ptr_util.h" #include "cc/animation/scroll_timeline.h" #include "cc/test/animation_test_common.h" #include "cc/test/animation_timelines_test_common.h" diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc index ede2ef124ed..7ac7fb42f5e 100644 --- a/chromium/cc/base/math_util.cc +++ b/chromium/cc/base/math_util.cc @@ -270,6 +270,22 @@ gfx::RectF MathUtil::ProjectClippedRect(const gfx::Transform& transform, return ComputeEnclosingClippedRect(h1, h2, h3, h4); } +gfx::QuadF MathUtil::InverseMapQuadToLocalSpace( + const gfx::Transform& device_transform, + const gfx::QuadF& device_quad) { + gfx::Transform inverse_device_transform(gfx::Transform::kSkipInitialization); + DCHECK(device_transform.IsInvertible()); + bool did_invert = device_transform.GetInverse(&inverse_device_transform); + DCHECK(did_invert); + bool clipped = false; + gfx::QuadF local_quad = + MathUtil::MapQuad(inverse_device_transform, device_quad, &clipped); + // We should not DCHECK(!clipped) here, because anti-aliasing inflation may + // cause device_quad to become clipped. To our knowledge this scenario does + // not need to be handled differently than the unclipped case. + return local_quad; +} + gfx::Rect MathUtil::MapEnclosedRectWith2dAxisAlignedTransform( const gfx::Transform& transform, const gfx::Rect& rect) { diff --git a/chromium/cc/base/math_util.h b/chromium/cc/base/math_util.h index 240f1642a8c..fd9855f8bbb 100644 --- a/chromium/cc/base/math_util.h +++ b/chromium/cc/base/math_util.h @@ -159,6 +159,13 @@ class CC_BASE_EXPORT MathUtil { static gfx::RectF ProjectClippedRect(const gfx::Transform& transform, const gfx::RectF& rect); + // Map device space quad to local space. Device_transform has no 3d + // component since it was flattened, so we don't need to project. We should + // have already checked that the transform was invertible before this call. + static gfx::QuadF InverseMapQuadToLocalSpace( + const gfx::Transform& device_transform, + const gfx::QuadF& device_quad); + // This function is only valid when the transform preserves 2d axis // alignment and the resulting rect will not be clipped. static gfx::Rect MapEnclosedRectWith2dAxisAlignedTransform( diff --git a/chromium/cc/base/rtree.h b/chromium/cc/base/rtree.h index e71f615068e..d6a1e8d8da8 100644 --- a/chromium/cc/base/rtree.h +++ b/chromium/cc/base/rtree.h @@ -71,6 +71,10 @@ class RTree { // Returns the total bounds of all items in this rtree. gfx::Rect GetBounds() const; + // Returns respective bounds of all items in this rtree in the order of items. + // Production code except tracing should not use this method. + std::vector<gfx::Rect> GetAllBoundsForTracing() const; + void Reset(); private: @@ -118,6 +122,9 @@ class RTree { Branch<T> BuildRecursive(std::vector<Branch<T>>* branches, int level); Node<T>* AllocateNodeAtLevel(int level); + void GetAllBoundsRecursive(Node<T>* root, + std::vector<gfx::Rect>* results) const; + // This is the count of data elements (rather than total nodes in the tree) size_t num_data_elements_ = 0u; Branch<T> root_; @@ -330,6 +337,25 @@ gfx::Rect RTree<T>::GetBounds() const { } template <typename T> +std::vector<gfx::Rect> RTree<T>::GetAllBoundsForTracing() const { + std::vector<gfx::Rect> results; + if (num_data_elements_ > 0) + GetAllBoundsRecursive(root_.subtree, &results); + return results; +} + +template <typename T> +void RTree<T>::GetAllBoundsRecursive(Node<T>* node, + std::vector<gfx::Rect>* results) const { + for (uint16_t i = 0; i < node->num_children; ++i) { + if (node->level == 0) + results->push_back(node->children[i].bounds); + else + GetAllBoundsRecursive(node->children[i].subtree, results); + } +} + +template <typename T> void RTree<T>::Reset() { num_data_elements_ = 0; nodes_.clear(); diff --git a/chromium/cc/base/rtree_unittest.cc b/chromium/cc/base/rtree_unittest.cc index 683bbb3f37f..d5242a1a4aa 100644 --- a/chromium/cc/base/rtree_unittest.cc +++ b/chromium/cc/base/rtree_unittest.cc @@ -110,6 +110,7 @@ TEST(RTreeTest, SortedResults) { TEST(RTreeTest, GetBoundsEmpty) { RTree<size_t> rtree; EXPECT_EQ(gfx::Rect(), rtree.GetBounds()); + EXPECT_TRUE(rtree.GetAllBoundsForTracing().empty()); } TEST(RTreeTest, GetBoundsNonOverlapping) { @@ -121,6 +122,7 @@ TEST(RTreeTest, GetBoundsNonOverlapping) { rtree.Build(rects); EXPECT_EQ(gfx::Rect(5, 6, 19, 20), rtree.GetBounds()); + EXPECT_EQ(rects, rtree.GetAllBoundsForTracing()); } TEST(RTreeTest, GetBoundsOverlapping) { @@ -132,6 +134,7 @@ TEST(RTreeTest, GetBoundsOverlapping) { rtree.Build(rects); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), rtree.GetBounds()); + EXPECT_EQ(rects, rtree.GetAllBoundsForTracing()); } TEST(RTreeTest, BuildAfterReset) { @@ -147,10 +150,12 @@ TEST(RTreeTest, BuildAfterReset) { // Resetting should give the same as an empty rtree. rtree.Reset(); EXPECT_EQ(gfx::Rect(), rtree.GetBounds()); + EXPECT_TRUE(rtree.GetAllBoundsForTracing().empty()); // Should be able to rebuild from a reset rtree. rtree.Build(rects); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), rtree.GetBounds()); + EXPECT_EQ(rects, rtree.GetAllBoundsForTracing()); } TEST(RTreeTest, Payload) { diff --git a/chromium/cc/benchmarks/invalidation_benchmark.cc b/chromium/cc/benchmarks/invalidation_benchmark.cc index 173df8ebdab..81ce31b3546 100644 --- a/chromium/cc/benchmarks/invalidation_benchmark.cc +++ b/chromium/cc/benchmarks/invalidation_benchmark.cc @@ -9,7 +9,6 @@ #include <algorithm> #include <limits> -#include "base/memory/ptr_util.h" #include "base/rand_util.h" #include "base/values.h" #include "cc/layers/layer.h" diff --git a/chromium/cc/benchmarks/micro_benchmark_controller.cc b/chromium/cc/benchmarks/micro_benchmark_controller.cc index 57758adf228..3c1b166ac47 100644 --- a/chromium/cc/benchmarks/micro_benchmark_controller.cc +++ b/chromium/cc/benchmarks/micro_benchmark_controller.cc @@ -8,7 +8,6 @@ #include <string> #include "base/callback.h" -#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc index c983cda1cde..5d1afd1357b 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc @@ -98,8 +98,12 @@ void RasterizeAndRecordBenchmark::DidUpdateLayers( DCHECK(!results_.get()); results_ = base::WrapUnique(new base::DictionaryValue); results_->SetInteger("pixels_recorded", record_results_.pixels_recorded); - results_->SetInteger("picture_memory_usage", - static_cast<int>(record_results_.bytes_used)); + results_->SetInteger("painter_memory_usage", + static_cast<int>(record_results_.painter_memory_usage)); + results_->SetInteger("paint_op_memory_usage", + static_cast<int>(record_results_.paint_op_memory_usage)); + results_->SetInteger("paint_op_count", + static_cast<int>(record_results_.paint_op_count)); for (int i = 0; i < RecordingSource::RECORDING_MODE_COUNT; i++) { std::string name = base::StringPrintf("record_time%s_ms", kModeSuffixes[i]); @@ -146,7 +150,8 @@ void RasterizeAndRecordBenchmark::RunOnLayer(PictureLayer* layer) { RecordingModeToPaintingControlSetting( static_cast<RecordingSource::RecordingMode>(mode_index)); base::TimeDelta min_time = base::TimeDelta::Max(); - size_t memory_used = 0; + size_t paint_op_memory_usage = 0; + size_t paint_op_count = 0; scoped_refptr<DisplayItemList> display_list; for (int i = 0; i < record_repeat_count_; ++i) { @@ -162,11 +167,13 @@ void RasterizeAndRecordBenchmark::RunOnLayer(PictureLayer* layer) { display_list, painter->GetApproximateUnsharedMemoryUsage(), layer_tree_host_->recording_scale_factor()); - if (memory_used) { + if (paint_op_memory_usage) { // Verify we are recording the same thing each time. - DCHECK_EQ(memory_used, display_list->BytesUsed()); + DCHECK_EQ(paint_op_memory_usage, display_list->BytesUsed()); + DCHECK_EQ(paint_op_count, display_list->TotalOpCount()); } else { - memory_used = display_list->BytesUsed(); + paint_op_memory_usage = display_list->BytesUsed(); + paint_op_count = display_list->TotalOpCount(); } timer.NextLap(); @@ -178,8 +185,10 @@ void RasterizeAndRecordBenchmark::RunOnLayer(PictureLayer* layer) { } if (mode_index == RecordingSource::RECORD_NORMALLY) { - record_results_.bytes_used += - memory_used + painter->GetApproximateUnsharedMemoryUsage(); + record_results_.painter_memory_usage += + painter->GetApproximateUnsharedMemoryUsage(); + record_results_.paint_op_memory_usage += paint_op_memory_usage; + record_results_.paint_op_count += paint_op_count; record_results_.pixels_recorded += painter->PaintableRegion().width() * painter->PaintableRegion().height(); } @@ -187,9 +196,7 @@ void RasterizeAndRecordBenchmark::RunOnLayer(PictureLayer* layer) { } } -RasterizeAndRecordBenchmark::RecordResults::RecordResults() - : pixels_recorded(0), bytes_used(0) {} - +RasterizeAndRecordBenchmark::RecordResults::RecordResults() = default; RasterizeAndRecordBenchmark::RecordResults::~RecordResults() = default; } // namespace cc diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark.h b/chromium/cc/benchmarks/rasterize_and_record_benchmark.h index 9e865cee45e..efb6266b9fb 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark.h +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark.h @@ -45,8 +45,10 @@ class RasterizeAndRecordBenchmark : public MicroBenchmark { RecordResults(); ~RecordResults(); - int pixels_recorded; - size_t bytes_used; + int pixels_recorded = 0; + size_t painter_memory_usage = 0; + size_t paint_op_memory_usage = 0; + size_t paint_op_count = 0; base::TimeDelta total_best_time[RecordingSource::RECORDING_MODE_COUNT]; }; diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc index 48b578f5b1b..e4d5e25a8aa 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc @@ -153,8 +153,6 @@ void RasterizeAndRecordBenchmarkImpl::DidCompleteCommit( std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); result->SetDouble("rasterize_time_ms", rasterize_results_.total_best_time.InMillisecondsF()); - result->SetDouble("total_pictures_in_pile_size", - static_cast<int>(rasterize_results_.total_memory_usage)); result->SetInteger("pixels_rasterized", rasterize_results_.pixels_rasterized); result->SetInteger("pixels_rasterized_with_non_solid_color", rasterize_results_.pixels_rasterized_with_non_solid_color); @@ -191,7 +189,8 @@ void RasterizeAndRecordBenchmarkImpl::RunOnLayer(PictureLayerImpl* layer) { const LayerTreeSettings& settings = layer->layer_tree_impl()->settings(); std::unique_ptr<PictureLayerTilingSet> tiling_set = PictureLayerTilingSet::Create( - layer->GetTree(), &client, settings.tiling_interest_area_padding, + layer->IsActive() ? ACTIVE_TREE : PENDING_TREE, &client, + settings.tiling_interest_area_padding, settings.skewport_target_time_in_seconds, settings.skewport_extrapolation_limit_in_screen_pixels, settings.max_preraster_distance_in_screen_pixels); @@ -225,17 +224,12 @@ void RasterizeAndRecordBenchmarkImpl::RunOnLayer(PictureLayerImpl* layer) { rasterize_results_.pixels_rasterized += tile_size; rasterize_results_.total_best_time += min_time; } - - const RasterSource* layer_raster_source = layer->GetRasterSource(); - rasterize_results_.total_memory_usage += - layer_raster_source->GetMemoryUsage(); } RasterizeAndRecordBenchmarkImpl::RasterizeResults::RasterizeResults() : pixels_rasterized(0), pixels_rasterized_with_non_solid_color(0), pixels_rasterized_as_opaque(0), - total_memory_usage(0), total_layers(0), total_picture_layers(0), total_picture_layers_with_no_content(0), diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.h b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.h index 5c224da62e6..be8a6de987c 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.h +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.h @@ -42,7 +42,6 @@ class RasterizeAndRecordBenchmarkImpl : public MicroBenchmarkImpl { int pixels_rasterized_with_non_solid_color; int pixels_rasterized_as_opaque; base::TimeDelta total_best_time; - size_t total_memory_usage; int total_layers; int total_picture_layers; int total_picture_layers_with_no_content; diff --git a/chromium/cc/blink/BUILD.gn b/chromium/cc/blink/BUILD.gn index 31c054800a8..8b48a4097a7 100644 --- a/chromium/cc/blink/BUILD.gn +++ b/chromium/cc/blink/BUILD.gn @@ -41,7 +41,7 @@ cc_component("blink") { "//cc", "//cc/paint", "//gpu", - "//third_party/WebKit/public:blink", + "//third_party/blink/public:blink", "//ui/gfx", "//ui/gfx/geometry", ] @@ -68,7 +68,7 @@ cc_test("cc_blink_unittests") { "//skia", "//testing/gmock", "//testing/gtest", - "//third_party/WebKit/public:blink", + "//third_party/blink/public:blink", "//ui/gfx:test_support", "//ui/gfx/geometry", ] diff --git a/chromium/cc/blink/DEPS b/chromium/cc/blink/DEPS index 3054d92a649..fc25dd99ad3 100644 --- a/chromium/cc/blink/DEPS +++ b/chromium/cc/blink/DEPS @@ -1,3 +1,3 @@ include_rules = [ - "+third_party/WebKit/public/platform" + "+third_party/blink/public/platform" ] diff --git a/chromium/cc/blink/scrollbar_impl.cc b/chromium/cc/blink/scrollbar_impl.cc index 0ed7a3377a7..f92b4acb522 100644 --- a/chromium/cc/blink/scrollbar_impl.cc +++ b/chromium/cc/blink/scrollbar_impl.cc @@ -5,8 +5,8 @@ #include "cc/blink/scrollbar_impl.h" #include "base/logging.h" -#include "third_party/WebKit/public/platform/WebScrollbar.h" -#include "third_party/WebKit/public/platform/WebScrollbarThemeGeometry.h" +#include "third_party/blink/public/platform/web_scrollbar.h" +#include "third_party/blink/public/platform/web_scrollbar_theme_geometry.h" using blink::WebScrollbar; @@ -84,6 +84,10 @@ gfx::Rect ScrollbarImpl::NinePatchThumbAperture() const { return geometry_->NinePatchThumbAperture(scrollbar_.get()); } +bool ScrollbarImpl::HasTickmarks() const { + return scrollbar_->HasTickmarks(); +} + void ScrollbarImpl::PaintPart(cc::PaintCanvas* canvas, cc::ScrollbarPart part, const gfx::Rect& content_rect) { @@ -92,6 +96,11 @@ void ScrollbarImpl::PaintPart(cc::PaintCanvas* canvas, return; } + if (part == cc::TICKMARKS) { + painter_.PaintTickmarks(canvas, content_rect); + return; + } + // The following is a simplification of ScrollbarThemeComposite::paint. painter_.PaintScrollbarBackground(canvas, content_rect); diff --git a/chromium/cc/blink/scrollbar_impl.h b/chromium/cc/blink/scrollbar_impl.h index c0534d5b5e7..40157360fc8 100644 --- a/chromium/cc/blink/scrollbar_impl.h +++ b/chromium/cc/blink/scrollbar_impl.h @@ -10,7 +10,7 @@ #include "base/macros.h" #include "cc/input/scrollbar.h" #include "cc/paint/paint_canvas.h" -#include "third_party/WebKit/public/platform/WebScrollbarThemePainter.h" +#include "third_party/blink/public/platform/web_scrollbar_theme_painter.h" namespace blink { class WebScrollbar; @@ -37,6 +37,7 @@ class ScrollbarImpl : public cc::Scrollbar { gfx::Rect TrackRect() const override; float ThumbOpacity() const override; bool NeedsPaintPart(cc::ScrollbarPart part) const override; + bool HasTickmarks() const override; void PaintPart(cc::PaintCanvas* canvas, cc::ScrollbarPart part, const gfx::Rect& content_rect) override; diff --git a/chromium/cc/blink/web_blend_mode.h b/chromium/cc/blink/web_blend_mode.h index 8d39804fa61..998868c299e 100644 --- a/chromium/cc/blink/web_blend_mode.h +++ b/chromium/cc/blink/web_blend_mode.h @@ -5,7 +5,7 @@ #ifndef CC_BLINK_WEB_BLEND_MODE_H_ #define CC_BLINK_WEB_BLEND_MODE_H_ -#include "third_party/WebKit/public/platform/WebBlendMode.h" +#include "third_party/blink/public/platform/web_blend_mode.h" #include "third_party/skia/include/core/SkBlendMode.h" namespace cc_blink { diff --git a/chromium/cc/blink/web_compositor_support_impl.cc b/chromium/cc/blink/web_compositor_support_impl.cc index c3874e45e0d..04d038b181b 100644 --- a/chromium/cc/blink/web_compositor_support_impl.cc +++ b/chromium/cc/blink/web_compositor_support_impl.cc @@ -6,7 +6,6 @@ #include <utility> -#include "base/memory/ptr_util.h" #include "cc/blink/web_content_layer_impl.h" #include "cc/blink/web_display_item_list_impl.h" #include "cc/blink/web_external_texture_layer_impl.h" diff --git a/chromium/cc/blink/web_compositor_support_impl.h b/chromium/cc/blink/web_compositor_support_impl.h index 46748bdcbe7..9a24211b9a2 100644 --- a/chromium/cc/blink/web_compositor_support_impl.h +++ b/chromium/cc/blink/web_compositor_support_impl.h @@ -8,9 +8,9 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "cc/blink/cc_blink_export.h" -#include "third_party/WebKit/public/platform/WebCompositorSupport.h" -#include "third_party/WebKit/public/platform/WebContentLayerClient.h" -#include "third_party/WebKit/public/platform/WebLayer.h" +#include "third_party/blink/public/platform/web_compositor_support.h" +#include "third_party/blink/public/platform/web_content_layer_client.h" +#include "third_party/blink/public/platform/web_layer.h" namespace cc_blink { diff --git a/chromium/cc/blink/web_content_layer_impl.cc b/chromium/cc/blink/web_content_layer_impl.cc index c72dffe6584..5dc2f9c2d81 100644 --- a/chromium/cc/blink/web_content_layer_impl.cc +++ b/chromium/cc/blink/web_content_layer_impl.cc @@ -7,15 +7,14 @@ #include <stddef.h> #include "base/command_line.h" -#include "base/memory/ptr_util.h" #include "cc/base/switches.h" #include "cc/blink/web_display_item_list_impl.h" #include "cc/layers/picture_layer.h" -#include "third_party/WebKit/public/platform/WebContentLayerClient.h" -#include "third_party/WebKit/public/platform/WebFloatPoint.h" -#include "third_party/WebKit/public/platform/WebFloatRect.h" -#include "third_party/WebKit/public/platform/WebRect.h" -#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/blink/public/platform/web_content_layer_client.h" +#include "third_party/blink/public/platform/web_float_point.h" +#include "third_party/blink/public/platform/web_float_rect.h" +#include "third_party/blink/public/platform/web_rect.h" +#include "third_party/blink/public/platform/web_size.h" #include "third_party/skia/include/core/SkMatrix44.h" using cc::PictureLayer; diff --git a/chromium/cc/blink/web_content_layer_impl.h b/chromium/cc/blink/web_content_layer_impl.h index 046b79d0e64..6f82ec478e9 100644 --- a/chromium/cc/blink/web_content_layer_impl.h +++ b/chromium/cc/blink/web_content_layer_impl.h @@ -13,7 +13,7 @@ #include "cc/blink/cc_blink_export.h" #include "cc/blink/web_layer_impl.h" #include "cc/layers/content_layer_client.h" -#include "third_party/WebKit/public/platform/WebContentLayer.h" +#include "third_party/blink/public/platform/web_content_layer.h" namespace blink { class WebContentLayerClient; diff --git a/chromium/cc/blink/web_display_item_list_impl.cc b/chromium/cc/blink/web_display_item_list_impl.cc index 0c4c2ea81de..5be83ce9a64 100644 --- a/chromium/cc/blink/web_display_item_list_impl.cc +++ b/chromium/cc/blink/web_display_item_list_impl.cc @@ -12,8 +12,8 @@ #include "cc/paint/paint_filter.h" #include "cc/paint/paint_op_buffer.h" #include "cc/paint/render_surface_filters.h" -#include "third_party/WebKit/public/platform/WebFloatRect.h" -#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/blink/public/platform/web_float_rect.h" +#include "third_party/blink/public/platform/web_rect.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -84,7 +84,7 @@ void WebDisplayItemListImpl::AppendEndClipPathItem() { void WebDisplayItemListImpl::AppendFloatClipItem( const blink::WebFloatRect& clip_rect) { - bool antialias = false; + bool antialias = true; display_item_list_->StartPaint(); display_item_list_->push<cc::SaveOp>(); display_item_list_->push<cc::ClipRectOp>(gfx::RectFToSkRect(clip_rect), diff --git a/chromium/cc/blink/web_display_item_list_impl.h b/chromium/cc/blink/web_display_item_list_impl.h index de666a0dde3..5db188c85c8 100644 --- a/chromium/cc/blink/web_display_item_list_impl.h +++ b/chromium/cc/blink/web_display_item_list_impl.h @@ -9,8 +9,8 @@ #include "base/memory/ref_counted.h" #include "cc/blink/cc_blink_export.h" #include "cc/paint/display_item_list.h" -#include "third_party/WebKit/public/platform/WebDisplayItemList.h" -#include "third_party/WebKit/public/platform/WebVector.h" +#include "third_party/blink/public/platform/web_display_item_list.h" +#include "third_party/blink/public/platform/web_vector.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "ui/gfx/geometry/point_f.h" diff --git a/chromium/cc/blink/web_display_item_list_impl_unittest.cc b/chromium/cc/blink/web_display_item_list_impl_unittest.cc index 5d920c13e92..f9efd761eed 100644 --- a/chromium/cc/blink/web_display_item_list_impl_unittest.cc +++ b/chromium/cc/blink/web_display_item_list_impl_unittest.cc @@ -7,7 +7,7 @@ #include "cc/paint/display_item_list.h" #include "cc/paint/paint_op_buffer.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebFloatPoint.h" +#include "third_party/blink/public/platform/web_float_point.h" namespace cc_blink { namespace { diff --git a/chromium/cc/blink/web_external_texture_layer_impl.cc b/chromium/cc/blink/web_external_texture_layer_impl.cc index b6d042bce5c..64fbf26cbd4 100644 --- a/chromium/cc/blink/web_external_texture_layer_impl.cc +++ b/chromium/cc/blink/web_external_texture_layer_impl.cc @@ -54,4 +54,12 @@ void WebExternalTextureLayerImpl::SetNearestNeighbor(bool nearest_neighbor) { ->SetNearestNeighbor(nearest_neighbor); } +void WebExternalTextureLayerImpl::SetUV( + const blink::WebFloatPoint left_top, + const blink::WebFloatPoint right_bottom) { + static_cast<TextureLayer*>(layer_->layer()) + ->SetUV(gfx::PointF(left_top.x, left_top.y), + gfx::PointF(right_bottom.x, right_bottom.y)); +} + } // namespace cc_blink diff --git a/chromium/cc/blink/web_external_texture_layer_impl.h b/chromium/cc/blink/web_external_texture_layer_impl.h index e7bea81b8ff..11ca63cf5b1 100644 --- a/chromium/cc/blink/web_external_texture_layer_impl.h +++ b/chromium/cc/blink/web_external_texture_layer_impl.h @@ -9,7 +9,7 @@ #include "base/macros.h" #include "cc/blink/cc_blink_export.h" -#include "third_party/WebKit/public/platform/WebExternalTextureLayer.h" +#include "third_party/blink/public/platform/web_external_texture_layer.h" namespace cc { class TextureLayerClient; @@ -31,6 +31,8 @@ class WebExternalTextureLayerImpl : public blink::WebExternalTextureLayer { void SetPremultipliedAlpha(bool premultiplied) override; void SetBlendBackgroundColor(bool blend) override; void SetNearestNeighbor(bool nearest_neighbor) override; + void SetUV(const blink::WebFloatPoint left_top, + const blink::WebFloatPoint right_bottom) override; private: std::unique_ptr<WebLayerImpl> layer_; diff --git a/chromium/cc/blink/web_image_layer_impl.h b/chromium/cc/blink/web_image_layer_impl.h index 67af12c0929..11b76dc95e7 100644 --- a/chromium/cc/blink/web_image_layer_impl.h +++ b/chromium/cc/blink/web_image_layer_impl.h @@ -10,7 +10,7 @@ #include "base/macros.h" #include "cc/blink/cc_blink_export.h" #include "cc/paint/paint_image.h" -#include "third_party/WebKit/public/platform/WebImageLayer.h" +#include "third_party/blink/public/platform/web_image_layer.h" namespace cc_blink { diff --git a/chromium/cc/blink/web_layer_impl.cc b/chromium/cc/blink/web_layer_impl.cc index 4b3e0caecf5..b8cb7aeb9eb 100644 --- a/chromium/cc/blink/web_layer_impl.cc +++ b/chromium/cc/blink/web_layer_impl.cc @@ -24,12 +24,12 @@ #include "cc/layers/touch_action_region.h" #include "cc/trees/element_id.h" #include "cc/trees/layer_tree_host.h" -#include "third_party/WebKit/public/platform/WebFloatPoint.h" -#include "third_party/WebKit/public/platform/WebFloatRect.h" -#include "third_party/WebKit/public/platform/WebLayerPositionConstraint.h" -#include "third_party/WebKit/public/platform/WebLayerScrollClient.h" -#include "third_party/WebKit/public/platform/WebLayerStickyPositionConstraint.h" -#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/blink/public/platform/web_float_point.h" +#include "third_party/blink/public/platform/web_float_rect.h" +#include "third_party/blink/public/platform/web_layer_position_constraint.h" +#include "third_party/blink/public/platform/web_layer_scroll_client.h" +#include "third_party/blink/public/platform/web_layer_sticky_position_constraint.h" +#include "third_party/blink/public/platform/web_size.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h" @@ -460,8 +460,8 @@ void WebLayerImpl::SetScrollOffsetFromImplSideForTesting( layer_->SetScrollOffsetFromImplSide(offset); } -void WebLayerImpl::SetLayerClient(cc::LayerClient* client) { - layer_->SetLayerClient(client); +void WebLayerImpl::SetLayerClient(base::WeakPtr<cc::LayerClient> client) { + layer_->SetLayerClient(std::move(client)); } const cc::Layer* WebLayerImpl::CcLayer() const { diff --git a/chromium/cc/blink/web_layer_impl.h b/chromium/cc/blink/web_layer_impl.h index 84e3d2b6472..9be421d303f 100644 --- a/chromium/cc/blink/web_layer_impl.h +++ b/chromium/cc/blink/web_layer_impl.h @@ -16,16 +16,16 @@ #include "base/memory/ref_counted.h" #include "cc/blink/cc_blink_export.h" #include "cc/layers/layer_client.h" -#include "third_party/WebKit/public/platform/WebColor.h" -#include "third_party/WebKit/public/platform/WebDoublePoint.h" -#include "third_party/WebKit/public/platform/WebFloatPoint.h" -#include "third_party/WebKit/public/platform/WebFloatSize.h" -#include "third_party/WebKit/public/platform/WebLayer.h" -#include "third_party/WebKit/public/platform/WebPoint.h" -#include "third_party/WebKit/public/platform/WebRect.h" -#include "third_party/WebKit/public/platform/WebSize.h" -#include "third_party/WebKit/public/platform/WebString.h" -#include "third_party/WebKit/public/platform/WebVector.h" +#include "third_party/blink/public/platform/web_color.h" +#include "third_party/blink/public/platform/web_double_point.h" +#include "third_party/blink/public/platform/web_float_point.h" +#include "third_party/blink/public/platform/web_float_size.h" +#include "third_party/blink/public/platform/web_layer.h" +#include "third_party/blink/public/platform/web_point.h" +#include "third_party/blink/public/platform/web_rect.h" +#include "third_party/blink/public/platform/web_size.h" +#include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/public/platform/web_vector.h" #include "third_party/skia/include/core/SkMatrix44.h" namespace cc { @@ -122,7 +122,7 @@ class CC_BLINK_EXPORT WebLayerImpl : public blink::WebLayer { const override; void SetScrollClient(blink::WebLayerScrollClient* client) override; void SetScrollOffsetFromImplSideForTesting(const gfx::ScrollOffset&) override; - void SetLayerClient(cc::LayerClient* client) override; + void SetLayerClient(base::WeakPtr<cc::LayerClient> client) override; const cc::Layer* CcLayer() const override; cc::Layer* CcLayer() override; void SetElementId(const cc::ElementId&) override; diff --git a/chromium/cc/blink/web_layer_impl_fixed_bounds.cc b/chromium/cc/blink/web_layer_impl_fixed_bounds.cc index aa6ccbcf468..dc6b90993c4 100644 --- a/chromium/cc/blink/web_layer_impl_fixed_bounds.cc +++ b/chromium/cc/blink/web_layer_impl_fixed_bounds.cc @@ -5,8 +5,8 @@ #include "cc/blink/web_layer_impl_fixed_bounds.h" #include "cc/layers/layer.h" -#include "third_party/WebKit/public/platform/WebFloatPoint.h" -#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/blink/public/platform/web_float_point.h" +#include "third_party/blink/public/platform/web_size.h" #include "third_party/skia/include/core/SkMatrix44.h" using cc::Layer; diff --git a/chromium/cc/blink/web_layer_impl_fixed_bounds_unittest.cc b/chromium/cc/blink/web_layer_impl_fixed_bounds_unittest.cc index 3dd88c1baa0..354326e36aa 100644 --- a/chromium/cc/blink/web_layer_impl_fixed_bounds_unittest.cc +++ b/chromium/cc/blink/web_layer_impl_fixed_bounds_unittest.cc @@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "cc/blink/web_layer_impl_fixed_bounds.h" #include <vector> #include "cc/animation/animation_host.h" -#include "cc/blink/web_layer_impl_fixed_bounds.h" #include "cc/layers/picture_image_layer.h" #include "cc/test/fake_layer_tree_host.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host_common.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebFloatPoint.h" -#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/blink/public/platform/web_float_point.h" +#include "third_party/blink/public/platform/web_size.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/gfx/geometry/point3_f.h" diff --git a/chromium/cc/blink/web_scrollbar_layer_impl.cc b/chromium/cc/blink/web_scrollbar_layer_impl.cc index 2a2d4e1187e..f501002d486 100644 --- a/chromium/cc/blink/web_scrollbar_layer_impl.cc +++ b/chromium/cc/blink/web_scrollbar_layer_impl.cc @@ -6,7 +6,6 @@ #include <utility> -#include "base/memory/ptr_util.h" #include "cc/blink/scrollbar_impl.h" #include "cc/blink/web_layer_impl.h" #include "cc/layers/layer.h" diff --git a/chromium/cc/blink/web_scrollbar_layer_impl.h b/chromium/cc/blink/web_scrollbar_layer_impl.h index e8e0fe438aa..c80c31df230 100644 --- a/chromium/cc/blink/web_scrollbar_layer_impl.h +++ b/chromium/cc/blink/web_scrollbar_layer_impl.h @@ -9,8 +9,8 @@ #include "base/macros.h" #include "cc/blink/cc_blink_export.h" -#include "third_party/WebKit/public/platform/WebScrollbar.h" -#include "third_party/WebKit/public/platform/WebScrollbarLayer.h" +#include "third_party/blink/public/platform/web_scrollbar.h" +#include "third_party/blink/public/platform/web_scrollbar_layer.h" namespace blink { class WebScrollbarThemeGeometry; diff --git a/chromium/cc/input/browser_controls_offset_manager_unittest.cc b/chromium/cc/input/browser_controls_offset_manager_unittest.cc index 7f039cd66a1..098a701ece5 100644 --- a/chromium/cc/input/browser_controls_offset_manager_unittest.cc +++ b/chromium/cc/input/browser_controls_offset_manager_unittest.cc @@ -9,7 +9,6 @@ #include <memory> #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "cc/input/browser_controls_offset_manager_client.h" #include "cc/layers/layer_impl.h" diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index 04c27fce4c6..312f103e941 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -49,6 +49,9 @@ struct CC_EXPORT InputHandlerScrollResult { // How the browser should handle the overscroll navigation based on the css // property scroll-boundary-behavior. OverscrollBehavior overscroll_behavior; + // The current offset of the currently scrolling node. It is in DIP or + // physical pixels depending on the use-zoom-for-dsf flag. + gfx::Vector2dF current_offset; }; class CC_EXPORT InputHandlerClient { @@ -231,6 +234,16 @@ class CC_EXPORT InputHandler { virtual bool ScrollingShouldSwitchtoMainThread() = 0; + // Sets the initial and target offset for scroll snapping for the currently + // scrolling node and the given natural displacement. + // |natural_displacement_in_viewport| is the estimated total scrolling for + // the active scroll sequence. + // Returns false if their is no position to snap to. + virtual bool GetSnapFlingInfo( + const gfx::Vector2dF& natural_displacement_in_viewport, + gfx::Vector2dF* initial_offset, + gfx::Vector2dF* target_offset) const = 0; + protected: InputHandler() {} virtual ~InputHandler() {} diff --git a/chromium/cc/input/scroll_snap_data.cc b/chromium/cc/input/scroll_snap_data.cc index 31b00d8ed15..58b225849d4 100644 --- a/chromium/cc/input/scroll_snap_data.cc +++ b/chromium/cc/input/scroll_snap_data.cc @@ -4,21 +4,103 @@ #include "cc/input/scroll_snap_data.h" +#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 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.snap_axis == SnapAxis::kX || + area.snap_axis == SnapAxis::kBoth + : area.snap_axis == SnapAxis::kY || + area.snap_axis == SnapAxis::kBoth; +} + +// 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; + + 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; + + 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; +} + +} // namespace -SnapContainerData::SnapContainerData() = default; +SnapContainerData::SnapContainerData() + : proximity_range_(gfx::ScrollOffset(std::numeric_limits<float>::max(), + std::numeric_limits<float>::max())) {} SnapContainerData::SnapContainerData(ScrollSnapType type) - : scroll_snap_type_(type) {} + : scroll_snap_type_(type), + proximity_range_(gfx::ScrollOffset(std::numeric_limits<float>::max(), + std::numeric_limits<float>::max())) {} SnapContainerData::SnapContainerData(ScrollSnapType type, gfx::ScrollOffset max) - : scroll_snap_type_(type), max_position_(max) {} + : scroll_snap_type_(type), + 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() = default; @@ -29,6 +111,7 @@ SnapContainerData& SnapContainerData::operator=( 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; } @@ -37,52 +120,70 @@ void SnapContainerData::AddSnapAreaData(SnapAreaData snap_area_data) { snap_area_list_.push_back(snap_area_data); } -gfx::ScrollOffset SnapContainerData::FindSnapPosition( +bool SnapContainerData::FindSnapPosition( const gfx::ScrollOffset& current_position, bool should_snap_on_x, - bool should_snap_on_y) const { + bool should_snap_on_y, + gfx::ScrollOffset* snap_position) const { SnapAxis axis = scroll_snap_type_.axis; should_snap_on_x &= (axis == SnapAxis::kX || axis == SnapAxis::kBoth); should_snap_on_y &= (axis == SnapAxis::kY || axis == SnapAxis::kBoth); if (!should_snap_on_x && !should_snap_on_y) - return current_position; - - float smallest_distance_x = std::numeric_limits<float>::max(); - float smallest_distance_y = std::numeric_limits<float>::max(); - - gfx::ScrollOffset snap_position = current_position; - - for (const SnapAreaData& snap_area_data : snap_area_list_) { - // TODO(sunyunjia): We should consider visiblity when choosing snap offset. - if (should_snap_on_x && (snap_area_data.snap_axis == SnapAxis::kX || - snap_area_data.snap_axis == SnapAxis::kBoth)) { - float offset = snap_area_data.snap_position.x(); - if (offset == SnapAreaData::kInvalidScrollPosition) - continue; - float distance = std::abs(current_position.x() - offset); - if (distance < smallest_distance_x) { - smallest_distance_x = distance; - snap_position.set_x(offset); - } - } - if (should_snap_on_y && (snap_area_data.snap_axis == SnapAxis::kY || - snap_area_data.snap_axis == SnapAxis::kBoth)) { - float offset = snap_area_data.snap_position.y(); - if (offset == SnapAreaData::kInvalidScrollPosition) - continue; - float distance = std::abs(current_position.y() - offset); - if (distance < smallest_distance_y) { - smallest_distance_y = distance; - snap_position.set_y(offset); - } + return false; + + base::Optional<SnapAreaData> 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_); + } + if (should_snap_on_y) { + closest_y = FindClosestValidArea(SearchAxis::kY, current_position, + current_position, scrollable_region, + proximity_range_, snap_area_list_); + } + + if (!closest_x.has_value() && !closest_y.has_value()) + return false; + + // If snapping in one axis pushes off-screen the other snap area, this snap + // position is invalid. https://drafts.csswg.org/css-scroll-snap-1/#snap-scope + // In this case, we choose the axis whose snap area is closer, and find a + // mutual visible snap area on the other axis. + 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_); } } - return snap_position; + + *snap_position = current_position; + if (closest_x.has_value()) + snap_position->set_x(closest_x.value().snap_position.x()); + if (closest_y.has_value()) + snap_position->set_y(closest_y.value().snap_position.y()); + + return true; } std::ostream& operator<<(std::ostream& ostream, const SnapAreaData& area_data) { - return ostream << area_data.snap_position.x() << ", " - << area_data.snap_position.y(); + return ostream << area_data.snap_position.ToString() << "\t" + << "visible in: " << area_data.visible_region.ToString(); } std::ostream& operator<<(std::ostream& ostream, diff --git a/chromium/cc/input/scroll_snap_data.h b/chromium/cc/input/scroll_snap_data.h index 1ae42c33405..dbc0b9128f5 100644 --- a/chromium/cc/input/scroll_snap_data.h +++ b/chromium/cc/input/scroll_snap_data.h @@ -8,6 +8,7 @@ #include <vector> #include "cc/cc_export.h" +#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/scroll_offset.h" namespace cc { @@ -21,6 +22,9 @@ enum class SnapAxis : unsigned { kInline, }; +// A helper enum to specify the the axis when doing calculations. +enum class SearchAxis : unsigned { kX, kY }; + // See https://www.w3.org/TR/css-scroll-snap-1/#snap-strictness // TODO(sunyunjia): Add kNone for SnapStrictness to match the spec. // crbug.com/791663 @@ -58,24 +62,26 @@ struct ScrollSnapType { struct ScrollSnapAlign { ScrollSnapAlign() - : alignmentX(SnapAlignment::kNone), alignmentY(SnapAlignment::kNone) {} + : alignment_inline(SnapAlignment::kNone), + alignment_block(SnapAlignment::kNone) {} explicit ScrollSnapAlign(SnapAlignment alignment) - : alignmentX(alignment), alignmentY(alignment) {} + : alignment_inline(alignment), alignment_block(alignment) {} - ScrollSnapAlign(SnapAlignment x, SnapAlignment y) - : alignmentX(x), alignmentY(y) {} + ScrollSnapAlign(SnapAlignment i, SnapAlignment b) + : alignment_inline(i), alignment_block(b) {} bool operator==(const ScrollSnapAlign& other) const { - return alignmentX == other.alignmentX && alignmentY == other.alignmentY; + return alignment_inline == other.alignment_inline && + alignment_block == other.alignment_block; } bool operator!=(const ScrollSnapAlign& other) const { return !(*this == other); } - SnapAlignment alignmentX; - SnapAlignment alignmentY; + SnapAlignment alignment_inline; + SnapAlignment alignment_block; }; // Snap area is a bounding box that could be snapped to when a scroll happens in @@ -90,12 +96,19 @@ struct SnapAreaData { SnapAreaData() {} - SnapAreaData(SnapAxis axis, gfx::ScrollOffset position, bool msnap) - : snap_axis(axis), snap_position(position), must_snap(msnap) {} + SnapAreaData(SnapAxis axis, + gfx::ScrollOffset position, + gfx::RectF visible, + bool msnap) + : snap_axis(axis), + snap_position(position), + visible_region(visible), + 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); } @@ -111,6 +124,11 @@ struct SnapAreaData { // overflow rect. gfx::ScrollOffset snap_position; + // 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; + // Whether this area has scroll-snap-stop: always. // See https://www.w3.org/TR/css-scroll-snap-1/#scroll-snap-stop bool must_snap; @@ -119,6 +137,8 @@ struct SnapAreaData { // snapping. }; +typedef std::vector<SnapAreaData> SnapAreaList; + // Snap container is a scroll container that has non-'none' value for // scroll-snap-type. It can be snapped to one of its snap areas when a scroll // happens. @@ -139,6 +159,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.proximity_range_ == proximity_range_) && (other.snap_area_list_ == snap_area_list_); } @@ -146,9 +167,10 @@ class CC_EXPORT SnapContainerData { return !(*this == other); } - gfx::ScrollOffset FindSnapPosition(const gfx::ScrollOffset& current_position, - bool should_snap_on_x, - bool should_snap_on_y) const; + bool FindSnapPosition(const gfx::ScrollOffset& current_position, + bool should_snap_on_x, + bool should_snap_on_y, + gfx::ScrollOffset* snap_position) const; void AddSnapAreaData(SnapAreaData snap_area_data); size_t size() const { return snap_area_list_.size(); } @@ -162,6 +184,11 @@ class CC_EXPORT SnapContainerData { } gfx::ScrollOffset max_position() const { return max_position_; } + void set_proximity_range(const gfx::ScrollOffset& range) { + proximity_range_ = range; + } + gfx::ScrollOffset proximity_range() const { return proximity_range_; } + private: // Specifies whether a scroll container is a scroll snap container, how // strictly it snaps, and which axes are considered. @@ -172,6 +199,10 @@ class CC_EXPORT SnapContainerData { // with blink's scroll position. gfx::ScrollOffset max_position_; + // A valid snap position should be within the |proximity_range_| of the + // current offset on the snapping axis. + gfx::ScrollOffset proximity_range_; + // The SnapAreaData for the snap areas in this snap container. When a scroll // happens, we iterate through the snap_area_list to find the best snap // position. diff --git a/chromium/cc/input/scroll_snap_data_unittest.cc b/chromium/cc/input/scroll_snap_data_unittest.cc index c80fc3d92fc..794f83faeaf 100644 --- a/chromium/cc/input/scroll_snap_data_unittest.cc +++ b/chromium/cc/input/scroll_snap_data_unittest.cc @@ -17,16 +17,18 @@ TEST_F(ScrollSnapDataTest, FindsClosestSnapPositionIndependently) { gfx::ScrollOffset current_position(100, 100); SnapAreaData snap_x_only( SnapAxis::kX, gfx::ScrollOffset(80, SnapAreaData::kInvalidScrollPosition), - false); + gfx::RectF(0, 0, 360, 380), false); SnapAreaData snap_y_only( SnapAxis::kY, gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 70), - false); - SnapAreaData snap_on_both(SnapAxis::kBoth, gfx::ScrollOffset(50, 150), false); + 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); - gfx::ScrollOffset snap_position = - data.FindSnapPosition(current_position, true, true); + gfx::ScrollOffset snap_position; + EXPECT_TRUE( + data.FindSnapPosition(current_position, true, true, &snap_position)); EXPECT_EQ(80, snap_position.x()); EXPECT_EQ(70, snap_position.y()); } @@ -38,16 +40,18 @@ TEST_F(ScrollSnapDataTest, FindsClosestSnapPositionOnAxisValueBoth) { gfx::ScrollOffset current_position(40, 150); SnapAreaData snap_x_only( SnapAxis::kX, gfx::ScrollOffset(80, SnapAreaData::kInvalidScrollPosition), - false); + gfx::RectF(0, 0, 360, 380), false); SnapAreaData snap_y_only( SnapAxis::kY, gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 70), - false); - SnapAreaData snap_on_both(SnapAxis::kBoth, gfx::ScrollOffset(50, 150), false); + 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); - gfx::ScrollOffset snap_position = - data.FindSnapPosition(current_position, true, true); + gfx::ScrollOffset snap_position; + EXPECT_TRUE( + data.FindSnapPosition(current_position, true, true, &snap_position)); EXPECT_EQ(50, snap_position.x()); EXPECT_EQ(150, snap_position.y()); } @@ -59,14 +63,83 @@ TEST_F(ScrollSnapDataTest, DoesNotSnapOnNonScrolledAxis) { gfx::ScrollOffset current_position(100, 100); SnapAreaData snap_x_only( SnapAxis::kX, gfx::ScrollOffset(80, SnapAreaData::kInvalidScrollPosition), - false); + gfx::RectF(0, 0, 360, 380), false); SnapAreaData snap_y_only( SnapAxis::kY, gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 70), - false); + gfx::RectF(0, 0, 360, 380), false); data.AddSnapAreaData(snap_x_only); data.AddSnapAreaData(snap_y_only); - gfx::ScrollOffset snap_position = - data.FindSnapPosition(current_position, true, false); + gfx::ScrollOffset snap_position; + EXPECT_TRUE( + data.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( + 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::ScrollOffset snap_position; + EXPECT_FALSE( + data.FindSnapPosition(current_position, true, true, &snap_position)); +} + +TEST_F(ScrollSnapDataTest, SnapOnClosestAxisFirstIfVisibilityConflicts) { + SnapContainerData data( + ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), + gfx::ScrollOffset(360, 380)); + gfx::ScrollOffset current_position(100, 100); + + // Both the areas are currently visible. + // However, if we snap to them on x and y independently, none is visible after + // snapping. So we only snap on the axis that has a closer snap point first. + // 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); + SnapAreaData snap_y1( + SnapAxis::kY, + gfx::ScrollOffset(SnapAreaData::kInvalidScrollPosition, 130), + gfx::RectF(90, 90, 60, 60), 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); + 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()); +} + +TEST_F(ScrollSnapDataTest, DoesNotSnapToPositionsOutsideProximityRange) { + SnapContainerData data( + ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory), + gfx::ScrollOffset(360, 380)); + data.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); + gfx::ScrollOffset snap_position; + EXPECT_TRUE( + data.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 + // [50, 150], so we should only snap on x. EXPECT_EQ(80, snap_position.x()); EXPECT_EQ(100, snap_position.y()); } diff --git a/chromium/cc/input/scrollbar.h b/chromium/cc/input/scrollbar.h index e0302bffcb6..ab67bce81c0 100644 --- a/chromium/cc/input/scrollbar.h +++ b/chromium/cc/input/scrollbar.h @@ -16,7 +16,7 @@ enum ScrollbarOrientation { HORIZONTAL, VERTICAL }; enum ScrollDirection { SCROLL_BACKWARD, SCROLL_FORWARD }; // For now, TRACK includes everything but the thumb including background and // buttons. -enum ScrollbarPart { THUMB, TRACK }; +enum ScrollbarPart { THUMB, TRACK, TICKMARKS }; class Scrollbar { public: @@ -31,6 +31,7 @@ class Scrollbar { virtual int ThumbLength() const = 0; virtual gfx::Rect TrackRect() const = 0; virtual float ThumbOpacity() const = 0; + virtual bool HasTickmarks() const = 0; virtual bool NeedsPaintPart(ScrollbarPart part) const = 0; virtual void PaintPart(PaintCanvas* canvas, ScrollbarPart part, diff --git a/chromium/cc/input/scrollbar_animation_controller.cc b/chromium/cc/input/scrollbar_animation_controller.cc index 0ee16d510ed..55f97f09d1f 100644 --- a/chromium/cc/input/scrollbar_animation_controller.cc +++ b/chromium/cc/input/scrollbar_animation_controller.cc @@ -54,6 +54,7 @@ ScrollbarAnimationController::ScrollbarAnimationController( show_scrollbars_on_scroll_gesture_(false), need_thinning_animation_(false), is_mouse_down_(false), + tickmarks_showing_(false), weak_factory_(this) {} ScrollbarAnimationController::ScrollbarAnimationController( @@ -76,6 +77,7 @@ ScrollbarAnimationController::ScrollbarAnimationController( show_scrollbars_on_scroll_gesture_(true), need_thinning_animation_(true), is_mouse_down_(false), + tickmarks_showing_(false), weak_factory_(this) { vertical_controller_ = SingleScrollbarAnimationControllerThinning::Create( scroll_element_id, ScrollbarOrientation::VERTICAL, client, @@ -194,11 +196,15 @@ void ScrollbarAnimationController::DidScrollEnd() { if (need_thinning_animation_ && MouseIsNearAnyScrollbar()) return; - if (has_scrolled) + if (has_scrolled && !tickmarks_showing_) PostDelayedAnimation(FADE_OUT); } void ScrollbarAnimationController::DidScrollUpdate() { + UpdateScrollbarState(); +} + +void ScrollbarAnimationController::UpdateScrollbarState() { if (need_thinning_animation_ && Captured()) return; @@ -209,10 +215,14 @@ void ScrollbarAnimationController::DidScrollUpdate() { // As an optimization, we avoid spamming fade delay tasks during active fast // scrolls. But if we're not within one, we need to post every scroll update. if (!currently_scrolling_) { - // We don't fade out scrollbar if they need thinning animation and mouse is - // near. - if (!need_thinning_animation_ || !MouseIsNearAnyScrollbar()) + // We don't fade out scrollbar if they need thinning animation (Aura + // Overlay) and mouse is near or tickmarks show. + if (need_thinning_animation_) { + if (!MouseIsNearAnyScrollbar() && !tickmarks_showing_) + PostDelayedAnimation(FADE_OUT); + } else { PostDelayedAnimation(FADE_OUT); + } } else { show_in_fast_scroll_ = true; } @@ -225,11 +235,22 @@ void ScrollbarAnimationController::DidScrollUpdate() { void ScrollbarAnimationController::WillUpdateScroll() { if (show_scrollbars_on_scroll_gesture_) - DidScrollUpdate(); + UpdateScrollbarState(); } void ScrollbarAnimationController::DidRequestShowFromMainThread() { - DidScrollUpdate(); + UpdateScrollbarState(); +} + +void ScrollbarAnimationController::UpdateTickmarksVisibility(bool show) { + if (!need_thinning_animation_) + return; + + if (tickmarks_showing_ == show) + return; + + tickmarks_showing_ = show; + UpdateScrollbarState(); } void ScrollbarAnimationController::DidMouseDown() { @@ -267,7 +288,7 @@ void ScrollbarAnimationController::DidMouseUp() { vertical_controller_->DidMouseUp(); horizontal_controller_->DidMouseUp(); - if (!MouseIsNearAnyScrollbar() && !ScrollbarsHidden()) + if (!MouseIsNearAnyScrollbar() && !ScrollbarsHidden() && !tickmarks_showing_) PostDelayedAnimation(FADE_OUT); } @@ -281,7 +302,7 @@ void ScrollbarAnimationController::DidMouseLeave() { delayed_scrollbar_animation_.Cancel(); need_trigger_scrollbar_fade_in_ = false; - if (ScrollbarsHidden() || Captured()) + if (ScrollbarsHidden() || Captured() || tickmarks_showing_) return; PostDelayedAnimation(FADE_OUT); @@ -297,7 +318,7 @@ void ScrollbarAnimationController::DidMouseMove( vertical_controller_->DidMouseMove(device_viewport_point); horizontal_controller_->DidMouseMove(device_viewport_point); - if (Captured()) { + if (Captured() || tickmarks_showing_) { DCHECK(!ScrollbarsHidden()); return; } diff --git a/chromium/cc/input/scrollbar_animation_controller.h b/chromium/cc/input/scrollbar_animation_controller.h index 64bf2b6ac06..9473b416bf8 100644 --- a/chromium/cc/input/scrollbar_animation_controller.h +++ b/chromium/cc/input/scrollbar_animation_controller.h @@ -84,6 +84,8 @@ class CC_EXPORT ScrollbarAnimationController { // ScrollableArea::showOverlayScrollbars). void DidRequestShowFromMainThread(); + void UpdateTickmarksVisibility(bool show); + // These methods are public for testing. bool MouseIsOverScrollbarThumb(ScrollbarOrientation orientation) const; bool MouseIsNearScrollbarThumb(ScrollbarOrientation orientation) const; @@ -114,6 +116,10 @@ class CC_EXPORT ScrollbarAnimationController { SingleScrollbarAnimationControllerThinning& GetScrollbarAnimationController( ScrollbarOrientation) const; + // Any scrollbar state update would show scrollbar hen post the delay fade out + // if needed. + void UpdateScrollbarState(); + // Returns how far through the animation we are as a progress value from // 0 to 1. float AnimationProgressAtTime(base::TimeTicks now); @@ -156,6 +162,8 @@ class CC_EXPORT ScrollbarAnimationController { bool is_mouse_down_; + bool tickmarks_showing_; + std::unique_ptr<SingleScrollbarAnimationControllerThinning> vertical_controller_; std::unique_ptr<SingleScrollbarAnimationControllerThinning> diff --git a/chromium/cc/input/scrollbar_animation_controller_unittest.cc b/chromium/cc/input/scrollbar_animation_controller_unittest.cc index b142096e58b..9749fc77148 100644 --- a/chromium/cc/input/scrollbar_animation_controller_unittest.cc +++ b/chromium/cc/input/scrollbar_animation_controller_unittest.cc @@ -1318,6 +1318,54 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, client_.start_fade().IsCancelled()); } +// Ensure Aura Overlay Scrollbars shows and did not fade out when tickmarks show +// and fade out when tickmarks hide. +TEST_F(ScrollbarAnimationControllerAuraOverlayTest, TickmakrsShowHide) { + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(1); + + // Overlay Scrollbar hidden at beginnging. + EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden()); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Scrollbars show when tickmarks show. + scrollbar_controller_->UpdateTickmarksVisibility(true); + EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden()); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Scroll update, no delay fade animation. + scrollbar_controller_->DidScrollUpdate(); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Scroll update with phase, no delay fade animation. + scrollbar_controller_->DidScrollBegin(); + scrollbar_controller_->DidScrollUpdate(); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + scrollbar_controller_->DidScrollEnd(); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Move mouse, no delay fade animation. + scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(0, 0)); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Mouse leave, no delay fade animation. + scrollbar_controller_->DidMouseLeave(); + EXPECT_TRUE(client_.start_fade().is_null() || + client_.start_fade().IsCancelled()); + + // Scrollbars fade out animation has enqueued when tickmarks hide. + scrollbar_controller_->UpdateTickmarksVisibility(false); + EXPECT_FALSE(client_.start_fade().is_null()); + EXPECT_FALSE(client_.start_fade().IsCancelled()); + EXPECT_EQ(kFadeDelay, client_.delay()); +} + class ScrollbarAnimationControllerAndroidTest : public testing::Test, public ScrollbarAnimationControllerClient { diff --git a/chromium/cc/ipc/cc_param_traits.cc b/chromium/cc/ipc/cc_param_traits.cc index c9ad471b488..c66518e291d 100644 --- a/chromium/cc/ipc/cc_param_traits.cc +++ b/chromium/cc/ipc/cc_param_traits.cc @@ -675,7 +675,7 @@ void ParamTraits<viz::LocalSurfaceId>::Write(base::Pickle* m, DCHECK(p.is_valid()); WriteParam(m, p.parent_sequence_number()); WriteParam(m, p.child_sequence_number()); - WriteParam(m, p.nonce()); + WriteParam(m, p.embed_token()); } bool ParamTraits<viz::LocalSurfaceId>::Read(const base::Pickle* m, @@ -689,12 +689,12 @@ bool ParamTraits<viz::LocalSurfaceId>::Read(const base::Pickle* m, if (!ReadParam(m, iter, &child_sequence_number)) return false; - base::UnguessableToken nonce; - if (!ReadParam(m, iter, &nonce)) + base::UnguessableToken embed_token; + if (!ReadParam(m, iter, &embed_token)) return false; - *p = - viz::LocalSurfaceId(parent_sequence_number, child_sequence_number, nonce); + *p = viz::LocalSurfaceId(parent_sequence_number, child_sequence_number, + embed_token); return p->is_valid(); } @@ -705,7 +705,7 @@ void ParamTraits<viz::LocalSurfaceId>::Log(const param_type& p, l->append(", "); LogParam(p.child_sequence_number(), l); l->append(", "); - LogParam(p.nonce(), l); + LogParam(p.embed_token(), l); l->append(")"); } diff --git a/chromium/cc/ipc/cc_param_traits_macros.h b/chromium/cc/ipc/cc_param_traits_macros.h index 181295889cc..1141b237a82 100644 --- a/chromium/cc/ipc/cc_param_traits_macros.h +++ b/chromium/cc/ipc/cc_param_traits_macros.h @@ -117,6 +117,7 @@ IPC_STRUCT_TRAITS_BEGIN(viz::TileDrawQuad) IPC_STRUCT_TRAITS_MEMBER(tex_coord_rect) IPC_STRUCT_TRAITS_MEMBER(texture_size) IPC_STRUCT_TRAITS_MEMBER(swizzle_contents) + IPC_STRUCT_TRAITS_MEMBER(is_premultiplied) IPC_STRUCT_TRAITS_MEMBER(nearest_neighbor) IPC_STRUCT_TRAITS_MEMBER(force_anti_aliasing_off) IPC_STRUCT_TRAITS_END() diff --git a/chromium/cc/ipc/cc_param_traits_unittest.cc b/chromium/cc/ipc/cc_param_traits_unittest.cc index 97e58901e1c..867bb9c39b0 100644 --- a/chromium/cc/ipc/cc_param_traits_unittest.cc +++ b/chromium/cc/ipc/cc_param_traits_unittest.cc @@ -405,10 +405,10 @@ TEST_F(CCParamTraitsTest, AllQuads) { pass_cmp->CopyFromAndAppendDrawQuad(texture_in); TileDrawQuad* tile_in = pass_in->CreateAndAppendDrawQuad<TileDrawQuad>(); - tile_in->SetAll(shared_state3_in, arbitrary_rect2, - arbitrary_rect1_inside_rect2, arbitrary_bool1, - arbitrary_resourceid3, arbitrary_rectf1, arbitrary_size1, - arbitrary_bool2, arbitrary_bool3, arbitrary_bool4); + tile_in->SetAll( + shared_state3_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, + arbitrary_bool1, arbitrary_resourceid3, arbitrary_rectf1, arbitrary_size1, + arbitrary_bool2, arbitrary_bool3, arbitrary_bool4, arbitrary_bool5); pass_cmp->CopyFromAndAppendDrawQuad(tile_in); YUVVideoDrawQuad* yuvvideo_in = diff --git a/chromium/cc/ipc/cc_serialization_perftest.cc b/chromium/cc/ipc/cc_serialization_perftest.cc index d8741b8de02..7dc52dcff3f 100644 --- a/chromium/cc/ipc/cc_serialization_perftest.cc +++ b/chromium/cc/ipc/cc_serialization_perftest.cc @@ -14,7 +14,7 @@ #include "gpu/ipc/common/mailbox_struct_traits.h" #include "gpu/ipc/common/sync_token_struct_traits.h" #include "ipc/ipc_message.h" -#include "mojo/common/time_struct_traits.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" #include "mojo/public/cpp/bindings/message.h" #include "services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h" #include "services/viz/public/cpp/compositing/compositor_frame_struct_traits.h" @@ -361,10 +361,11 @@ class CCSerializationPerfTest : public testing::Test { arbitrary_blend_mode2, arbitrary_context_id2); for (uint32_t j = 0; j < 6; ++j) { auto* tile_in = pass_in->CreateAndAppendDrawQuad<viz::TileDrawQuad>(); - tile_in->SetAll( - shared_state2_in, arbitrary_rect2, arbitrary_rect1_inside_rect2, - arbitrary_bool1, arbitrary_resourceid3, arbitrary_rectf1, - arbitrary_size1, arbitrary_bool2, arbitrary_bool3, arbitrary_bool4); + tile_in->SetAll(shared_state2_in, arbitrary_rect2, + arbitrary_rect1_inside_rect2, arbitrary_bool1, + arbitrary_resourceid3, arbitrary_rectf1, + arbitrary_size1, arbitrary_bool2, arbitrary_bool3, + arbitrary_bool4, arbitrary_bool5); } } diff --git a/chromium/cc/layers/deadline_policy.cc b/chromium/cc/layers/deadline_policy.cc index 91ba1912cc1..9c0f6fc8e24 100644 --- a/chromium/cc/layers/deadline_policy.cc +++ b/chromium/cc/layers/deadline_policy.cc @@ -4,6 +4,8 @@ #include "cc/layers/deadline_policy.h" +#include <limits> + namespace cc { // static @@ -19,17 +21,18 @@ DeadlinePolicy DeadlinePolicy::UseDefaultDeadline() { // static DeadlinePolicy DeadlinePolicy::UseSpecifiedDeadline( uint32_t deadline_in_frames) { - return DeadlinePolicy(deadline_in_frames); + return DeadlinePolicy(Type::kUseSpecifiedDeadline, deadline_in_frames); } -DeadlinePolicy::DeadlinePolicy(Type policy_type) - : policy_type_(policy_type), deadline_in_frames_(base::nullopt) { - DCHECK_NE(Type::kUseSpecifiedDeadline, policy_type_); +// static +DeadlinePolicy DeadlinePolicy::UseInfiniteDeadline() { + return DeadlinePolicy(Type::kUseInfiniteDeadline, + std::numeric_limits<uint32_t>::max()); } -DeadlinePolicy::DeadlinePolicy(uint32_t deadline_in_frames) - : policy_type_(Type::kUseSpecifiedDeadline), - deadline_in_frames_(deadline_in_frames) {} +DeadlinePolicy::DeadlinePolicy(Type policy_type, + base::Optional<uint32_t> deadline_in_frames) + : policy_type_(policy_type), deadline_in_frames_(deadline_in_frames) {} DeadlinePolicy::DeadlinePolicy(const DeadlinePolicy& other) = default; diff --git a/chromium/cc/layers/deadline_policy.h b/chromium/cc/layers/deadline_policy.h index a5dd80311fb..156c6df9432 100644 --- a/chromium/cc/layers/deadline_policy.h +++ b/chromium/cc/layers/deadline_policy.h @@ -18,7 +18,8 @@ class CC_EXPORT DeadlinePolicy { enum Type { kUseExistingDeadline, kUseDefaultDeadline, - kUseSpecifiedDeadline + kUseSpecifiedDeadline, + kUseInfiniteDeadline }; static DeadlinePolicy UseExistingDeadline(); @@ -27,6 +28,8 @@ class CC_EXPORT DeadlinePolicy { static DeadlinePolicy UseSpecifiedDeadline(uint32_t deadline_in_frames); + static DeadlinePolicy UseInfiniteDeadline(); + DeadlinePolicy(const DeadlinePolicy& other); DeadlinePolicy& operator=(const DeadlinePolicy& other) = default; @@ -39,7 +42,8 @@ class CC_EXPORT DeadlinePolicy { base::Optional<uint32_t> deadline_in_frames() const { DCHECK(policy_type_ == Type::kUseDefaultDeadline || - policy_type_ == Type::kUseSpecifiedDeadline); + policy_type_ == Type::kUseSpecifiedDeadline || + policy_type_ == Type::kUseInfiniteDeadline); return deadline_in_frames_; } @@ -55,9 +59,9 @@ class CC_EXPORT DeadlinePolicy { } private: - explicit DeadlinePolicy(Type policy_type); - - explicit DeadlinePolicy(uint32_t deadline_in_frames); + explicit DeadlinePolicy( + Type policy_type, + base::Optional<uint32_t> deadline_in_frames = base::nullopt); Type policy_type_; base::Optional<uint32_t> deadline_in_frames_; diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index b411caf3edf..8f2a21ea3fd 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -19,6 +19,7 @@ #include "cc/raster/scoped_gpu_raster.h" #include "cc/resources/memory_history.h" #include "cc/trees/frame_rate_counter.h" +#include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -26,7 +27,7 @@ #include "components/viz/common/gpu/texture_allocation.h" #include "components/viz/common/quads/solid_color_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" -#include "components/viz/common/resources/shared_bitmap_manager.h" +#include "components/viz/common/resources/bitmap_allocation.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "skia/ext/platform_canvas.h" @@ -117,6 +118,20 @@ class HudGpuBacking : public ResourcePool::GpuBacking { GLuint texture_id; }; +class HudSoftwareBacking : public ResourcePool::SoftwareBacking { + public: + ~HudSoftwareBacking() override { + layer_tree_frame_sink->DidDeleteSharedBitmap(shared_bitmap_id); + } + + base::UnguessableToken SharedMemoryGuid() override { + return shared_memory->mapped_id(); + } + + LayerTreeFrameSink* layer_tree_frame_sink; + std::unique_ptr<base::SharedMemory> shared_memory; +}; + bool HeadsUpDisplayLayerImpl::WillDraw( DrawMode draw_mode, LayerTreeResourceProvider* resource_provider) { @@ -155,6 +170,7 @@ void HeadsUpDisplayLayerImpl::AppendQuads(viz::RenderPass* render_pass, void HeadsUpDisplayLayerImpl::UpdateHudTexture( DrawMode draw_mode, + LayerTreeFrameSink* layer_tree_frame_sink, LayerTreeResourceProvider* resource_provider, bool gpu_raster, const viz::RenderPassList& list) { @@ -228,13 +244,22 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( } } else { DCHECK_EQ(draw_mode, DRAW_MODE_SOFTWARE); - viz::SharedBitmapManager* shared_bitmap_manager = - layer_tree_impl()->shared_bitmap_manager(); - DCHECK(shared_bitmap_manager); - if (!pool_resource.shared_bitmap()) { - pool_resource.set_shared_bitmap( - shared_bitmap_manager->AllocateSharedBitmap(pool_resource.size())); + if (!pool_resource.software_backing()) { + auto backing = std::make_unique<HudSoftwareBacking>(); + backing->layer_tree_frame_sink = layer_tree_frame_sink; + backing->shared_bitmap_id = viz::SharedBitmap::GenerateId(); + backing->shared_memory = viz::bitmap_allocation::AllocateMappedBitmap( + pool_resource.size(), pool_resource.format()); + + mojo::ScopedSharedBufferHandle handle = + viz::bitmap_allocation::DuplicateAndCloseMappedBitmap( + backing->shared_memory.get(), pool_resource.size(), + pool_resource.format()); + layer_tree_frame_sink->DidAllocateSharedBitmap(std::move(handle), + backing->shared_bitmap_id); + + pool_resource.set_software_backing(std::move(backing)); } } @@ -253,8 +278,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( LayerTreeResourceProvider::ScopedSkSurface scoped_surface( context_provider->GrContext(), backing->texture_id, backing->texture_target, pool_resource.size(), pool_resource.format(), - false /* use_distance_field_text */, false /* can_use_lcd_text */, - 0 /* msaa_sample_count */); + false /* can_use_lcd_text */, 0 /* msaa_sample_count */); SkSurface* surface = scoped_surface.surface(); if (!surface) { pool_->ReleaseResource(std::move(pool_resource)); @@ -298,12 +322,14 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( // memory bitmap, wrapped in an SkSurface, that can be shared to the display // compositor. DCHECK_EQ(draw_mode, DRAW_MODE_SOFTWARE); - DCHECK(pool_resource.shared_bitmap()); + DCHECK(pool_resource.software_backing()); SkImageInfo info = SkImageInfo::MakeN32Premul( pool_resource.size().width(), pool_resource.size().height()); + auto* backing = + static_cast<HudSoftwareBacking*>(pool_resource.software_backing()); sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect( - info, pool_resource.shared_bitmap()->pixels(), info.minRowBytes()); + info, backing->shared_memory->memory(), info.minRowBytes()); DrawHudContents(surface->getCanvas()); } diff --git a/chromium/cc/layers/heads_up_display_layer_impl.h b/chromium/cc/layers/heads_up_display_layer_impl.h index 5994907193e..b6f5070ddd3 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.h +++ b/chromium/cc/layers/heads_up_display_layer_impl.h @@ -26,6 +26,7 @@ struct SkRect; namespace cc { class FrameRateCounter; +class LayerTreeFrameSink; class LayerTreeResourceProvider; class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { @@ -44,6 +45,7 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; void UpdateHudTexture(DrawMode draw_mode, + LayerTreeFrameSink* frame_sink, LayerTreeResourceProvider* 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 375b88012c5..6397ea81bb0 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl_unittest.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl_unittest.cc @@ -17,6 +17,7 @@ namespace cc { namespace { void CheckDrawLayer(HeadsUpDisplayLayerImpl* layer, + LayerTreeFrameSink* frame_sink, LayerTreeResourceProvider* resource_provider, viz::ContextProvider* context_provider, DrawMode draw_mode) { @@ -27,8 +28,8 @@ void CheckDrawLayer(HeadsUpDisplayLayerImpl* layer, layer->AppendQuads(render_pass.get(), &data); viz::RenderPassList pass_list; pass_list.push_back(std::move(render_pass)); - layer->UpdateHudTexture(draw_mode, resource_provider, context_provider, - pass_list); + layer->UpdateHudTexture(draw_mode, frame_sink, resource_provider, + context_provider, pass_list); if (will_draw) layer->DidDraw(resource_provider); @@ -57,14 +58,16 @@ TEST(HeadsUpDisplayLayerImplTest, ResourcelessSoftwareDrawAfterResourceLoss) { host_impl.pending_tree()->BuildLayerListAndPropertyTreesForTesting(); // Check regular hardware draw is ok. - CheckDrawLayer(layer, host_impl.resource_provider(), + CheckDrawLayer(layer, host_impl.layer_tree_frame_sink(), + host_impl.resource_provider(), layer_tree_frame_sink->context_provider(), DRAW_MODE_HARDWARE); // Simulate a resource loss on transitioning to resourceless software mode. layer->ReleaseResources(); // Should skip resourceless software draw and not crash in UpdateHudTexture. - CheckDrawLayer(layer, host_impl.resource_provider(), + CheckDrawLayer(layer, host_impl.layer_tree_frame_sink(), + host_impl.resource_provider(), layer_tree_frame_sink->context_provider(), DRAW_MODE_RESOURCELESS_SOFTWARE); } @@ -88,7 +91,8 @@ TEST(HeadsUpDisplayLayerImplTest, CPUAndGPURasterCanvas) { host_impl.pending_tree()->BuildLayerListAndPropertyTreesForTesting(); // Check Ganesh canvas drawing is ok. - CheckDrawLayer(layer, host_impl.resource_provider(), + CheckDrawLayer(layer, host_impl.layer_tree_frame_sink(), + host_impl.resource_provider(), layer_tree_frame_sink->context_provider(), DRAW_MODE_HARDWARE); host_impl.ReleaseLayerTreeFrameSink(); @@ -96,8 +100,8 @@ TEST(HeadsUpDisplayLayerImplTest, CPUAndGPURasterCanvas) { host_impl.InitializeRenderer(layer_tree_frame_sink.get()); // Check SW canvas drawing is ok. - CheckDrawLayer(layer, host_impl.resource_provider(), nullptr, - DRAW_MODE_SOFTWARE); + CheckDrawLayer(layer, host_impl.layer_tree_frame_sink(), + host_impl.resource_provider(), nullptr, DRAW_MODE_SOFTWARE); } } // namespace diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index 54123327c01..f8f478a6486 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -65,7 +65,7 @@ Layer::Inputs::Inputs(int layer_id) has_will_change_transform_hint(false), trilinear_filtering(false), hide_layer_and_subtree(false), - client(nullptr), + client_rawptr(nullptr), overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto) {} Layer::Inputs::~Inputs() = default; @@ -290,7 +290,9 @@ void Layer::SetBounds(const gfx::Size& size) { if (!layer_tree_host_) return; - if (masks_to_bounds()) { + // Both bounds clipping and mask clipping can result in new areas of subtrees + // being exposed on a bounds change. Ensure the damaged areas are updated. + if (masks_to_bounds() || inputs_.mask_layer.get()) { SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); } @@ -1160,6 +1162,13 @@ void Layer::SetStickyPositionConstraint( SetNeedsCommit(); } +void Layer::SetLayerClient(base::WeakPtr<LayerClient> client) { + inputs_.client = std::move(client); + // Both binds the weak_ptr to the main thread and stores a rawptr for access + // during commit. + inputs_.client_rawptr = client.get(); +} + bool Layer::IsSnapped() { return scrollable(); } @@ -1314,8 +1323,15 @@ bool Layer::HasNonAAPaint() const { std::unique_ptr<base::trace_event::ConvertableToTraceFormat> Layer::TakeDebugInfo() { - if (inputs_.client) - return inputs_.client->TakeDebugInfo(this); + // TakeDebugInfo is called from the compositor thread while the main thread is + // blocked so we use the raw client pointer as we can't check whether the + // reference is safe on the weak pointer. + // TODO(flackr): Remove client_rawptr and figure out if we can take the debug + // info from the main thread and store it on inputs before doing the commit or + // make a WeakPtr which understands the commit and allows checked access from + // the compositor thread. https://crbug.com/826455 + if (inputs_.client_rawptr) + return inputs_.client_rawptr->TakeDebugInfo(this); else return nullptr; } @@ -1341,7 +1357,7 @@ void Layer::SetMayContainVideo(bool yes) { void Layer::SetScrollbarsHiddenFromImplSide(bool hidden) { if (inputs_.client) - inputs_.client->didChangeScrollbarsHidden(hidden); + inputs_.client->didChangeScrollbarsHiddenIfOverlay(hidden); } // On<Property>Animated is called due to an ongoing accelerated animation. diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index ad25a15d577..eca81a044cf 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -335,7 +335,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { TakeDebugInfo(); virtual void didUpdateMainThreadScrollingReasons(); - void SetLayerClient(LayerClient* client) { inputs_.client = client; } + void SetLayerClient(base::WeakPtr<LayerClient> client); virtual bool IsSnapped(); @@ -627,7 +627,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { bool hide_layer_and_subtree : 1; // The following elements can not and are not serialized. - LayerClient* client; + base::WeakPtr<LayerClient> client; + // During commit, the main thread is blocked on the compositor thread, so + // we use the raw pointer to the LayerClient. + LayerClient* client_rawptr; + base::Callback<void(const gfx::ScrollOffset&, const ElementId&)> did_scroll_callback; std::vector<std::unique_ptr<viz::CopyOutputRequest>> copy_requests; diff --git a/chromium/cc/layers/layer_client.h b/chromium/cc/layers/layer_client.h index 85c97722c0d..10ded49b5a5 100644 --- a/chromium/cc/layers/layer_client.h +++ b/chromium/cc/layers/layer_client.h @@ -31,7 +31,7 @@ class CC_EXPORT LayerClient { virtual std::unique_ptr<base::trace_event::ConvertableToTraceFormat> TakeDebugInfo(Layer* layer) = 0; virtual void didUpdateMainThreadScrollingReasons() = 0; - virtual void didChangeScrollbarsHidden(bool) = 0; + virtual void didChangeScrollbarsHiddenIfOverlay(bool) = 0; protected: virtual ~LayerClient() {} diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index 6e1dfe9bf90..89b4bdf6a80 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -11,7 +11,6 @@ #include <utility> #include "base/json/json_reader.h" -#include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" @@ -494,10 +493,6 @@ void LayerImpl::ResetChangeTracking() { damage_rect_.SetRect(0, 0, 0, 0); } -bool LayerImpl::has_copy_requests_in_target_subtree() { - return GetEffectTree().Node(effect_tree_index())->subtree_has_copy_request; -} - bool LayerImpl::IsActive() const { return layer_tree_impl_->IsActiveTree(); } @@ -638,10 +633,6 @@ float LayerImpl::Opacity() const { return 1.f; } -const gfx::Transform& LayerImpl::Transform() const { - return GetTransformTree().Node(transform_tree_index())->local; -} - void LayerImpl::SetElementId(ElementId element_id) { if (element_id == element_id_) return; @@ -947,4 +938,8 @@ void LayerImpl::EnsureValidPropertyTreeIndices() const { DCHECK(GetScrollTree().Node(scroll_tree_index())); } +bool LayerImpl::is_surface_layer() const { + return false; +} + } // namespace cc diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h index 01032faf6e0..6eb7daabac1 100644 --- a/chromium/cc/layers/layer_impl.h +++ b/chromium/cc/layers/layer_impl.h @@ -86,7 +86,8 @@ class CC_EXPORT LayerImpl { int id() const { return layer_id_; } - // Interactions with attached animations. + // Whether this layer is on the active tree, return false if it's on the + // pending tree. bool IsActive() const; void SetHasTransformNode(bool val) { has_transform_node_ = val; } @@ -123,8 +124,6 @@ class CC_EXPORT LayerImpl { bool is_clipped() const { return draw_properties_.is_clipped; } - void UpdatePropertyTreeScrollOffset(); - LayerTreeImpl* layer_tree_impl() const { return layer_tree_impl_; } void PopulateSharedQuadState(viz::SharedQuadState* state, @@ -196,7 +195,6 @@ class CC_EXPORT LayerImpl { bool contents_opaque() const { return contents_opaque_; } float Opacity() const; - const gfx::Transform& Transform() const; // Stable identifier for clients. See comment in cc/trees/element_id.h. void SetElementId(ElementId element_id); @@ -419,8 +417,6 @@ class CC_EXPORT LayerImpl { virtual gfx::Rect GetEnclosingRectInTargetSpace() const; - bool has_copy_requests_in_target_subtree(); - void UpdatePropertyTreeForAnimationIfNeeded(ElementId element_id); float GetIdealContentsScale() const; @@ -450,6 +446,9 @@ class CC_EXPORT LayerImpl { void EnsureValidPropertyTreeIndices() const; + // TODO(sunxd): Remove this function and replace it with visitor pattern. + virtual bool is_surface_layer() const; + protected: LayerImpl(LayerTreeImpl* layer_impl, int id, @@ -533,7 +532,6 @@ class CC_EXPORT LayerImpl { gfx::PointF position_; - gfx::Rect clip_rect_in_target_space_; int transform_tree_index_; int effect_tree_index_; int clip_tree_index_; diff --git a/chromium/cc/layers/layer_impl_test_properties.h b/chromium/cc/layers/layer_impl_test_properties.h index f2cace080cd..0171895c604 100644 --- a/chromium/cc/layers/layer_impl_test_properties.h +++ b/chromium/cc/layers/layer_impl_test_properties.h @@ -8,7 +8,6 @@ #include <set> #include <vector> -#include "base/memory/ptr_util.h" #include "cc/input/overscroll_behavior.h" #include "cc/input/scroll_snap_data.h" #include "cc/layers/layer_collections.h" diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index 53e58744d20..3c898c8c12d 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -6,7 +6,6 @@ #include <stddef.h> -#include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" #include "cc/animation/animation_id_provider.h" @@ -255,6 +254,13 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { std::unique_ptr<LayerImpl> dummy_layer1_impl = LayerImpl::Create(host_impl_.active_tree(), dummy_layer1->id()); + // 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( @@ -264,6 +270,12 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { grand_child->PushPropertiesTo(grand_child_impl.get()); dummy_layer1->PushPropertiesTo(dummy_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)); + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetMasksToBounds(true)); EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( @@ -322,7 +334,7 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { // Should be a different size than previous call, to ensure it marks tree // changed. - gfx::Size arbitrary_size = gfx::Size(111, 222); + 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)); diff --git a/chromium/cc/layers/nine_patch_generator.h b/chromium/cc/layers/nine_patch_generator.h index bb0cd34fd44..f413e4f5d9c 100644 --- a/chromium/cc/layers/nine_patch_generator.h +++ b/chromium/cc/layers/nine_patch_generator.h @@ -9,7 +9,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "cc/cc_export.h" #include "cc/resources/ui_resource_client.h" #include "ui/gfx/geometry/rect.h" diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc index f60f6cd77f2..acb02addf13 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.cc @@ -90,6 +90,11 @@ void PaintedOverlayScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { scrollbar_layer->SetAperture(gfx::Rect()); scrollbar_layer->set_thumb_ui_resource_id(0); } + + if (track_resource_.get()) + scrollbar_layer->set_track_ui_resource_id(track_resource_->id()); + else + scrollbar_layer->set_track_ui_resource_id(0); } ScrollbarLayerInterface* PaintedOverlayScrollbarLayer::ToScrollbarLayer() { @@ -99,8 +104,10 @@ ScrollbarLayerInterface* PaintedOverlayScrollbarLayer::ToScrollbarLayer() { 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. - if (host != layer_tree_host()) - thumb_resource_ = nullptr; + if (host != layer_tree_host()) { + thumb_resource_.reset(); + track_resource_.reset(); + } Layer::SetLayerTreeHost(host); } @@ -121,6 +128,7 @@ bool PaintedOverlayScrollbarLayer::Update() { updated |= UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_); updated |= UpdateProperty(scrollbar_->ThumbLength(), &thumb_length_); updated |= PaintThumbIfNeeded(); + updated |= PaintTickmarks(); return updated; } @@ -159,4 +167,43 @@ bool PaintedOverlayScrollbarLayer::PaintThumbIfNeeded() { return true; } +bool PaintedOverlayScrollbarLayer::PaintTickmarks() { + if (!scrollbar_->HasTickmarks()) { + if (!track_resource_) { + return false; + } else { + // Remove previous tickmarks. + track_resource_.reset(); + SetNeedsPushProperties(); + return true; + } + } + + gfx::Rect paint_rect = gfx::Rect(gfx::Point(), track_rect_.size()); + + DCHECK(!paint_rect.size().IsEmpty()); + + SkBitmap skbitmap; + skbitmap.allocN32Pixels(paint_rect.width(), paint_rect.height()); + SkiaPaintCanvas canvas(skbitmap); + + SkRect content_skrect = RectToSkRect(paint_rect); + PaintFlags flags; + flags.setAntiAlias(false); + flags.setBlendMode(SkBlendMode::kClear); + canvas.drawRect(content_skrect, flags); + canvas.clipRect(content_skrect); + + scrollbar_->PaintPart(&canvas, TICKMARKS, paint_rect); + // Make sure that the pixels are no longer mutable to unavoid unnecessary + // allocation and copying. + skbitmap.setImmutable(); + + track_resource_ = ScopedUIResource::Create( + layer_tree_host()->GetUIResourceManager(), UIResourceBitmap(skbitmap)); + + SetNeedsPushProperties(); + return true; +} + } // namespace cc diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer.h b/chromium/cc/layers/painted_overlay_scrollbar_layer.h index eb5a749fdde..7f4c555d6ce 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer.h +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer.h @@ -53,6 +53,7 @@ class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerInterface, } bool PaintThumbIfNeeded(); + bool PaintTickmarks(); std::unique_ptr<Scrollbar> scrollbar_; ElementId scroll_element_id_; @@ -65,6 +66,7 @@ class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerInterface, gfx::Rect aperture_; std::unique_ptr<ScopedUIResource> thumb_resource_; + std::unique_ptr<ScopedUIResource> track_resource_; DISALLOW_COPY_AND_ASSIGN(PaintedOverlayScrollbarLayer); }; diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc index cd24f21c667..4f5a61eb060 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc @@ -3,7 +3,10 @@ // found in the LICENSE file. #include "cc/layers/painted_overlay_scrollbar_layer_impl.h" + #include "cc/trees/layer_tree_impl.h" +#include "components/viz/common/quads/solid_color_draw_quad.h" +#include "components/viz/common/quads/texture_draw_quad.h" namespace cc { @@ -27,6 +30,7 @@ PaintedOverlayScrollbarLayerImpl::PaintedOverlayScrollbarLayerImpl( is_left_side_vertical_scrollbar, true), thumb_ui_resource_id_(0), + track_ui_resource_id_(0), thumb_thickness_(0), thumb_length_(0), track_start_(0), @@ -55,6 +59,7 @@ void PaintedOverlayScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { scrollbar_layer->SetAperture(aperture_); scrollbar_layer->set_thumb_ui_resource_id(thumb_ui_resource_id_); + scrollbar_layer->set_track_ui_resource_id(track_ui_resource_id_); } bool PaintedOverlayScrollbarLayerImpl::WillDraw( @@ -67,11 +72,19 @@ bool PaintedOverlayScrollbarLayerImpl::WillDraw( void PaintedOverlayScrollbarLayerImpl::AppendQuads( viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) { + viz::SharedQuadState* shared_quad_state = + render_pass->CreateAndAppendSharedQuadState(); + AppendThumbQuads(render_pass, append_quads_data, shared_quad_state); + AppendTrackQuads(render_pass, append_quads_data, shared_quad_state); +} + +void PaintedOverlayScrollbarLayerImpl::AppendThumbQuads( + viz::RenderPass* render_pass, + AppendQuadsData* append_quads_data, + viz::SharedQuadState* shared_quad_state) { if (aperture_.IsEmpty()) return; - viz::SharedQuadState* shared_quad_state = - render_pass->CreateAndAppendSharedQuadState(); bool is_resource = thumb_ui_resource_id_ && layer_tree_impl()->ResourceIdForUIResource(thumb_ui_resource_id_); @@ -118,6 +131,41 @@ void PaintedOverlayScrollbarLayerImpl::AppendQuads( shared_quad_state, patches); } +void PaintedOverlayScrollbarLayerImpl::AppendTrackQuads( + viz::RenderPass* render_pass, + AppendQuadsData* append_quads_data, + viz::SharedQuadState* shared_quad_state) { + viz::ResourceId track_resource_id = + layer_tree_impl()->ResourceIdForUIResource(track_ui_resource_id_); + if (!track_resource_id) + return; + + bool nearest_neighbor = false; + + gfx::Rect track_quad_rect(bounds()); + gfx::Rect scaled_track_quad_rect(bounds()); + gfx::Rect visible_track_quad_rect = + draw_properties().occlusion_in_content_space.GetUnoccludedContentRect( + track_quad_rect); + gfx::Rect scaled_visible_track_quad_rect = + gfx::ScaleToEnclosingRect(visible_track_quad_rect, 1.f); + + bool needs_blending = !contents_opaque(); + bool premultipled_alpha = true; + bool flipped = false; + gfx::PointF uv_top_left(0.f, 0.f); + gfx::PointF uv_bottom_right(1.f, 1.f); + float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; + viz::TextureDrawQuad* quad = + render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>(); + quad->SetNew(shared_quad_state, scaled_track_quad_rect, + scaled_visible_track_quad_rect, needs_blending, + track_resource_id, premultipled_alpha, uv_top_left, + uv_bottom_right, SK_ColorTRANSPARENT, opacity, flipped, + nearest_neighbor, false); + ValidateQuadResources(quad); +} + void PaintedOverlayScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) { if (thumb_thickness_ == thumb_thickness) return; @@ -184,4 +232,8 @@ const char* PaintedOverlayScrollbarLayerImpl::LayerTypeAsString() const { return "cc::PaintedOverlayScrollbarLayerImpl"; } +bool PaintedOverlayScrollbarLayerImpl::HasFindInPageTickmarks() const { + return track_ui_resource_id_ != 0; +} + } // namespace cc diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.h b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.h index 104287b1995..ca436bd40b9 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.h +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.h @@ -47,6 +47,12 @@ class CC_EXPORT PaintedOverlayScrollbarLayerImpl thumb_ui_resource_id_ = uid; } + void set_track_ui_resource_id(UIResourceId uid) { + track_ui_resource_id_ = uid; + } + + bool HasFindInPageTickmarks() const override; + protected: PaintedOverlayScrollbarLayerImpl(LayerTreeImpl* tree_impl, int id, @@ -63,7 +69,16 @@ class CC_EXPORT PaintedOverlayScrollbarLayerImpl private: const char* LayerTypeAsString() const override; + void AppendThumbQuads(viz::RenderPass* render_pass, + AppendQuadsData* append_quads_data, + viz::SharedQuadState* shared_quad_state); + + void AppendTrackQuads(viz::RenderPass* render_pass, + AppendQuadsData* append_quads_data, + viz::SharedQuadState* shared_quad_state); + UIResourceId thumb_ui_resource_id_; + UIResourceId track_ui_resource_id_; int thumb_thickness_; int thumb_length_; diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer_unittest.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer_unittest.cc new file mode 100644 index 00000000000..c510444b898 --- /dev/null +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer_unittest.cc @@ -0,0 +1,88 @@ +// 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/layers/painted_overlay_scrollbar_layer.h" + +#include "cc/animation/animation_host.h" +#include "cc/test/fake_layer_tree_host.h" +#include "cc/test/fake_layer_tree_host_client.h" +#include "cc/test/fake_scrollbar.h" +#include "cc/test/test_task_graph_runner.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { + +namespace { + +class MockScrollbar : public FakeScrollbar { + public: + MockScrollbar() : FakeScrollbar(true, true, true) {} + + void PaintPart(PaintCanvas* canvas, + ScrollbarPart part, + const gfx::Rect& content_rect) override { + if (part == TICKMARKS) + paint_tickmarks_called_ = true; + } + + bool UsesNinePatchThumbResource() const override { return true; } + + gfx::Size NinePatchThumbCanvasSize() const override { + return gfx::Size(10, 10); + } + + bool PaintTickmarksCalled() { return paint_tickmarks_called_; } + + void SetPaintTickmarksCalled(bool called) { + paint_tickmarks_called_ = called; + } + + private: + bool paint_tickmarks_called_ = false; +}; + +TEST(PaintedOverlayScrollbarLayerTest, PaintTickmarks) { + FakeLayerTreeHostClient fake_client_; + TestTaskGraphRunner task_graph_runner_; + + auto animation_host = AnimationHost::CreateForTesting(ThreadInstance::MAIN); + auto layer_tree_host = FakeLayerTreeHost::Create( + &fake_client_, &task_graph_runner_, animation_host.get()); + + MockScrollbar* scrollbar = new MockScrollbar(); + scrollbar->set_has_tickmarks(false); + + scoped_refptr<PaintedOverlayScrollbarLayer> scrollbar_layer = + PaintedOverlayScrollbarLayer::Create( + std::unique_ptr<Scrollbar>(scrollbar)); + + scrollbar_layer->SetIsDrawable(true); + scrollbar_layer->SetBounds(gfx::Size(100, 100)); + + layer_tree_host->SetRootLayer(scrollbar_layer); + EXPECT_EQ(scrollbar_layer->GetLayerTreeHostForTesting(), + layer_tree_host.get()); + + // Request no paint when initialization. + scrollbar_layer->Update(); + EXPECT_FALSE(scrollbar->PaintTickmarksCalled()); + + // The next update will paint nothing because still no tickmarks applied. + scrollbar_layer->Update(); + EXPECT_FALSE(scrollbar->PaintTickmarksCalled()); + + // Enable the tickmarks. + scrollbar->set_has_tickmarks(true); + scrollbar_layer->Update(); + EXPECT_TRUE(scrollbar->PaintTickmarksCalled()); + scrollbar->SetPaintTickmarksCalled(false); + + // Disable the tickmarks. No paint. + scrollbar->set_has_tickmarks(false); + scrollbar_layer->Update(); + EXPECT_FALSE(scrollbar->PaintTickmarksCalled()); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index bc1573be04f..e52dfccf2c4 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -106,7 +106,8 @@ gfx::Size ApplyDsfAdjustment(gfx::Size device_pixels_size, float dsf) { // don't need any settings. The current approach uses 4 tiles to cover the // viewport vertically. gfx::Size CalculateGpuTileSize(const gfx::Size& base_tile_size, - const gfx::Size& content_bounds) { + const gfx::Size& content_bounds, + const gfx::Size& max_tile_size) { int tile_width = base_tile_size.width(); // Increase the height proportionally as the width decreases, and pad by our @@ -130,6 +131,11 @@ gfx::Size CalculateGpuTileSize(const gfx::Size& base_tile_size, tile_height = std::max(tile_height, kMinHeightForGpuRasteredTile); + if (!max_tile_size.IsEmpty()) { + tile_width = std::min(tile_width, max_tile_size.width()); + tile_height = std::min(tile_height, max_tile_size.height()); + } + return gfx::Size(tile_width, tile_height); } @@ -470,7 +476,7 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass, offset_visible_geometry_rect, needs_blending, draw_info.resource_id_for_export(), texture_rect, draw_info.resource_size(), draw_info.contents_swizzled(), - nearest_neighbor_, + draw_info.is_premultiplied(), nearest_neighbor_, !layer_tree_impl()->settings().enable_edge_anti_aliasing); ValidateQuadResources(quad); has_draw_quad = true; @@ -940,6 +946,9 @@ gfx::Size PictureLayerImpl::CalculateTileSize( int default_tile_width = 0; int default_tile_height = 0; if (layer_tree_impl()->use_gpu_rasterization()) { + gfx::Size max_tile_size = + layer_tree_impl()->settings().max_gpu_raster_tile_size; + // Calculate |base_tile_size based| on |gpu_raster_max_texture_size_|, // adjusting for ceil operations that may occur due to DSF. gfx::Size base_tile_size = ApplyDsfAdjustment( @@ -948,14 +957,15 @@ gfx::Size PictureLayerImpl::CalculateTileSize( // Set our initial size assuming a |base_tile_size| equal to our // |viewport_size|. gfx::Size default_tile_size = - CalculateGpuTileSize(base_tile_size, content_bounds); + CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size); // Use half-width GPU tiles when the content_width is greater than our // calculated tile size. if (content_bounds.width() > default_tile_size.width()) { // Divide width by 2 and round up. base_tile_size.set_width((base_tile_size.width() + 1) / 2); - default_tile_size = CalculateGpuTileSize(base_tile_size, content_bounds); + default_tile_size = + CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size); } default_tile_width = default_tile_size.width(); @@ -1504,7 +1514,8 @@ std::unique_ptr<PictureLayerTilingSet> PictureLayerImpl::CreatePictureLayerTilingSet() { const LayerTreeSettings& settings = layer_tree_impl()->settings(); return PictureLayerTilingSet::Create( - GetTree(), this, settings.tiling_interest_area_padding, + IsActive() ? ACTIVE_TREE : PENDING_TREE, this, + settings.tiling_interest_area_padding, layer_tree_impl()->use_gpu_rasterization() ? settings.gpu_rasterization_skewport_target_time_in_seconds : settings.skewport_target_time_in_seconds, @@ -1624,10 +1635,6 @@ void PictureLayerImpl::RunMicroBenchmark(MicroBenchmarkImpl* benchmark) { benchmark->RunOnLayer(this); } -WhichTree PictureLayerImpl::GetTree() const { - return layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE; -} - bool PictureLayerImpl::IsOnActiveOrPendingTree() const { return !layer_tree_impl()->IsRecycleTree(); } @@ -1679,9 +1686,6 @@ void PictureLayerImpl::RegisterAnimatedImages() { return; auto* controller = layer_tree_impl()->image_animation_controller(); - if (!controller) - return; - const auto& metadata = raster_source_->GetDisplayItemList() ->discardable_image_map() .animated_images_metadata(); @@ -1698,9 +1702,6 @@ void PictureLayerImpl::UnregisterAnimatedImages() { return; auto* controller = layer_tree_impl()->image_animation_controller(); - if (!controller) - return; - const auto& metadata = raster_source_->GetDisplayItemList() ->discardable_image_map() .animated_images_metadata(); diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index 319300cf130..d0348102464 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -78,7 +78,6 @@ class CC_EXPORT PictureLayerImpl bool UpdateTiles(); // Returns true if the LCD state changed. bool UpdateCanUseLCDTextAfterCommit(); - WhichTree GetTree() const; // Mask-related functions. void GetContentsResourceId(viz::ResourceId* resource_id, diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index dfccb52a619..38e0a3ac30a 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -83,7 +83,6 @@ class PictureLayerImplTest : public TestLayerTreeHostBase { settings.commit_to_active_tree = false; settings.layer_transforms_should_scale_layer_contents = true; settings.create_low_res_tiling = true; - settings.enable_image_animations = true; return settings; } diff --git a/chromium/cc/layers/picture_layer_unittest.cc b/chromium/cc/layers/picture_layer_unittest.cc index baa05d6ae46..1c92c7e9785 100644 --- a/chromium/cc/layers/picture_layer_unittest.cc +++ b/chromium/cc/layers/picture_layer_unittest.cc @@ -6,7 +6,6 @@ #include <stddef.h> -#include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" #include "cc/layers/append_quads_data.h" diff --git a/chromium/cc/layers/recording_source.cc b/chromium/cc/layers/recording_source.cc index 80648d3850b..66c44cc06a5 100644 --- a/chromium/cc/layers/recording_source.cc +++ b/chromium/cc/layers/recording_source.cc @@ -141,11 +141,11 @@ void RecordingSource::DetermineIfSolidColor() { is_solid_color_ = false; solid_color_ = SK_ColorTRANSPARENT; - if (display_list_->op_count() > kMaxOpsToAnalyzeForLayer) + if (display_list_->TotalOpCount() > kMaxOpsToAnalyzeForLayer) return; TRACE_EVENT1("cc", "RecordingSource::DetermineIfSolidColor", "opcount", - display_list_->op_count()); + display_list_->TotalOpCount()); is_solid_color_ = display_list_->GetColorIfSolidInRect( gfx::ScaleToRoundedRect(gfx::Rect(GetSize()), recording_scale_factor_), &solid_color_, kMaxOpsToAnalyzeForLayer); diff --git a/chromium/cc/layers/recording_source_unittest.cc b/chromium/cc/layers/recording_source_unittest.cc index 82c424ff8ef..15b5249fc10 100644 --- a/chromium/cc/layers/recording_source_unittest.cc +++ b/chromium/cc/layers/recording_source_unittest.cc @@ -4,7 +4,6 @@ #include <vector> -#include "base/memory/ptr_util.h" #include "cc/base/region.h" #include "cc/raster/raster_source.h" #include "cc/test/fake_content_layer_client.h" diff --git a/chromium/cc/layers/render_surface_unittest.cc b/chromium/cc/layers/render_surface_unittest.cc index 8385acee844..714ddd49916 100644 --- a/chromium/cc/layers/render_surface_unittest.cc +++ b/chromium/cc/layers/render_surface_unittest.cc @@ -68,7 +68,7 @@ class FakePictureLayerImplForRenderSurfaceTest : public FakePictureLayerImpl { for (const auto& rect : quad_rects_) { auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>(); quad->SetNew(shared_quad_state, rect, rect, needs_blending, 0, - gfx::RectF(rect), bounds(), false, false, false); + gfx::RectF(rect), bounds(), false, false, false, false); } } diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.cc b/chromium/cc/layers/scrollbar_layer_impl_base.cc index 1758c6f154a..6a0f9eb613e 100644 --- a/chromium/cc/layers/scrollbar_layer_impl_base.cc +++ b/chromium/cc/layers/scrollbar_layer_impl_base.cc @@ -270,4 +270,8 @@ ScrollbarLayerImplBase::GetScrollbarAnimator() const { return layer_tree_impl()->settings().scrollbar_animator; } +bool ScrollbarLayerImplBase::HasFindInPageTickmarks() const { + return false; +} + } // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.h b/chromium/cc/layers/scrollbar_layer_impl_base.h index df9b3d1eaa1..8c38e0e1584 100644 --- a/chromium/cc/layers/scrollbar_layer_impl_base.h +++ b/chromium/cc/layers/scrollbar_layer_impl_base.h @@ -64,6 +64,10 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { virtual LayerTreeSettings::ScrollbarAnimator GetScrollbarAnimator() const; + // Only PaintedOverlayScrollbar(Aura Overlay Scrollbar) need to know + // tickmarks's state. + virtual bool HasFindInPageTickmarks() const; + protected: ScrollbarLayerImplBase(LayerTreeImpl* tree_impl, int id, diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc index 892bbd71bb7..94935c08f07 100644 --- a/chromium/cc/layers/scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/scrollbar_layer_unittest.cc @@ -6,7 +6,6 @@ #include <unordered_map> -#include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" #include "cc/input/scrollbar_animation_controller.h" diff --git a/chromium/cc/layers/surface_layer.cc b/chromium/cc/layers/surface_layer.cc index 2aae0de35f2..8a123ca8587 100644 --- a/chromium/cc/layers/surface_layer.cc +++ b/chromium/cc/layers/surface_layer.cc @@ -7,7 +7,6 @@ #include <stdint.h> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "cc/layers/surface_layer_impl.h" #include "cc/trees/layer_tree_host.h" @@ -60,6 +59,13 @@ void SurfaceLayer::SetStretchContentToFillBounds( SetNeedsPushProperties(); } +void SurfaceLayer::SetHitTestable(bool hit_testable) { + if (hit_testable_ == hit_testable) + return; + hit_testable_ = hit_testable; + SetNeedsPushProperties(); +} + std::unique_ptr<LayerImpl> SurfaceLayer::CreateLayerImpl( LayerTreeImpl* tree_impl) { return SurfaceLayerImpl::Create(tree_impl, id()); @@ -94,6 +100,7 @@ void SurfaceLayer::PushPropertiesTo(LayerImpl* layer) { deadline_in_frames_ = 0u; layer_impl->SetFallbackSurfaceId(fallback_surface_id_); layer_impl->SetStretchContentToFillBounds(stretch_content_to_fill_bounds_); + layer_impl->SetHitTestable(hit_testable_); } } // namespace cc diff --git a/chromium/cc/layers/surface_layer.h b/chromium/cc/layers/surface_layer.h index 2dbe358ab02..3ed0b9ad140 100644 --- a/chromium/cc/layers/surface_layer.h +++ b/chromium/cc/layers/surface_layer.h @@ -28,6 +28,12 @@ class CC_EXPORT SurfaceLayer : public Layer { // When stretch_content_to_fill_bounds is true, the scale of the embedded // surface is ignored and the content will be stretched to fill the bounds. void SetStretchContentToFillBounds(bool stretch_content_to_fill_bounds); + bool stretch_content_to_fill_bounds() const { + return stretch_content_to_fill_bounds_; + } + + void SetHitTestable(bool hit_testable); + bool hit_testable() const { return hit_testable_; } // Layer overrides. std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; @@ -58,6 +64,7 @@ class CC_EXPORT SurfaceLayer : public Layer { base::Optional<uint32_t> deadline_in_frames_ = 0u; bool stretch_content_to_fill_bounds_ = false; + bool hit_testable_ = false; DISALLOW_COPY_AND_ASSIGN(SurfaceLayer); }; diff --git a/chromium/cc/layers/surface_layer_impl.cc b/chromium/cc/layers/surface_layer_impl.cc index e42794a74c4..d90e233bfd3 100644 --- a/chromium/cc/layers/surface_layer_impl.cc +++ b/chromium/cc/layers/surface_layer_impl.cc @@ -54,6 +54,14 @@ void SurfaceLayerImpl::SetStretchContentToFillBounds(bool stretch_content) { NoteLayerPropertyChanged(); } +void SurfaceLayerImpl::SetHitTestable(bool hit_testable) { + if (hit_testable_ == hit_testable) + return; + + hit_testable_ = hit_testable; + NoteLayerPropertyChanged(); +} + void SurfaceLayerImpl::PushPropertiesTo(LayerImpl* layer) { LayerImpl::PushPropertiesTo(layer); SurfaceLayerImpl* layer_impl = static_cast<SurfaceLayerImpl*>(layer); @@ -63,6 +71,7 @@ void SurfaceLayerImpl::PushPropertiesTo(LayerImpl* layer) { deadline_in_frames_ = 0u; layer_impl->SetFallbackSurfaceId(fallback_surface_id_); layer_impl->SetStretchContentToFillBounds(stretch_content_to_fill_bounds_); + layer_impl->SetHitTestable(hit_testable_); } void SurfaceLayerImpl::AppendQuads(viz::RenderPass* render_pass, @@ -93,6 +102,10 @@ void SurfaceLayerImpl::AppendQuads(viz::RenderPass* render_pass, deadline_in_frames_ = 0u; } +bool SurfaceLayerImpl::is_surface_layer() const { + return true; +} + viz::SurfaceDrawQuad* SurfaceLayerImpl::CreateSurfaceDrawQuad( viz::RenderPass* render_pass, const viz::SurfaceId& primary_surface_id, diff --git a/chromium/cc/layers/surface_layer_impl.h b/chromium/cc/layers/surface_layer_impl.h index 380e8b909c1..d51dd6ad9a6 100644 --- a/chromium/cc/layers/surface_layer_impl.h +++ b/chromium/cc/layers/surface_layer_impl.h @@ -50,11 +50,15 @@ class CC_EXPORT SurfaceLayerImpl : public LayerImpl { return stretch_content_to_fill_bounds_; } + void SetHitTestable(bool hit_testable); + bool hit_testable() const { return hit_testable_; } + // LayerImpl overrides. std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; void PushPropertiesTo(LayerImpl* layer) 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); @@ -75,6 +79,7 @@ class CC_EXPORT SurfaceLayerImpl : public LayerImpl { base::Optional<uint32_t> deadline_in_frames_; bool stretch_content_to_fill_bounds_ = false; + bool hit_testable_ = false; DISALLOW_COPY_AND_ASSIGN(SurfaceLayerImpl); }; diff --git a/chromium/cc/layers/surface_layer_unittest.cc b/chromium/cc/layers/surface_layer_unittest.cc index 45def505da1..9eb987a0f9e 100644 --- a/chromium/cc/layers/surface_layer_unittest.cc +++ b/chromium/cc/layers/surface_layer_unittest.cc @@ -86,6 +86,18 @@ TEST_F(SurfaceLayerTest, UseExistingDeadlineForNewSurfaceLayer) { EXPECT_EQ(0u, layer->deadline_in_frames()); } +// This test verifies that if UseInfiniteDeadline() is used on a new +// SurfaceLayer then the deadline will be max number of frames. +TEST_F(SurfaceLayerTest, UseInfiniteDeadlineForNewSurfaceLayer) { + 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::UseInfiniteDeadline()); + EXPECT_EQ(std::numeric_limits<uint32_t>::max(), layer->deadline_in_frames()); +} + // This test verifies that SurfaceLayer properties are pushed across to // SurfaceLayerImpl. TEST_F(SurfaceLayerTest, PushProperties) { diff --git a/chromium/cc/layers/texture_layer.cc b/chromium/cc/layers/texture_layer.cc index 935ef9ab5c3..0e55c54f17f 100644 --- a/chromium/cc/layers/texture_layer.cc +++ b/chromium/cc/layers/texture_layer.cc @@ -7,7 +7,6 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/location.h" -#include "base/memory/ptr_util.h" #include "base/synchronization/lock.h" #include "base/trace_event/trace_event.h" #include "cc/base/simple_enclosed_region.h" @@ -23,7 +22,8 @@ scoped_refptr<TextureLayer> TextureLayer::CreateForMailbox( return scoped_refptr<TextureLayer>(new TextureLayer(client)); } -TextureLayer::TextureLayer(TextureLayerClient* client) : client_(client) {} +TextureLayer::TextureLayer(TextureLayerClient* client) + : client_(client), weak_ptr_factory_(this) {} TextureLayer::~TextureLayer() = default; @@ -154,6 +154,15 @@ void TextureLayer::SetLayerTreeHost(LayerTreeHost* host) { // commit is called complete. SetNextCommitWaitsForActivation(); } + if (host) { + // When attached to a new LayerTreHost, all previously registered + // SharedBitmapIds will need to be re-sent to the new TextureLayerImpl + // representing this layer on the compositor thread. + to_register_bitmaps_.insert( + std::make_move_iterator(registered_bitmaps_.begin()), + std::make_move_iterator(registered_bitmaps_.end())); + registered_bitmaps_.clear(); + } Layer::SetLayerTreeHost(host); } @@ -166,7 +175,8 @@ bool TextureLayer::Update() { if (client_) { viz::TransferableResource resource; std::unique_ptr<viz::SingleReleaseCallback> release_callback; - if (client_->PrepareTransferableResource(&resource, &release_callback)) { + if (client_->PrepareTransferableResource(this, &resource, + &release_callback)) { // Already within a commit, no need to do another one immediately. bool requires_commit = false; SetTransferableResourceInternal(resource, std::move(release_callback), @@ -210,6 +220,49 @@ void TextureLayer::PushPropertiesTo(LayerImpl* layer) { std::move(release_callback)); needs_set_resource_ = false; } + for (auto& pair : to_register_bitmaps_) + texture_layer->RegisterSharedBitmapId(pair.first, pair.second); + // Store the registered SharedBitmapIds in case we get a new TextureLayerImpl, + // in a new tree, to re-send them to. + registered_bitmaps_.insert( + std::make_move_iterator(to_register_bitmaps_.begin()), + std::make_move_iterator(to_register_bitmaps_.end())); + to_register_bitmaps_.clear(); + for (const auto& id : to_unregister_bitmap_ids_) + texture_layer->UnregisterSharedBitmapId(id); + to_unregister_bitmap_ids_.clear(); +} + +SharedBitmapIdRegistration TextureLayer::RegisterSharedBitmapId( + const viz::SharedBitmapId& id, + scoped_refptr<CrossThreadSharedBitmap> bitmap) { + DCHECK(to_register_bitmaps_.find(id) == to_register_bitmaps_.end()); + DCHECK(registered_bitmaps_.find(id) == registered_bitmaps_.end()); + to_register_bitmaps_[id] = std::move(bitmap); + base::Erase(to_unregister_bitmap_ids_, id); + // This does not SetNeedsCommit() to be as lazy as possible. Notifying a + // SharedBitmapId is not needed until it is used, and using it will require + // a commit, so we can wait for that commit before forwarding the + // notification instead of forcing it to happen as a side effect of this + // method. + SetNeedsPushProperties(); + return SharedBitmapIdRegistration(weak_ptr_factory_.GetWeakPtr(), id); +} + +void TextureLayer::UnregisterSharedBitmapId(viz::SharedBitmapId id) { + // If we didn't get to sending the registration to the compositor thread yet, + // just remove it. + to_register_bitmaps_.erase(id); + // Since we also track all previously sent registrations, we must remove that + // to in order to prevent re-registering on another LayerTreeHost. + registered_bitmaps_.erase(id); + + to_unregister_bitmap_ids_.push_back(id); + // Unregistering a SharedBitmapId needs to happen eventually to prevent + // leaking the SharedMemory in the display compositor. But this attempts to be + // lazy and not force a commit prematurely, so just requests a + // PushPropertiesTo() without requesting a commit. + SetNeedsPushProperties(); } TextureLayer::TransferableResourceHolder::MainThreadReference:: diff --git a/chromium/cc/layers/texture_layer.h b/chromium/cc/layers/texture_layer.h index ae19b371308..af32a87c81e 100644 --- a/chromium/cc/layers/texture_layer.h +++ b/chromium/cc/layers/texture_layer.h @@ -9,10 +9,13 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/synchronization/lock.h" #include "base/threading/thread_checker.h" #include "cc/cc_export.h" #include "cc/layers/layer.h" +#include "cc/resources/cross_thread_shared_bitmap.h" +#include "cc/resources/shared_bitmap_id_registrar.h" #include "components/viz/common/resources/transferable_resource.h" namespace gpu { @@ -25,10 +28,14 @@ class SingleReleaseCallback; namespace cc { class SingleReleaseCallback; +class TextureLayer; class TextureLayerClient; -// A Layer containing a the rendered output of a plugin instance. -class CC_EXPORT TextureLayer : public Layer { +// A Layer containing a the rendered output of a plugin instance. It can be used +// to display gpu or software resources, depending if the compositor is working +// in gpu or software compositing mode (the resources must match the compositing +// mode). +class CC_EXPORT TextureLayer : public Layer, SharedBitmapIdRegistrar { public: class CC_EXPORT TransferableResourceHolder : public base::RefCountedThreadSafe<TransferableResourceHolder> { @@ -143,6 +150,17 @@ class CC_EXPORT TextureLayer : public Layer { bool IsSnapped() override; void PushPropertiesTo(LayerImpl* layer) override; + // Request a mapping from SharedBitmapId to SharedMemory be registered via the + // LayerTreeFrameSink with the display compositor. Once this mapping is + // registered, the SharedBitmapId can be used in TransferableResources given + // to the TextureLayer for display. The SharedBitmapId registration will end + // when the returned SharedBitmapIdRegistration object is destroyed. + // Implemented as a SharedBitmapIdRegistrar interface so that clients can + // have a limited API access. + SharedBitmapIdRegistration RegisterSharedBitmapId( + const viz::SharedBitmapId& id, + scoped_refptr<CrossThreadSharedBitmap> bitmap) override; + protected: explicit TextureLayer(TextureLayerClient* client); ~TextureLayer() override; @@ -154,6 +172,12 @@ class CC_EXPORT TextureLayer : public Layer { std::unique_ptr<viz::SingleReleaseCallback> release_callback, bool requires_commit); + // Friends to give access to UnregisterSharedBitmapId(). + friend SharedBitmapIdRegistration; + // Remove a mapping from SharedBitmapId to SharedMemory in the display + // compositor. + void UnregisterSharedBitmapId(viz::SharedBitmapId id); + TextureLayerClient* client_; bool flipped_ = true; @@ -168,6 +192,24 @@ class CC_EXPORT TextureLayer : public Layer { std::unique_ptr<TransferableResourceHolder::MainThreadReference> holder_ref_; bool needs_set_resource_ = false; + // The set of SharedBitmapIds to register with the LayerTreeFrameSink on the + // compositor thread. These requests are forwarded to the TextureLayerImpl to + // use, then stored in |registered_bitmaps_| to re-send if the + // TextureLayerImpl object attached to this layer changes, by moving out of + // the LayerTreeHost. + base::flat_map<viz::SharedBitmapId, scoped_refptr<CrossThreadSharedBitmap>> + to_register_bitmaps_; + // The set of previously registered SharedBitmapIds for the current + // LayerTreeHost. If the LayerTreeHost changes, these must be re-sent to the + // (new) TextureLayerImpl to be re-registered. + base::flat_map<viz::SharedBitmapId, scoped_refptr<CrossThreadSharedBitmap>> + registered_bitmaps_; + // The SharedBitmapIds to unregister on the compositor thread, passed to the + // TextureLayerImpl. + std::vector<viz::SharedBitmapId> to_unregister_bitmap_ids_; + + base::WeakPtrFactory<TextureLayer> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(TextureLayer); }; diff --git a/chromium/cc/layers/texture_layer_client.h b/chromium/cc/layers/texture_layer_client.h index a535d504b54..71c217eee29 100644 --- a/chromium/cc/layers/texture_layer_client.h +++ b/chromium/cc/layers/texture_layer_client.h @@ -12,6 +12,7 @@ struct TransferableResource; } namespace cc { +class SharedBitmapIdRegistrar; class TextureLayerClient { public: @@ -19,6 +20,7 @@ class TextureLayerClient { // Returns false if no new data is available // and the old mailbox is to be reused. virtual bool PrepareTransferableResource( + SharedBitmapIdRegistrar* bitmap_registar, viz::TransferableResource* transferable_resource, std::unique_ptr<viz::SingleReleaseCallback>* release_callback) = 0; diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc index b57570f2e40..b0203d5362f 100644 --- a/chromium/cc/layers/texture_layer_impl.cc +++ b/chromium/cc/layers/texture_layer_impl.cc @@ -10,10 +10,12 @@ #include <vector> #include "base/strings/stringprintf.h" +#include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/occlusion.h" #include "components/viz/common/quads/solid_color_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" +#include "components/viz/common/resources/bitmap_allocation.h" #include "components/viz/common/resources/platform_color.h" #include "components/viz/common/resources/single_release_callback.h" @@ -24,17 +26,15 @@ TextureLayerImpl::TextureLayerImpl(LayerTreeImpl* tree_impl, int id) TextureLayerImpl::~TextureLayerImpl() { FreeTransferableResource(); -} -void TextureLayerImpl::SetTransferableResource( - const viz::TransferableResource& resource, - std::unique_ptr<viz::SingleReleaseCallback> release_callback) { - DCHECK_EQ(resource.mailbox_holder.mailbox.IsZero(), !release_callback); - FreeTransferableResource(); - transferable_resource_ = resource; - release_callback_ = std::move(release_callback); - own_resource_ = true; - SetNeedsPushProperties(); + LayerTreeFrameSink* sink = layer_tree_impl()->layer_tree_frame_sink(); + // The LayerTreeFrameSink may be gone, in which case there's no need to + // unregister anything. + if (sink) { + for (const auto& pair : registered_bitmaps_) { + sink->DidDeleteSharedBitmap(pair.first); + } + } } std::unique_ptr<LayerImpl> TextureLayerImpl::CreateLayerImpl( @@ -61,6 +61,12 @@ void TextureLayerImpl::PushPropertiesTo(LayerImpl* layer) { std::move(release_callback_)); own_resource_ = false; } + for (auto& pair : to_register_bitmaps_) + texture_layer->RegisterSharedBitmapId(pair.first, std::move(pair.second)); + to_register_bitmaps_.clear(); + for (const auto& id : to_unregister_bitmap_ids_) + texture_layer->UnregisterSharedBitmapId(id); + to_unregister_bitmap_ids_.clear(); } bool TextureLayerImpl::WillDraw(DrawMode draw_mode, @@ -97,6 +103,25 @@ void TextureLayerImpl::AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) { DCHECK(resource_id_); + LayerTreeFrameSink* sink = layer_tree_impl()->layer_tree_frame_sink(); + for (const auto& pair : to_register_bitmaps_) { + // Because we may want to notify a display compositor about this + // base::SharedMemory more than one time, we need to be able to keep + // making handles to share with it, so we can't close the + // base::SharedMemory. + mojo::ScopedSharedBufferHandle handle = + viz::bitmap_allocation::DuplicateWithoutClosingMappedBitmap( + pair.second->shared_memory(), pair.second->size(), + pair.second->format()); + sink->DidAllocateSharedBitmap(std::move(handle), pair.first); + } + // All |to_register_bitmaps_| have been registered above, so we can move them + // all to the |registered_bitmaps_|. + registered_bitmaps_.insert( + std::make_move_iterator(to_register_bitmaps_.begin()), + std::make_move_iterator(to_register_bitmaps_.end())); + to_register_bitmaps_.clear(); + SkColor bg_color = blend_background_color_ ? background_color() : SK_ColorTRANSPARENT; bool are_contents_opaque = @@ -143,6 +168,19 @@ SimpleEnclosedRegion TextureLayerImpl::VisibleOpaqueRegion() const { void TextureLayerImpl::ReleaseResources() { FreeTransferableResource(); resource_id_ = 0; + + // The LayerTreeFrameSink is gone and being replaced, so we will have to + // re-register all SharedBitmapIds on the new LayerTreeFrameSink. We don't + // need to do that until the SharedBitmapIds will be used, in AppendQuads(), + // but we mark them all as to be registered here. + to_register_bitmaps_.insert( + std::make_move_iterator(registered_bitmaps_.begin()), + std::make_move_iterator(registered_bitmaps_.end())); + registered_bitmaps_.clear(); + // The |to_unregister_bitmap_ids_| are kept since the active layer will re- + // register its SharedBitmapIds with a new LayerTreeFrameSink in the future, + // so we must remember that we want to unregister it (or avoid registering at + // all) instead. } void TextureLayerImpl::SetPremultipliedAlpha(bool premultiplied_alpha) { @@ -186,12 +224,60 @@ void TextureLayerImpl::SetVertexOpacity(const float vertex_opacity[4]) { SetNeedsPushProperties(); } +void TextureLayerImpl::SetTransferableResource( + const viz::TransferableResource& resource, + std::unique_ptr<viz::SingleReleaseCallback> release_callback) { + DCHECK_EQ(resource.mailbox_holder.mailbox.IsZero(), !release_callback); + FreeTransferableResource(); + transferable_resource_ = resource; + release_callback_ = std::move(release_callback); + own_resource_ = true; + SetNeedsPushProperties(); +} + +void TextureLayerImpl::RegisterSharedBitmapId( + viz::SharedBitmapId id, + scoped_refptr<CrossThreadSharedBitmap> bitmap) { + // If a TextureLayer leaves and rejoins a tree without the TextureLayerImpl + // being destroyed, then it will re-request registration of ids that are still + // registered on the impl side, so we can just ignore these requests. + if (registered_bitmaps_.find(id) == registered_bitmaps_.end()) { + // If this is a pending layer, these will be moved to the active layer when + // we PushPropertiesTo(). Otherwise, we don't need to notify these to the + // LayerTreeFrameSink until we're going to use them, so defer it until + // AppendQuads(). + to_register_bitmaps_[id] = std::move(bitmap); + } + base::Erase(to_unregister_bitmap_ids_, id); + SetNeedsPushProperties(); +} + +void TextureLayerImpl::UnregisterSharedBitmapId(viz::SharedBitmapId id) { + if (IsActive()) { + LayerTreeFrameSink* sink = layer_tree_impl()->layer_tree_frame_sink(); + if (sink && registered_bitmaps_.find(id) != registered_bitmaps_.end()) + sink->DidDeleteSharedBitmap(id); + to_register_bitmaps_.erase(id); + registered_bitmaps_.erase(id); + } else { + // The active layer will unregister. We do this because it may be using the + // SharedBitmapId, so we should remove the SharedBitmapId only after we've + // had a chance to replace it with activation. + to_unregister_bitmap_ids_.push_back(id); + SetNeedsPushProperties(); + } +} + const char* TextureLayerImpl::LayerTypeAsString() const { return "cc::TextureLayerImpl"; } 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 @@ -202,6 +288,9 @@ 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 d6889023b20..49b6be2ffa1 100644 --- a/chromium/cc/layers/texture_layer_impl.h +++ b/chromium/cc/layers/texture_layer_impl.h @@ -8,10 +8,12 @@ #include <string> #include "base/callback.h" +#include "base/containers/flat_map.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "cc/cc_export.h" #include "cc/layers/layer_impl.h" +#include "cc/resources/cross_thread_shared_bitmap.h" #include "components/viz/common/resources/transferable_resource.h" namespace viz { @@ -60,6 +62,20 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { const viz::TransferableResource& resource, std::unique_ptr<viz::SingleReleaseCallback> release_callback); + // These methods notify the display compositor, through the + // CompositorFrameSink, of the existance of a SharedBitmapId and its + // mapping to a SharedMemory in |bitmap|. Then this SharedBitmapId can be used + // in TransferableResources inserted on the layer while it is registered. If + // the layer is destroyed, the SharedBitmapId will be unregistered + // automatically, and if the CompositorFrameSink is replaced, it will be + // re-registered on the new one. The SharedMemory must be kept alive while it + // is registered. + // If this is a pending layer, the registration is deferred to the active + // layer. + void RegisterSharedBitmapId(viz::SharedBitmapId id, + scoped_refptr<CrossThreadSharedBitmap> bitmap); + void UnregisterSharedBitmapId(viz::SharedBitmapId id); + private: TextureLayerImpl(LayerTreeImpl* tree_impl, int id); @@ -87,6 +103,26 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { viz::ResourceId resource_id_ = 0; std::unique_ptr<viz::SingleReleaseCallback> release_callback_; + // As a pending layer, the set of SharedBitmapIds and the underlying + // base::SharedMemory that must be notified to the display compositor through + // the LayerTreeFrameSink. These will be passed to the active layer. As an + // active layer, the set of SharedBitmapIds that need to be registered but + // have not been yet, since it is done lazily. + base::flat_map<viz::SharedBitmapId, scoped_refptr<CrossThreadSharedBitmap>> + to_register_bitmaps_; + + // For active layers only. The set of SharedBitmapIds and ownership of the + // underlying base::SharedMemory that have been notified to the display + // compositor through the LayerTreeFrameSink. These will need to be + // re-registered if the LayerTreeFrameSink changes (ie ReleaseResources() + // occurs). + base::flat_map<viz::SharedBitmapId, scoped_refptr<CrossThreadSharedBitmap>> + registered_bitmaps_; + + // As a pending layer, the set of SharedBitmapIds that the active layer should + // unregister. + std::vector<viz::SharedBitmapId> to_unregister_bitmap_ids_; + DISALLOW_COPY_AND_ASSIGN(TextureLayerImpl); }; diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc index 137135e85ee..24463ef1a26 100644 --- a/chromium/cc/layers/texture_layer_unittest.cc +++ b/chromium/cc/layers/texture_layer_unittest.cc @@ -38,8 +38,12 @@ #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/resources/bitmap_allocation.h" #include "components/viz/common/resources/returned_resource.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" #include "components/viz/test/test_layer_tree_frame_sink.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gmock/include/gmock/gmock.h" @@ -129,13 +133,14 @@ struct CommonResourceObjects { resource2_ = viz::TransferableResource::MakeGL( mailbox_name2_, GL_LINEAR, arbitrary_target2, sync_token2_); gfx::Size size(128, 128); - shared_bitmap_ = manager->AllocateSharedBitmap(size); + shared_bitmap_ = manager->AllocateSharedBitmap(size, viz::RGBA_8888); DCHECK(shared_bitmap_); release_callback3_ = base::Bind(&MockReleaseCallback::Release2, base::Unretained(&mock_callback_), shared_bitmap_.get()); resource3_ = viz::TransferableResource::MakeSoftware( - shared_bitmap_->id(), shared_bitmap_->sequence_number(), size); + shared_bitmap_->id(), shared_bitmap_->sequence_number(), size, + viz::RGBA_8888); } using RepeatingReleaseCallback = @@ -925,6 +930,7 @@ class TextureLayerNoExtraCommitForMailboxTest public: // TextureLayerClient implementation. bool PrepareTransferableResource( + SharedBitmapIdRegistrar* bitmap_registrar, viz::TransferableResource* resource, std::unique_ptr<viz::SingleReleaseCallback>* release_callback) override { if (layer_tree_host()->SourceFrameNumber() == 1) { @@ -1003,6 +1009,7 @@ class TextureLayerChangeInvisibleMailboxTest // TextureLayerClient implementation. bool PrepareTransferableResource( + SharedBitmapIdRegistrar* bitmap_registrar, viz::TransferableResource* resource, std::unique_ptr<viz::SingleReleaseCallback>* release_callback) override { ++prepare_called_; @@ -1122,6 +1129,7 @@ class TextureLayerReleaseResourcesBase public: // TextureLayerClient implementation. bool PrepareTransferableResource( + SharedBitmapIdRegistrar* bitmap_registrar, viz::TransferableResource* resource, std::unique_ptr<viz::SingleReleaseCallback>* release_callback) override { *resource = viz::TransferableResource::MakeGL( @@ -1329,5 +1337,557 @@ class TextureLayerWithResourceImplThreadDeleted : public LayerTreeTest { SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerWithResourceImplThreadDeleted); +class StubTextureLayerClient : public TextureLayerClient { + public: + // TextureLayerClient implementation. + bool PrepareTransferableResource( + SharedBitmapIdRegistrar* bitmap_registrar, + viz::TransferableResource* resource, + std::unique_ptr<viz::SingleReleaseCallback>* release_callback) override { + return false; + } +}; + +class SoftwareLayerTreeHostClient : public StubLayerTreeHostClient { + public: + SoftwareLayerTreeHostClient() = default; + ~SoftwareLayerTreeHostClient() override = default; + + // Caller responsible for unsetting this and maintaining the host's lifetime. + void SetLayerTreeHost(LayerTreeHost* host) { host_ = host; } + + // StubLayerTreeHostClient overrides. + void RequestNewLayerTreeFrameSink() override { + auto sink = FakeLayerTreeFrameSink::CreateSoftware(); + frame_sink_ = sink.get(); + host_->SetLayerTreeFrameSink(std::move(sink)); + } + + FakeLayerTreeFrameSink* frame_sink() const { return frame_sink_; } + + private: + FakeLayerTreeFrameSink* frame_sink_ = nullptr; + LayerTreeHost* host_ = nullptr; +}; + +class SoftwareTextureLayerTest : public LayerTreeTest { + protected: + void SetupTree() override { + root_ = Layer::Create(); + root_->SetBounds(gfx::Size(10, 10)); + + // A drawable layer so that frames always get drawn. + solid_color_layer_ = SolidColorLayer::Create(); + solid_color_layer_->SetIsDrawable(true); + solid_color_layer_->SetBackgroundColor(SK_ColorRED); + solid_color_layer_->SetBounds(gfx::Size(10, 10)); + root_->AddChild(solid_color_layer_); + + texture_layer_ = TextureLayer::CreateForMailbox(&client_); + texture_layer_->SetIsDrawable(true); + texture_layer_->SetBounds(gfx::Size(10, 10)); + layer_tree_host()->SetRootLayer(root_); + LayerTreeTest::SetupTree(); + } + + 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 { + constexpr bool disable_display_vsync = false; + bool synchronous_composite = + !HasImplThread() && + !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; + auto sink = std::make_unique<viz::TestLayerTreeFrameSink>( + nullptr, nullptr, shared_bitmap_manager(), gpu_memory_buffer_manager(), + renderer_settings, ImplThreadTaskRunner(), synchronous_composite, + disable_display_vsync, refresh_rate); + frame_sink_ = sink.get(); + num_frame_sinks_created_++; + return sink; + } + + std::unique_ptr<viz::OutputSurface> CreateDisplayOutputSurfaceOnThread( + scoped_refptr<viz::ContextProvider> compositor_context_provider) + override { + return viz::FakeOutputSurface::CreateSoftware( + std::make_unique<viz::SoftwareOutputDevice>()); + } + + StubTextureLayerClient client_; + scoped_refptr<Layer> root_; + scoped_refptr<SolidColorLayer> solid_color_layer_; + scoped_refptr<TextureLayer> texture_layer_; + viz::TestLayerTreeFrameSink* frame_sink_ = nullptr; + int num_frame_sinks_created_ = 0; +}; + +class SoftwareTextureLayerSwitchTreesTest : public SoftwareTextureLayerTest { + protected: + void BeginTest() override { + PostSetNeedsCommitToMainThread(); + + gfx::Size size(1, 1); + 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_, 0, gfx::Size(1, 1), + viz::RGBA_8888), + viz::SingleReleaseCallback::Create( + base::BindOnce([](const gpu::SyncToken&, bool) {}))); + break; + case 2: + // When the layer is removed from the tree, the bitmap should be + // unregistered. + texture_layer_->RemoveFromParent(); + break; + case 3: + // When the layer is added to a new tree, the SharedBitmapId is + // registered again. + root_->AddChild(texture_layer_); + break; + case 4: + // If the layer is removed and added back to the same tree in one + // commit, there should be no side effects, the bitmap stays + // registered. + texture_layer_->RemoveFromParent(); + root_->AddChild(texture_layer_); + break; + case 5: + // Release the TransferableResource before shutdown. + texture_layer_->ClearClient(); + break; + case 6: + EndTest(); + } + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + switch (step_) { + case 0: + // Before commit 1, the |texture_layer_| has no SharedBitmapId yet. + EXPECT_EQ(0u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + case 1: + // For commit 1, we added a SharedBitmapId to |texture_layer_|. + EXPECT_EQ(1u, frame_sink_->owned_bitmaps().size()); + EXPECT_EQ(*frame_sink_->owned_bitmaps().begin(), id_); + verified_frames_++; + break; + case 2: + // For commit 2, we removed |texture_layer_| from the tree. + EXPECT_EQ(0u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + case 3: + // For commit 3, we added |texture_layer_| back to the tree. + EXPECT_EQ(1u, frame_sink_->owned_bitmaps().size()); + EXPECT_EQ(*frame_sink_->owned_bitmaps().begin(), id_); + verified_frames_++; + break; + case 4: + // For commit 3, we removed+added |texture_layer_| back to the tree. + EXPECT_EQ(1u, frame_sink_->owned_bitmaps().size()); + EXPECT_EQ(*frame_sink_->owned_bitmaps().begin(), id_); + verified_frames_++; + break; + } + } + + void AfterTest() override { EXPECT_EQ(5, verified_frames_); } + + int step_ = 0; + int verified_frames_ = 0; + viz::SharedBitmapId id_; + SharedBitmapIdRegistration registration_; + scoped_refptr<CrossThreadSharedBitmap> bitmap_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTextureLayerSwitchTreesTest); + +class SoftwareTextureLayerMultipleRegisterTest + : public SoftwareTextureLayerTest { + protected: + void BeginTest() override { + PostSetNeedsCommitToMainThread(); + + gfx::Size size(1, 1); + viz::ResourceFormat format = viz::RGBA_8888; + + id1_ = viz::SharedBitmap::GenerateId(); + bitmap1_ = base::MakeRefCounted<CrossThreadSharedBitmap>( + id1_, viz::bitmap_allocation::AllocateMappedBitmap(size, format), size, + format); + id2_ = viz::SharedBitmap::GenerateId(); + bitmap2_ = base::MakeRefCounted<CrossThreadSharedBitmap>( + id2_, 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 2 SharedBitmapIds, which should be given to the + // LayerTreeFrameSink. + registration1_ = texture_layer_->RegisterSharedBitmapId(id1_, bitmap1_); + registration2_ = texture_layer_->RegisterSharedBitmapId(id2_, bitmap2_); + // 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(id1_, 0, gfx::Size(1, 1), + viz::RGBA_8888), + viz::SingleReleaseCallback::Create( + base::BindOnce([](const gpu::SyncToken&, bool) {}))); + break; + case 2: + // Drop one registration, and force a commit and SubmitCompositorFrame + // so that we can see it. + registration2_ = SharedBitmapIdRegistration(); + texture_layer_->SetNeedsDisplay(); + break; + case 3: + // Drop the other registration. + texture_layer_->ClearClient(); + registration1_ = SharedBitmapIdRegistration(); + break; + case 4: + EndTest(); + } + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + switch (step_) { + case 0: + // Before commit 1, the |texture_layer_| has no SharedBitmapId yet. + EXPECT_EQ(0u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + case 1: + // For commit 1, we added 2 SharedBitmapIds to |texture_layer_|. + EXPECT_EQ(2u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + case 2: + // For commit 2, we removed one SharedBitmapId. + EXPECT_EQ(1u, frame_sink_->owned_bitmaps().size()); + EXPECT_EQ(*frame_sink_->owned_bitmaps().begin(), id1_); + verified_frames_++; + break; + case 3: + // For commit 3, we removed the other SharedBitmapId. + EXPECT_EQ(0u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + } + } + + void AfterTest() override { EXPECT_EQ(4, verified_frames_); } + + int step_ = 0; + int verified_frames_ = 0; + viz::SharedBitmapId id1_; + viz::SharedBitmapId id2_; + SharedBitmapIdRegistration registration1_; + SharedBitmapIdRegistration registration2_; + scoped_refptr<CrossThreadSharedBitmap> bitmap1_; + scoped_refptr<CrossThreadSharedBitmap> bitmap2_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTextureLayerMultipleRegisterTest); + +class SoftwareTextureLayerRegisterUnregisterTest + : public SoftwareTextureLayerTest { + protected: + void BeginTest() override { + PostSetNeedsCommitToMainThread(); + + gfx::Size size(1, 1); + viz::ResourceFormat format = viz::RGBA_8888; + + id1_ = viz::SharedBitmap::GenerateId(); + bitmap1_ = base::MakeRefCounted<CrossThreadSharedBitmap>( + id1_, viz::bitmap_allocation::AllocateMappedBitmap(size, format), size, + format); + id2_ = viz::SharedBitmap::GenerateId(); + bitmap2_ = base::MakeRefCounted<CrossThreadSharedBitmap>( + id2_, 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 2 SharedBitmapIds, which would be given to the + // LayerTreeFrameSink. But we unregister one. + { + registration1_ = + texture_layer_->RegisterSharedBitmapId(id1_, bitmap1_); + // We explicitly drop this registration by letting it go out of scope + // and being destroyed. Versus the registration1_ which we drop by + // assigning an empty registration to it. Both should do the same + // thing. + SharedBitmapIdRegistration temp_reg = + texture_layer_->RegisterSharedBitmapId(id2_, bitmap2_); + } + // 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(id1_, 0, gfx::Size(1, 1), + viz::RGBA_8888), + viz::SingleReleaseCallback::Create( + base::BindOnce([](const gpu::SyncToken&, bool) {}))); + break; + case 2: + // Drop the other registration. + texture_layer_->ClearClient(); + registration1_ = SharedBitmapIdRegistration(); + break; + case 3: + EndTest(); + } + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + switch (step_) { + case 0: + // Before commit 1, the |texture_layer_| has no SharedBitmapId yet. + EXPECT_EQ(0u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + case 1: + // For commit 1, we added 1 SharedBitmapId to |texture_layer_|. + EXPECT_EQ(1u, frame_sink_->owned_bitmaps().size()); + EXPECT_EQ(*frame_sink_->owned_bitmaps().begin(), id1_); + verified_frames_++; + break; + case 2: + // For commit 2, we removed the other SharedBitmapId. + EXPECT_EQ(0u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + } + } + + void AfterTest() override { EXPECT_EQ(3, verified_frames_); } + + int step_ = 0; + int verified_frames_ = 0; + viz::SharedBitmapId id1_; + viz::SharedBitmapId id2_; + SharedBitmapIdRegistration registration1_; + scoped_refptr<CrossThreadSharedBitmap> bitmap1_; + scoped_refptr<CrossThreadSharedBitmap> bitmap2_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTextureLayerRegisterUnregisterTest); + +class SoftwareTextureLayerLoseFrameSinkTest : public SoftwareTextureLayerTest { + protected: + void BeginTest() override { + PostSetNeedsCommitToMainThread(); + + gfx::Size size(1, 1); + 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_, 0, gfx::Size(1, 1), + viz::RGBA_8888), + viz::SingleReleaseCallback::Create( + base::BindOnce([](const gpu::SyncToken&, bool) {}))); + break; + case 2: + // The frame sink is lost. The host will make a new one and submit + // another frame, with the id being registered again. + layer_tree_host()->SetVisible(false); + first_frame_sink_ = + 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_, 0, gfx::Size(1, 1), + viz::RGBA_8888), + viz::SingleReleaseCallback::Create( + base::BindOnce([](const gpu::SyncToken&, bool) {}))); + break; + case 3: + // Release the TransferableResource before shutdown. + texture_layer_->ClearClient(); + break; + case 4: + EndTest(); + } + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + switch (step_) { + case 0: + // Before commit 1, the |texture_layer_| has no SharedBitmapId yet. + EXPECT_EQ(0u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + case 1: + // For commit 1, we added a SharedBitmapId to |texture_layer_|. + EXPECT_EQ(1, num_frame_sinks_created_); + EXPECT_EQ(1u, frame_sink_->owned_bitmaps().size()); + EXPECT_EQ(*frame_sink_->owned_bitmaps().begin(), id_); + verified_frames_++; + break; + case 2: + // For commit 2, we should still have the SharedBitmapId in the new + // frame sink. + EXPECT_EQ(2, num_frame_sinks_created_); + EXPECT_EQ(1u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + } + } + + void AfterTest() override { EXPECT_EQ(3, verified_frames_); } + + int step_ = 0; + int verified_frames_ = 0; + viz::SharedBitmapId id_; + SharedBitmapIdRegistration registration_; + scoped_refptr<CrossThreadSharedBitmap> bitmap_; + // Keeps a pointer value of the first frame sink, which will be removed + // from the host and destroyed. + void* first_frame_sink_; +}; + +// TODO(crbug.com/829923): Flaky with a heap-use-after-free. +// SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTextureLayerLoseFrameSinkTest); + +class SoftwareTextureLayerUnregisterRegisterTest + : public SoftwareTextureLayerTest { + protected: + void BeginTest() override { + PostSetNeedsCommitToMainThread(); + + gfx::Size size(1, 1); + 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_); + + // We do a Register request, Unregister request, and then another + // Register request. The final register request should stick. + // And registers 2 SharedBitmapIds, which would be given to the + // LayerTreeFrameSink. But we unregister one. + { + // Register-Unregister- + SharedBitmapIdRegistration temp_reg = + texture_layer_->RegisterSharedBitmapId(id_, bitmap_); + } + // Register. + 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_, 0, gfx::Size(1, 1), + viz::RGBA_8888), + viz::SingleReleaseCallback::Create( + base::BindOnce([](const gpu::SyncToken&, bool) {}))); + break; + case 2: + // Release the TransferableResource before shutdown. + texture_layer_->ClearClient(); + break; + case 3: + EndTest(); + } + } + + void DisplayReceivedCompositorFrameOnThread( + const viz::CompositorFrame& frame) override { + switch (step_) { + case 0: + // Before commit 1, the |texture_layer_| has no SharedBitmapId yet. + EXPECT_EQ(0u, frame_sink_->owned_bitmaps().size()); + verified_frames_++; + break; + case 1: + // For commit 1, we added 1 SharedBitmapId to |texture_layer_|. + EXPECT_EQ(1u, frame_sink_->owned_bitmaps().size()); + EXPECT_EQ(*frame_sink_->owned_bitmaps().begin(), id_); + verified_frames_++; + break; + } + } + + void AfterTest() override { EXPECT_EQ(2, verified_frames_); } + + int step_ = 0; + int verified_frames_ = 0; + viz::SharedBitmapId id_; + SharedBitmapIdRegistration registration_; + scoped_refptr<CrossThreadSharedBitmap> bitmap_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(SoftwareTextureLayerUnregisterRegisterTest); + } // namespace } // namespace cc diff --git a/chromium/cc/layers/touch_action_region.cc b/chromium/cc/layers/touch_action_region.cc index 9d516a0dff8..c61b1377038 100644 --- a/chromium/cc/layers/touch_action_region.cc +++ b/chromium/cc/layers/touch_action_region.cc @@ -4,7 +4,6 @@ #include "cc/layers/touch_action_region.h" -#include "base/memory/ptr_util.h" #include "ui/gfx/geometry/rect.h" namespace cc { diff --git a/chromium/cc/layers/ui_resource_layer.cc b/chromium/cc/layers/ui_resource_layer.cc index b95ca27bd7c..364e97c09ed 100644 --- a/chromium/cc/layers/ui_resource_layer.cc +++ b/chromium/cc/layers/ui_resource_layer.cc @@ -4,7 +4,6 @@ #include "cc/layers/ui_resource_layer.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "cc/layers/ui_resource_layer_impl.h" #include "cc/resources/scoped_ui_resource.h" diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc index 05bbba27b92..d431a96572c 100644 --- a/chromium/cc/layers/video_layer_impl.cc +++ b/chromium/cc/layers/video_layer_impl.cc @@ -101,10 +101,13 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode, return false; if (!updater_) { - updater_.reset(new VideoResourceUpdater( + const LayerTreeSettings& settings = layer_tree_impl()->settings(); + updater_ = std::make_unique<VideoResourceUpdater>( layer_tree_impl()->context_provider(), + layer_tree_impl()->layer_tree_frame_sink(), layer_tree_impl()->resource_provider(), - layer_tree_impl()->settings().use_stream_video_draw_quad)); + settings.use_stream_video_draw_quad, + settings.resource_settings.use_gpu_memory_buffer_resources); } updater_->ObtainFrameResources(frame_); return true; diff --git a/chromium/cc/paint/BUILD.gn b/chromium/cc/paint/BUILD.gn index fa8e19b69db..535acba1472 100644 --- a/chromium/cc/paint/BUILD.gn +++ b/chromium/cc/paint/BUILD.gn @@ -24,6 +24,7 @@ cc_component("paint") { "filter_operation.h", "filter_operations.cc", "filter_operations.h", + "image_analysis_state.h", "image_animation_count.h", "image_id.h", "image_provider.cc", @@ -134,3 +135,17 @@ fuzzer_test("paint_op_buffer_eq_fuzzer") { "//cc/paint", ] } + +fuzzer_test("transfer_cache_fuzzer") { + sources = [ + "transfer_cache_fuzzer.cc", + ] + + libfuzzer_options = [ "max_len=4096" ] + + deps = [ + "//cc:test_support", + "//cc/paint", + "//components/viz/test:test_support", + ] +} diff --git a/chromium/cc/paint/DEPS b/chromium/cc/paint/DEPS index 75d19a3fc61..3761569771d 100644 --- a/chromium/cc/paint/DEPS +++ b/chromium/cc/paint/DEPS @@ -2,4 +2,7 @@ specific_include_rules = { "paint_op_buffer_fuzzer.cc": [ "+components/viz/test", ], + "transfer_cache_fuzzer.cc": [ + "+components/viz/test", + ], } diff --git a/chromium/cc/paint/color_space_transfer_cache_entry.cc b/chromium/cc/paint/color_space_transfer_cache_entry.cc index 43765c4af2f..adde3371f4e 100644 --- a/chromium/cc/paint/color_space_transfer_cache_entry.cc +++ b/chromium/cc/paint/color_space_transfer_cache_entry.cc @@ -46,8 +46,8 @@ size_t ServiceColorSpaceTransferCacheEntry::CachedSize() const { bool ServiceColorSpaceTransferCacheEntry::Deserialize( GrContext* context, - base::span<uint8_t> data) { - base::Pickle pickle(reinterpret_cast<char*>(data.data()), data.size()); + base::span<const uint8_t> data) { + base::Pickle pickle(reinterpret_cast<const char*>(data.data()), data.size()); base::PickleIterator iterator(pickle); if (!IPC::ParamTraits<gfx::ColorSpace>::Read(&pickle, &iterator, &color_space_)) diff --git a/chromium/cc/paint/color_space_transfer_cache_entry.h b/chromium/cc/paint/color_space_transfer_cache_entry.h index a31467ebab4..baeeb4ce68d 100644 --- a/chromium/cc/paint/color_space_transfer_cache_entry.h +++ b/chromium/cc/paint/color_space_transfer_cache_entry.h @@ -44,7 +44,7 @@ class CC_PAINT_EXPORT ServiceColorSpaceTransferCacheEntry final ServiceColorSpaceTransferCacheEntry(); ~ServiceColorSpaceTransferCacheEntry() override; size_t CachedSize() const override; - bool Deserialize(GrContext* context, base::span<uint8_t> data) override; + bool Deserialize(GrContext* context, base::span<const uint8_t> data) override; const gfx::ColorSpace& color_space() const { return color_space_; } diff --git a/chromium/cc/paint/decode_stashing_image_provider.cc b/chromium/cc/paint/decode_stashing_image_provider.cc index e995f51b28d..f8b4f940c05 100644 --- a/chromium/cc/paint/decode_stashing_image_provider.cc +++ b/chromium/cc/paint/decode_stashing_image_provider.cc @@ -15,8 +15,8 @@ DecodeStashingImageProvider::~DecodeStashingImageProvider() = default; ImageProvider::ScopedDecodedDrawImage DecodeStashingImageProvider::GetDecodedDrawImage(const DrawImage& draw_image) { auto decode = source_provider_->GetDecodedDrawImage(draw_image); - if (!decode) - return ScopedDecodedDrawImage(); + if (!decode.needs_unlock()) + return decode; // No need to add any destruction callback to the returned image. The images // decoded here match the lifetime of this provider. diff --git a/chromium/cc/paint/discardable_image_map.cc b/chromium/cc/paint/discardable_image_map.cc index e963a143837..232dab6a1d4 100644 --- a/chromium/cc/paint/discardable_image_map.cc +++ b/chromium/cc/paint/discardable_image_map.cc @@ -11,9 +11,9 @@ #include "base/auto_reset.h" #include "base/containers/adapters.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" +#include "cc/paint/paint_filter.h" #include "cc/paint/paint_op_buffer.h" #include "third_party/skia/include/utils/SkNoDrawCanvas.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -124,6 +124,26 @@ class DiscardableImageGenerator { } private: + class ImageGatheringProvider : public ImageProvider { + public: + ImageGatheringProvider(DiscardableImageGenerator* generator, + const gfx::Rect& op_rect) + : generator_(generator), op_rect_(op_rect) {} + ~ImageGatheringProvider() override = default; + + ScopedDecodedDrawImage GetDecodedDrawImage( + const DrawImage& draw_image) override { + generator_->AddImage(draw_image.paint_image(), + SkRect::Make(draw_image.src_rect()), op_rect_, + SkMatrix::I(), draw_image.filter_quality()); + return ScopedDecodedDrawImage(); + } + + private: + DiscardableImageGenerator* generator_; + gfx::Rect op_rect_; + }; + // Adds discardable images from |buffer| to the set of images tracked by // this generator. If |buffer| is being used in a DrawOp that requires // rasterization of the buffer as a pre-processing step for execution of the @@ -144,12 +164,13 @@ class DiscardableImageGenerator { // TODO(khushalsagar): Optimize out save/restore blocks if there are no // images in the draw ops between them. for (auto* op : PaintOpBuffer::Iterator(buffer)) { - if (!op->IsDrawOp()) { + // We need to play non-draw ops on the SkCanvas since they can affect the + // transform/clip state. + if (!op->IsDrawOp()) op->Raster(canvas, params); + + if (!PaintOp::OpHasDiscardableImages(op)) continue; - } else if (!PaintOp::OpHasDiscardableImages(op)) { - continue; - } gfx::Rect op_rect; base::Optional<gfx::Rect> local_op_rect; @@ -202,9 +223,10 @@ class DiscardableImageGenerator { gfx::Rect transformed_rect; SkRect op_rect; - if (!PaintOp::GetBounds(op, &op_rect)) { + if (!op->IsDrawOp() || !PaintOp::GetBounds(op, &op_rect)) { // If we can't provide a conservative bounding rect for the op, assume it // covers the complete current clip. + // TODO(khushalsagar): See if we can do something better for non-draw ops. transformed_rect = gfx::ToEnclosingRect(gfx::SkRectToRectF(clip_rect)); } else { const PaintFlags* flags = @@ -247,34 +269,48 @@ class DiscardableImageGenerator { void AddImageFromFlags(const gfx::Rect& op_rect, const PaintFlags& flags, const SkMatrix& ctm) { - if (!flags.getShader()) + AddImageFromShader(op_rect, flags.getShader(), ctm, + flags.getFilterQuality()); + AddImageFromFilter(op_rect, flags.getImageFilter().get()); + } + + void AddImageFromShader(const gfx::Rect& op_rect, + const PaintShader* shader, + const SkMatrix& ctm, + SkFilterQuality filter_quality) { + if (!shader || !shader->has_discardable_images()) return; - if (flags.getShader()->shader_type() == PaintShader::Type::kImage) { - const PaintImage& paint_image = flags.getShader()->paint_image(); + if (shader->shader_type() == PaintShader::Type::kImage) { + const PaintImage& paint_image = shader->paint_image(); SkMatrix matrix = ctm; - matrix.postConcat(flags.getShader()->GetLocalMatrix()); + matrix.postConcat(shader->GetLocalMatrix()); AddImage(paint_image, SkRect::MakeWH(paint_image.width(), paint_image.height()), - op_rect, matrix, flags.getFilterQuality()); - } else if (flags.getShader()->shader_type() == - PaintShader::Type::kPaintRecord && - flags.getShader()->paint_record()->HasDiscardableImages()) { + op_rect, matrix, filter_quality); + return; + } + + if (shader->shader_type() == PaintShader::Type::kPaintRecord) { + // For record backed shaders, only analyze them if they have animated + // images. + if (shader->image_analysis_state() == + ImageAnalysisState::kNoAnimatedImages) { + return; + } + SkRect scaled_tile_rect; - if (!flags.getShader()->GetRasterizationTileRect(ctm, - &scaled_tile_rect)) { + if (!shader->GetRasterizationTileRect(ctm, &scaled_tile_rect)) { return; } PaintTrackingCanvas canvas(scaled_tile_rect.width(), scaled_tile_rect.height()); - canvas.setMatrix(SkMatrix::MakeRectToRect(flags.getShader()->tile(), - scaled_tile_rect, - SkMatrix::kFill_ScaleToFit)); - base::AutoReset<bool> auto_reset(&iterating_record_shaders_, true); + canvas.setMatrix(SkMatrix::MakeRectToRect( + shader->tile(), scaled_tile_rect, SkMatrix::kFill_ScaleToFit)); + base::AutoReset<bool> auto_reset(&only_gather_animated_images_, true); size_t prev_image_set_size = image_set_.size(); - GatherDiscardableImages(flags.getShader()->paint_record().get(), &op_rect, - &canvas); + GatherDiscardableImages(shader->paint_record().get(), &op_rect, &canvas); // We only track animated images for PaintShaders. If we added any entry // to the |image_set_|, this shader any has animated images. @@ -282,9 +318,29 @@ class DiscardableImageGenerator { // PaintShader here since the analysis is done on the main thread, before // the PaintOpBuffer is used for rasterization. DCHECK_GE(image_set_.size(), prev_image_set_size); - if (image_set_.size() > prev_image_set_size) - const_cast<PaintShader*>(flags.getShader())->set_has_animated_images(); + const bool has_animated_images = image_set_.size() > prev_image_set_size; + const_cast<PaintShader*>(shader)->set_has_animated_images( + has_animated_images); + } + } + + void AddImageFromFilter(const gfx::Rect& op_rect, const PaintFilter* filter) { + // Only analyze filters if they have animated images. + if (!filter || !filter->has_discardable_images() || + filter->image_analysis_state() == + ImageAnalysisState::kNoAnimatedImages) { + return; } + + base::AutoReset<bool> auto_reset(&only_gather_animated_images_, true); + size_t prev_image_set_size = image_set_.size(); + ImageGatheringProvider image_provider(this, op_rect); + filter->SnapshotWithImages(&image_provider); + + DCHECK_GE(image_set_.size(), prev_image_set_size); + const bool has_animated_images = image_set_.size() > prev_image_set_size; + const_cast<PaintFilter*>(filter)->set_has_animated_images( + has_animated_images); } void AddImage(PaintImage paint_image, @@ -335,7 +391,8 @@ class DiscardableImageGenerator { // are animated. We defer decoding of images in record shaders to skia, but // we still need to track animated images to invalidate and advance the // animation in cc. - bool add_image = !iterating_record_shaders_ || paint_image.ShouldAnimate(); + bool add_image = + !only_gather_animated_images_ || paint_image.ShouldAnimate(); if (add_image) { image_set_.emplace_back( DrawImage(std::move(paint_image), src_irect, filter_quality, matrix), @@ -348,7 +405,7 @@ class DiscardableImageGenerator { std::vector<DiscardableImageMap::AnimatedImageMetadata> animated_images_metadata_; base::flat_map<PaintImage::Id, PaintImage::DecodingMode> decoding_mode_map_; - bool iterating_record_shaders_ = false; + bool only_gather_animated_images_ = false; // Statistics about the number of images and pixels that will require color // conversion if the target color space is not sRGB. diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc index 72906af6d90..72d608b2e35 100644 --- a/chromium/cc/paint/discardable_image_map_unittest.cc +++ b/chromium/cc/paint/discardable_image_map_unittest.cc @@ -756,9 +756,11 @@ TEST_F(DiscardableImageMapTest, CapturesImagesInPaintRecordShaders) { display_list->EndPaintOfUnpaired(visible_rect); display_list->Finalize(); - EXPECT_FALSE(flags.getShader()->has_animated_images()); + EXPECT_EQ(flags.getShader()->image_analysis_state(), + ImageAnalysisState::kNoAnalysis); display_list->GenerateDiscardableImagesMetadata(); - EXPECT_TRUE(flags.getShader()->has_animated_images()); + EXPECT_EQ(flags.getShader()->image_analysis_state(), + ImageAnalysisState::kAnimatedImages); const auto& image_map = display_list->discardable_image_map(); // The image rect is set to the rect for the DrawRectOp, and only animated @@ -776,6 +778,75 @@ TEST_F(DiscardableImageMapTest, CapturesImagesInPaintRecordShaders) { EXPECT_EQ(SkSize::Make(4.f, 4.f), draw_images[0].scale); } +TEST_F(DiscardableImageMapTest, CapturesImagesInPaintFilters) { + // Create the record to use in the filter. + auto filter_record = sk_make_sp<PaintOpBuffer>(); + + PaintImage static_image = CreateDiscardablePaintImage(gfx::Size(100, 100)); + filter_record->push<DrawImageOp>(static_image, 0.f, 0.f, nullptr); + + std::vector<FrameMetadata> frames = { + FrameMetadata(true, base::TimeDelta::FromMilliseconds(1)), + FrameMetadata(true, base::TimeDelta::FromMilliseconds(1))}; + PaintImage animated_image = CreateAnimatedImage(gfx::Size(100, 100), frames); + filter_record->push<DrawImageOp>(animated_image, 0.f, 0.f, nullptr); + + gfx::Rect visible_rect(500, 500); + scoped_refptr<DisplayItemList> display_list = new DisplayItemList(); + display_list->StartPaint(); + PaintFlags flags; + flags.setImageFilter(sk_make_sp<RecordPaintFilter>( + filter_record, SkRect::MakeWH(100.f, 100.f))); + display_list->push<DrawRectOp>(SkRect::MakeWH(200, 200), flags); + display_list->EndPaintOfUnpaired(visible_rect); + display_list->Finalize(); + + EXPECT_EQ(flags.getImageFilter()->image_analysis_state(), + ImageAnalysisState::kNoAnalysis); + display_list->GenerateDiscardableImagesMetadata(); + EXPECT_EQ(flags.getImageFilter()->image_analysis_state(), + ImageAnalysisState::kAnimatedImages); + const auto& image_map = display_list->discardable_image_map(); + + // The image rect is set to the rect for the DrawRectOp, and only animated + // images in a filter are tracked. + std::vector<PositionScaleDrawImage> draw_images = + GetDiscardableImagesInRect(image_map, visible_rect); + std::vector<gfx::Rect> inset_rects = InsetImageRects(draw_images); + ASSERT_EQ(draw_images.size(), 1u); + EXPECT_EQ(draw_images[0].image, animated_image); + // The position of the image is the position of the DrawRectOp that uses the + // filter. + EXPECT_EQ(gfx::Rect(200, 200), inset_rects[0]); + // Images in a filter are decoded at the original size. + EXPECT_EQ(SkSize::Make(1.f, 1.f), draw_images[0].scale); +} + +TEST_F(DiscardableImageMapTest, CapturesImagesInSaveLayers) { + PaintFlags flags; + PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100)); + flags.setShader(PaintShader::MakeImage(image, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, nullptr)); + + gfx::Rect visible_rect(500, 500); + scoped_refptr<DisplayItemList> display_list = new DisplayItemList(); + display_list->StartPaint(); + display_list->push<SaveLayerOp>(nullptr, &flags); + display_list->push<DrawColorOp>(SK_ColorBLUE, SkBlendMode::kSrc); + display_list->EndPaintOfUnpaired(visible_rect); + display_list->Finalize(); + + display_list->GenerateDiscardableImagesMetadata(); + const auto& image_map = display_list->discardable_image_map(); + std::vector<PositionScaleDrawImage> draw_images = + GetDiscardableImagesInRect(image_map, visible_rect); + std::vector<gfx::Rect> inset_rects = InsetImageRects(draw_images); + ASSERT_EQ(draw_images.size(), 1u); + EXPECT_EQ(draw_images[0].image, image); + EXPECT_EQ(gfx::Rect(500, 500), inset_rects[0]); + EXPECT_EQ(SkSize::Make(1.f, 1.f), draw_images[0].scale); +} + TEST_F(DiscardableImageMapTest, EmbeddedShaderWithAnimatedImages) { // Create the record with animated image to use in the shader. SkRect tile = SkRect::MakeWH(100, 100); @@ -806,8 +877,10 @@ TEST_F(DiscardableImageMapTest, EmbeddedShaderWithAnimatedImages) { display_list->EndPaintOfUnpaired(visible_rect); display_list->Finalize(); display_list->GenerateDiscardableImagesMetadata(); - EXPECT_TRUE(shader_with_image->has_animated_images()); - EXPECT_TRUE(shader_with_shader_with_image->has_animated_images()); + EXPECT_EQ(shader_with_image->image_analysis_state(), + ImageAnalysisState::kAnimatedImages); + EXPECT_EQ(shader_with_shader_with_image->image_analysis_state(), + ImageAnalysisState::kAnimatedImages); } TEST_F(DiscardableImageMapTest, DecodingModeHintsBasic) { diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc index d88cfa7aef3..21e5044b906 100644 --- a/chromium/cc/paint/display_item_list.cc +++ b/chromium/cc/paint/display_item_list.cc @@ -8,7 +8,6 @@ #include <string> -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "cc/base/math_util.h" @@ -56,21 +55,16 @@ void DisplayItemList::Raster(SkCanvas* canvas, paint_op_buffer_.Playback(canvas, PlaybackParams(image_provider), &offsets); } -void DisplayItemList::GrowCurrentBeginItemVisualRect( - const gfx::Rect& visual_rect) { - DCHECK(usage_hint_ == kTopLevelDisplayItemList); - if (!begin_paired_indices_.empty()) - visual_rects_[begin_paired_indices_.back().first].Union(visual_rect); -} - void DisplayItemList::Finalize() { TRACE_EVENT0("cc", "DisplayItemList::Finalize"); +#if DCHECK_IS_ON() // If this fails a call to StartPaint() was not ended. - DCHECK(!in_painting_); + DCHECK(!IsPainting()); // If this fails we had more calls to EndPaintOfPairedBegin() than // to EndPaintOfPairedEnd(). - DCHECK_EQ(0, in_paired_begin_count_); + DCHECK(begin_paired_indices_.empty()); DCHECK_EQ(visual_rects_.size(), offsets_.size()); +#endif if (usage_hint_ == kTopLevelDisplayItemList) { rtree_.Build(visual_rects_, @@ -120,8 +114,12 @@ DisplayItemList::CreateTracedValue(bool include_items) const { state->BeginArray("items"); PlaybackParams params(nullptr, SkMatrix::I()); + const auto& bounds = rtree_.GetAllBoundsForTracing(); + size_t i = 0; for (const PaintOp* op : PaintOpBuffer::Iterator(&paint_op_buffer_)) { state->BeginDictionary(); + state->SetString("name", PaintOpTypeToString(op->GetType())); + MathUtil::AddToTracedValue("visual_rect", bounds[i++], state.get()); SkPictureRecorder recorder; SkCanvas* canvas = @@ -129,9 +127,11 @@ DisplayItemList::CreateTracedValue(bool include_items) const { op->Raster(canvas, params); sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); - std::string b64_picture; - PictureDebugUtil::SerializeAsBase64(picture.get(), &b64_picture); - state->SetString("skp64", b64_picture); + if (picture->approximateOpCount()) { + std::string b64_picture; + PictureDebugUtil::SerializeAsBase64(picture.get(), &b64_picture); + state->SetString("skp64", b64_picture); + } state->EndDictionary(); } @@ -164,8 +164,10 @@ void DisplayItemList::GenerateDiscardableImagesMetadata() { } void DisplayItemList::Reset() { - DCHECK(!in_painting_); - DCHECK_EQ(0, in_paired_begin_count_); +#if DCHECK_IS_ON() + DCHECK(!IsPainting()); + DCHECK(begin_paired_indices_.empty()); +#endif rtree_.Reset(); image_map_.Reset(); @@ -176,9 +178,6 @@ void DisplayItemList::Reset() { offsets_.shrink_to_fit(); begin_paired_indices_.clear(); begin_paired_indices_.shrink_to_fit(); - current_range_start_ = 0; - in_paired_begin_count_ = 0; - in_painting_ = false; } sk_sp<PaintRecord> DisplayItemList::ReleaseAsRecord() { diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h index c3e50a3ed18..0ade2b179f3 100644 --- a/chromium/cc/paint/display_item_list.h +++ b/chromium/cc/paint/display_item_list.h @@ -59,26 +59,42 @@ class CC_PAINT_EXPORT DisplayItemList void Raster(SkCanvas* canvas, ImageProvider* image_provider = nullptr) const; - // TODO(vmpstr): This is only used to keep track of debugging info, so we can - // probably remove it? But it would be nice to delimit painting in a block - // somehow (RAII object maybe). void StartPaint() { - DCHECK(!in_painting_); - in_painting_ = true; +#if DCHECK_IS_ON() + DCHECK(!IsPainting()); + current_range_start_ = paint_op_buffer_.size(); +#endif } // Push functions construct a new op on the paint op buffer, while maintaining // bookkeeping information. Must be called after invoking StartPaint(). + // Returns the id (which is an opaque value) of the operation that can be used + // in UpdateSaveLayerBounds(). template <typename T, typename... Args> - void push(Args&&... args) { - DCHECK(in_painting_); + size_t push(Args&&... args) { +#if DCHECK_IS_ON() + DCHECK(IsPainting()); +#endif + size_t offset = paint_op_buffer_.next_op_offset(); if (usage_hint_ == kTopLevelDisplayItemList) - offsets_.push_back(paint_op_buffer_.next_op_offset()); + offsets_.push_back(offset); paint_op_buffer_.push<T>(std::forward<Args>(args)...); + return offset; + } + + // Called by blink::PaintChunksToCcLayer when an effect ends, to update the + // bounds of a SaveLayer[Alpha]Op which was emitted when the effect started. + // This is needed because blink doesn't know the bounds when an effect starts. + // Don't add other mutation methods like this if there is better alternative. + void UpdateSaveLayerBounds(size_t id, const SkRect& bounds) { + paint_op_buffer_.UpdateSaveLayerBounds(id, bounds); } void EndPaintOfUnpaired(const gfx::Rect& visual_rect) { - in_painting_ = false; +#if DCHECK_IS_ON() + DCHECK(IsPainting()); + current_range_start_ = kNotPainting; +#endif if (usage_hint_ == kToBeReleasedAsPaintOpBuffer) return; @@ -88,26 +104,32 @@ class CC_PAINT_EXPORT DisplayItemList } void EndPaintOfPairedBegin(const gfx::Rect& visual_rect = gfx::Rect()) { - in_painting_ = false; +#if DCHECK_IS_ON() + DCHECK(IsPainting()); + DCHECK_LT(current_range_start_, paint_op_buffer_.size()); + current_range_start_ = kNotPainting; +#endif if (usage_hint_ == kToBeReleasedAsPaintOpBuffer) return; - DCHECK_NE(visual_rects_.size(), paint_op_buffer_.size()); + + DCHECK_LT(visual_rects_.size(), paint_op_buffer_.size()); size_t count = paint_op_buffer_.size() - visual_rects_.size(); for (size_t i = 0; i < count; ++i) visual_rects_.push_back(visual_rect); begin_paired_indices_.push_back( std::make_pair(visual_rects_.size() - 1, count)); - - in_paired_begin_count_++; } void EndPaintOfPairedEnd() { - in_painting_ = false; +#if DCHECK_IS_ON() + DCHECK(IsPainting()); + DCHECK_LT(current_range_start_, paint_op_buffer_.size()); + current_range_start_ = kNotPainting; +#endif if (usage_hint_ == kToBeReleasedAsPaintOpBuffer) return; - DCHECK_NE(current_range_start_, paint_op_buffer_.size()); - DCHECK(in_paired_begin_count_); + DCHECK(begin_paired_indices_.size()); size_t last_begin_index = begin_paired_indices_.back().first; size_t last_begin_count = begin_paired_indices_.back().second; DCHECK_GT(last_begin_count, 0u); @@ -131,8 +153,6 @@ class CC_PAINT_EXPORT DisplayItemList // The block that ended needs to be included in the bounds of the enclosing // block. GrowCurrentBeginItemVisualRect(visual_rect); - - in_paired_begin_count_--; } // Called after all items are appended, to process the items. @@ -142,7 +162,7 @@ class CC_PAINT_EXPORT DisplayItemList bool HasNonAAPaint() const { return paint_op_buffer_.HasNonAAPaint(); } // This gives the total number of PaintOps. - size_t op_count() const { return paint_op_buffer_.size(); } + size_t TotalOpCount() const { return paint_op_buffer_.total_op_count(); } size_t BytesUsed() const; const DiscardableImageMap& discardable_image_map() const { @@ -183,7 +203,11 @@ class CC_PAINT_EXPORT DisplayItemList // If we're currently within a paired display item block, unions the // given visual rect with the begin display item's visual rect. - void GrowCurrentBeginItemVisualRect(const gfx::Rect& visual_rect); + void GrowCurrentBeginItemVisualRect(const gfx::Rect& visual_rect) { + DCHECK_EQ(usage_hint_, kTopLevelDisplayItemList); + if (!begin_paired_indices_.empty()) + visual_rects_[begin_paired_indices_.back().first].Union(visual_rect); + } // RTree stores indices into the paint op buffer. // TODO(vmpstr): Update the rtree to store offsets instead. @@ -202,15 +226,14 @@ class CC_PAINT_EXPORT DisplayItemList // counts refer to the number of visual rects in that begin sequence that end // with the index. std::vector<std::pair<size_t, size_t>> begin_paired_indices_; + +#if DCHECK_IS_ON() // While recording a range of ops, this is the position in the PaintOpBuffer // where the recording started. - size_t current_range_start_ = 0; - // For debugging, tracks the number of currently nested visual rects being - // added. - int in_paired_begin_count_ = 0; - // For debugging, tracks if we're painting a visual rect range, to prevent - // nesting. - bool in_painting_ = false; + bool IsPainting() const { return current_range_start_ != kNotPainting; } + const size_t kNotPainting = static_cast<size_t>(-1); + size_t current_range_start_ = kNotPainting; +#endif UsageHint usage_hint_; diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc index 43215b4a21e..5bcca13606d 100644 --- a/chromium/cc/paint/display_item_list_unittest.cc +++ b/chromium/cc/paint/display_item_list_unittest.cc @@ -8,7 +8,6 @@ #include <vector> -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event_argument.h" #include "base/values.h" #include "cc/paint/filter_operation.h" @@ -64,6 +63,20 @@ bool CompareN32Pixels(void* actual_pixels, } // namespace +#define EXPECT_TRACED_RECT(x, y, width, height, rect_list) \ + do { \ + ASSERT_EQ(4u, rect_list->GetSize()); \ + double d; \ + EXPECT_TRUE((rect_list)->GetDouble(0, &d)); \ + EXPECT_EQ(x, d); \ + EXPECT_TRUE((rect_list)->GetDouble(1, &d)); \ + EXPECT_EQ(y, d); \ + EXPECT_TRUE((rect_list)->GetDouble(2, &d)); \ + EXPECT_EQ(width, d); \ + EXPECT_TRUE((rect_list)->GetDouble(3, &d)); \ + EXPECT_EQ(height, d); \ + } while (false) + TEST(DisplayItemListTest, SingleUnpairedRange) { gfx::Rect layer_rect(100, 100); PaintFlags blue_flags; @@ -114,7 +127,7 @@ TEST(DisplayItemListTest, EmptyUnpairedRangeDoesNotAddVisualRect) { list->EndPaintOfUnpaired(layer_rect); } // No ops. - EXPECT_EQ(0u, list->op_count()); + EXPECT_EQ(0u, list->TotalOpCount()); { list->StartPaint(); @@ -123,7 +136,7 @@ TEST(DisplayItemListTest, EmptyUnpairedRangeDoesNotAddVisualRect) { list->EndPaintOfUnpaired(layer_rect); } // Two ops. - EXPECT_EQ(2u, list->op_count()); + EXPECT_EQ(2u, list->TotalOpCount()); } TEST(DisplayItemListTest, ClipPairedRange) { @@ -399,15 +412,10 @@ TEST(DisplayItemListTest, AsValueWithNoOps) { // The real contents of the traced value is in here. { const base::ListValue* list; - double d; // The layer_rect field is present by empty. ASSERT_TRUE(params_dict->GetList("layer_rect", &list)); - ASSERT_EQ(4u, list->GetSize()); - EXPECT_TRUE(list->GetDouble(0, &d) && d == 0) << d; - EXPECT_TRUE(list->GetDouble(1, &d) && d == 0) << d; - EXPECT_TRUE(list->GetDouble(2, &d) && d == 0) << d; - EXPECT_TRUE(list->GetDouble(3, &d) && d == 0) << d; + EXPECT_TRACED_RECT(0, 0, 0, 0, list); // The items list is there but empty. ASSERT_TRUE(params_dict->GetList("items", &list)); @@ -426,15 +434,10 @@ TEST(DisplayItemListTest, AsValueWithNoOps) { // The real contents of the traced value is in here. { const base::ListValue* list; - double d; // The layer_rect field is present by empty. ASSERT_TRUE(params_dict->GetList("layer_rect", &list)); - ASSERT_EQ(4u, list->GetSize()); - EXPECT_TRUE(list->GetDouble(0, &d) && d == 0) << d; - EXPECT_TRUE(list->GetDouble(1, &d) && d == 0) << d; - EXPECT_TRUE(list->GetDouble(2, &d) && d == 0) << d; - EXPECT_TRUE(list->GetDouble(3, &d) && d == 0) << d; + EXPECT_TRACED_RECT(0, 0, 0, 0, list); // The items list is not there since we asked for no ops. ASSERT_FALSE(params_dict->GetList("items", &list)); @@ -463,10 +466,11 @@ TEST(DisplayItemListTest, AsValueWithOps) { PaintFlags red_paint; red_paint.setColor(SK_ColorRED); - list->push<SaveOp>(); + list->push<SaveLayerOp>(nullptr, &red_paint); list->push<TranslateOp>(static_cast<float>(offset.x()), static_cast<float>(offset.y())); list->push<DrawRectOp>(SkRect::MakeWH(4, 4), red_paint); + list->push<RestoreOp>(); list->EndPaintOfUnpaired(bounds); } @@ -490,28 +494,35 @@ TEST(DisplayItemListTest, AsValueWithOps) { // The real contents of the traced value is in here. { - const base::ListValue* list; - double d; - + const base::ListValue* layer_rect; // The layer_rect field is present and has the bounds of the rtree. - ASSERT_TRUE(params_dict->GetList("layer_rect", &list)); - ASSERT_EQ(4u, list->GetSize()); - EXPECT_TRUE(list->GetDouble(0, &d) && d == 2) << d; - EXPECT_TRUE(list->GetDouble(1, &d) && d == 3) << d; - EXPECT_TRUE(list->GetDouble(2, &d) && d == 8) << d; - EXPECT_TRUE(list->GetDouble(3, &d) && d == 9) << d; + ASSERT_TRUE(params_dict->GetList("layer_rect", &layer_rect)); + EXPECT_TRACED_RECT(2, 3, 8, 9, layer_rect); // The items list has 3 things in it since we built 3 visual rects. - ASSERT_TRUE(params_dict->GetList("items", &list)); - EXPECT_EQ(6u, list->GetSize()); + const base::ListValue* items; + ASSERT_TRUE(params_dict->GetList("items", &items)); + ASSERT_EQ(7u, items->GetSize()); - for (int i = 0; i < 6; ++i) { + const char* expected_names[] = {"Save", "Concat", "SaveLayer", + "Translate", "DrawRect", "Restore", + "Restore"}; + bool expected_has_skp[] = {false, true, true, true, true, false, false}; + + for (int i = 0; i < 7; ++i) { const base::DictionaryValue* item_dict; + ASSERT_TRUE(items->GetDictionary(i, &item_dict)); + + const base::ListValue* visual_rect; + ASSERT_TRUE(item_dict->GetList("visual_rect", &visual_rect)); + EXPECT_TRACED_RECT(2, 3, 8, 9, visual_rect); - ASSERT_TRUE(list->GetDictionary(i, &item_dict)); + std::string name; + EXPECT_TRUE(item_dict->GetString("name", &name)); + EXPECT_EQ(expected_names[i], name); - // The SkPicture for each item exists. - EXPECT_TRUE( + EXPECT_EQ( + expected_has_skp[i], item_dict->GetString("skp64", static_cast<std::string*>(nullptr))); } } @@ -528,15 +539,9 @@ TEST(DisplayItemListTest, AsValueWithOps) { // The real contents of the traced value is in here. { const base::ListValue* list; - double d; - // The layer_rect field is present and has the bounds of the rtree. ASSERT_TRUE(params_dict->GetList("layer_rect", &list)); - ASSERT_EQ(4u, list->GetSize()); - EXPECT_TRUE(list->GetDouble(0, &d) && d == 2) << d; - EXPECT_TRUE(list->GetDouble(1, &d) && d == 3) << d; - EXPECT_TRUE(list->GetDouble(2, &d) && d == 8) << d; - EXPECT_TRUE(list->GetDouble(3, &d) && d == 9) << d; + EXPECT_TRACED_RECT(2, 3, 8, 9, list); // The items list is not present since we asked for no ops. ASSERT_FALSE(params_dict->GetList("items", &list)); @@ -546,7 +551,7 @@ TEST(DisplayItemListTest, AsValueWithOps) { TEST(DisplayItemListTest, SizeEmpty) { auto list = base::MakeRefCounted<DisplayItemList>(); - EXPECT_EQ(0u, list->op_count()); + EXPECT_EQ(0u, list->TotalOpCount()); } TEST(DisplayItemListTest, SizeOne) { @@ -557,7 +562,7 @@ TEST(DisplayItemListTest, SizeOne) { list->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags()); list->EndPaintOfUnpaired(drawing_bounds); } - EXPECT_EQ(1u, list->op_count()); + EXPECT_EQ(1u, list->TotalOpCount()); } TEST(DisplayItemListTest, SizeMultiple) { @@ -575,7 +580,7 @@ TEST(DisplayItemListTest, SizeMultiple) { list->push<RestoreOp>(); list->EndPaintOfPairedEnd(); } - EXPECT_EQ(3u, list->op_count()); + EXPECT_EQ(3u, list->TotalOpCount()); } TEST(DisplayItemListTest, AppendVisualRectSimple) { @@ -590,7 +595,7 @@ TEST(DisplayItemListTest, AppendVisualRectSimple) { list->EndPaintOfUnpaired(drawing_bounds); } - EXPECT_EQ(1u, list->op_count()); + EXPECT_EQ(1u, list->TotalOpCount()); EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(0)); } @@ -613,7 +618,7 @@ TEST(DisplayItemListTest, AppendVisualRectEmptyBlock) { list->EndPaintOfPairedEnd(); } - EXPECT_EQ(3u, list->op_count()); + EXPECT_EQ(3u, list->TotalOpCount()); EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(0)); EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(1)); EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(2)); @@ -650,7 +655,7 @@ TEST(DisplayItemListTest, AppendVisualRectEmptyBlockContainingEmptyBlock) { list->EndPaintOfPairedEnd(); } - EXPECT_EQ(5u, list->op_count()); + EXPECT_EQ(5u, list->TotalOpCount()); EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(0)); EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(1)); EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(2)); @@ -685,7 +690,7 @@ TEST(DisplayItemListTest, AppendVisualRectBlockContainingDrawing) { list->EndPaintOfPairedEnd(); } - EXPECT_EQ(4u, list->op_count()); + EXPECT_EQ(4u, list->TotalOpCount()); EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(0)); EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(1)); EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(2)); @@ -719,7 +724,7 @@ TEST(DisplayItemListTest, AppendVisualRectBlockContainingEscapedDrawing) { list->EndPaintOfPairedEnd(); } - EXPECT_EQ(4u, list->op_count()); + EXPECT_EQ(4u, list->TotalOpCount()); EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(0)); EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(1)); EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(2)); @@ -762,7 +767,7 @@ TEST(DisplayItemListTest, list->EndPaintOfPairedEnd(); } - EXPECT_EQ(5u, list->op_count()); + EXPECT_EQ(5u, list->TotalOpCount()); EXPECT_RECT_EQ(drawing_a_bounds, list->VisualRectForTesting(0)); EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(1)); EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(2)); @@ -818,7 +823,7 @@ TEST(DisplayItemListTest, AppendVisualRectTwoBlocksTwoDrawings) { list->EndPaintOfPairedEnd(); } - EXPECT_EQ(8u, list->op_count()); + EXPECT_EQ(8u, list->TotalOpCount()); gfx::Rect merged_drawing_bounds = gfx::Rect(drawing_a_bounds); merged_drawing_bounds.Union(drawing_b_bounds); EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(0)); @@ -881,7 +886,7 @@ TEST(DisplayItemListTest, list->EndPaintOfPairedEnd(); } - EXPECT_EQ(8u, list->op_count()); + EXPECT_EQ(8u, list->TotalOpCount()); gfx::Rect merged_drawing_bounds = gfx::Rect(drawing_a_bounds); merged_drawing_bounds.Union(drawing_b_bounds); EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(0)); @@ -944,7 +949,7 @@ TEST(DisplayItemListTest, list->EndPaintOfPairedEnd(); } - EXPECT_EQ(8u, list->op_count()); + EXPECT_EQ(8u, list->TotalOpCount()); gfx::Rect merged_drawing_bounds = gfx::Rect(drawing_a_bounds); merged_drawing_bounds.Union(drawing_b_bounds); EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(0)); @@ -1007,7 +1012,7 @@ TEST(DisplayItemListTest, list->EndPaintOfPairedEnd(); } - EXPECT_EQ(8u, list->op_count()); + EXPECT_EQ(8u, list->TotalOpCount()); gfx::Rect merged_drawing_bounds = gfx::Rect(drawing_a_bounds); merged_drawing_bounds.Union(drawing_b_bounds); EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(0)); @@ -1045,10 +1050,31 @@ TEST(DisplayItemListTest, VisualRectForPairsEnclosingEmptyPainting) { list->EndPaintOfPairedEnd(); } - EXPECT_EQ(3u, list->op_count()); + EXPECT_EQ(3u, list->TotalOpCount()); EXPECT_RECT_EQ(visual_rect, list->VisualRectForTesting(0)); EXPECT_RECT_EQ(visual_rect, list->VisualRectForTesting(1)); EXPECT_RECT_EQ(visual_rect, list->VisualRectForTesting(2)); } +TEST(DisplayItemListTest, TotalOpCount) { + auto list = base::MakeRefCounted<DisplayItemList>(); + auto sub_list = base::MakeRefCounted<DisplayItemList>(); + + sub_list->StartPaint(); + sub_list->push<SaveOp>(); + sub_list->push<TranslateOp>(10.f, 20.f); + sub_list->push<DrawRectOp>(SkRect::MakeWH(10, 20), PaintFlags()); + sub_list->push<RestoreOp>(); + sub_list->EndPaintOfUnpaired(gfx::Rect()); + EXPECT_EQ(4u, sub_list->TotalOpCount()); + + list->StartPaint(); + list->push<SaveOp>(); + list->push<TranslateOp>(10.f, 20.f); + list->push<DrawRecordOp>(sub_list->ReleaseAsRecord()); + list->push<RestoreOp>(); + list->EndPaintOfUnpaired(gfx::Rect()); + EXPECT_EQ(8u, list->TotalOpCount()); +} + } // namespace cc diff --git a/chromium/cc/paint/image_analysis_state.h b/chromium/cc/paint/image_analysis_state.h new file mode 100644 index 00000000000..ebf085dcc35 --- /dev/null +++ b/chromium/cc/paint/image_analysis_state.h @@ -0,0 +1,18 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_PAINT_IMAGE_ANALYSIS_STATE_H_ +#define CC_PAINT_IMAGE_ANALYSIS_STATE_H_ + +namespace cc { + +enum class ImageAnalysisState { + kNoAnalysis, + kAnimatedImages, + kNoAnimatedImages, +}; + +} // namespace cc + +#endif // CC_PAINT_IMAGE_ANALYSIS_STATE_H_ diff --git a/chromium/cc/paint/image_transfer_cache_entry.cc b/chromium/cc/paint/image_transfer_cache_entry.cc index 82be5625783..c0f6d025f2c 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.cc +++ b/chromium/cc/paint/image_transfer_cache_entry.cc @@ -15,7 +15,15 @@ namespace cc { ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( const SkPixmap* pixmap, const SkColorSpace* target_color_space) - : id_(s_next_id_.GetNext()), pixmap_(pixmap) { + : id_(s_next_id_.GetNext()), + pixmap_(pixmap), + target_color_space_(target_color_space) { + size_t target_color_space_size = + target_color_space ? target_color_space->writeToMemory(nullptr) : 0u; + size_t pixmap_color_space_size = + pixmap_->colorSpace() ? pixmap_->colorSpace()->writeToMemory(nullptr) + : 0u; + // Compute and cache the size of the data. // We write the following: // - Image color type (uint32_t) @@ -30,9 +38,11 @@ ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( 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); size_ = safe_size.ValueOrDie(); - // TODO(ericrk): Handle colorspace. } + ClientImageTransferCacheEntry::~ClientImageTransferCacheEntry() = default; // static @@ -55,9 +65,10 @@ bool ClientImageTransferCacheEntry::Serialize(base::span<uint8_t> data) const { writer.Write(pixmap_->height()); 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.WriteData(pixmap_size, pixmap_->addr()); - // TODO(ericrk): Handle colorspace. - if (writer.size() != data.size()) return false; @@ -76,8 +87,9 @@ size_t ServiceImageTransferCacheEntry::CachedSize() const { return size_; } -bool ServiceImageTransferCacheEntry::Deserialize(GrContext* context, - base::span<uint8_t> data) { +bool ServiceImageTransferCacheEntry::Deserialize( + GrContext* context, + base::span<const uint8_t> data) { PaintOpReader reader(data.data(), data.size(), nullptr); SkColorType color_type; reader.Read(&color_type); @@ -88,12 +100,18 @@ bool ServiceImageTransferCacheEntry::Deserialize(GrContext* context, size_t pixel_size; reader.ReadSize(&pixel_size); size_ = data.size(); + sk_sp<SkColorSpace> pixmap_color_space; + reader.Read(&pixmap_color_space); + sk_sp<SkColorSpace> target_color_space; + reader.Read(&target_color_space); + if (!reader.valid()) return false; - // TODO(ericrk): Handle colorspace. - SkImageInfo image_info = - SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType); + SkImageInfo image_info = SkImageInfo::Make( + width, height, color_type, kPremul_SkAlphaType, pixmap_color_space); + if (image_info.computeMinByteSize() > pixel_size) + return false; const volatile void* pixel_data = reader.ExtractReadableMemory(pixel_size); if (!reader.valid()) return false; @@ -106,16 +124,39 @@ bool ServiceImageTransferCacheEntry::Deserialize(GrContext* context, // Depending on whether the pixmap will fit in a GPU texture, either create // a software or GPU SkImage. - uint32_t max_size = context->caps()->maxTextureSize(); + uint32_t max_size = context->maxTextureSize(); bool fits_on_gpu = width <= max_size && height <= max_size; if (fits_on_gpu) { sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr); - DCHECK(image); + 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); + } } else { - image_ = SkImage::MakeRasterCopy(pixmap); + 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); + // If color space conversion is a noop, use original data. + if (image_ == original) + image_ = SkImage::MakeRasterCopy(pixmap); + } else { + // No color conversion to do, use original data. + image_ = SkImage::MakeRasterCopy(pixmap); + } } + // TODO(enne): consider adding in the DeleteSkImageAndPreventCaching + // optimization from GpuImageDecodeCache where we forcefully remove the + // intermediate from Skia's cache. return image_; } diff --git a/chromium/cc/paint/image_transfer_cache_entry.h b/chromium/cc/paint/image_transfer_cache_entry.h index a941c193e5d..fd3937d0926 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.h +++ b/chromium/cc/paint/image_transfer_cache_entry.h @@ -37,6 +37,7 @@ class CC_PAINT_EXPORT ClientImageTransferCacheEntry private: uint32_t id_; const SkPixmap* const pixmap_; + const SkColorSpace* const target_color_space_; size_t size_ = 0; static base::AtomicSequenceNumber s_next_id_; }; @@ -53,7 +54,7 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry // ServiceTransferCacheEntry implementation: size_t CachedSize() const final; - bool Deserialize(GrContext* context, base::span<uint8_t> data) final; + bool Deserialize(GrContext* context, base::span<const uint8_t> data) final; void set_image_for_testing(sk_sp<SkImage> image) { image_ = std::move(image); diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc index 824e4c7d33e..6850d6f9ad9 100644 --- a/chromium/cc/paint/oop_pixeltest.cc +++ b/chromium/cc/paint/oop_pixeltest.cc @@ -10,9 +10,12 @@ #include "cc/base/region.h" #include "cc/layers/recording_source.h" #include "cc/paint/display_item_list.h" +#include "cc/paint/paint_image_builder.h" +#include "cc/raster/playback_image_provider.h" #include "cc/raster/raster_source.h" #include "cc/test/pixel_test_utils.h" #include "cc/test/test_in_process_context_provider.h" +#include "cc/tiles/gpu_image_decode_cache.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/raster_implementation_gles.h" @@ -44,22 +47,6 @@ scoped_refptr<DisplayItemList> MakeNoopDisplayItemList() { return display_item_list; } -class NoOpImageProvider : public ImageProvider { - public: - ~NoOpImageProvider() override = default; - - ScopedDecodedDrawImage GetDecodedDrawImage( - const DrawImage& draw_image) override { - SkBitmap bitmap; - bitmap.allocPixelsFlags(SkImageInfo::MakeN32Premul(10, 10), - SkBitmap::kZeroPixels_AllocFlag); - sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); - return ScopedDecodedDrawImage(DecodedDrawImage(image, SkSize::Make(10, 10), - SkSize::Make(1, 1), - kLow_SkFilterQuality, true)); - } -}; - class OopPixelTest : public testing::Test { public: void SetUp() override { @@ -72,54 +59,31 @@ class OopPixelTest : public testing::Test { switches::kEnableOOPRasterization); } - // Setup a GL context for reading back pixels. - bool is_offscreen = true; - gpu::ContextCreationAttribs attribs; - attribs.alpha_size = -1; - attribs.depth_size = 24; - attribs.stencil_size = 8; - attribs.samples = 0; - attribs.sample_buffers = 0; - attribs.fail_if_major_perf_caveat = false; - attribs.bind_generates_resource = false; - - context_ = gpu::GLInProcessContext::CreateWithoutInit(); - auto result = context_->Initialize( - nullptr, nullptr, is_offscreen, gpu::kNullSurfaceHandle, nullptr, - attribs, gpu::SharedMemoryLimits(), &gpu_memory_buffer_manager_, - &image_factory_, nullptr, base::ThreadTaskRunnerHandle::Get()); - - ASSERT_EQ(result, gpu::ContextResult::kSuccess); - - // Setup a second context with OOP rasterization enabled and a - // RasterInterface on top of it. - attribs.enable_oop_rasterization = true; - - raster_context_ = gpu::GLInProcessContext::CreateWithoutInit(); - result = raster_context_->Initialize( - nullptr, nullptr, is_offscreen, gpu::kNullSurfaceHandle, nullptr, - attribs, gpu::SharedMemoryLimits(), &gpu_memory_buffer_manager_, - &image_factory_, nullptr, base::ThreadTaskRunnerHandle::Get()); - ASSERT_EQ(result, gpu::ContextResult::kSuccess); - ASSERT_TRUE(raster_context_->GetCapabilities().supports_oop_raster); - raster_implementation_ = - std::make_unique<gpu::raster::RasterImplementationGLES>( - raster_context_->GetImplementation(), - raster_context_->GetImplementation(), - raster_context_->GetCapabilities()); + context_provider_ = + base::MakeRefCounted<TestInProcessContextProvider>(nullptr, true); + int max_texture_size = + context_provider_->ContextCapabilities().max_texture_size; + oop_image_cache_.reset(new GpuImageDecodeCache( + context_provider_.get(), true, kRGBA_8888_SkColorType, kWorkingSetSize, + max_texture_size)); + gpu_image_cache_.reset(new GpuImageDecodeCache( + context_provider_.get(), false, kRGBA_8888_SkColorType, kWorkingSetSize, + max_texture_size)); } - void TearDown() override { - raster_implementation_.reset(); - raster_context_.reset(); - context_.reset(); - } + class RasterOptions { + public: + RasterOptions() = default; + explicit RasterOptions(const gfx::Size& playback_size) { + resource_size = playback_size; + content_size = resource_size; + full_raster_rect = gfx::Rect(playback_size); + playback_rect = gfx::Rect(playback_size); + } - struct RasterOptions { SkColor background_color = SK_ColorBLACK; int msaa_sample_count = 0; bool use_lcd_text = false; - bool use_distance_field_text = false; SkColorType color_type = kRGBA_8888_SkColorType; gfx::Size resource_size; gfx::Size content_size; @@ -131,67 +95,69 @@ class OopPixelTest : public testing::Test { bool requires_clear = false; bool preclear = false; SkColor preclear_color; + ImageDecodeCache* image_cache = nullptr; }; SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list, const gfx::Size& playback_size) { - RasterOptions options; - options.resource_size = playback_size; - options.content_size = options.resource_size; - options.full_raster_rect = gfx::Rect(playback_size); - options.playback_rect = gfx::Rect(playback_size); + RasterOptions options(playback_size); return Raster(display_item_list, options); } SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list, const RasterOptions& options) { - gpu::gles2::GLES2Interface* gl = context_->GetImplementation(); + TestInProcessContextProvider::ScopedRasterContextLock lock( + context_provider_.get()); + + PlaybackImageProvider image_provider(oop_image_cache_.get(), + options.color_space, + PlaybackImageProvider::Settings()); + + gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); int width = options.resource_size.width(); int height = options.resource_size.height(); // Create and allocate a texture on the raster interface. GLuint raster_texture_id; - raster_implementation_->GenTextures(1, &raster_texture_id); - raster_implementation_->BindTexture(GL_TEXTURE_2D, raster_texture_id); - raster_implementation_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - raster_implementation_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_LINEAR); - - EXPECT_EQ(raster_implementation_->GetError(), + auto* raster_implementation = 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->TexParameteri(raster_texture_id, + GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + EXPECT_EQ(raster_implementation->GetError(), static_cast<unsigned>(GL_NO_ERROR)); RasterColorSpace color_space(options.color_space, ++color_space_id_); if (options.preclear) { - raster_implementation_->BeginRasterCHROMIUM( + raster_implementation->BeginRasterCHROMIUM( raster_texture_id, options.preclear_color, options.msaa_sample_count, - options.use_lcd_text, options.use_distance_field_text, - options.color_type, color_space); - raster_implementation_->EndRasterCHROMIUM(); + options.use_lcd_text, options.color_type, color_space); + raster_implementation->EndRasterCHROMIUM(); } // "Out of process" raster! \o/ - raster_implementation_->BeginRasterCHROMIUM( + raster_implementation->BeginRasterCHROMIUM( raster_texture_id, options.background_color, options.msaa_sample_count, - options.use_lcd_text, options.use_distance_field_text, - options.color_type, color_space); - raster_implementation_->RasterCHROMIUM( - display_item_list.get(), &image_provider_, options.content_size, + options.use_lcd_text, options.color_type, color_space); + 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); - raster_implementation_->EndRasterCHROMIUM(); + 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_->GenMailboxCHROMIUM(mailbox.name); - raster_implementation_->ProduceTextureDirectCHROMIUM(raster_texture_id, - mailbox.name); - raster_implementation_->OrderingBarrierCHROMIUM(); + raster_implementation->GenMailbox(mailbox.name); + raster_implementation->ProduceTextureDirect(raster_texture_id, + mailbox.name); + raster_implementation->OrderingBarrierCHROMIUM(); - EXPECT_EQ(raster_implementation_->GetError(), + EXPECT_EQ(raster_implementation->GetError(), static_cast<unsigned>(GL_NO_ERROR)); // Import the texture in gl, create an fbo and bind the texture to it. @@ -211,7 +177,7 @@ class OopPixelTest : public testing::Test { gl->DeleteFramebuffers(1, &fbo_id); gl->OrderingBarrierCHROMIUM(); - raster_implementation_->DeleteTextures(1, &raster_texture_id); + raster_implementation->DeleteTextures(1, &raster_texture_id); // Swizzle rgba->bgra if needed. std::vector<SkPMColor> colors; @@ -237,16 +203,17 @@ class OopPixelTest : public testing::Test { SkBitmap RasterExpectedBitmap( scoped_refptr<DisplayItemList> display_item_list, const gfx::Size& playback_size) { - RasterOptions options; - options.resource_size = playback_size; - options.full_raster_rect = gfx::Rect(playback_size); - options.playback_rect = gfx::Rect(playback_size); + RasterOptions options(playback_size); return RasterExpectedBitmap(display_item_list, options); } SkBitmap RasterExpectedBitmap( scoped_refptr<DisplayItemList> display_item_list, const RasterOptions& options) { + TestInProcessContextProvider::ScopedRasterContextLock lock( + context_provider_.get()); + context_provider_->GrContext()->resetContext(); + // Generate bitmap via the "in process" raster path. This verifies // that the preamble setup in RasterSource::PlaybackToCanvas matches // the same setup done in GLES2Implementation::RasterCHROMIUM. @@ -260,31 +227,24 @@ class OopPixelTest : public testing::Test { layer_rect); recording.SetRequiresClear(options.requires_clear); + PlaybackImageProvider image_provider(gpu_image_cache_.get(), + options.color_space, + PlaybackImageProvider::Settings()); + auto raster_source = recording.CreateRasterSource(); RasterSource::PlaybackSettings settings; settings.use_lcd_text = options.use_lcd_text; - // TODO(enne): add a fake image provider here. + settings.image_provider = &image_provider; - uint32_t flags = options.use_distance_field_text - ? SkSurfaceProps::kUseDistanceFieldFonts_Flag - : 0; + uint32_t flags = 0; SkSurfaceProps surface_props(flags, kUnknown_SkPixelGeometry); if (options.use_lcd_text) { surface_props = SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType); } - gpu::Capabilities capabilities; - gpu::gles2::GLES2Interface* gl = context_->GetImplementation(); - size_t max_resource_cache_bytes; - size_t max_glyph_cache_texture_bytes; - skia_bindings::GrContextForGLES2Interface::DefaultCacheLimitsForTests( - &max_resource_cache_bytes, &max_glyph_cache_texture_bytes); - skia_bindings::GrContextForGLES2Interface scoped_grcontext( - gl, capabilities, max_resource_cache_bytes, - max_glyph_cache_texture_bytes); SkImageInfo image_info = SkImageInfo::MakeN32Premul( options.resource_size.width(), options.resource_size.height()); - auto surface = SkSurface::MakeRenderTarget(scoped_grcontext.get(), + auto surface = SkSurface::MakeRenderTarget(context_provider_->GrContext(), SkBudgeted::kYes, image_info); SkCanvas* canvas = surface->getCanvas(); if (options.preclear) @@ -294,13 +254,13 @@ class OopPixelTest : public testing::Test { gfx::AxisTransform2d raster_transform(options.post_scale, options.post_translate); - gfx::ColorSpace target_color_space; raster_source->PlaybackToCanvas( canvas, options.color_space, options.content_size, options.full_raster_rect, options.playback_rect, raster_transform, settings); surface->prepareForExternalIO(); - EXPECT_EQ(gl->GetError(), static_cast<unsigned>(GL_NO_ERROR)); + EXPECT_EQ(context_provider_->ContextGL()->GetError(), + static_cast<unsigned>(GL_NO_ERROR)); SkBitmap bitmap; SkImageInfo info = SkImageInfo::Make( @@ -309,7 +269,8 @@ class OopPixelTest : public testing::Test { bitmap.allocPixels(info, options.resource_size.width() * 4); bool success = surface->readPixels(bitmap, 0, 0); CHECK(success); - EXPECT_EQ(gl->GetError(), static_cast<unsigned>(GL_NO_ERROR)); + EXPECT_EQ(context_provider_->ContextGL()->GetError(), + static_cast<unsigned>(GL_NO_ERROR)); return bitmap; } @@ -330,17 +291,27 @@ class OopPixelTest : public testing::Test { } } - private: - viz::TestGpuMemoryBufferManager gpu_memory_buffer_manager_; - TestImageFactory image_factory_; - std::unique_ptr<gpu::GLInProcessContext> context_; - std::unique_ptr<gpu::GLInProcessContext> raster_context_; - std::unique_ptr<gpu::raster::RasterImplementationGLES> raster_implementation_; + protected: + enum { kWorkingSetSize = 64 * 1024 * 1024 }; + scoped_refptr<TestInProcessContextProvider> context_provider_; + std::unique_ptr<GpuImageDecodeCache> gpu_image_cache_; + std::unique_ptr<GpuImageDecodeCache> oop_image_cache_; gl::DisableNullDrawGLBindings enable_pixel_output_; - NoOpImageProvider image_provider_; + std::unique_ptr<ImageProvider> image_provider_; int color_space_id_ = 0; }; +class OopImagePixelTest : public OopPixelTest, + public ::testing::WithParamInterface<bool> { + public: + bool UseTooLargeImage() { return GetParam(); } + gfx::Size GetImageSize() { + const int kMaxSize = 20000; + DCHECK_GT(kMaxSize, context_provider_->GrContext()->maxTextureSize()); + return UseTooLargeImage() ? gfx::Size(10, kMaxSize) : gfx::Size(10, 10); + } +}; + TEST_F(OopPixelTest, DrawColor) { gfx::Rect rect(10, 10); auto display_item_list = base::MakeRefCounted<DisplayItemList>(); @@ -349,14 +320,39 @@ TEST_F(OopPixelTest, DrawColor) { display_item_list->EndPaintOfUnpaired(rect); display_item_list->Finalize(); - auto actual = Raster(std::move(display_item_list), rect.size()); std::vector<SkPMColor> expected_pixels(rect.width() * rect.height(), SkPreMultiplyARGB(255, 0, 0, 255)); SkBitmap expected; expected.installPixels( SkImageInfo::MakeN32Premul(rect.width(), rect.height()), expected_pixels.data(), rect.width() * sizeof(SkColor)); + + auto actual_oop = Raster(display_item_list, rect.size()); + ExpectEquals(actual_oop, expected, "oop"); + + auto actual_gpu = RasterExpectedBitmap(display_item_list, rect.size()); + ExpectEquals(actual_gpu, expected, "gpu"); +} + +TEST_F(OopPixelTest, DrawColorWithTargetColorSpace) { + gfx::Rect rect(10, 10); + auto display_item_list = base::MakeRefCounted<DisplayItemList>(); + display_item_list->StartPaint(); + display_item_list->push<DrawColorOp>(SK_ColorBLUE, SkBlendMode::kSrc); + display_item_list->EndPaintOfUnpaired(rect); + display_item_list->Finalize(); + + gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateXYZD50(); + + RasterOptions options(rect.size()); + options.color_space = target_color_space; + + auto actual = Raster(display_item_list, options); + auto expected = RasterExpectedBitmap(display_item_list, options); ExpectEquals(actual, expected); + + // Verify conversion. + EXPECT_EQ(SkColorSetARGB(255, 38, 15, 221), expected.getColor(0, 0)); } TEST_F(OopPixelTest, DrawRect) { @@ -404,6 +400,156 @@ TEST_F(OopPixelTest, DrawRect) { ExpectEquals(actual, expected); } +TEST_P(OopImagePixelTest, DrawImage) { + gfx::Rect rect(10, 10); + gfx::Size image_size = GetImageSize(); + + SkBitmap bitmap; + bitmap.allocPixelsFlags( + SkImageInfo::MakeN32Premul(image_size.width(), image_size.height()), + SkBitmap::kZeroPixels_AllocFlag); + + SkCanvas canvas(bitmap); + canvas.drawColor(SK_ColorMAGENTA); + SkPaint green; + green.setColor(SK_ColorGREEN); + canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green); + + sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); + const PaintImage::Id kSomeId = 32; + auto builder = + PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId); + auto paint_image = builder.TakePaintImage(); + + auto display_item_list = base::MakeRefCounted<DisplayItemList>(); + display_item_list->StartPaint(); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr); + display_item_list->EndPaintOfUnpaired(rect); + display_item_list->Finalize(); + + auto actual = Raster(display_item_list, rect.size()); + auto expected = RasterExpectedBitmap(display_item_list, rect.size()); + ExpectEquals(actual, expected); + + EXPECT_EQ(actual.getColor(0, 0), SK_ColorMAGENTA); +} + +TEST_P(OopImagePixelTest, DrawImageWithTargetColorSpace) { + gfx::Rect rect(10, 10); + gfx::Size image_size = GetImageSize(); + + SkBitmap bitmap; + bitmap.allocPixelsFlags( + SkImageInfo::MakeN32Premul(image_size.width(), image_size.height()), + SkBitmap::kZeroPixels_AllocFlag); + + SkCanvas canvas(bitmap); + canvas.drawColor(SK_ColorMAGENTA); + SkPaint green; + green.setColor(SK_ColorGREEN); + canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green); + + sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); + const PaintImage::Id kSomeId = 32; + auto builder = + PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId); + auto paint_image = builder.TakePaintImage(); + + auto display_item_list = base::MakeRefCounted<DisplayItemList>(); + display_item_list->StartPaint(); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr); + display_item_list->EndPaintOfUnpaired(rect); + display_item_list->Finalize(); + + RasterOptions options(rect.size()); + options.color_space = gfx::ColorSpace::CreateDisplayP3D65(); + + auto actual = Raster(display_item_list, options); + auto expected = RasterExpectedBitmap(display_item_list, options); + ExpectEquals(actual, expected); + + // Verify some conversion occurred here and that actual != bitmap. + EXPECT_NE(actual.getColor(0, 0), SK_ColorMAGENTA); +} + +TEST_P(OopImagePixelTest, DrawImageWithSourceColorSpace) { + gfx::Rect rect(10, 10); + gfx::Size image_size = GetImageSize(); + + auto color_space = gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace(); + SkBitmap bitmap; + bitmap.allocPixelsFlags( + SkImageInfo::MakeN32Premul(image_size.width(), image_size.height(), + color_space), + SkBitmap::kZeroPixels_AllocFlag); + + SkCanvas canvas(bitmap); + canvas.drawColor(SK_ColorMAGENTA); + SkPaint green; + green.setColor(SK_ColorGREEN); + canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green); + + sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); + const PaintImage::Id kSomeId = 32; + auto builder = + PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId); + auto paint_image = builder.TakePaintImage(); + EXPECT_EQ(paint_image.color_space(), color_space.get()); + + auto display_item_list = base::MakeRefCounted<DisplayItemList>(); + display_item_list->StartPaint(); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr); + display_item_list->EndPaintOfUnpaired(rect); + display_item_list->Finalize(); + + RasterOptions options(rect.size()); + + auto actual = Raster(display_item_list, options); + auto expected = RasterExpectedBitmap(display_item_list, options); + ExpectEquals(actual, expected); + + // Colors get converted when being drawn to the bitmap. + EXPECT_NE(bitmap.getColor(0, 0), SK_ColorMAGENTA); +} + +TEST_P(OopImagePixelTest, DrawImageWithSourceAndTargetColorSpace) { + gfx::Rect rect(10, 10); + + gfx::Size image_size = GetImageSize(); + auto color_space = gfx::ColorSpace::CreateXYZD50().ToSkColorSpace(); + SkBitmap bitmap; + bitmap.allocPixelsFlags( + SkImageInfo::MakeN32Premul(image_size.width(), image_size.height(), + color_space), + SkBitmap::kZeroPixels_AllocFlag); + + SkCanvas canvas(bitmap); + canvas.drawColor(SK_ColorMAGENTA); + SkPaint green; + green.setColor(SK_ColorGREEN); + canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green); + + sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); + const PaintImage::Id kSomeId = 32; + auto builder = + PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId); + auto paint_image = builder.TakePaintImage(); + EXPECT_EQ(paint_image.color_space(), color_space.get()); + + auto display_item_list = base::MakeRefCounted<DisplayItemList>(); + display_item_list->StartPaint(); + display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr); + display_item_list->EndPaintOfUnpaired(rect); + display_item_list->Finalize(); + + RasterOptions options(rect.size()); + options.color_space = gfx::ColorSpace::CreateDisplayP3D65(); + + auto actual = Raster(display_item_list, options); + auto expected = RasterExpectedBitmap(display_item_list, options); + ExpectEquals(actual, expected); +} + TEST_F(OopPixelTest, Preclear) { gfx::Rect rect(10, 10); auto display_item_list = base::MakeRefCounted<DisplayItemList>(); @@ -901,5 +1047,7 @@ TEST_F(OopPixelTest, DrawRectColorSpace) { ExpectEquals(actual, expected); } +INSTANTIATE_TEST_CASE_P(P, OopImagePixelTest, ::testing::Values(false, true)); + } // namespace } // namespace cc diff --git a/chromium/cc/paint/paint_filter.cc b/chromium/cc/paint/paint_filter.cc index 514fb3e9fda..947973cb130 100644 --- a/chromium/cc/paint/paint_filter.cc +++ b/chromium/cc/paint/paint_filter.cc @@ -5,6 +5,7 @@ #include "cc/paint/paint_filter.h" #include "cc/paint/filter_operations.h" +#include "cc/paint/paint_image_builder.h" #include "cc/paint/paint_op_writer.h" #include "cc/paint/paint_record.h" #include "third_party/skia/include/core/SkColorFilter.h" @@ -26,6 +27,8 @@ namespace cc { namespace { +const bool kHasNoDiscardableImages = false; + bool AreFiltersEqual(const PaintFilter* one, const PaintFilter* two) { if (!one || !two) return !one && !two; @@ -36,9 +39,31 @@ bool AreScalarsEqual(SkScalar one, SkScalar two) { return PaintOp::AreEqualEvenIfNaN(one, two); } +bool HasDiscardableImages(const sk_sp<PaintFilter>& filter) { + return filter ? filter->has_discardable_images() : false; +} + +bool HasDiscardableImages(const sk_sp<PaintFilter>* const filters, int count) { + for (int i = 0; i < count; ++i) { + if (filters[i] && filters[i]->has_discardable_images()) + return true; + } + return false; +} + +sk_sp<PaintFilter> Snapshot(const sk_sp<PaintFilter>& filter, + ImageProvider* image_provider) { + if (!filter) + return nullptr; + return filter->SnapshotWithImages(image_provider); +} + } // namespace -PaintFilter::PaintFilter(Type type, const CropRect* crop_rect) : type_(type) { +PaintFilter::PaintFilter(Type type, + const CropRect* crop_rect, + bool has_discardable_images) + : type_(type), has_discardable_images_(has_discardable_images) { if (crop_rect) crop_rect_.emplace(*crop_rect); } @@ -120,6 +145,13 @@ size_t PaintFilter::BaseSerializedSize() const { return total_size; } +sk_sp<PaintFilter> PaintFilter::SnapshotWithImages( + ImageProvider* image_provider) const { + if (!has_discardable_images_) + return sk_ref_sp<PaintFilter>(this); + return SnapshotWithImagesInternal(image_provider); +} + bool PaintFilter::operator==(const PaintFilter& other) const { if (type_ != other.type_) return false; @@ -211,7 +243,7 @@ ColorFilterPaintFilter::ColorFilterPaintFilter( sk_sp<SkColorFilter> color_filter, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), color_filter_(std::move(color_filter)), input_(std::move(input)) { DCHECK(color_filter_); @@ -229,6 +261,12 @@ size_t ColorFilterPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> ColorFilterPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<ColorFilterPaintFilter>( + color_filter_, Snapshot(input_, image_provider), crop_rect()); +} + bool ColorFilterPaintFilter::operator==( const ColorFilterPaintFilter& other) const { return PaintOp::AreSkFlattenablesEqual(color_filter_.get(), @@ -241,7 +279,7 @@ BlurPaintFilter::BlurPaintFilter(SkScalar sigma_x, TileMode tile_mode, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), sigma_x_(sigma_x), sigma_y_(sigma_y), tile_mode_(tile_mode), @@ -260,6 +298,13 @@ size_t BlurPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> BlurPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<BlurPaintFilter>(sigma_x_, sigma_y_, tile_mode_, + Snapshot(input_, image_provider), + crop_rect()); +} + bool BlurPaintFilter::operator==(const BlurPaintFilter& other) const { return PaintOp::AreEqualEvenIfNaN(sigma_x_, other.sigma_x_) && PaintOp::AreEqualEvenIfNaN(sigma_y_, other.sigma_y_) && @@ -275,7 +320,7 @@ DropShadowPaintFilter::DropShadowPaintFilter(SkScalar dx, ShadowMode shadow_mode, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), dx_(dx), dy_(dy), sigma_x_(sigma_x), @@ -298,6 +343,13 @@ size_t DropShadowPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> DropShadowPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<DropShadowPaintFilter>( + dx_, dy_, sigma_x_, sigma_y_, color_, shadow_mode_, + Snapshot(input_, image_provider), crop_rect()); +} + bool DropShadowPaintFilter::operator==( const DropShadowPaintFilter& other) const { return PaintOp::AreEqualEvenIfNaN(dx_, other.dx_) && @@ -312,7 +364,7 @@ MagnifierPaintFilter::MagnifierPaintFilter(const SkRect& src_rect, SkScalar inset, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), src_rect_(src_rect), inset_(inset), input_(std::move(input)) { @@ -329,6 +381,12 @@ size_t MagnifierPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> MagnifierPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<MagnifierPaintFilter>( + src_rect_, inset_, Snapshot(input_, image_provider), crop_rect()); +} + bool MagnifierPaintFilter::operator==(const MagnifierPaintFilter& other) const { return PaintOp::AreSkRectsEqual(src_rect_, other.src_rect_) && PaintOp::AreEqualEvenIfNaN(inset_, other.inset_) && @@ -337,7 +395,9 @@ bool MagnifierPaintFilter::operator==(const MagnifierPaintFilter& other) const { ComposePaintFilter::ComposePaintFilter(sk_sp<PaintFilter> outer, sk_sp<PaintFilter> inner) - : PaintFilter(Type::kCompose, nullptr), + : PaintFilter(Type::kCompose, + nullptr, + HasDiscardableImages(outer) || HasDiscardableImages(inner)), outer_(std::move(outer)), inner_(std::move(inner)) { cached_sk_filter_ = SkComposeImageFilter::Make(GetSkFilter(outer_.get()), @@ -353,6 +413,12 @@ size_t ComposePaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> ComposePaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<ComposePaintFilter>(Snapshot(outer_, image_provider), + Snapshot(inner_, image_provider)); +} + bool ComposePaintFilter::operator==(const ComposePaintFilter& other) const { return AreFiltersEqual(outer_.get(), other.outer_.get()) && AreFiltersEqual(inner_.get(), other.inner_.get()); @@ -363,7 +429,7 @@ AlphaThresholdPaintFilter::AlphaThresholdPaintFilter(const SkRegion& region, SkScalar outer_max, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), region_(region), inner_min_(inner_min), outer_max_(outer_max), @@ -383,6 +449,13 @@ size_t AlphaThresholdPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> AlphaThresholdPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<AlphaThresholdPaintFilter>(region_, inner_min_, outer_max_, + Snapshot(input_, image_provider), + crop_rect()); +} + bool AlphaThresholdPaintFilter::operator==( const AlphaThresholdPaintFilter& other) const { return region_ == other.region_ && @@ -395,7 +468,10 @@ XfermodePaintFilter::XfermodePaintFilter(SkBlendMode blend_mode, sk_sp<PaintFilter> background, sk_sp<PaintFilter> foreground, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter( + kType, + crop_rect, + HasDiscardableImages(background) || HasDiscardableImages(foreground)), blend_mode_(blend_mode), background_(std::move(background)), foreground_(std::move(foreground)) { @@ -414,6 +490,13 @@ size_t XfermodePaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> XfermodePaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<XfermodePaintFilter>( + blend_mode_, Snapshot(background_, image_provider), + Snapshot(foreground_, image_provider), crop_rect()); +} + bool XfermodePaintFilter::operator==(const XfermodePaintFilter& other) const { return blend_mode_ == other.blend_mode_ && AreFiltersEqual(background_.get(), other.background_.get()) && @@ -428,7 +511,10 @@ ArithmeticPaintFilter::ArithmeticPaintFilter(float k1, sk_sp<PaintFilter> background, sk_sp<PaintFilter> foreground, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter( + kType, + crop_rect, + HasDiscardableImages(background) || HasDiscardableImages(foreground)), k1_(k1), k2_(k2), k3_(k3), @@ -452,6 +538,14 @@ size_t ArithmeticPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> ArithmeticPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<ArithmeticPaintFilter>( + k1_, k2_, k3_, k4_, enforce_pm_color_, + Snapshot(background_, image_provider), + Snapshot(foreground_, image_provider), crop_rect()); +} + bool ArithmeticPaintFilter::operator==( const ArithmeticPaintFilter& other) const { return PaintOp::AreEqualEvenIfNaN(k1_, other.k1_) && @@ -473,7 +567,7 @@ MatrixConvolutionPaintFilter::MatrixConvolutionPaintFilter( bool convolve_alpha, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), kernel_size_(kernel_size), gain_(gain), bias_(bias), @@ -503,6 +597,13 @@ size_t MatrixConvolutionPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> MatrixConvolutionPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<MatrixConvolutionPaintFilter>( + kernel_size_, &kernel_[0], gain_, bias_, kernel_offset_, tile_mode_, + convolve_alpha_, Snapshot(input_, image_provider), crop_rect()); +} + bool MatrixConvolutionPaintFilter::operator==( const MatrixConvolutionPaintFilter& other) const { return kernel_size_ == other.kernel_size_ && @@ -523,7 +624,10 @@ DisplacementMapEffectPaintFilter::DisplacementMapEffectPaintFilter( sk_sp<PaintFilter> displacement, sk_sp<PaintFilter> color, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter( + kType, + crop_rect, + HasDiscardableImages(displacement) || HasDiscardableImages(color)), channel_x_(channel_x), channel_y_(channel_y), scale_(scale), @@ -545,6 +649,13 @@ size_t DisplacementMapEffectPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> DisplacementMapEffectPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<DisplacementMapEffectPaintFilter>( + channel_x_, channel_y_, scale_, Snapshot(displacement_, image_provider), + Snapshot(color_, image_provider), crop_rect()); +} + bool DisplacementMapEffectPaintFilter::operator==( const DisplacementMapEffectPaintFilter& other) const { return channel_x_ == other.channel_x_ && channel_y_ == other.channel_y_ && @@ -557,7 +668,7 @@ ImagePaintFilter::ImagePaintFilter(PaintImage image, const SkRect& src_rect, const SkRect& dst_rect, SkFilterQuality filter_quality) - : PaintFilter(kType, nullptr), + : PaintFilter(kType, nullptr, image.IsLazyGenerated()), image_(std::move(image)), src_rect_(src_rect), dst_rect_(dst_rect), @@ -576,6 +687,26 @@ size_t ImagePaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> ImagePaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + DrawImage draw_image(image_, SkIRect::MakeWH(image_.width(), image_.height()), + filter_quality_, SkMatrix::I()); + auto scoped_decoded_image = image_provider->GetDecodedDrawImage(draw_image); + if (!scoped_decoded_image) + return nullptr; + + auto decoded_sk_image = sk_ref_sp<SkImage>( + const_cast<SkImage*>(scoped_decoded_image.decoded_image().image().get())); + PaintImage decoded_paint_image = + PaintImageBuilder::WithDefault() + .set_id(image_.stable_id()) + .set_image(decoded_sk_image, PaintImage::GetNextContentId()) + .TakePaintImage(); + + return sk_make_sp<ImagePaintFilter>(std::move(decoded_paint_image), src_rect_, + dst_rect_, filter_quality_); +} + bool ImagePaintFilter::operator==(const ImagePaintFilter& other) const { return !!image_ == !!other.image_ && PaintOp::AreSkRectsEqual(src_rect_, other.src_rect_) && @@ -585,11 +716,16 @@ bool ImagePaintFilter::operator==(const ImagePaintFilter& other) const { RecordPaintFilter::RecordPaintFilter(sk_sp<PaintRecord> record, const SkRect& record_bounds) - : PaintFilter(kType, nullptr), + : RecordPaintFilter(std::move(record), record_bounds, nullptr) {} + +RecordPaintFilter::RecordPaintFilter(sk_sp<PaintRecord> record, + const SkRect& record_bounds, + ImageProvider* image_provider) + : PaintFilter(kType, nullptr, record->HasDiscardableImages()), record_(std::move(record)), record_bounds_(record_bounds) { - cached_sk_filter_ = - SkPictureImageFilter::Make(ToSkPicture(record_, record_bounds_)); + cached_sk_filter_ = SkPictureImageFilter::Make( + ToSkPicture(record_, record_bounds_, image_provider)); } RecordPaintFilter::~RecordPaintFilter() = default; @@ -601,21 +737,35 @@ size_t RecordPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> RecordPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_sp<RecordPaintFilter>( + new RecordPaintFilter(record_, record_bounds_, image_provider)); +} + bool RecordPaintFilter::operator==(const RecordPaintFilter& other) const { return !!record_ == !!other.record_ && PaintOp::AreSkRectsEqual(record_bounds_, other.record_bounds_); } -MergePaintFilter::MergePaintFilter(sk_sp<PaintFilter>* const filters, +MergePaintFilter::MergePaintFilter(const sk_sp<PaintFilter>* const filters, int count, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect) { + : MergePaintFilter(filters, count, crop_rect, nullptr) {} + +MergePaintFilter::MergePaintFilter(const sk_sp<PaintFilter>* const filters, + int count, + const CropRect* crop_rect, + ImageProvider* image_provider) + : PaintFilter(kType, crop_rect, HasDiscardableImages(filters, count)) { std::vector<sk_sp<SkImageFilter>> sk_filters; sk_filters.reserve(count); for (int i = 0; i < count; ++i) { - inputs_->push_back(filters[i]); - sk_filters.push_back(GetSkFilter(filters[i].get())); + auto filter = + image_provider ? Snapshot(filters[i], image_provider) : filters[i]; + inputs_->push_back(std::move(filter)); + sk_filters.push_back(GetSkFilter(inputs_->back().get())); } cached_sk_filter_ = SkMergeImageFilter::Make( @@ -633,6 +783,12 @@ size_t MergePaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> MergePaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_sp<MergePaintFilter>(new MergePaintFilter( + &inputs_[0], inputs_->size(), crop_rect(), image_provider)); +} + bool MergePaintFilter::operator==(const MergePaintFilter& other) const { if (inputs_->size() != other.inputs_->size()) return false; @@ -648,7 +804,7 @@ MorphologyPaintFilter::MorphologyPaintFilter(MorphType morph_type, int radius_y, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), morph_type_(morph_type), radius_x_(radius_x), radius_y_(radius_y), @@ -675,6 +831,13 @@ size_t MorphologyPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> MorphologyPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<MorphologyPaintFilter>(morph_type_, radius_x_, radius_y_, + Snapshot(input_, image_provider), + crop_rect()); +} + bool MorphologyPaintFilter::operator==( const MorphologyPaintFilter& other) const { return morph_type_ == other.morph_type_ && radius_x_ == other.radius_x_ && @@ -686,7 +849,7 @@ OffsetPaintFilter::OffsetPaintFilter(SkScalar dx, SkScalar dy, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), dx_(dx), dy_(dy), input_(std::move(input)) { @@ -703,6 +866,12 @@ size_t OffsetPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> OffsetPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<OffsetPaintFilter>( + dx_, dy_, Snapshot(input_, image_provider), crop_rect()); +} + bool OffsetPaintFilter::operator==(const OffsetPaintFilter& other) const { return PaintOp::AreEqualEvenIfNaN(dx_, other.dx_) && PaintOp::AreEqualEvenIfNaN(dy_, other.dy_) && @@ -712,7 +881,7 @@ bool OffsetPaintFilter::operator==(const OffsetPaintFilter& other) const { TilePaintFilter::TilePaintFilter(const SkRect& src, const SkRect& dst, sk_sp<PaintFilter> input) - : PaintFilter(kType, nullptr), + : PaintFilter(kType, nullptr, HasDiscardableImages(input)), src_(src), dst_(dst), input_(std::move(input)) { @@ -729,6 +898,12 @@ size_t TilePaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> TilePaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<TilePaintFilter>(src_, dst_, + Snapshot(input_, image_provider)); +} + bool TilePaintFilter::operator==(const TilePaintFilter& other) const { return PaintOp::AreSkRectsEqual(src_, other.src_) && PaintOp::AreSkRectsEqual(dst_, other.dst_) && @@ -742,7 +917,7 @@ TurbulencePaintFilter::TurbulencePaintFilter(TurbulenceType turbulence_type, SkScalar seed, const SkISize* tile_size, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, kHasNoDiscardableImages), turbulence_type_(turbulence_type), base_frequency_x_(base_frequency_x), base_frequency_y_(base_frequency_y), @@ -776,6 +951,13 @@ size_t TurbulencePaintFilter::SerializedSize() const { sizeof(num_octaves_) + sizeof(seed_) + sizeof(tile_size_); } +sk_sp<PaintFilter> TurbulencePaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<TurbulencePaintFilter>(turbulence_type_, base_frequency_x_, + base_frequency_y_, num_octaves_, + seed_, &tile_size_, crop_rect()); +} + bool TurbulencePaintFilter::operator==( const TurbulencePaintFilter& other) const { return turbulence_type_ == other.turbulence_type_ && @@ -790,8 +972,20 @@ bool TurbulencePaintFilter::operator==( PaintFlagsPaintFilter::PaintFlagsPaintFilter(PaintFlags flags, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), flags_(std::move(flags)) { - cached_sk_filter_ = SkPaintImageFilter::Make(flags_.ToSkPaint(), crop_rect); + : PaintFlagsPaintFilter(std::move(flags), nullptr, crop_rect) {} + +PaintFlagsPaintFilter::PaintFlagsPaintFilter(PaintFlags flags, + ImageProvider* image_provider, + const CropRect* crop_rect) + : PaintFilter(kType, crop_rect, flags.HasDiscardableImages()), + flags_(std::move(flags)) { + if (image_provider) { + raster_flags_.emplace(&flags_, image_provider, SkMatrix::I(), 255u, + true /* create_skia_shaders */); + } + cached_sk_filter_ = SkPaintImageFilter::Make( + raster_flags_ ? raster_flags_->flags()->ToSkPaint() : flags_.ToSkPaint(), + crop_rect); } PaintFlagsPaintFilter::~PaintFlagsPaintFilter() = default; @@ -802,6 +996,12 @@ size_t PaintFlagsPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> PaintFlagsPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_sp<PaintFilter>( + new PaintFlagsPaintFilter(flags_, image_provider, crop_rect())); +} + bool PaintFlagsPaintFilter::operator==( const PaintFlagsPaintFilter& other) const { return flags_ == other.flags_; @@ -810,7 +1010,7 @@ bool PaintFlagsPaintFilter::operator==( MatrixPaintFilter::MatrixPaintFilter(const SkMatrix& matrix, SkFilterQuality filter_quality, sk_sp<PaintFilter> input) - : PaintFilter(Type::kMatrix, nullptr), + : PaintFilter(Type::kMatrix, nullptr, HasDiscardableImages(input)), matrix_(matrix), filter_quality_(filter_quality), input_(std::move(input)) { @@ -827,6 +1027,12 @@ size_t MatrixPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> MatrixPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<MatrixPaintFilter>(matrix_, filter_quality_, + Snapshot(input_, image_provider)); +} + bool MatrixPaintFilter::operator==(const MatrixPaintFilter& other) const { return PaintOp::AreSkMatricesEqual(matrix_, other.matrix_) && filter_quality_ == other.filter_quality_ && @@ -842,7 +1048,7 @@ LightingDistantPaintFilter::LightingDistantPaintFilter( SkScalar shininess, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), lighting_type_(lighting_type), direction_(direction), light_color_(light_color), @@ -875,6 +1081,13 @@ size_t LightingDistantPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> LightingDistantPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<LightingDistantPaintFilter>( + lighting_type_, direction_, light_color_, surface_scale_, kconstant_, + shininess_, Snapshot(input_, image_provider), crop_rect()); +} + bool LightingDistantPaintFilter::operator==( const LightingDistantPaintFilter& other) const { return lighting_type_ == other.lighting_type_ && @@ -894,7 +1107,7 @@ LightingPointPaintFilter::LightingPointPaintFilter(LightingType lighting_type, SkScalar shininess, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), lighting_type_(lighting_type), location_(location), light_color_(light_color), @@ -927,6 +1140,13 @@ size_t LightingPointPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> LightingPointPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<LightingPointPaintFilter>( + lighting_type_, location_, light_color_, surface_scale_, kconstant_, + shininess_, Snapshot(input_, image_provider), crop_rect()); +} + bool LightingPointPaintFilter::operator==( const LightingPointPaintFilter& other) const { return lighting_type_ == other.lighting_type_ && @@ -949,7 +1169,7 @@ LightingSpotPaintFilter::LightingSpotPaintFilter(LightingType lighting_type, SkScalar shininess, sk_sp<PaintFilter> input, const CropRect* crop_rect) - : PaintFilter(kType, crop_rect), + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), lighting_type_(lighting_type), location_(location), target_(target), @@ -987,6 +1207,14 @@ size_t LightingSpotPaintFilter::SerializedSize() const { return total_size.ValueOrDefault(0u); } +sk_sp<PaintFilter> LightingSpotPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<LightingSpotPaintFilter>( + lighting_type_, location_, target_, specular_exponent_, cutoff_angle_, + light_color_, surface_scale_, kconstant_, shininess_, + Snapshot(input_, image_provider), crop_rect()); +} + bool LightingSpotPaintFilter::operator==( const LightingSpotPaintFilter& other) const { return lighting_type_ == other.lighting_type_ && diff --git a/chromium/cc/paint/paint_filter.h b/chromium/cc/paint/paint_filter.h index 162559ccade..cab890edff5 100644 --- a/chromium/cc/paint/paint_filter.h +++ b/chromium/cc/paint/paint_filter.h @@ -8,9 +8,11 @@ #include "base/containers/stack_container.h" #include "base/logging.h" #include "base/optional.h" +#include "base/stl_util.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_flags.h" #include "cc/paint/paint_image.h" +#include "cc/paint/scoped_raster_flags.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkPoint3.h" @@ -22,10 +24,12 @@ namespace viz { class GLRenderer; +class SkiaRenderer; class SoftwareRenderer; } // namespace viz namespace cc { +class ImageProvider; class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { public: @@ -98,11 +102,26 @@ class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { return str.c_str(); } const CropRect* crop_rect() const { - return crop_rect_ ? &*crop_rect_ : nullptr; + return base::OptionalOrNullptr(crop_rect_); + } + + bool has_discardable_images() const { return has_discardable_images_; } + ImageAnalysisState image_analysis_state() const { + return image_analysis_state_; + } + void set_has_animated_images(bool has_animated_images) { + image_analysis_state_ = has_animated_images + ? ImageAnalysisState::kAnimatedImages + : ImageAnalysisState::kNoAnimatedImages; } virtual size_t SerializedSize() const = 0; + // Returns a snaphot of the PaintFilter with images replaced using + // |image_provider|. Note that this may return the same filter if the filter + // has no images. + sk_sp<PaintFilter> SnapshotWithImages(ImageProvider* image_provider) const; + // Note that this operation is potentially slow. It also only compares things // that are easy to compare. As an example, it doesn't compare equality of // images, rather only its existence. This is meant to be used only by tests @@ -113,7 +132,9 @@ class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { bool operator!=(const PaintFilter& other) const { return !(*this == other); } protected: - PaintFilter(Type type, const CropRect* crop_rect); + PaintFilter(Type type, + const CropRect* crop_rect, + bool has_discardable_images); static sk_sp<SkImageFilter> GetSkFilter(const PaintFilter* paint_filter) { return paint_filter ? paint_filter->cached_sk_filter_ : nullptr; @@ -123,6 +144,8 @@ class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { } size_t BaseSerializedSize() const; + virtual sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const = 0; // This should be created by each sub-class at construction time, to ensure // that subsequent access to the filter is thread-safe. @@ -133,10 +156,14 @@ class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { // raster. friend class PaintFlags; friend class viz::GLRenderer; + friend class viz::SkiaRenderer; friend class viz::SoftwareRenderer; const Type type_; base::Optional<CropRect> crop_rect_; + const bool has_discardable_images_; + + ImageAnalysisState image_analysis_state_ = ImageAnalysisState::kNoAnalysis; DISALLOW_COPY_AND_ASSIGN(PaintFilter); }; @@ -155,6 +182,10 @@ class CC_PAINT_EXPORT ColorFilterPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const ColorFilterPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: sk_sp<SkColorFilter> color_filter_; sk_sp<PaintFilter> input_; @@ -180,6 +211,10 @@ class CC_PAINT_EXPORT BlurPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const BlurPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkScalar sigma_x_; SkScalar sigma_y_; @@ -212,6 +247,10 @@ class CC_PAINT_EXPORT DropShadowPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const DropShadowPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkScalar dx_; SkScalar dy_; @@ -238,6 +277,10 @@ class CC_PAINT_EXPORT MagnifierPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const MagnifierPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkRect src_rect_; SkScalar inset_; @@ -256,6 +299,10 @@ class CC_PAINT_EXPORT ComposePaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const ComposePaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: sk_sp<PaintFilter> outer_; sk_sp<PaintFilter> inner_; @@ -279,6 +326,10 @@ class CC_PAINT_EXPORT AlphaThresholdPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const AlphaThresholdPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkRegion region_; SkScalar inner_min_; @@ -302,6 +353,10 @@ class CC_PAINT_EXPORT XfermodePaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const XfermodePaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkBlendMode blend_mode_; sk_sp<PaintFilter> background_; @@ -332,6 +387,10 @@ class CC_PAINT_EXPORT ArithmeticPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const ArithmeticPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: float k1_; float k2_; @@ -369,6 +428,10 @@ class CC_PAINT_EXPORT MatrixConvolutionPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const MatrixConvolutionPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkISize kernel_size_; base::StackVector<SkScalar, 3> kernel_; @@ -402,6 +465,10 @@ class CC_PAINT_EXPORT DisplacementMapEffectPaintFilter final size_t SerializedSize() const override; bool operator==(const DisplacementMapEffectPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: ChannelSelectorType channel_x_; ChannelSelectorType channel_y_; @@ -427,6 +494,10 @@ class CC_PAINT_EXPORT ImagePaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const ImagePaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: PaintImage image_; SkRect src_rect_; @@ -446,7 +517,15 @@ class CC_PAINT_EXPORT RecordPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const RecordPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: + RecordPaintFilter(sk_sp<PaintRecord> record, + const SkRect& record_bounds, + ImageProvider* image_provider); + sk_sp<PaintRecord> record_; SkRect record_bounds_; }; @@ -454,7 +533,7 @@ class CC_PAINT_EXPORT RecordPaintFilter final : public PaintFilter { class CC_PAINT_EXPORT MergePaintFilter final : public PaintFilter { public: static constexpr Type kType = Type::kMerge; - MergePaintFilter(sk_sp<PaintFilter>* const filters, + MergePaintFilter(const sk_sp<PaintFilter>* const filters, int count, const CropRect* crop_rect = nullptr); ~MergePaintFilter() override; @@ -468,7 +547,15 @@ class CC_PAINT_EXPORT MergePaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const MergePaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: + MergePaintFilter(const sk_sp<PaintFilter>* const filters, + int count, + const CropRect* crop_rect, + ImageProvider* image_provider); base::StackVector<sk_sp<PaintFilter>, 2> inputs_; }; @@ -491,6 +578,10 @@ class CC_PAINT_EXPORT MorphologyPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const MorphologyPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: MorphType morph_type_; int radius_x_; @@ -514,6 +605,10 @@ class CC_PAINT_EXPORT OffsetPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const OffsetPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkScalar dx_; SkScalar dy_; @@ -535,6 +630,10 @@ class CC_PAINT_EXPORT TilePaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const TilePaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkRect src_; SkRect dst_; @@ -568,6 +667,10 @@ class CC_PAINT_EXPORT TurbulencePaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const TurbulencePaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: TurbulenceType turbulence_type_; SkScalar base_frequency_x_; @@ -589,8 +692,17 @@ class CC_PAINT_EXPORT PaintFlagsPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const PaintFlagsPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: + PaintFlagsPaintFilter(PaintFlags flags, + ImageProvider* image_provider, + const CropRect* crop_rect); + PaintFlags flags_; + base::Optional<ScopedRasterFlags> raster_flags_; }; class CC_PAINT_EXPORT MatrixPaintFilter final : public PaintFilter { @@ -608,6 +720,10 @@ class CC_PAINT_EXPORT MatrixPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const MatrixPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: SkMatrix matrix_; SkFilterQuality filter_quality_; @@ -641,6 +757,10 @@ class CC_PAINT_EXPORT LightingDistantPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const LightingDistantPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: LightingType lighting_type_; SkPoint3 direction_; @@ -678,6 +798,10 @@ class CC_PAINT_EXPORT LightingPointPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const LightingPointPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: LightingType lighting_type_; SkPoint3 location_; @@ -721,6 +845,10 @@ class CC_PAINT_EXPORT LightingSpotPaintFilter final : public PaintFilter { size_t SerializedSize() const override; bool operator==(const LightingSpotPaintFilter& other) const; + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + private: LightingType lighting_type_; SkPoint3 location_; diff --git a/chromium/cc/paint/paint_filter_unittest.cc b/chromium/cc/paint/paint_filter_unittest.cc new file mode 100644 index 00000000000..de1ea45132c --- /dev/null +++ b/chromium/cc/paint/paint_filter_unittest.cc @@ -0,0 +1,197 @@ +// 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/paint/paint_filter.h" + +#include "cc/paint/paint_op_buffer.h" +#include "cc/test/skia_common.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/effects/SkLumaColorFilter.h" + +namespace cc { +namespace { + +class MockImageProvider : public ImageProvider { + public: + MockImageProvider() = default; + ~MockImageProvider() override = default; + + ScopedDecodedDrawImage GetDecodedDrawImage( + const DrawImage& draw_image) override { + image_count_++; + return ScopedDecodedDrawImage(DecodedDrawImage( + CreateBitmapImage(gfx::Size(10, 10)).GetSkImage(), SkSize::MakeEmpty(), + SkSize::Make(1.0f, 1.0f), draw_image.filter_quality(), true)); + } + int image_count_ = 0; +}; + +sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, + bool has_discardable_images) { + PaintImage image; + if (has_discardable_images) + image = CreateDiscardablePaintImage(gfx::Size(100, 100)); + else + image = CreateBitmapImage(gfx::Size(100, 100)); + + auto image_filter = sk_make_sp<ImagePaintFilter>( + image, SkRect::MakeWH(100.f, 100.f), SkRect::MakeWH(100.f, 100.f), + kNone_SkFilterQuality); + auto record = sk_make_sp<PaintOpBuffer>(); + record->push<DrawImageOp>(image, 0.f, 0.f, nullptr); + auto record_filter = + sk_make_sp<RecordPaintFilter>(record, SkRect::MakeWH(100.f, 100.f)); + + SkImageFilter::CropRect crop_rect(SkRect::MakeWH(100.f, 100.f)); + + switch (filter_type) { + case PaintFilter::Type::kNullFilter: + NOTREACHED(); + return nullptr; + case PaintFilter::Type::kColorFilter: + return sk_make_sp<ColorFilterPaintFilter>(SkLumaColorFilter::Make(), + image_filter, &crop_rect); + case PaintFilter::Type::kBlur: + return sk_make_sp<BlurPaintFilter>(0.1f, 0.2f, + SkBlurImageFilter::kClamp_TileMode, + record_filter, &crop_rect); + case PaintFilter::Type::kDropShadow: + return sk_make_sp<DropShadowPaintFilter>( + 0.1, 0.2f, 0.3f, 0.4f, SK_ColorWHITE, + SkDropShadowImageFilter::kDrawShadowOnly_ShadowMode, image_filter, + &crop_rect); + case PaintFilter::Type::kMagnifier: + return sk_make_sp<MagnifierPaintFilter>(SkRect::MakeWH(100.f, 100.f), + 0.1f, record_filter, &crop_rect); + case PaintFilter::Type::kCompose: + return sk_make_sp<ComposePaintFilter>(image_filter, record_filter); + case PaintFilter::Type::kAlphaThreshold: + return sk_make_sp<AlphaThresholdPaintFilter>( + SkRegion(SkIRect::MakeWH(100, 100)), 0.1f, 0.2f, image_filter, + &crop_rect); + case PaintFilter::Type::kXfermode: + return sk_make_sp<XfermodePaintFilter>(SkBlendMode::kSrc, image_filter, + record_filter, &crop_rect); + case PaintFilter::Type::kArithmetic: + return sk_make_sp<ArithmeticPaintFilter>(0.1f, 0.2f, 0.3f, 0.4f, true, + image_filter, record_filter, + &crop_rect); + case PaintFilter::Type::kMatrixConvolution: { + SkScalar scalars[9] = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f}; + return sk_make_sp<MatrixConvolutionPaintFilter>( + SkISize::Make(3, 3), scalars, 0.1f, 0.2f, SkIPoint::Make(2, 2), + SkMatrixConvolutionImageFilter::TileMode::kRepeat_TileMode, false, + image_filter, &crop_rect); + } + case PaintFilter::Type::kDisplacementMapEffect: + return sk_make_sp<DisplacementMapEffectPaintFilter>( + SkDisplacementMapEffect::ChannelSelectorType::kR_ChannelSelectorType, + SkDisplacementMapEffect::ChannelSelectorType::kR_ChannelSelectorType, + 0.1f, image_filter, record_filter, &crop_rect); + case PaintFilter::Type::kImage: + return image_filter; + case PaintFilter::Type::kPaintRecord: + return record_filter; + case PaintFilter::Type::kMerge: { + sk_sp<PaintFilter> filters[2] = {image_filter, record_filter}; + return sk_make_sp<MergePaintFilter>(filters, 2, &crop_rect); + } + case PaintFilter::Type::kMorphology: + return sk_make_sp<MorphologyPaintFilter>( + MorphologyPaintFilter::MorphType::kDilate, 1, 2, image_filter, + &crop_rect); + case PaintFilter::Type::kOffset: + return sk_make_sp<OffsetPaintFilter>(0.1f, 0.2f, image_filter, + &crop_rect); + case PaintFilter::Type::kTile: + return sk_make_sp<TilePaintFilter>(SkRect::MakeWH(100.f, 100.f), + SkRect::MakeWH(200.f, 200.f), + record_filter); + case PaintFilter::Type::kTurbulence: + return sk_make_sp<TurbulencePaintFilter>( + TurbulencePaintFilter::TurbulenceType::kTurbulence, 0.1f, 0.2f, 2, + 0.3f, nullptr, &crop_rect); + case PaintFilter::Type::kPaintFlags: { + PaintFlags flags; + flags.setShader(PaintShader::MakeImage(image, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, + nullptr)); + return sk_make_sp<PaintFlagsPaintFilter>(flags, &crop_rect); + } + case PaintFilter::Type::kMatrix: + return sk_make_sp<MatrixPaintFilter>(SkMatrix::I(), kNone_SkFilterQuality, + record_filter); + case PaintFilter::Type::kLightingDistant: + return sk_make_sp<LightingDistantPaintFilter>( + PaintFilter::LightingType::kDiffuse, SkPoint3::Make(0.1f, 0.2f, 0.3f), + SK_ColorWHITE, 0.1f, 0.2f, 0.3f, image_filter, &crop_rect); + case PaintFilter::Type::kLightingPoint: + return sk_make_sp<LightingPointPaintFilter>( + PaintFilter::LightingType::kDiffuse, SkPoint3::Make(0.1f, 0.2f, 0.3f), + SK_ColorWHITE, 0.1f, 0.2f, 0.3f, record_filter, &crop_rect); + case PaintFilter::Type::kLightingSpot: + return sk_make_sp<LightingSpotPaintFilter>( + PaintFilter::LightingType::kDiffuse, SkPoint3::Make(0.1f, 0.2f, 0.3f), + SkPoint3::Make(0.4f, 0.5f, 0.6f), 0.1f, 0.2f, SK_ColorWHITE, 0.4f, + 0.5f, 0.6f, image_filter, &crop_rect); + } + NOTREACHED(); + return nullptr; +} + +} // namespace + +class PaintFilterTest : public ::testing::TestWithParam<uint8_t> { + public: + PaintFilter::Type GetParamType() const { + return static_cast<PaintFilter::Type>(GetParam()); + } +}; + +INSTANTIATE_TEST_CASE_P( + P, + PaintFilterTest, + ::testing::Range(static_cast<uint8_t>(PaintFilter::Type::kColorFilter), + static_cast<uint8_t>(PaintFilter::Type::kMaxFilterType))); + +TEST_P(PaintFilterTest, HasDiscardableImagesYes) { + // TurbulencePaintFilter can not embed images. + if (GetParamType() == PaintFilter::Type::kTurbulence) + return; + + EXPECT_TRUE(CreateTestFilter(GetParamType(), true)->has_discardable_images()) + << PaintFilter::TypeToString(GetParamType()); +} + +TEST_P(PaintFilterTest, HasDiscardableImagesNo) { + EXPECT_FALSE( + CreateTestFilter(GetParamType(), false)->has_discardable_images()) + << PaintFilter::TypeToString(GetParamType()); +} + +TEST_P(PaintFilterTest, SnapshotWithImages) { + auto filter = CreateTestFilter(GetParamType(), true); + MockImageProvider image_provider; + auto snapshot_filter = filter->SnapshotWithImages(&image_provider); + if (GetParamType() != PaintFilter::Type::kTurbulence) { + // TurbulencePaintFilter can not embed images. + EXPECT_GT(image_provider.image_count_, 0) + << PaintFilter::TypeToString(GetParamType()); + } + EXPECT_EQ(*filter, *snapshot_filter) + << PaintFilter::TypeToString(GetParamType()); +} + +TEST(PaintFilterTest, ImageAnalysisState) { + auto filter = CreateTestFilter(PaintFilter::Type::kImage, true); + EXPECT_EQ(filter->image_analysis_state(), ImageAnalysisState::kNoAnalysis); + filter->set_has_animated_images(true); + EXPECT_EQ(filter->image_analysis_state(), + ImageAnalysisState::kAnimatedImages); + filter->set_has_animated_images(false); + EXPECT_EQ(filter->image_analysis_state(), + ImageAnalysisState::kNoAnimatedImages); +} + +} // namespace cc diff --git a/chromium/cc/paint/paint_flags.cc b/chromium/cc/paint/paint_flags.cc index 3a75c09ff9d..41b561ff738 100644 --- a/chromium/cc/paint/paint_flags.cc +++ b/chromium/cc/paint/paint_flags.cc @@ -36,7 +36,21 @@ PaintFlags::PaintFlags(const PaintFlags& flags) = default; PaintFlags::PaintFlags(PaintFlags&& other) = default; -PaintFlags::~PaintFlags() = default; +PaintFlags::~PaintFlags() { + // TODO(enne): non-default dtor to investigate http://crbug.com/790915 + + // Sanity check accessing this object doesn't crash. + blend_mode_ = static_cast<uint32_t>(SkBlendMode::kLastMode); + + // Free refcounted objects one by one. + typeface_.reset(); + path_effect_.reset(); + shader_.reset(); + mask_filter_.reset(); + color_filter_.reset(); + draw_looper_.reset(); + image_filter_.reset(); +} PaintFlags& PaintFlags::operator=(const PaintFlags& other) = default; @@ -199,13 +213,8 @@ bool PaintFlags::operator==(const PaintFlags& other) const { } bool PaintFlags::HasDiscardableImages() const { - if (!shader_) - return false; - else if (shader_->shader_type() == PaintShader::Type::kImage) - return shader_->paint_image().IsLazyGenerated(); - else if (shader_->shader_type() == PaintShader::Type::kPaintRecord) - return shader_->paint_record()->HasDiscardableImages(); - return false; + return (shader_ && shader_->has_discardable_images()) || + (image_filter_ && image_filter_->has_discardable_images()); } size_t PaintFlags::GetSerializedSize() const { diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h index 645ee279612..d45fe686ccb 100644 --- a/chromium/cc/paint/paint_image.h +++ b/chromium/cc/paint/paint_image.h @@ -53,6 +53,7 @@ class CC_PAINT_EXPORT PaintImage { uint64_t hash() const { return hash_; } std::string ToString() const; size_t frame_index() const { return frame_index_; } + ContentId content_id() const { return content_id_; } private: ContentId content_id_; diff --git a/chromium/cc/paint/paint_image_builder.h b/chromium/cc/paint/paint_image_builder.h index b5e9671b083..b714235275a 100644 --- a/chromium/cc/paint/paint_image_builder.h +++ b/chromium/cc/paint/paint_image_builder.h @@ -5,7 +5,6 @@ #ifndef CC_PAINT_PAINT_IMAGE_BUILDER_H_ #define CC_PAINT_PAINT_IMAGE_BUILDER_H_ -#include "base/memory/ptr_util.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_image.h" #include "cc/paint/paint_image_generator.h" diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc index 864a5f39749..446fc6e1ec6 100644 --- a/chromium/cc/paint/paint_op_buffer.cc +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -4,7 +4,6 @@ #include "cc/paint/paint_op_buffer.h" -#include "base/memory/ptr_util.h" #include "cc/paint/decoded_draw_image.h" #include "cc/paint/display_item_list.h" #include "cc/paint/image_provider.h" @@ -269,6 +268,10 @@ std::string PaintOpTypeToString(PaintOpType type) { return "UNKNOWN"; } +std::ostream& operator<<(std::ostream& os, PaintOpType type) { + return os << PaintOpTypeToString(type); +} + template <typename T> size_t SimpleSerialize(const PaintOp* op, void* memory, size_t size) { if (sizeof(T) > size) @@ -1232,6 +1235,8 @@ void DrawRecordOp::Raster(const DrawRecordOp* op, SkCanvas* canvas, const PlaybackParams& params) { // Don't use drawPicture here, as it adds an implicit clip. + // TODO(enne): Temporary CHECK debugging for http://crbug.com/823835 + CHECK(op->record); op->record->Playback(canvas, params); } @@ -1889,7 +1894,8 @@ bool PaintOp::GetBounds(const PaintOp* op, SkRect* rect) { // static bool PaintOp::QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) { - DCHECK(op->IsDrawOp()); + if (!op->IsDrawOp()) + return false; SkRect rect; if (!PaintOp::GetBounds(op, &rect)) @@ -1933,7 +1939,7 @@ void PaintOp::DestroyThis() { } bool PaintOpWithFlags::HasDiscardableImagesFromFlags() const { - return IsDrawOp() && flags.HasDiscardableImages(); + return flags.HasDiscardableImages(); } void PaintOpWithFlags::RasterWithFlags(SkCanvas* canvas, @@ -2048,6 +2054,10 @@ size_t DrawRecordOp::AdditionalBytesUsed() const { return record->bytes_used(); } +size_t DrawRecordOp::AdditionalOpCount() const { + return record->total_op_count(); +} + bool DrawRecordOp::HasDiscardableImages() const { return record->HasDiscardableImages(); } @@ -2095,6 +2105,7 @@ void PaintOpBuffer::operator=(PaintOpBuffer&& other) { op_count_ = other.op_count_; num_slow_paths_ = other.num_slow_paths_; subrecord_bytes_used_ = other.subrecord_bytes_used_; + subrecord_op_count_ = other.subrecord_op_count_; has_non_aa_paint_ = other.has_non_aa_paint_; has_discardable_images_ = other.has_discardable_images_; @@ -2115,6 +2126,7 @@ void PaintOpBuffer::Reset() { num_slow_paths_ = 0; has_non_aa_paint_ = false; subrecord_bytes_used_ = 0; + subrecord_op_count_ = 0; has_discardable_images_ = false; } @@ -2372,6 +2384,8 @@ bool PaintOpBuffer::operator==(const PaintOpBuffer& other) const { return false; if (subrecord_bytes_used_ != other.subrecord_bytes_used_) return false; + if (subrecord_op_count_ != other.subrecord_op_count_) + return false; if (has_non_aa_paint_ != other.has_non_aa_paint_) return false; if (has_discardable_images_ != other.has_discardable_images_) diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h index 122730b8440..2228e487e9a 100644 --- a/chromium/cc/paint/paint_op_buffer.h +++ b/chromium/cc/paint/paint_op_buffer.h @@ -90,6 +90,7 @@ enum class PaintOpType : uint8_t { }; CC_PAINT_EXPORT std::string PaintOpTypeToString(PaintOpType type); +CC_PAINT_EXPORT std::ostream& operator<<(std::ostream&, PaintOpType); struct CC_PAINT_EXPORT PlaybackParams { using CustomDataRasterCallback = @@ -209,6 +210,9 @@ class CC_PAINT_EXPORT PaintOp { // and display lists. This doesn't count other objects like paths or blobs. size_t AdditionalBytesUsed() const { return 0; } + // Returns the number of ops in referenced sub records and display lists. + size_t AdditionalOpCount() const { return 0; } + // Run the destructor for the derived op type. Ops are usually contained in // memory buffers and so don't have their destructors run automatically. void DestroyThis(); @@ -600,6 +604,7 @@ class CC_PAINT_EXPORT DrawRecordOp final : public PaintOp { bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); size_t AdditionalBytesUsed() const; + size_t AdditionalOpCount() const; bool HasDiscardableImages() const; int CountSlowPaths() const; bool HasNonAAPaint() const; @@ -862,6 +867,9 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { size_t bytes_used() const { return sizeof(*this) + reserved_ + subrecord_bytes_used_; } + // Returns the total number of ops including sub-records. + size_t total_op_count() const { return op_count_ + subrecord_op_count_; } + size_t next_op_offset() const { return used_; } int numSlowPaths() const { return num_slow_paths_; } bool HasNonAAPaint() const { return has_non_aa_paint_; } @@ -893,6 +901,25 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { AnalyzeAddedOp(op); } + void UpdateSaveLayerBounds(size_t offset, const SkRect& bounds) { + CHECK_LT(offset, used_); + CHECK_LE(offset + sizeof(PaintOp), used_); + + auto* op = reinterpret_cast<PaintOp*>(data_.get() + offset); + switch (op->GetType()) { + case SaveLayerOp::kType: + CHECK_LE(offset + sizeof(SaveLayerOp), used_); + static_cast<SaveLayerOp*>(op)->bounds = bounds; + break; + case SaveLayerAlphaOp::kType: + CHECK_LE(offset + sizeof(SaveLayerAlphaOp), used_); + static_cast<SaveLayerAlphaOp*>(op)->bounds = bounds; + break; + default: + NOTREACHED(); + } + } + template <typename T> void AnalyzeAddedOp(const T* op) { static_assert(!std::is_same<T, PaintOp>::value, @@ -907,6 +934,17 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { has_discardable_images_ |= op->HasDiscardableImagesFromFlags(); subrecord_bytes_used_ += op->AdditionalBytesUsed(); + subrecord_op_count_ += op->AdditionalOpCount(); + } + + template <typename T> + const T* GetOpAtForTesting(size_t index) const { + size_t i = 0; + for (PaintOpBuffer::Iterator it(this); it && i <= index; ++it, ++i) { + if (i == index && (*it)->GetType() == T::kType) + return static_cast<const T*>(*it); + } + return nullptr; } class CC_PAINT_EXPORT Iterator { @@ -1110,6 +1148,8 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { int num_slow_paths_ = 0; // Record additional bytes used by referenced sub-records and display lists. size_t subrecord_bytes_used_ = 0; + // Record total op count of referenced sub-record and display lists. + size_t subrecord_op_count_ = 0; bool has_non_aa_paint_ : 1; bool has_discardable_images_ : 1; diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc index 88b57dd1d74..7ab3a8c6ad7 100644 --- a/chromium/cc/paint/paint_op_buffer_unittest.cc +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "cc/paint/paint_op_buffer.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "cc/paint/decoded_draw_image.h" #include "cc/paint/display_item_list.h" @@ -19,7 +18,7 @@ #include "cc/test/test_skcanvas.h" #include "cc/test/transfer_cache_test_helper.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/effects/SkBlurMaskFilter.h" +#include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/effects/SkColorMatrixFilter.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" #include "third_party/skia/include/effects/SkLayerDrawLooper.h" @@ -1134,9 +1133,8 @@ std::vector<PaintFlags> test_flags = { SkScalar intervals[] = {1.f, 1.f}; flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); - flags.setMaskFilter( - SkBlurMaskFilter::Make(SkBlurStyle::kOuter_SkBlurStyle, 4.3f, - test_rects[0], kHigh_SkBlurQuality)); + flags.setMaskFilter(SkMaskFilter::MakeBlur( + SkBlurStyle::kOuter_SkBlurStyle, 4.3f, test_rects[0])); flags.setColorFilter(SkColorMatrixFilter::MakeLightingFilter( SK_ColorYELLOW, SK_ColorGREEN)); @@ -1956,7 +1954,7 @@ TEST_P(PaintOpSerializationTest, UsesOverridenFlags) { PaintOp* written = PaintOp::Deserialize( output_.get(), bytes_written, deserialized.get(), deserialized_size, &bytes_read, options_provider.deserialize_options()); - ASSERT_TRUE(written); + ASSERT_TRUE(written) << PaintOpTypeToString(GetParamType()); EXPECT_EQ(*op, *written); written->DestroyThis(); written = nullptr; @@ -3102,7 +3100,7 @@ TEST(PaintOpBufferTest, RecordShadersSerializeScaledImages) { record_buffer, SkRect::MakeWH(10.f, 10.f), SkShader::TileMode::kRepeat_TileMode, SkShader::TileMode::kRepeat_TileMode, nullptr); - shader->set_has_animated_images(); + shader->set_has_animated_images(true); auto buffer = sk_make_sp<PaintOpBuffer>(); buffer->push<ScaleOp>(0.5f, 0.8f); PaintFlags flags; @@ -3125,4 +3123,20 @@ TEST(PaintOpBufferTest, RecordShadersSerializeScaledImages) { EXPECT_EQ(scale.height(), 0.8f); } +TEST(PaintOpBufferTest, TotalOpCount) { + auto record_buffer = sk_make_sp<PaintOpBuffer>(); + auto sub_record_buffer = sk_make_sp<PaintOpBuffer>(); + auto sub_sub_record_buffer = sk_make_sp<PaintOpBuffer>(); + PushDrawRectOps(sub_sub_record_buffer.get()); + PushDrawRectOps(sub_record_buffer.get()); + PushDrawRectOps(record_buffer.get()); + sub_record_buffer->push<DrawRecordOp>(sub_sub_record_buffer); + record_buffer->push<DrawRecordOp>(sub_record_buffer); + + size_t len = std::min(test_rects.size(), test_flags.size()); + EXPECT_EQ(len, sub_sub_record_buffer->total_op_count()); + EXPECT_EQ(2 * len + 1, sub_record_buffer->total_op_count()); + EXPECT_EQ(3 * len + 2, record_buffer->total_op_count()); +} + } // namespace cc diff --git a/chromium/cc/paint/paint_op_perftest.cc b/chromium/cc/paint/paint_op_perftest.cc index 93d7ef01134..ee9f92f5eb7 100644 --- a/chromium/cc/paint/paint_op_perftest.cc +++ b/chromium/cc/paint/paint_op_perftest.cc @@ -11,7 +11,7 @@ #include "cc/paint/paint_op_buffer_serializer.h" #include "cc/test/transfer_cache_test_helper.h" #include "testing/perf/perf_test.h" -#include "third_party/skia/include/effects/SkBlurMaskFilter.h" +#include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/effects/SkColorMatrixFilter.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" #include "third_party/skia/include/effects/SkLayerDrawLooper.h" @@ -123,9 +123,8 @@ TEST_F(PaintOpPerfTest, ManyFlagsOps) { PaintFlags flags; SkScalar intervals[] = {1.f, 1.f}; flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); - flags.setMaskFilter(SkBlurMaskFilter::Make(SkBlurStyle::kOuter_SkBlurStyle, - 4.3f, SkRect::MakeXYWH(1, 1, 1, 1), - kHigh_SkBlurQuality)); + flags.setMaskFilter(SkMaskFilter::MakeBlur( + SkBlurStyle::kOuter_SkBlurStyle, 4.3f, SkRect::MakeXYWH(1, 1, 1, 1))); flags.setColorFilter( SkColorMatrixFilter::MakeLightingFilter(SK_ColorYELLOW, SK_ColorGREEN)); diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc index b89c8e677dd..2e7dbe089d9 100644 --- a/chromium/cc/paint/paint_op_reader.cc +++ b/chromium/cc/paint/paint_op_reader.cc @@ -7,6 +7,7 @@ #include <stddef.h> #include <algorithm> +#include "base/stl_util.h" #include "cc/paint/image_transfer_cache_entry.h" #include "cc/paint/paint_flags.h" #include "cc/paint/paint_image_builder.h" @@ -16,6 +17,7 @@ #include "cc/paint/transfer_cache_deserialize_helper.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkSerialProcs.h" #include "third_party/skia/include/core/SkTextBlob.h" namespace cc { @@ -32,8 +34,15 @@ struct TypefacesCatalog { bool had_null = false; }; -sk_sp<SkTypeface> ResolveTypeface(uint32_t id, void* ctx) { +sk_sp<SkTypeface> ResolveTypeface(const void* data, size_t length, void* ctx) { TypefacesCatalog* catalog = static_cast<TypefacesCatalog*>(ctx); + if (length != 4) { + catalog->had_null = true; + return nullptr; + } + + uint32_t id; + memcpy(&id, data, length); auto* entry = catalog->transfer_cache ->GetEntryAs<ServicePaintTypefaceTransferCacheEntry>(id); // TODO(vmpstr): The !entry->typeface() check is here because not all @@ -359,25 +368,45 @@ void PaintOpReader::Read(sk_sp<SkData>* data) { remaining_bytes_ -= bytes; } -void PaintOpReader::Read(scoped_refptr<PaintTextBlob>* paint_blob) { - sk_sp<SkData> data; - Read(&data); - if (!data || !valid_) { - SetInvalid(); +void PaintOpReader::Read(sk_sp<SkColorSpace>* color_space) { + size_t size = 0; + ReadSize(&size); + if (remaining_bytes_ < size) + valid_ = false; + if (!valid_ || size == 0) return; - } - // Skia expects the following to be true, make sure we don't pass it incorrect - // data. - if (!data->data() || !SkIsAlign4(data->size())) { + // To avoid TOCTOU issues, make a copy of this prior to turning it + // into an SkColorSpace. SkColorSpace::Deserialize reads header + // fields multiple times, so is not safe to pass memory_ to directly. + std::unique_ptr<char[]> data(new char[size]); + memcpy(data.get(), const_cast<const char*>(memory_), size); + + *color_space = SkColorSpace::Deserialize(data.get(), size); + // If this had non-zero bytes, it should be a valid color space. + if (!color_space) SetInvalid(); + + memory_ += size; + remaining_bytes_ -= size; +} + +void PaintOpReader::Read(scoped_refptr<PaintTextBlob>* paint_blob) { + size_t data_bytes = 0u; + ReadSimple(&data_bytes); + if (remaining_bytes_ < data_bytes || data_bytes == 0u) + SetInvalid(); + if (!valid_) return; - } TypefacesCatalog catalog; catalog.transfer_cache = transfer_cache_; - sk_sp<SkTextBlob> blob = SkTextBlob::Deserialize(data->data(), data->size(), - &ResolveTypeface, &catalog); + + SkDeserialProcs procs; + procs.fTypefaceProc = &ResolveTypeface; + procs.fTypefaceCtx = &catalog; + sk_sp<SkTextBlob> blob = SkTextBlob::Deserialize( + const_cast<const char*>(memory_), data_bytes, procs); // TODO(vmpstr): If we couldn't serialize |blob|, we should make |paint_blob| // nullptr. However, this causes GL errors right now, because not all // typefaces are serialized. Fix this once we serialize everything. For now @@ -388,6 +417,8 @@ void PaintOpReader::Read(scoped_refptr<PaintTextBlob>* paint_blob) { blob = nullptr; *paint_blob = base::MakeRefCounted<PaintTextBlob>( std::move(blob), std::vector<PaintTypeface>()); + memory_ += data_bytes; + remaining_bytes_ -= data_bytes; } void PaintOpReader::Read(sk_sp<PaintShader>* shader) { @@ -483,7 +514,7 @@ void PaintOpReader::Read(SkMatrix* matrix) { } void PaintOpReader::Read(SkColorType* color_type) { - uint32_t raw_color_type; + uint32_t raw_color_type = kUnknown_SkColorType; ReadSimple(&raw_color_type); if (raw_color_type > kLastEnum_SkColorType) { @@ -643,7 +674,7 @@ void PaintOpReader::ReadColorFilterPaintFilter( return; filter->reset(new ColorFilterPaintFilter(std::move(color_filter), std::move(input), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadBlurPaintFilter( @@ -662,7 +693,7 @@ void PaintOpReader::ReadBlurPaintFilter( return; filter->reset(new BlurPaintFilter(sigma_x, sigma_y, tile_mode, std::move(input), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadDropShadowPaintFilter( @@ -691,7 +722,7 @@ void PaintOpReader::ReadDropShadowPaintFilter( return; filter->reset(new DropShadowPaintFilter(dx, dy, sigma_x, sigma_y, color, shadow_mode, std::move(input), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadMagnifierPaintFilter( @@ -707,7 +738,7 @@ void PaintOpReader::ReadMagnifierPaintFilter( if (!valid_) return; filter->reset(new MagnifierPaintFilter(src_rect, inset, std::move(input), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadComposePaintFilter( @@ -739,7 +770,7 @@ void PaintOpReader::ReadAlphaThresholdPaintFilter( return; filter->reset(new AlphaThresholdPaintFilter( region, inner_min, outer_max, std::move(input), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadXfermodePaintFilter( @@ -761,7 +792,7 @@ void PaintOpReader::ReadXfermodePaintFilter( filter->reset(new XfermodePaintFilter(blend_mode, std::move(background), std::move(foreground), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadArithmeticPaintFilter( @@ -785,7 +816,7 @@ void PaintOpReader::ReadArithmeticPaintFilter( return; filter->reset(new ArithmeticPaintFilter( k1, k2, k3, k4, enforce_pm_color, std::move(background), - std::move(foreground), crop_rect ? &*crop_rect : nullptr)); + std::move(foreground), base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadMatrixConvolutionPaintFilter( @@ -825,7 +856,7 @@ void PaintOpReader::ReadMatrixConvolutionPaintFilter( static_cast<MatrixConvolutionPaintFilter::TileMode>(tile_mode_int); filter->reset(new MatrixConvolutionPaintFilter( kernel_size, kernel.data(), gain, bias, kernel_offset, tile_mode, - convolve_alpha, std::move(input), crop_rect ? &*crop_rect : nullptr)); + convolve_alpha, std::move(input), base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadDisplacementMapEffectPaintFilter( @@ -860,7 +891,7 @@ void PaintOpReader::ReadDisplacementMapEffectPaintFilter( channel_y_int); filter->reset(new DisplacementMapEffectPaintFilter( channel_x, channel_y, scale, std::move(displacement), std::move(color), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadImagePaintFilter( @@ -914,7 +945,7 @@ void PaintOpReader::ReadMergePaintFilter( return; filter->reset(new MergePaintFilter(inputs.data(), static_cast<int>(input_count), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadMorphologyPaintFilter( @@ -938,7 +969,7 @@ void PaintOpReader::ReadMorphologyPaintFilter( static_cast<MorphologyPaintFilter::MorphType>(morph_type_int); filter->reset(new MorphologyPaintFilter(morph_type, radius_x, radius_y, std::move(input), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadOffsetPaintFilter( @@ -954,7 +985,7 @@ void PaintOpReader::ReadOffsetPaintFilter( if (!valid_) return; filter->reset(new OffsetPaintFilter(dx, dy, std::move(input), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadTilePaintFilter( @@ -999,7 +1030,7 @@ void PaintOpReader::ReadTurbulencePaintFilter( static_cast<TurbulencePaintFilter::TurbulenceType>(turbulence_type_int); filter->reset(new TurbulencePaintFilter( turbulence_type, base_frequency_x, base_frequency_y, num_octaves, seed, - &tile_size, crop_rect ? &*crop_rect : nullptr)); + &tile_size, base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadPaintFlagsPaintFilter( @@ -1011,7 +1042,7 @@ void PaintOpReader::ReadPaintFlagsPaintFilter( if (!valid_) return; filter->reset( - new PaintFlagsPaintFilter(flags, crop_rect ? &*crop_rect : nullptr)); + new PaintFlagsPaintFilter(flags, base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadMatrixPaintFilter( @@ -1060,7 +1091,7 @@ void PaintOpReader::ReadLightingDistantPaintFilter( static_cast<PaintFilter::LightingType>(lighting_type_int); filter->reset(new LightingDistantPaintFilter( lighting_type, direction, light_color, surface_scale, kconstant, - shininess, std::move(input), crop_rect ? &*crop_rect : nullptr)); + shininess, std::move(input), base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadLightingPointPaintFilter( @@ -1091,7 +1122,7 @@ void PaintOpReader::ReadLightingPointPaintFilter( static_cast<PaintFilter::LightingType>(lighting_type_int); filter->reset(new LightingPointPaintFilter( lighting_type, location, light_color, surface_scale, kconstant, shininess, - std::move(input), crop_rect ? &*crop_rect : nullptr)); + std::move(input), base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::ReadLightingSpotPaintFilter( @@ -1130,7 +1161,7 @@ void PaintOpReader::ReadLightingSpotPaintFilter( filter->reset(new LightingSpotPaintFilter( lighting_type, location, target, specular_exponent, cutoff_angle, light_color, surface_scale, kconstant, shininess, std::move(input), - crop_rect ? &*crop_rect : nullptr)); + base::OptionalOrNullptr(crop_rect))); } void PaintOpReader::Read(sk_sp<PaintRecord>* record) { diff --git a/chromium/cc/paint/paint_op_reader.h b/chromium/cc/paint/paint_op_reader.h index 03625cd28f5..5fc268af0ec 100644 --- a/chromium/cc/paint/paint_op_reader.h +++ b/chromium/cc/paint/paint_op_reader.h @@ -66,6 +66,7 @@ class CC_PAINT_EXPORT PaintOpReader { void Read(SkMatrix* matrix); void Read(SkColorType* color_type); void Read(SkImageInfo* info); + void Read(sk_sp<SkColorSpace>* color_space); void Read(SkClipOp* op) { uint8_t value = 0u; diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc index f70fe61da19..697a9ea4647 100644 --- a/chromium/cc/paint/paint_op_writer.cc +++ b/chromium/cc/paint/paint_op_writer.cc @@ -12,16 +12,27 @@ #include "cc/paint/paint_shader.h" #include "cc/paint/paint_typeface_transfer_cache_entry.h" #include "cc/paint/transfer_cache_serialize_helper.h" +#include "third_party/skia/include/core/SkSerialProcs.h" #include "third_party/skia/include/core/SkTextBlob.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/skia_util.h" namespace cc { namespace { -void TypefaceCataloger(SkTypeface* typeface, void* ctx) { +const size_t kSkiaAlignment = 4u; + +sk_sp<SkData> TypefaceCataloger(SkTypeface* typeface, void* ctx) { static_cast<TransferCacheSerializeHelper*>(ctx)->AssertLocked( TransferCacheEntryType::kPaintTypeface, typeface->uniqueID()); + + uint32_t id = typeface->uniqueID(); + return SkData::MakeWithCopy(&id, sizeof(uint32_t)); } + +size_t RoundDownToAlignment(size_t bytes, size_t alignment) { + return bytes - (bytes & (alignment - 1)); +} + } // namespace // static @@ -97,16 +108,28 @@ void PaintOpWriter::WriteSimple(const T& val) { void PaintOpWriter::WriteFlattenable(const SkFlattenable* val) { DCHECK(SkIsAlign4(reinterpret_cast<uintptr_t>(memory_))) << "Flattenable must start writing at 4 byte alignment."; - if (!val) { WriteSize(static_cast<size_t>(0u)); return; } - sk_sp<SkData> data = val->serialize(); - WriteSize(data->size()); - if (!data->isEmpty()) - WriteData(data->size(), data->data()); + size_t size_offset = sizeof(size_t); + EnsureBytes(size_offset); + if (!valid_) + return; + char* size_memory = memory_; + memory_ += size_offset; + remaining_bytes_ -= size_offset; + + size_t bytes_written = val->serialize( + memory_, RoundDownToAlignment(remaining_bytes_, kSkiaAlignment)); + if (bytes_written == 0u) { + valid_ = false; + return; + } + reinterpret_cast<size_t*>(size_memory)[0] = bytes_written; + memory_ += bytes_written; + remaining_bytes_ -= bytes_written; } void PaintOpWriter::WriteSize(size_t size) { @@ -230,9 +253,48 @@ void PaintOpWriter::Write(const sk_sp<SkData>& data) { } } +void PaintOpWriter::Write(const SkColorSpace* color_space) { + if (!color_space) { + WriteSize(static_cast<size_t>(0)); + return; + } + size_t size = color_space->writeToMemory(nullptr); + WriteSize(size); + + EnsureBytes(size); + if (!valid_) + return; + + size_t written = color_space->writeToMemory(memory_); + CHECK_EQ(written, size); + + memory_ += written; + remaining_bytes_ -= written; +} + void PaintOpWriter::Write(const sk_sp<SkTextBlob>& blob) { - auto data = blob->serialize(&TypefaceCataloger, transfer_cache_); - Write(data); + DCHECK(blob); + + size_t size_offset = sizeof(size_t); + EnsureBytes(size_offset); + if (!valid_) + return; + + char* size_memory = memory_; + memory_ += size_offset; + remaining_bytes_ -= size_offset; + SkSerialProcs procs; + procs.fTypefaceProc = &TypefaceCataloger; + procs.fTypefaceCtx = transfer_cache_; + size_t bytes_written = blob->serialize( + procs, memory_, RoundDownToAlignment(remaining_bytes_, kSkiaAlignment)); + if (bytes_written == 0u) { + valid_ = false; + return; + } + reinterpret_cast<size_t*>(size_memory)[0] = bytes_written; + memory_ += bytes_written; + remaining_bytes_ -= bytes_written; } void PaintOpWriter::Write(const scoped_refptr<PaintTextBlob>& blob) { diff --git a/chromium/cc/paint/paint_op_writer.h b/chromium/cc/paint/paint_op_writer.h index 63fd7536f66..685ff92c857 100644 --- a/chromium/cc/paint/paint_op_writer.h +++ b/chromium/cc/paint/paint_op_writer.h @@ -60,6 +60,7 @@ class CC_PAINT_EXPORT PaintOpWriter { void Write(const PaintFlags& flags); void Write(const DrawImage& image); void Write(const sk_sp<SkData>& data); + void Write(const SkColorSpace* data); void Write(const PaintShader* shader); void Write(const PaintFilter* filter); void Write(const scoped_refptr<PaintTextBlob>& blob); diff --git a/chromium/cc/paint/paint_record.cc b/chromium/cc/paint/paint_record.cc index 9593bbb25bd..6609e82ee05 100644 --- a/chromium/cc/paint/paint_record.cc +++ b/chromium/cc/paint/paint_record.cc @@ -9,18 +9,23 @@ namespace cc { -sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record, const SkRect& bounds) { +sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record, + const SkRect& bounds, + ImageProvider* image_provider) { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(bounds); - record->Playback(canvas); + PlaybackParams params(image_provider); + record->Playback(canvas, params); return recorder.finishRecordingAsPicture(); } sk_sp<const SkPicture> ToSkPicture(sk_sp<const PaintRecord> record, - const SkRect& bounds) { + const SkRect& bounds, + ImageProvider* image_provider) { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(bounds); - record->Playback(canvas); + PlaybackParams params(image_provider); + record->Playback(canvas, params); return recorder.finishRecordingAsPicture(); } diff --git a/chromium/cc/paint/paint_record.h b/chromium/cc/paint/paint_record.h index 1509bac0529..6e0c1d77c85 100644 --- a/chromium/cc/paint/paint_record.h +++ b/chromium/cc/paint/paint_record.h @@ -10,6 +10,7 @@ #include "third_party/skia/include/core/SkPicture.h" namespace cc { +class ImageProvider; // TODO(enne): Don't want to rename the world for this. Using these as the // same types for now prevents an extra allocation. Probably PaintRecord @@ -17,12 +18,15 @@ namespace cc { using PaintRecord = PaintOpBuffer; // TODO(enne): Remove these if possible, they are really expensive. -CC_PAINT_EXPORT sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record, - const SkRect& bounds); +CC_PAINT_EXPORT sk_sp<SkPicture> ToSkPicture( + sk_sp<PaintRecord> record, + const SkRect& bounds, + ImageProvider* image_provider = nullptr); CC_PAINT_EXPORT sk_sp<const SkPicture> ToSkPicture( sk_sp<const PaintRecord> record, - const SkRect& bounds); + const SkRect& bounds, + ImageProvider* image_provider = nullptr); } // namespace cc diff --git a/chromium/cc/paint/paint_recorder.h b/chromium/cc/paint/paint_recorder.h index 1b0bf24d01d..c8f1c8a0ab8 100644 --- a/chromium/cc/paint/paint_recorder.h +++ b/chromium/cc/paint/paint_recorder.h @@ -7,7 +7,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/optional.h" #include "cc/paint/paint_record.h" #include "cc/paint/record_paint_canvas.h" diff --git a/chromium/cc/paint/paint_shader.cc b/chromium/cc/paint/paint_shader.cc index 104ada8c4e6..6baee9fe6d3 100644 --- a/chromium/cc/paint/paint_shader.cc +++ b/chromium/cc/paint/paint_shader.cc @@ -4,7 +4,6 @@ #include "cc/paint/paint_shader.h" -#include "base/memory/ptr_util.h" #include "cc/paint/paint_op_writer.h" #include "cc/paint/paint_record.h" #include "third_party/skia/include/core/SkPictureRecorder.h" @@ -186,6 +185,11 @@ size_t PaintShader::GetSerializedSize(const PaintShader* shader) { PaintShader::PaintShader(Type type) : shader_type_(type) {} PaintShader::~PaintShader() = default; +bool PaintShader::has_discardable_images() const { + return (image_ && image_.IsLazyGenerated()) || + (record_ && record_->HasDiscardableImages()); +} + bool PaintShader::GetRasterizationTileRect(const SkMatrix& ctm, SkRect* tile_rect) const { DCHECK_EQ(shader_type_, Type::kPaintRecord); @@ -288,7 +292,7 @@ void PaintShader::CreateSkShader(ImageProvider* image_provider) { points, colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, - local_matrix_ ? &*local_matrix_ : nullptr); + base::OptionalOrNullptr(local_matrix_)); break; } case Type::kRadialGradient: @@ -296,26 +300,26 @@ void PaintShader::CreateSkShader(ImageProvider* image_provider) { center_, start_radius_, colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, - local_matrix_ ? &*local_matrix_ : nullptr); + base::OptionalOrNullptr(local_matrix_)); break; case Type::kTwoPointConicalGradient: cached_shader_ = SkGradientShader::MakeTwoPointConical( start_point_, start_radius_, end_point_, end_radius_, colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, - local_matrix_ ? &*local_matrix_ : nullptr); + base::OptionalOrNullptr(local_matrix_)); break; case Type::kSweepGradient: cached_shader_ = SkGradientShader::MakeSweep( center_.x(), center_.y(), colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, start_degrees_, end_degrees_, - flags_, local_matrix_ ? &*local_matrix_ : nullptr); + flags_, base::OptionalOrNullptr(local_matrix_)); break; case Type::kImage: if (image_) { cached_shader_ = image_.GetSkImage()->makeShader( - tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr); + tx_, ty_, base::OptionalOrNullptr(local_matrix_)); } break; case Type::kPaintRecord: { @@ -328,7 +332,7 @@ void PaintShader::CreateSkShader(ImageProvider* image_provider) { case ScalingBehavior::kRasterAtScale: cached_shader_ = SkShader::MakePictureShader( std::move(picture), tx_, ty_, - local_matrix_ ? &*local_matrix_ : nullptr, nullptr); + base::OptionalOrNullptr(local_matrix_), nullptr); break; // For fixed scale, we create an image shader with an image backed by // the picture. @@ -338,7 +342,7 @@ void PaintShader::CreateSkShader(ImageProvider* image_provider) { nullptr, nullptr, SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()); cached_shader_ = image->makeShader( - tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr); + tx_, ty_, base::OptionalOrNullptr(local_matrix_)); break; } } diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h index f5e7f4f49b4..6c197b7919d 100644 --- a/chromium/cc/paint/paint_shader.h +++ b/chromium/cc/paint/paint_shader.h @@ -9,6 +9,8 @@ #include <vector> #include "base/optional.h" +#include "base/stl_util.h" +#include "cc/paint/image_analysis_state.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_image.h" #include "third_party/skia/include/core/SkImage.h" @@ -105,8 +107,16 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { ~PaintShader() override; - void set_has_animated_images() { has_animated_images_ = true; } - bool has_animated_images() const { return has_animated_images_; } + void set_has_animated_images(bool has_animated_images) { + image_analysis_state_ = has_animated_images + ? ImageAnalysisState::kAnimatedImages + : ImageAnalysisState::kNoAnimatedImages; + } + ImageAnalysisState image_analysis_state() const { + return image_analysis_state_; + } + + bool has_discardable_images() const; SkMatrix GetLocalMatrix() const { return local_matrix_ ? *local_matrix_ : SkMatrix::I(); @@ -118,7 +128,7 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { } const gfx::SizeF* tile_scale() const { - return tile_scale_ ? &*tile_scale_ : nullptr; + return base::OptionalOrNullptr(tile_scale_); } const sk_sp<PaintRecord>& paint_record() const { return record_; } bool GetRasterizationTileRect(const SkMatrix& ctm, SkRect* tile_rect) const; @@ -199,7 +209,7 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { // accesses to it are thread-safe. sk_sp<SkShader> cached_shader_; - bool has_animated_images_ = false; + ImageAnalysisState image_analysis_state_ = ImageAnalysisState::kNoAnalysis; DISALLOW_COPY_AND_ASSIGN(PaintShader); }; diff --git a/chromium/cc/paint/paint_shader_unittest.cc b/chromium/cc/paint/paint_shader_unittest.cc index 55ce7c5211c..ac6f9e0f435 100644 --- a/chromium/cc/paint/paint_shader_unittest.cc +++ b/chromium/cc/paint/paint_shader_unittest.cc @@ -84,7 +84,7 @@ TEST(PaintShaderTest, DecodePaintRecord) { auto record_shader = PaintShader::MakePaintRecord( record, SkRect::MakeWH(100, 100), SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode, &local_matrix); - record_shader->set_has_animated_images(); + record_shader->set_has_animated_images(true); PaintOpBuffer buffer; PaintFlags flags; diff --git a/chromium/cc/paint/paint_text_blob.h b/chromium/cc/paint/paint_text_blob.h index 2bdac367b32..ff6b4f54e99 100644 --- a/chromium/cc/paint/paint_text_blob.h +++ b/chromium/cc/paint/paint_text_blob.h @@ -22,7 +22,7 @@ class CC_PAINT_EXPORT PaintTextBlob PaintTextBlob(sk_sp<SkTextBlob> blob, std::vector<PaintTypeface> typefaces); const sk_sp<SkTextBlob>& ToSkTextBlob() const { return sk_blob_; } - const std::vector<PaintTypeface> typefaces() const { return typefaces_; } + const std::vector<PaintTypeface>& typefaces() const { return typefaces_; } operator bool() const { return !!sk_blob_; } diff --git a/chromium/cc/paint/paint_typeface_transfer_cache_entry.cc b/chromium/cc/paint/paint_typeface_transfer_cache_entry.cc index 78235671290..34def03d2cb 100644 --- a/chromium/cc/paint/paint_typeface_transfer_cache_entry.cc +++ b/chromium/cc/paint/paint_typeface_transfer_cache_entry.cc @@ -118,7 +118,7 @@ size_t ServicePaintTypefaceTransferCacheEntry::CachedSize() const { bool ServicePaintTypefaceTransferCacheEntry::Deserialize( GrContext* context, - base::span<uint8_t> data) { + base::span<const uint8_t> data) { data_ = data; size_t initial_size = data_.size(); @@ -198,7 +198,7 @@ bool ServicePaintTypefaceTransferCacheEntry::Deserialize( // Set the size to however much data we read. size_ = initial_size - data_.size(); - data_ = base::span<uint8_t>(nullptr); + data_ = base::span<uint8_t>(); return valid_; } @@ -208,7 +208,7 @@ void ServicePaintTypefaceTransferCacheEntry::ReadSimple(T* val) { valid_ = false; if (!valid_) return; - *val = *reinterpret_cast<T*>(data_.data()); + *val = *reinterpret_cast<const T*>(data_.data()); data_ = data_.subspan(sizeof(T)); } diff --git a/chromium/cc/paint/paint_typeface_transfer_cache_entry.h b/chromium/cc/paint/paint_typeface_transfer_cache_entry.h index 938c66b844a..f1c1e75129e 100644 --- a/chromium/cc/paint/paint_typeface_transfer_cache_entry.h +++ b/chromium/cc/paint/paint_typeface_transfer_cache_entry.h @@ -37,7 +37,7 @@ class CC_PAINT_EXPORT ServicePaintTypefaceTransferCacheEntry ServicePaintTypefaceTransferCacheEntry(); ~ServicePaintTypefaceTransferCacheEntry() final; size_t CachedSize() const final; - bool Deserialize(GrContext* context, base::span<uint8_t> data) final; + bool Deserialize(GrContext* context, base::span<const uint8_t> data) final; const PaintTypeface& typeface() const { return typeface_; } @@ -50,7 +50,9 @@ class CC_PAINT_EXPORT ServicePaintTypefaceTransferCacheEntry PaintTypeface typeface_; size_t size_ = 0; bool valid_ = true; - base::span<uint8_t> data_; + // TODO(enne): this transient value shouldn't be a member and should just be + // passed around internally to functions that need it. + base::span<const uint8_t> data_; }; } // namespace cc diff --git a/chromium/cc/paint/raw_memory_transfer_cache_entry.cc b/chromium/cc/paint/raw_memory_transfer_cache_entry.cc index 07e51c01c92..2c41fabd9d9 100644 --- a/chromium/cc/paint/raw_memory_transfer_cache_entry.cc +++ b/chromium/cc/paint/raw_memory_transfer_cache_entry.cc @@ -43,8 +43,9 @@ size_t ServiceRawMemoryTransferCacheEntry::CachedSize() const { return data_.size(); } -bool ServiceRawMemoryTransferCacheEntry::Deserialize(GrContext* context, - base::span<uint8_t> data) { +bool ServiceRawMemoryTransferCacheEntry::Deserialize( + GrContext* context, + base::span<const uint8_t> data) { data_ = std::vector<uint8_t>(data.begin(), data.end()); return true; } diff --git a/chromium/cc/paint/raw_memory_transfer_cache_entry.h b/chromium/cc/paint/raw_memory_transfer_cache_entry.h index d5efd624ba4..c6ce52af731 100644 --- a/chromium/cc/paint/raw_memory_transfer_cache_entry.h +++ b/chromium/cc/paint/raw_memory_transfer_cache_entry.h @@ -37,7 +37,7 @@ class CC_PAINT_EXPORT ServiceRawMemoryTransferCacheEntry ServiceRawMemoryTransferCacheEntry(); ~ServiceRawMemoryTransferCacheEntry() final; size_t CachedSize() const final; - bool Deserialize(GrContext* context, base::span<uint8_t> data) final; + bool Deserialize(GrContext* context, base::span<const uint8_t> data) final; const std::vector<uint8_t>& data() { return data_; } private: diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc index a39f278836f..c601f19296d 100644 --- a/chromium/cc/paint/record_paint_canvas.cc +++ b/chromium/cc/paint/record_paint_canvas.cc @@ -4,7 +4,6 @@ #include "cc/paint/record_paint_canvas.h" -#include "base/memory/ptr_util.h" #include "cc/paint/display_item_list.h" #include "cc/paint/paint_image_builder.h" #include "cc/paint/paint_record.h" diff --git a/chromium/cc/paint/scoped_raster_flags.cc b/chromium/cc/paint/scoped_raster_flags.cc index 6e45512c0e1..b54f137e1dc 100644 --- a/chromium/cc/paint/scoped_raster_flags.cc +++ b/chromium/cc/paint/scoped_raster_flags.cc @@ -5,6 +5,7 @@ #include "cc/paint/scoped_raster_flags.h" #include "cc/paint/image_provider.h" +#include "cc/paint/paint_filter.h" #include "cc/paint/paint_image_builder.h" namespace cc { @@ -15,22 +16,19 @@ ScopedRasterFlags::ScopedRasterFlags(const PaintFlags* flags, bool create_skia_shader) : original_flags_(flags) { if (flags->HasDiscardableImages() && image_provider) { - DCHECK(flags->HasShader()); - // TODO(khushalsagar): The decoding of images in PaintFlags here is a bit of // a mess. We decode image shaders at the correct scale but ignore that // during serialization and just use the original image. decode_stashing_image_provider_.emplace(image_provider); - if (flags->getShader()->shader_type() == PaintShader::Type::kImage) { - DecodeImageShader(ctm); - } else if (flags->getShader()->shader_type() == - PaintShader::Type::kPaintRecord) { - DecodeRecordShader(ctm, create_skia_shader); - } else { - NOTREACHED(); - } // We skip the op if any images fail to decode. + DecodeImageShader(ctm); + if (decode_failed_) + return; + DecodeRecordShader(ctm, create_skia_shader); + if (decode_failed_) + return; + DecodeFilter(); if (decode_failed_) return; } @@ -46,6 +44,10 @@ ScopedRasterFlags::ScopedRasterFlags(const PaintFlags* flags, ScopedRasterFlags::~ScopedRasterFlags() = default; void ScopedRasterFlags::DecodeImageShader(const SkMatrix& ctm) { + if (!flags()->HasShader() || + flags()->getShader()->shader_type() != PaintShader::Type::kImage) + return; + const PaintImage& paint_image = flags()->getShader()->paint_image(); SkMatrix matrix = flags()->getShader()->GetLocalMatrix(); @@ -100,9 +102,14 @@ void ScopedRasterFlags::DecodeImageShader(const SkMatrix& ctm) { void ScopedRasterFlags::DecodeRecordShader(const SkMatrix& ctm, bool create_skia_shader) { + if (!flags()->HasShader() || + flags()->getShader()->shader_type() != PaintShader::Type::kPaintRecord) + return; + // TODO(khushalsagar): For OOP, we have to decode everything during // serialization. This will force us to use original sized decodes. - if (!flags()->getShader()->has_animated_images()) + if (flags()->getShader()->image_analysis_state() != + ImageAnalysisState::kAnimatedImages) return; auto decoded_shader = flags()->getShader()->CreateDecodedPaintRecord( @@ -117,6 +124,18 @@ void ScopedRasterFlags::DecodeRecordShader(const SkMatrix& ctm, MutableFlags()->setShader(std::move(decoded_shader)); } +void ScopedRasterFlags::DecodeFilter() { + if (!flags()->getImageFilter() || + !flags()->getImageFilter()->has_discardable_images() || + flags()->getImageFilter()->image_analysis_state() != + ImageAnalysisState::kAnimatedImages) { + return; + } + + MutableFlags()->setImageFilter(flags()->getImageFilter()->SnapshotWithImages( + &*decode_stashing_image_provider_)); +} + void ScopedRasterFlags::AdjustStrokeIfNeeded(const SkMatrix& ctm) { // With anti-aliasing turned off, strokes with a device space width in (0, 1) // may not raster at all. To avoid this, we have two options: diff --git a/chromium/cc/paint/scoped_raster_flags.h b/chromium/cc/paint/scoped_raster_flags.h index 5b2f859c321..5407b5e2655 100644 --- a/chromium/cc/paint/scoped_raster_flags.h +++ b/chromium/cc/paint/scoped_raster_flags.h @@ -37,6 +37,8 @@ class CC_PAINT_EXPORT ScopedRasterFlags { private: void DecodeImageShader(const SkMatrix& ctm); void DecodeRecordShader(const SkMatrix& ctm, bool create_skia_shader); + void DecodeFilter(); + void AdjustStrokeIfNeeded(const SkMatrix& ctm); PaintFlags* MutableFlags() { diff --git a/chromium/cc/paint/scoped_raster_flags_unittest.cc b/chromium/cc/paint/scoped_raster_flags_unittest.cc index c912d07e63f..b0ac2654d1d 100644 --- a/chromium/cc/paint/scoped_raster_flags_unittest.cc +++ b/chromium/cc/paint/scoped_raster_flags_unittest.cc @@ -54,7 +54,7 @@ TEST(ScopedRasterFlagsTest, KeepsDecodesAlive) { auto record_shader = PaintShader::MakePaintRecord( record, SkRect::MakeWH(100, 100), SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode, &SkMatrix::I()); - record_shader->set_has_animated_images(); + record_shader->set_has_animated_images(true); MockImageProvider provider; PaintFlags flags; diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc index f9caa002ebb..83128d5c3a6 100644 --- a/chromium/cc/paint/skia_paint_canvas.cc +++ b/chromium/cc/paint/skia_paint_canvas.cc @@ -4,7 +4,6 @@ #include "cc/paint/skia_paint_canvas.h" -#include "base/memory/ptr_util.h" #include "cc/paint/display_item_list.h" #include "cc/paint/paint_recorder.h" #include "cc/paint/scoped_raster_flags.h" diff --git a/chromium/cc/paint/transfer_cache_entry.cc b/chromium/cc/paint/transfer_cache_entry.cc index 785bb3078a1..46252c1850d 100644 --- a/chromium/cc/paint/transfer_cache_entry.cc +++ b/chromium/cc/paint/transfer_cache_entry.cc @@ -27,7 +27,6 @@ std::unique_ptr<ServiceTransferCacheEntry> ServiceTransferCacheEntry::Create( return std::make_unique<ServiceColorSpaceTransferCacheEntry>(); } - NOTREACHED(); return nullptr; } diff --git a/chromium/cc/paint/transfer_cache_entry.h b/chromium/cc/paint/transfer_cache_entry.h index 5e0e9179969..9ecde14aedc 100644 --- a/chromium/cc/paint/transfer_cache_entry.h +++ b/chromium/cc/paint/transfer_cache_entry.h @@ -82,7 +82,8 @@ class CC_PAINT_EXPORT ServiceTransferCacheEntry { // Deserialize the cache entry from the given span of memory with the given // context. - virtual bool Deserialize(GrContext* context, base::span<uint8_t> data) = 0; + virtual bool Deserialize(GrContext* context, + base::span<const uint8_t> data) = 0; }; // Helpers to simplify subclassing. diff --git a/chromium/cc/paint/transfer_cache_fuzzer.cc b/chromium/cc/paint/transfer_cache_fuzzer.cc new file mode 100644 index 00000000000..54e2bee949e --- /dev/null +++ b/chromium/cc/paint/transfer_cache_fuzzer.cc @@ -0,0 +1,41 @@ +// 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 <stddef.h> +#include <stdint.h> + +#include "cc/paint/paint_op_buffer.h" +#include "cc/paint/raw_memory_transfer_cache_entry.h" +#include "cc/test/transfer_cache_test_helper.h" +#include "components/viz/test/test_context_provider.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GrContext.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < 4) + return 0; + + scoped_refptr<viz::TestContextProvider> context_provider = + viz::TestContextProvider::Create(); + context_provider->BindToCurrentThread(); + + cc::TransferCacheEntryType entry_type = + static_cast<cc::TransferCacheEntryType>(data[0]); + std::unique_ptr<cc::ServiceTransferCacheEntry> entry = + cc::ServiceTransferCacheEntry::Create(entry_type); + if (!entry) + return 0; + + // Align data. + base::span<const uint8_t> span(&data[4], size - 4); + if (!entry->Deserialize(context_provider->GrContext(), span)) + return 0; + + // TODO(enne): consider running Serialize() here to fuzz that codepath + // for bugs. However, that requires setting up a real context with + // a raster interface that supports gl operations and that has a + // TransferCacheTestHelper in it so the innards of client and service + // are accessible. + return 0; +} diff --git a/chromium/cc/raster/bitmap_raster_buffer_provider.cc b/chromium/cc/raster/bitmap_raster_buffer_provider.cc index def2324480b..ae5744092af 100644 --- a/chromium/cc/raster/bitmap_raster_buffer_provider.cc +++ b/chromium/cc/raster/bitmap_raster_buffer_provider.cc @@ -10,23 +10,35 @@ #include <algorithm> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "cc/raster/raster_source.h" -#include "cc/resources/layer_tree_resource_provider.h" #include "cc/resources/resource.h" +#include "cc/trees/layer_tree_frame_sink.h" +#include "components/viz/common/resources/bitmap_allocation.h" #include "components/viz/common/resources/platform_color.h" -#include "components/viz/common/resources/shared_bitmap_manager.h" namespace cc { namespace { +class BitmapSoftwareBacking : public ResourcePool::SoftwareBacking { + public: + ~BitmapSoftwareBacking() override { + frame_sink->DidDeleteSharedBitmap(shared_bitmap_id); + } + + base::UnguessableToken SharedMemoryGuid() override { + return shared_memory->mapped_id(); + } + + LayerTreeFrameSink* frame_sink; + std::unique_ptr<base::SharedMemory> shared_memory; +}; + class BitmapRasterBufferImpl : public RasterBuffer { public: - BitmapRasterBufferImpl(LayerTreeResourceProvider* resource_provider, - const gfx::Size& size, + BitmapRasterBufferImpl(const gfx::Size& size, const gfx::ColorSpace& color_space, void* pixels, uint64_t resource_content_id, @@ -73,10 +85,8 @@ class BitmapRasterBufferImpl : public RasterBuffer { } // namespace BitmapRasterBufferProvider::BitmapRasterBufferProvider( - LayerTreeResourceProvider* resource_provider, - viz::SharedBitmapManager* shared_bitmap_manager) - : resource_provider_(resource_provider), - shared_bitmap_manager_(shared_bitmap_manager) {} + LayerTreeFrameSink* frame_sink) + : frame_sink_(frame_sink) {} BitmapRasterBufferProvider::~BitmapRasterBufferProvider() = default; @@ -89,16 +99,27 @@ BitmapRasterBufferProvider::AcquireBufferForRaster( const gfx::Size& size = resource.size(); const gfx::ColorSpace& color_space = resource.color_space(); - if (!resource.shared_bitmap()) { - // Allocate a backing that can be shared out of process to the display - // compositor, and give ownership to the ResourcePool. - resource.set_shared_bitmap( - shared_bitmap_manager_->AllocateSharedBitmap(size)); + if (!resource.software_backing()) { + auto backing = std::make_unique<BitmapSoftwareBacking>(); + backing->frame_sink = frame_sink_; + backing->shared_bitmap_id = viz::SharedBitmap::GenerateId(); + backing->shared_memory = + viz::bitmap_allocation::AllocateMappedBitmap(size, viz::RGBA_8888); + + mojo::ScopedSharedBufferHandle handle = + viz::bitmap_allocation::DuplicateAndCloseMappedBitmap( + backing->shared_memory.get(), size, viz::RGBA_8888); + frame_sink_->DidAllocateSharedBitmap(std::move(handle), + backing->shared_bitmap_id); + + resource.set_software_backing(std::move(backing)); } + BitmapSoftwareBacking* backing = + static_cast<BitmapSoftwareBacking*>(resource.software_backing()); return std::make_unique<BitmapRasterBufferImpl>( - resource_provider_, size, color_space, resource.shared_bitmap()->pixels(), - resource_content_id, previous_content_id); + size, color_space, backing->shared_memory->memory(), resource_content_id, + previous_content_id); } void BitmapRasterBufferProvider::Flush() {} @@ -114,6 +135,11 @@ bool BitmapRasterBufferProvider::IsResourceSwizzleRequired( return ResourceFormatRequiresSwizzle(viz::RGBA_8888); } +bool BitmapRasterBufferProvider::IsResourcePremultiplied( + bool must_support_alpha) const { + return true; +} + bool BitmapRasterBufferProvider::CanPartialRasterIntoProvidedResource() const { return true; } diff --git a/chromium/cc/raster/bitmap_raster_buffer_provider.h b/chromium/cc/raster/bitmap_raster_buffer_provider.h index fbf774df995..1f33b863b5b 100644 --- a/chromium/cc/raster/bitmap_raster_buffer_provider.h +++ b/chromium/cc/raster/bitmap_raster_buffer_provider.h @@ -17,19 +17,14 @@ class ConvertableToTraceFormat; } } -namespace viz { -class SharedBitmapManager; -} - namespace cc { -class LayerTreeResourceProvider; +class LayerTreeFrameSink; class CC_EXPORT BitmapRasterBufferProvider : public RasterBufferProvider { public: ~BitmapRasterBufferProvider() override; - BitmapRasterBufferProvider(LayerTreeResourceProvider* resource_provider, - viz::SharedBitmapManager* shared_bitmap_manager); + explicit BitmapRasterBufferProvider(LayerTreeFrameSink* frame_sink); // Overridden from RasterBufferProvider: std::unique_ptr<RasterBuffer> AcquireBufferForRaster( @@ -39,6 +34,7 @@ class CC_EXPORT BitmapRasterBufferProvider : public RasterBufferProvider { void Flush() override; viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override; bool IsResourceSwizzleRequired(bool must_support_alpha) const override; + bool IsResourcePremultiplied(bool must_support_alpha) const override; bool CanPartialRasterIntoProvidedResource() const override; bool IsResourceReadyToDraw( const ResourcePool::InUsePoolResource& resource) const override; @@ -52,8 +48,7 @@ class CC_EXPORT BitmapRasterBufferProvider : public RasterBufferProvider { std::unique_ptr<base::trace_event::ConvertableToTraceFormat> StateAsValue() const; - LayerTreeResourceProvider* const resource_provider_; - viz::SharedBitmapManager* const shared_bitmap_manager_; + LayerTreeFrameSink* const frame_sink_; DISALLOW_COPY_AND_ASSIGN(BitmapRasterBufferProvider); }; diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc index 43fca42ba7c..ba2abde1adc 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.cc +++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc @@ -9,7 +9,6 @@ #include <algorithm> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" #include "cc/base/histograms.h" @@ -28,6 +27,7 @@ #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/raster_interface.h" #include "gpu/command_buffer/common/gpu_memory_buffer_support.h" +#include "skia/ext/texture_handle.h" #include "third_party/skia/include/core/SkMultiPictureDraw.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/core/SkSurface.h" @@ -38,6 +38,72 @@ namespace cc { namespace { +class ScopedSkSurfaceForUnpremultiplyAndDither { + public: + ScopedSkSurfaceForUnpremultiplyAndDither( + viz::RasterContextProvider* context_provider, + const gfx::Rect& playback_rect, + const gfx::Rect& raster_full_rect, + const gfx::Size& max_tile_size, + GLuint texture_id, + const gfx::Size& texture_size, + bool can_use_lcd_text, + int msaa_sample_count) + : context_provider_(context_provider), + texture_id_(texture_id), + offset_(playback_rect.OffsetFromOrigin() - + raster_full_rect.OffsetFromOrigin()), + size_(playback_rect.size()) { + // Determine the |intermediate_size| to use for our 32-bit texture. If we + // know the max tile size, use that. This prevents GPU cache explosion due + // to using lots of different 32-bit texture sizes. Otherwise just use the + // exact size of the target texture. + gfx::Size intermediate_size; + if (!max_tile_size.IsEmpty()) { + DCHECK_GE(max_tile_size.width(), texture_size.width()); + DCHECK_GE(max_tile_size.height(), texture_size.height()); + intermediate_size = max_tile_size; + } else { + intermediate_size = texture_size; + } + + // Allocate a 32-bit surface for raster. We will copy from that into our + // actual surface in destruction. + SkImageInfo n32Info = SkImageInfo::MakeN32Premul( + intermediate_size.width(), intermediate_size.height()); + SkSurfaceProps surface_props = + LayerTreeResourceProvider::ScopedSkSurface::ComputeSurfaceProps( + can_use_lcd_text); + surface_ = SkSurface::MakeRenderTarget( + context_provider->GrContext(), SkBudgeted::kNo, n32Info, + msaa_sample_count, kTopLeft_GrSurfaceOrigin, &surface_props); + } + + ~ScopedSkSurfaceForUnpremultiplyAndDither() { + // In lost-context cases, |surface_| may be null and there's nothing + // meaningful to do here. + if (!surface_) + return; + + GrBackendObject handle = + surface_->getTextureHandle(SkSurface::kFlushRead_BackendHandleAccess); + const GrGLTextureInfo* info = + skia::GrBackendObjectToGrGLTextureInfo(handle); + context_provider_->ContextGL()->UnpremultiplyAndDitherCopyCHROMIUM( + info->fID, texture_id_, offset_.x(), offset_.y(), size_.width(), + size_.height()); + } + + SkSurface* surface() { return surface_.get(); } + + private: + viz::RasterContextProvider* context_provider_; + GLuint texture_id_; + gfx::Vector2d offset_; + gfx::Size size_; + sk_sp<SkSurface> surface_; +}; + static void RasterizeSourceOOP( const RasterSource* raster_source, bool resource_has_previous_content, @@ -53,10 +119,11 @@ static void RasterizeSourceOOP( const gfx::AxisTransform2d& transform, const RasterSource::PlaybackSettings& playback_settings, viz::RasterContextProvider* context_provider, - bool use_distance_field_text, int msaa_sample_count) { gpu::raster::RasterInterface* ri = context_provider->RasterInterface(); - GLuint texture_id = ri->CreateAndConsumeTextureCHROMIUM(mailbox.name); + GLuint texture_id = ri->CreateAndConsumeTexture( + texture_is_overlay_candidate, gfx::BufferUsage::SCANOUT, resource_format, + mailbox.name); if (!texture_storage_allocated) { viz::TextureAllocation alloc = {texture_id, texture_target, texture_is_overlay_candidate}; @@ -69,7 +136,7 @@ static void RasterizeSourceOOP( // use GL_TEXTURE_2D. ri->BeginRasterCHROMIUM( texture_id, raster_source->background_color(), msaa_sample_count, - playback_settings.use_lcd_text, use_distance_field_text, + playback_settings.use_lcd_text, viz::ResourceFormatToClosestSkColorType(resource_format), playback_settings.raster_color_space); float recording_to_raster_scale = @@ -85,6 +152,9 @@ static void RasterizeSourceOOP( raster_source->requires_clear()); ri->EndRasterCHROMIUM(); + // TODO(ericrk): Handle unpremultiply+dither for 4444 cases. + // https://crbug.com/789153 + ri->DeleteTextures(1, &texture_id); } @@ -96,9 +166,6 @@ class ScopedGrContextAccess { : context_provider_(context_provider) { gpu::raster::RasterInterface* ri = context_provider_->RasterInterface(); ri->BeginGpuRaster(); - - class GrContext* gr_context = context_provider_->GrContext(); - gr_context->resetContext(); } ~ScopedGrContextAccess() { gpu::raster::RasterInterface* ri = context_provider_->RasterInterface(); @@ -124,12 +191,13 @@ static void RasterizeSource( const gfx::AxisTransform2d& transform, const RasterSource::PlaybackSettings& playback_settings, viz::RasterContextProvider* context_provider, - bool use_distance_field_text, - int msaa_sample_count) { - ScopedGrContextAccess gr_context_access(context_provider); - + int msaa_sample_count, + bool unpremultiply_and_dither, + const gfx::Size& max_tile_size) { gpu::raster::RasterInterface* ri = context_provider->RasterInterface(); - GLuint texture_id = ri->CreateAndConsumeTextureCHROMIUM(mailbox.name); + GLuint texture_id = ri->CreateAndConsumeTexture( + texture_is_overlay_candidate, gfx::BufferUsage::SCANOUT, resource_format, + mailbox.name); if (!texture_storage_allocated) { viz::TextureAllocation alloc = {texture_id, texture_target, texture_is_overlay_candidate}; @@ -139,12 +207,23 @@ static void RasterizeSource( } { - LayerTreeResourceProvider::ScopedSkSurface scoped_surface( - context_provider->GrContext(), texture_id, texture_target, - resource_size, resource_format, use_distance_field_text, - playback_settings.use_lcd_text, msaa_sample_count); - - SkSurface* surface = scoped_surface.surface(); + ScopedGrContextAccess gr_context_access(context_provider); + base::Optional<LayerTreeResourceProvider::ScopedSkSurface> scoped_surface; + base::Optional<ScopedSkSurfaceForUnpremultiplyAndDither> + scoped_dither_surface; + SkSurface* surface; + if (!unpremultiply_and_dither) { + scoped_surface.emplace(context_provider->GrContext(), texture_id, + texture_target, resource_size, resource_format, + playback_settings.use_lcd_text, msaa_sample_count); + surface = scoped_surface->surface(); + } else { + scoped_dither_surface.emplace( + context_provider, playback_rect, raster_full_rect, max_tile_size, + texture_id, resource_size, playback_settings.use_lcd_text, + msaa_sample_count); + surface = scoped_dither_surface->surface(); + } // Allocating an SkSurface will fail after a lost context. Pretend we // rasterized, as the contents of the resource don't matter anymore. @@ -208,7 +287,6 @@ GpuRasterBufferProvider::RasterBufferImpl::RasterBufferImpl( GpuRasterBufferProvider* client, const ResourcePool::InUsePoolResource& in_use_resource, GpuRasterBacking* backing, - const gpu::SyncToken& before_raster_sync_token, bool resource_has_previous_content) : client_(client), backing_(backing), @@ -216,7 +294,7 @@ GpuRasterBufferProvider::RasterBufferImpl::RasterBufferImpl( resource_format_(in_use_resource.format()), color_space_(in_use_resource.color_space()), resource_has_previous_content_(resource_has_previous_content), - before_raster_sync_token_(before_raster_sync_token), + before_raster_sync_token_(backing->returned_sync_token), mailbox_(backing->mailbox), texture_target_(backing->texture_target), texture_is_overlay_candidate_(backing->overlay_candidate), @@ -259,18 +337,21 @@ GpuRasterBufferProvider::GpuRasterBufferProvider( viz::ContextProvider* compositor_context_provider, viz::RasterContextProvider* worker_context_provider, LayerTreeResourceProvider* resource_provider, - bool use_distance_field_text, bool use_gpu_memory_buffer_resources, int gpu_rasterization_msaa_sample_count, viz::ResourceFormat preferred_tile_format, + const gfx::Size& max_tile_size, + bool unpremultiply_and_dither_low_bit_depth_tiles, bool enable_oop_rasterization) : compositor_context_provider_(compositor_context_provider), worker_context_provider_(worker_context_provider), resource_provider_(resource_provider), - use_distance_field_text_(use_distance_field_text), use_gpu_memory_buffer_resources_(use_gpu_memory_buffer_resources), msaa_sample_count_(gpu_rasterization_msaa_sample_count), preferred_tile_format_(preferred_tile_format), + max_tile_size_(max_tile_size), + unpremultiply_and_dither_low_bit_depth_tiles_( + unpremultiply_and_dither_low_bit_depth_tiles), enable_oop_rasterization_(enable_oop_rasterization) { DCHECK(compositor_context_provider); DCHECK(worker_context_provider); @@ -283,8 +364,6 @@ std::unique_ptr<RasterBuffer> GpuRasterBufferProvider::AcquireBufferForRaster( const ResourcePool::InUsePoolResource& resource, uint64_t resource_content_id, uint64_t previous_content_id) { - gpu::SyncToken before_raster_sync_token; - bool new_resource = false; if (!resource.gpu_backing()) { auto backing = std::make_unique<GpuRasterBacking>(); backing->compositor_context_provider = compositor_context_provider_; @@ -301,21 +380,18 @@ std::unique_ptr<RasterBuffer> GpuRasterBufferProvider::AcquireBufferForRaster( backing->mailbox = gpu::Mailbox::Generate(); gl->ProduceTextureDirectCHROMIUM(backing->texture_id, backing->mailbox.name); - before_raster_sync_token = + // 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); resource.set_gpu_backing(std::move(backing)); - new_resource = true; } GpuRasterBacking* backing = static_cast<GpuRasterBacking*>(resource.gpu_backing()); - if (!new_resource) - before_raster_sync_token = backing->returned_sync_token; - bool resource_has_previous_content = resource_content_id && resource_content_id == previous_content_id; return std::make_unique<RasterBufferImpl>(this, resource, backing, - before_raster_sync_token, resource_has_previous_content); } @@ -341,6 +417,12 @@ bool GpuRasterBufferProvider::IsResourceSwizzleRequired( return false; } +bool GpuRasterBufferProvider::IsResourcePremultiplied( + bool must_support_alpha) const { + return !ShouldUnpremultiplyAndDitherResource( + GetResourceFormat(must_support_alpha)); +} + bool GpuRasterBufferProvider::CanPartialRasterIntoProvidedResource() const { // Partial raster doesn't support MSAA, as the MSAA resolve is unaware of clip // rects. @@ -438,23 +520,34 @@ gpu::SyncToken GpuRasterBufferProvider::PlaybackOnWorkerThread( } if (enable_oop_rasterization_) { - RasterizeSourceOOP( + RasterizeSourceOOP(raster_source, resource_has_previous_content, mailbox, + texture_target, texture_is_overlay_candidate, + texture_storage_allocated, resource_size, + resource_format, color_space, raster_full_rect, + playback_rect, transform, playback_settings, + worker_context_provider_, msaa_sample_count_); + } else { + RasterizeSource( raster_source, resource_has_previous_content, mailbox, texture_target, texture_is_overlay_candidate, texture_storage_allocated, resource_size, resource_format, color_space, raster_full_rect, playback_rect, transform, playback_settings, worker_context_provider_, - use_distance_field_text_, msaa_sample_count_); - } else { - RasterizeSource(raster_source, resource_has_previous_content, mailbox, - texture_target, texture_is_overlay_candidate, - texture_storage_allocated, resource_size, resource_format, - color_space, raster_full_rect, playback_rect, transform, - playback_settings, worker_context_provider_, - use_distance_field_text_, msaa_sample_count_); + msaa_sample_count_, + ShouldUnpremultiplyAndDitherResource(resource_format), max_tile_size_); } // Generate sync token for cross context synchronization. return LayerTreeResourceProvider::GenerateSyncTokenHelper(ri); } +bool GpuRasterBufferProvider::ShouldUnpremultiplyAndDitherResource( + viz::ResourceFormat format) const { + switch (format) { + case viz::RGBA_4444: + return unpremultiply_and_dither_low_bit_depth_tiles_; + default: + return false; + } +} + } // namespace cc diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.h b/chromium/cc/raster/gpu_raster_buffer_provider.h index 18a5ec6a6ad..c019d77fa07 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.h +++ b/chromium/cc/raster/gpu_raster_buffer_provider.h @@ -24,10 +24,11 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { GpuRasterBufferProvider(viz::ContextProvider* compositor_context_provider, viz::RasterContextProvider* worker_context_provider, LayerTreeResourceProvider* resource_provider, - bool use_distance_field_text, bool use_gpu_memory_buffer_resources, int gpu_rasterization_msaa_sample_count, viz::ResourceFormat preferred_tile_format, + const gfx::Size& max_tile_size, + bool unpremultiply_and_dither_low_bit_depth_tiles, bool enable_oop_rasterization); ~GpuRasterBufferProvider() override; @@ -39,6 +40,7 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { void Flush() override; viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override; bool IsResourceSwizzleRequired(bool must_support_alpha) const override; + bool IsResourcePremultiplied(bool must_support_alpha) const override; bool CanPartialRasterIntoProvidedResource() const override; bool IsResourceReadyToDraw( const ResourcePool::InUsePoolResource& resource) const override; @@ -73,7 +75,6 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { RasterBufferImpl(GpuRasterBufferProvider* client, const ResourcePool::InUsePoolResource& in_use_resource, GpuRasterBacking* backing, - const gpu::SyncToken& before_raster_sync_token, bool resource_has_previous_content); ~RasterBufferImpl() override; @@ -109,13 +110,16 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { DISALLOW_COPY_AND_ASSIGN(RasterBufferImpl); }; + bool ShouldUnpremultiplyAndDitherResource(viz::ResourceFormat format) const; + viz::ContextProvider* const compositor_context_provider_; viz::RasterContextProvider* const worker_context_provider_; LayerTreeResourceProvider* const resource_provider_; - const bool use_distance_field_text_; const bool use_gpu_memory_buffer_resources_; const int msaa_sample_count_; const viz::ResourceFormat preferred_tile_format_; + const gfx::Size max_tile_size_; + const bool unpremultiply_and_dither_low_bit_depth_tiles_; const bool enable_oop_rasterization_; DISALLOW_COPY_AND_ASSIGN(GpuRasterBufferProvider); diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.cc b/chromium/cc/raster/one_copy_raster_buffer_provider.cc index 872e159d8b6..fdf03d5db1d 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.cc @@ -11,17 +11,16 @@ #include <utility> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" #include "cc/base/histograms.h" #include "cc/base/math_util.h" -#include "cc/resources/resource_util.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" #include "components/viz/common/resources/platform_color.h" #include "components/viz/common/resources/resource_format.h" +#include "components/viz/common/resources/resource_sizes.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" @@ -78,7 +77,6 @@ OneCopyRasterBufferProvider::RasterBufferImpl::RasterBufferImpl( LayerTreeResourceProvider* resource_provider, const ResourcePool::InUsePoolResource& in_use_resource, OneCopyGpuBacking* backing, - const gpu::SyncToken& before_raster_sync_token, uint64_t previous_content_id) : client_(client), backing_(backing), @@ -86,7 +84,7 @@ OneCopyRasterBufferProvider::RasterBufferImpl::RasterBufferImpl( resource_format_(in_use_resource.format()), color_space_(in_use_resource.color_space()), previous_content_id_(previous_content_id), - before_raster_sync_token_(before_raster_sync_token), + before_raster_sync_token_(backing->returned_sync_token), mailbox_(backing->mailbox), mailbox_texture_target_(backing->texture_target), mailbox_texture_is_overlay_candidate_(backing->overlay_candidate), @@ -156,16 +154,13 @@ OneCopyRasterBufferProvider::OneCopyRasterBufferProvider( DCHECK(worker_context_provider); } -OneCopyRasterBufferProvider::~OneCopyRasterBufferProvider() { -} +OneCopyRasterBufferProvider::~OneCopyRasterBufferProvider() {} std::unique_ptr<RasterBuffer> OneCopyRasterBufferProvider::AcquireBufferForRaster( const ResourcePool::InUsePoolResource& resource, uint64_t resource_content_id, uint64_t previous_content_id) { - gpu::SyncToken before_raster_sync_token; - bool new_resource = false; if (!resource.gpu_backing()) { auto backing = std::make_unique<OneCopyGpuBacking>(); backing->compositor_context_provider = compositor_context_provider_; @@ -182,22 +177,19 @@ OneCopyRasterBufferProvider::AcquireBufferForRaster( backing->mailbox = gpu::Mailbox::Generate(); gl->ProduceTextureDirectCHROMIUM(backing->texture_id, backing->mailbox.name); - before_raster_sync_token = + // 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); resource.set_gpu_backing(std::move(backing)); - new_resource = true; } OneCopyGpuBacking* backing = static_cast<OneCopyGpuBacking*>(resource.gpu_backing()); - if (!new_resource) - before_raster_sync_token = backing->returned_sync_token; - // TODO(danakj): If resource_content_id != 0, we only need to copy/upload // the dirty rect. return std::make_unique<RasterBufferImpl>(this, resource_provider_, resource, - backing, before_raster_sync_token, - previous_content_id); + backing, previous_content_id); } void OneCopyRasterBufferProvider::Flush() { @@ -225,6 +217,13 @@ bool OneCopyRasterBufferProvider::IsResourceSwizzleRequired( return ResourceFormatRequiresSwizzle(GetResourceFormat(must_support_alpha)); } +bool OneCopyRasterBufferProvider::IsResourcePremultiplied( + bool must_support_alpha) const { + // TODO(ericrk): Handle unpremultiply/dither in one-copy case as well. + // https://crbug.com/789153 + return true; +} + bool OneCopyRasterBufferProvider::CanPartialRasterIntoProvidedResource() const { // While OneCopyRasterBufferProvider has an internal partial raster // implementation, it cannot directly partial raster into the externally @@ -392,7 +391,10 @@ gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread( // making the mailbox. This ensures that the mailbox we consume here is valid // by the time the consume command executes. ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); - GLuint mailbox_texture_id = ri->CreateAndConsumeTextureCHROMIUM(mailbox.name); + GLuint mailbox_texture_id = ri->CreateAndConsumeTexture( + mailbox_texture_is_overlay_candidate, gfx::BufferUsage::SCANOUT, + resource_format, mailbox.name); + if (!mailbox_texture_storage_allocated) { viz::TextureAllocation alloc = {mailbox_texture_id, mailbox_texture_target, mailbox_texture_is_overlay_candidate}; @@ -401,20 +403,18 @@ gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread( resource_size, alloc, color_space); } - GLenum image_target = resource_provider_->GetImageTextureTarget( - worker_context_provider_->ContextCapabilities(), StagingBufferUsage(), - staging_buffer->format); - // Create and bind staging texture. if (!staging_buffer->texture_id) { - ri->GenTextures(1, &staging_buffer->texture_id); - ri->BindTexture(image_target, staging_buffer->texture_id); - ri->TexParameteri(image_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - ri->TexParameteri(image_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - ri->TexParameteri(image_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - ri->TexParameteri(image_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } else { - ri->BindTexture(image_target, staging_buffer->texture_id); + staging_buffer->texture_id = + ri->CreateTexture(true, StagingBufferUsage(), staging_buffer->format); + ri->TexParameteri(staging_buffer->texture_id, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + ri->TexParameteri(staging_buffer->texture_id, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + ri->TexParameteri(staging_buffer->texture_id, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + ri->TexParameteri(staging_buffer->texture_id, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); } // Create and bind image. @@ -424,15 +424,19 @@ gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread( staging_buffer->gpu_memory_buffer->AsClientBuffer(), staging_buffer->size.width(), staging_buffer->size.height(), GLInternalFormat(staging_buffer->format)); - ri->BindTexImage2DCHROMIUM(image_target, staging_buffer->image_id); + ri->BindTexImage2DCHROMIUM(staging_buffer->texture_id, + staging_buffer->image_id); } } else { - ri->ReleaseTexImage2DCHROMIUM(image_target, staging_buffer->image_id); - ri->BindTexImage2DCHROMIUM(image_target, staging_buffer->image_id); + ri->ReleaseTexImage2DCHROMIUM(staging_buffer->texture_id, + staging_buffer->image_id); + ri->BindTexImage2DCHROMIUM(staging_buffer->texture_id, + staging_buffer->image_id); } // Unbind staging texture. - ri->BindTexture(image_target, 0); + // TODO(vmiura): Need a way to ensure we don't hold onto bindings? + // ri->BindTexture(image_target, 0); if (resource_provider_->use_sync_query()) { if (!staging_buffer->query_id) @@ -453,7 +457,7 @@ gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread( ri->CompressedCopyTextureCHROMIUM(staging_buffer->texture_id, mailbox_texture_id); } else { - int bytes_per_row = ResourceUtil::UncheckedWidthInBytes<int>( + int bytes_per_row = viz::ResourceSizes::UncheckedWidthInBytes<int>( rect_to_copy.width(), staging_buffer->format); int chunk_size_in_rows = std::max(1, max_bytes_per_copy_operation_ / bytes_per_row); @@ -466,10 +470,8 @@ gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread( int rows_to_copy = std::min(chunk_size_in_rows, height - y); DCHECK_GT(rows_to_copy, 0); - ri->CopySubTextureCHROMIUM(staging_buffer->texture_id, 0, - mailbox_texture_target, mailbox_texture_id, 0, - 0, y, 0, y, rect_to_copy.width(), rows_to_copy, - false, false, false); + ri->CopySubTexture(staging_buffer->texture_id, mailbox_texture_id, 0, y, + 0, y, rect_to_copy.width(), rows_to_copy); y += rows_to_copy; // Increment |bytes_scheduled_since_last_flush_| by the amount of memory diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.h b/chromium/cc/raster/one_copy_raster_buffer_provider.h index d1408d13004..7e1fac1bab7 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.h +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.h @@ -44,6 +44,7 @@ class CC_EXPORT OneCopyRasterBufferProvider : public RasterBufferProvider { void Flush() override; viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override; bool IsResourceSwizzleRequired(bool must_support_alpha) const override; + bool IsResourcePremultiplied(bool must_support_alpha) const override; bool CanPartialRasterIntoProvidedResource() const override; bool IsResourceReadyToDraw( const ResourcePool::InUsePoolResource& resource) const override; @@ -80,7 +81,6 @@ class CC_EXPORT OneCopyRasterBufferProvider : public RasterBufferProvider { LayerTreeResourceProvider* resource_provider, const ResourcePool::InUsePoolResource& in_use_resource, OneCopyGpuBacking* backing, - const gpu::SyncToken& before_raster_sync_token, uint64_t previous_content_id); ~RasterBufferImpl() override; diff --git a/chromium/cc/raster/playback_image_provider.cc b/chromium/cc/raster/playback_image_provider.cc index 557b421bbae..75d8ddf0608 100644 --- a/chromium/cc/raster/playback_image_provider.cc +++ b/chromium/cc/raster/playback_image_provider.cc @@ -4,7 +4,6 @@ #include "cc/raster/playback_image_provider.h" -#include "base/memory/ptr_util.h" #include "cc/tiles/image_decode_cache.h" namespace cc { @@ -20,7 +19,7 @@ void UnrefImageFromCache(DrawImage draw_image, PlaybackImageProvider::PlaybackImageProvider( ImageDecodeCache* cache, const gfx::ColorSpace& target_color_space, - base::Optional<Settings> settings) + base::Optional<Settings>&& settings) : cache_(cache), target_color_space_(target_color_space), settings_(std::move(settings)) { @@ -70,7 +69,10 @@ PlaybackImageProvider::GetDecodedDrawImage(const DrawImage& draw_image) { } PlaybackImageProvider::Settings::Settings() = default; -PlaybackImageProvider::Settings::Settings(const Settings& other) = default; +PlaybackImageProvider::Settings::Settings(PlaybackImageProvider::Settings&&) = + default; PlaybackImageProvider::Settings::~Settings() = default; +PlaybackImageProvider::Settings& PlaybackImageProvider::Settings::operator=( + PlaybackImageProvider::Settings&&) = default; } // namespace cc diff --git a/chromium/cc/raster/playback_image_provider.h b/chromium/cc/raster/playback_image_provider.h index 67974f3f341..a33092d2b5b 100644 --- a/chromium/cc/raster/playback_image_provider.h +++ b/chromium/cc/raster/playback_image_provider.h @@ -20,8 +20,10 @@ class CC_EXPORT PlaybackImageProvider : public ImageProvider { public: struct CC_EXPORT Settings { Settings(); - Settings(const Settings& other); + Settings(const Settings&) = delete; + Settings(Settings&&); ~Settings(); + Settings& operator=(Settings&&); // The set of image ids to skip during raster. PaintImageIdFlatSet images_to_skip; @@ -34,7 +36,7 @@ class CC_EXPORT PlaybackImageProvider : public ImageProvider { // If no settings are provided, all images are skipped during rasterization. PlaybackImageProvider(ImageDecodeCache* cache, const gfx::ColorSpace& target_color_space, - base::Optional<Settings> settings); + base::Optional<Settings>&& settings); ~PlaybackImageProvider() override; PlaybackImageProvider(PlaybackImageProvider&& other); diff --git a/chromium/cc/raster/playback_image_provider_unittest.cc b/chromium/cc/raster/playback_image_provider_unittest.cc index 0206999d6e4..40036e87032 100644 --- a/chromium/cc/raster/playback_image_provider_unittest.cc +++ b/chromium/cc/raster/playback_image_provider_unittest.cc @@ -85,7 +85,8 @@ TEST(PlaybackImageProviderTest, SkipsSomeImages) { settings.emplace(); settings->images_to_skip = {skip_image.stable_id()}; - PlaybackImageProvider provider(&cache, gfx::ColorSpace(), settings); + PlaybackImageProvider provider(&cache, gfx::ColorSpace(), + std::move(settings)); SkIRect rect = SkIRect::MakeWH(10, 10); SkMatrix matrix = SkMatrix::I(); @@ -99,7 +100,8 @@ TEST(PlaybackImageProviderTest, RefAndUnrefDecode) { base::Optional<PlaybackImageProvider::Settings> settings; settings.emplace(); - PlaybackImageProvider provider(&cache, gfx::ColorSpace(), settings); + PlaybackImageProvider provider(&cache, gfx::ColorSpace(), + std::move(settings)); { SkRect rect = SkRect::MakeWH(10, 10); @@ -127,7 +129,8 @@ TEST(PlaybackImageProviderTest, SwapsGivenFrames) { settings.emplace(); settings->image_to_current_frame_index = image_to_frame; - PlaybackImageProvider provider(&cache, gfx::ColorSpace(), settings); + PlaybackImageProvider provider(&cache, gfx::ColorSpace(), + std::move(settings)); SkIRect rect = SkIRect::MakeWH(10, 10); SkMatrix matrix = SkMatrix::I(); @@ -143,7 +146,8 @@ TEST(PlaybackImageProviderTest, BitmapImages) { base::Optional<PlaybackImageProvider::Settings> settings; settings.emplace(); - PlaybackImageProvider provider(&cache, gfx::ColorSpace(), settings); + PlaybackImageProvider provider(&cache, gfx::ColorSpace(), + std::move(settings)); { SkIRect rect = SkIRect::MakeWH(10, 10); @@ -174,7 +178,8 @@ TEST(PlaybackImageProviderTest, TextureImages) { MockDecodeCache cache; base::Optional<PlaybackImageProvider::Settings> settings; settings.emplace(); - PlaybackImageProvider provider(&cache, gfx::ColorSpace(), settings); + PlaybackImageProvider provider(&cache, gfx::ColorSpace(), + std::move(settings)); { SkIRect rect = SkIRect::MakeWH(10, 10); SkMatrix matrix = SkMatrix::I(); diff --git a/chromium/cc/raster/raster_buffer_provider.h b/chromium/cc/raster/raster_buffer_provider.h index c25f2e91409..12b7e0d80da 100644 --- a/chromium/cc/raster/raster_buffer_provider.h +++ b/chromium/cc/raster/raster_buffer_provider.h @@ -62,6 +62,9 @@ class CC_EXPORT RasterBufferProvider { // Determine if the resource requires swizzling. virtual bool IsResourceSwizzleRequired(bool must_support_alpha) const = 0; + // Determines if the resource is premultiplied. + virtual bool IsResourcePremultiplied(bool must_support_alpha) const = 0; + // Determine if the RasterBufferProvider can handle partial raster into // the Resource provided in AcquireBufferForRaster. virtual bool CanPartialRasterIntoProvidedResource() const = 0; diff --git a/chromium/cc/raster/raster_buffer_provider_perftest.cc b/chromium/cc/raster/raster_buffer_provider_perftest.cc index 054183150de..4d96efeb552 100644 --- a/chromium/cc/raster/raster_buffer_provider_perftest.cc +++ b/chromium/cc/raster/raster_buffer_provider_perftest.cc @@ -6,7 +6,6 @@ #include <stdint.h> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/test/test_simple_task_runner.h" #include "base/time/time.h" #include "build/build_config.h" @@ -19,6 +18,7 @@ #include "cc/raster/zero_copy_raster_buffer_provider.h" #include "cc/resources/resource_pool.h" #include "cc/resources/resource_provider.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/common/gpu/context_cache_controller.h" @@ -27,7 +27,6 @@ #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_context_support.h" #include "components/viz/test/test_gpu_memory_buffer_manager.h" -#include "components/viz/test/test_shared_bitmap_manager.h" #include "gpu/command_buffer/client/raster_implementation_gles.h" #include "gpu/command_buffer/common/sync_token.h" #include "gpu/config/gpu_feature_info.h" @@ -122,9 +121,6 @@ class PerfContextProvider viz::ContextCacheController* CacheController() override { return &cache_controller_; } - void InvalidateGrContext(uint32_t state) override { - test_context_provider_->GrContext()->resetContext(state); - } base::Lock* GetLock() override { return &context_lock_; } void AddObserver(viz::ContextLostObserver* obs) override {} void RemoveObserver(viz::ContextLostObserver* obs) override {} @@ -334,6 +330,7 @@ class RasterBufferProviderPerfTestBase { protected: 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_; scoped_refptr<base::TestSimpleTaskRunner> task_runner_; std::unique_ptr<ResourcePool> resource_pool_; @@ -378,8 +375,8 @@ class RasterBufferProviderPerfTest Create3dResourceProvider(); raster_buffer_provider_ = std::make_unique<GpuRasterBufferProvider>( compositor_context_provider_.get(), worker_context_provider_.get(), - resource_provider_.get(), false, false, 0, - viz::PlatformColor::BestTextureFormat(), false); + resource_provider_.get(), false, 0, + viz::PlatformColor::BestTextureFormat(), gfx::Size(), true, false); resource_pool_ = std::make_unique<ResourcePool>( resource_provider_.get(), task_runner_, ResourcePool::kDefaultExpirationDelay, ResourcePool::Mode::kGpu, @@ -388,7 +385,7 @@ class RasterBufferProviderPerfTest case RASTER_BUFFER_PROVIDER_TYPE_BITMAP: CreateSoftwareResourceProvider(); raster_buffer_provider_ = std::make_unique<BitmapRasterBufferProvider>( - resource_provider_.get(), &shared_bitmap_manager_); + layer_tree_frame_sink_.get()); resource_pool_ = std::make_unique<ResourcePool>( resource_provider_.get(), task_runner_, ResourcePool::kDefaultExpirationDelay, @@ -527,8 +524,9 @@ class RasterBufferProviderPerfTest } void CreateSoftwareResourceProvider() { + layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware(); resource_provider_ = FakeResourceProvider::CreateLayerTreeResourceProvider( - nullptr, &shared_bitmap_manager_, nullptr); + nullptr, layer_tree_frame_sink_->shared_bitmap_manager(), nullptr); } std::string TestModifierString() const { @@ -549,7 +547,6 @@ class RasterBufferProviderPerfTest std::unique_ptr<TileTaskManager> tile_task_manager_; std::unique_ptr<RasterBufferProvider> raster_buffer_provider_; viz::TestGpuMemoryBufferManager gpu_memory_buffer_manager_; - viz::TestSharedBitmapManager shared_bitmap_manager_; }; TEST_P(RasterBufferProviderPerfTest, ScheduleTasks) { diff --git a/chromium/cc/raster/raster_buffer_provider_unittest.cc b/chromium/cc/raster/raster_buffer_provider_unittest.cc index fdbe51e9b9a..4bc78099834 100644 --- a/chromium/cc/raster/raster_buffer_provider_unittest.cc +++ b/chromium/cc/raster/raster_buffer_provider_unittest.cc @@ -14,7 +14,6 @@ #include "base/cancelable_callback.h" #include "base/location.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" @@ -28,6 +27,7 @@ #include "cc/raster/zero_copy_raster_buffer_provider.h" #include "cc/resources/resource_pool.h" #include "cc/resources/resource_provider.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" @@ -180,8 +180,8 @@ class RasterBufferProviderTest Create3dResourceProvider(); raster_buffer_provider_ = std::make_unique<GpuRasterBufferProvider>( context_provider_.get(), worker_context_provider_.get(), - resource_provider_.get(), false, false, 0, - viz::PlatformColor::BestTextureFormat(), false); + resource_provider_.get(), false, 0, + viz::PlatformColor::BestTextureFormat(), gfx::Size(), true, false); pool_ = std::make_unique<ResourcePool>( resource_provider_.get(), base::ThreadTaskRunnerHandle::Get(), base::TimeDelta(), ResourcePool::Mode::kGpu, true); @@ -189,7 +189,7 @@ class RasterBufferProviderTest case RASTER_BUFFER_PROVIDER_TYPE_BITMAP: CreateSoftwareResourceProvider(); raster_buffer_provider_ = std::make_unique<BitmapRasterBufferProvider>( - resource_provider_.get(), &shared_bitmap_manager_); + layer_tree_frame_sink_.get()); pool_ = std::make_unique<ResourcePool>( resource_provider_.get(), base::ThreadTaskRunnerHandle::Get(), base::TimeDelta(), ResourcePool::Mode::kSoftware, true); @@ -266,6 +266,15 @@ class RasterBufferProviderTest resources_.push_back(std::move(resource)); } + void AppendTaskWithResource(unsigned id, + const ResourcePool::InUsePoolResource* resource) { + std::unique_ptr<RasterBuffer> raster_buffer = + raster_buffer_provider_->AcquireBufferForRaster(*resource, 0, 0); + TileTask::Vector empty; + tasks_.push_back( + new TestRasterTaskImpl(this, id, std::move(raster_buffer), &empty)); + } + const std::vector<RasterTaskResult>& completed_tasks() const { return completed_tasks_; } @@ -302,12 +311,14 @@ class RasterBufferProviderTest viz::TestWebGraphicsContext3D* context3d = context_provider_->TestContext3d(); context3d->set_support_sync_query(true); + layer_tree_frame_sink_ = FakeLayerTreeFrameSink::Create3d(); resource_provider_ = FakeResourceProvider::CreateLayerTreeResourceProvider( context_provider_.get(), &shared_bitmap_manager_, &gpu_memory_buffer_manager_); } void CreateSoftwareResourceProvider() { + layer_tree_frame_sink_ = FakeLayerTreeFrameSink::CreateSoftware(); resource_provider_ = FakeResourceProvider::CreateLayerTreeResourceProvider( nullptr, &shared_bitmap_manager_, &gpu_memory_buffer_manager_); } @@ -321,6 +332,7 @@ class RasterBufferProviderTest scoped_refptr<viz::TestContextProvider> context_provider_; 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<TileTaskManager> tile_task_manager_; std::unique_ptr<RasterBufferProvider> raster_buffer_provider_; @@ -453,6 +465,47 @@ TEST_P(RasterBufferProviderTest, ReadyToDrawCallbackNoDuplicate) { EXPECT_TRUE(callback_id); } +TEST_P(RasterBufferProviderTest, WaitOnSyncTokenAfterReschedulingTask) { + if (GetParam() != RASTER_BUFFER_PROVIDER_TYPE_GPU && + GetParam() != RASTER_BUFFER_PROVIDER_TYPE_ONE_COPY) + return; + + base::Lock lock; + + // Schedule a task that is prevented from completing with a lock. + lock.Acquire(); + AppendBlockingTask(0u, &lock); + ScheduleTasks(); + + EXPECT_EQ(resources_.size(), 1u); + const ResourcePool::InUsePoolResource* resource = &resources_[0]; + + // Schedule another task to replace the still-pending task using the same + // resource. + RasterTaskVector tasks; + tasks.swap(tasks_); + AppendTaskWithResource(1u, resource); + ScheduleTasks(); + + // The first task is canceled, but the second task uses the same resource, and + // waits on the compositor sync token that was left by the first task. + RunMessageLoopUntilAllTasksHaveCompleted(); + + { + viz::ContextProvider::ScopedContextLock context_lock( + worker_context_provider_.get()); + viz::TestWebGraphicsContext3D* context3d = + worker_context_provider_->TestContext3d(); + EXPECT_TRUE(context3d->last_waited_sync_token().HasData()); + } + + lock.Release(); + + ASSERT_EQ(completed_tasks().size(), 2u); + EXPECT_TRUE(completed_tasks()[0].canceled); + EXPECT_FALSE(completed_tasks()[1].canceled); +} + INSTANTIATE_TEST_CASE_P( RasterBufferProviderTests, RasterBufferProviderTest, diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc index 61d356f0690..be224fe6d8e 100644 --- a/chromium/cc/raster/raster_source.cc +++ b/chromium/cc/raster/raster_source.cc @@ -162,7 +162,8 @@ void RasterSource::PlaybackToCanvas( void RasterSource::PlaybackToCanvas(SkCanvas* raster_canvas, ImageProvider* image_provider) const { - DCHECK(display_list_.get()); + // TODO(enne): Temporary CHECK debugging for http://crbug.com/823835 + CHECK(display_list_.get()); int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_); for (int i = 0; i < repeat_count; ++i) display_list_->Raster(raster_canvas, image_provider); diff --git a/chromium/cc/raster/scoped_gpu_raster.cc b/chromium/cc/raster/scoped_gpu_raster.cc index 9c0752ca1c6..3ba2be38286 100644 --- a/chromium/cc/raster/scoped_gpu_raster.cc +++ b/chromium/cc/raster/scoped_gpu_raster.cc @@ -3,7 +3,10 @@ // found in the LICENSE file. #include "cc/raster/scoped_gpu_raster.h" + +#include "build/build_config.h" #include "components/viz/common/gpu/context_provider.h" +#include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" @@ -30,8 +33,11 @@ void ScopedGpuRaster::BeginGpuRaster() { // arguments even when tracing is disabled. gl->TraceBeginCHROMIUM("ScopedGpuRaster", "GpuRasterization"); - class GrContext* gr_context = context_provider_->GrContext(); +#if defined(OS_ANDROID) + // TODO(crbug.com/832810): The following reset should not be necessary. + GrContext* gr_context = context_provider_->GrContext(); gr_context->resetContext(); +#endif } void ScopedGpuRaster::EndGpuRaster() { diff --git a/chromium/cc/raster/staging_buffer_pool.cc b/chromium/cc/raster/staging_buffer_pool.cc index 84ee9a31ada..37c39fc4102 100644 --- a/chromium/cc/raster/staging_buffer_pool.cc +++ b/chromium/cc/raster/staging_buffer_pool.cc @@ -7,13 +7,12 @@ #include <memory> #include "base/memory/memory_coordinator_client_registry.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_dump_manager.h" #include "cc/base/container_util.h" -#include "cc/resources/resource_util.h" #include "components/viz/common/gpu/raster_context_provider.h" +#include "components/viz/common/resources/resource_sizes.h" #include "gpu/command_buffer/client/raster_interface.h" #include "ui/gfx/gpu_memory_buffer.h" @@ -103,7 +102,7 @@ void StagingBuffer::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, MemoryAllocatorDump* buffer_dump = pmd->CreateAllocatorDump(buffer_dump_name); uint64_t buffer_size_in_bytes = - ResourceUtil::UncheckedSizeInBytes<uint64_t>(size, format); + viz::ResourceSizes::UncheckedSizeInBytes<uint64_t>(size, format); buffer_dump->AddScalar(MemoryAllocatorDump::kNameSize, MemoryAllocatorDump::kUnitsBytes, buffer_size_in_bytes); @@ -210,8 +209,8 @@ void StagingBufferPool::AddStagingBuffer(const StagingBuffer* staging_buffer, DCHECK(buffers_.find(staging_buffer) == buffers_.end()); buffers_.insert(staging_buffer); - int buffer_usage_in_bytes = - ResourceUtil::UncheckedSizeInBytes<int>(staging_buffer->size, format); + int buffer_usage_in_bytes = viz::ResourceSizes::UncheckedSizeInBytes<int>( + staging_buffer->size, format); staging_buffer_usage_in_bytes_ += buffer_usage_in_bytes; } @@ -221,7 +220,7 @@ void StagingBufferPool::RemoveStagingBuffer( DCHECK(buffers_.find(staging_buffer) != buffers_.end()); buffers_.erase(staging_buffer); - int buffer_usage_in_bytes = ResourceUtil::UncheckedSizeInBytes<int>( + int buffer_usage_in_bytes = viz::ResourceSizes::UncheckedSizeInBytes<int>( staging_buffer->size, staging_buffer->format); DCHECK_GE(staging_buffer_usage_in_bytes_, buffer_usage_in_bytes); staging_buffer_usage_in_bytes_ -= buffer_usage_in_bytes; @@ -231,7 +230,7 @@ void StagingBufferPool::MarkStagingBufferAsFree( const StagingBuffer* staging_buffer) { lock_.AssertAcquired(); - int buffer_usage_in_bytes = ResourceUtil::UncheckedSizeInBytes<int>( + int buffer_usage_in_bytes = viz::ResourceSizes::UncheckedSizeInBytes<int>( staging_buffer->size, staging_buffer->format); free_staging_buffer_usage_in_bytes_ += buffer_usage_in_bytes; } @@ -240,7 +239,7 @@ void StagingBufferPool::MarkStagingBufferAsBusy( const StagingBuffer* staging_buffer) { lock_.AssertAcquired(); - int buffer_usage_in_bytes = ResourceUtil::UncheckedSizeInBytes<int>( + int buffer_usage_in_bytes = viz::ResourceSizes::UncheckedSizeInBytes<int>( staging_buffer->size, staging_buffer->format); DCHECK_GE(free_staging_buffer_usage_in_bytes_, buffer_usage_in_bytes); free_staging_buffer_usage_in_bytes_ -= buffer_usage_in_bytes; diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc index c3c6ec60692..9cefe8af99b 100644 --- a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc @@ -251,6 +251,11 @@ bool ZeroCopyRasterBufferProvider::IsResourceSwizzleRequired( return ResourceFormatRequiresSwizzle(GetResourceFormat(must_support_alpha)); } +bool ZeroCopyRasterBufferProvider::IsResourcePremultiplied( + bool must_support_alpha) const { + return true; +} + bool ZeroCopyRasterBufferProvider::CanPartialRasterIntoProvidedResource() const { return false; diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.h b/chromium/cc/raster/zero_copy_raster_buffer_provider.h index 1c6c6ae7bbc..d7a118e2988 100644 --- a/chromium/cc/raster/zero_copy_raster_buffer_provider.h +++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.h @@ -42,6 +42,7 @@ class CC_EXPORT ZeroCopyRasterBufferProvider : public RasterBufferProvider { void Flush() override; viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override; bool IsResourceSwizzleRequired(bool must_support_alpha) const override; + bool IsResourcePremultiplied(bool must_support_alpha) const override; bool CanPartialRasterIntoProvidedResource() const override; bool IsResourceReadyToDraw( const ResourcePool::InUsePoolResource& resource) const override; diff --git a/chromium/cc/resources/cross_thread_shared_bitmap.cc b/chromium/cc/resources/cross_thread_shared_bitmap.cc new file mode 100644 index 00000000000..642bd39d89e --- /dev/null +++ b/chromium/cc/resources/cross_thread_shared_bitmap.cc @@ -0,0 +1,18 @@ +// 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/resources/cross_thread_shared_bitmap.h" + +namespace cc { + +CrossThreadSharedBitmap::CrossThreadSharedBitmap( + const viz::SharedBitmapId& id, + std::unique_ptr<base::SharedMemory> memory, + const gfx::Size& size, + viz::ResourceFormat format) + : id_(id), memory_(std::move(memory)), size_(size), format_(format) {} + +CrossThreadSharedBitmap::~CrossThreadSharedBitmap() = default; + +} // namespace cc diff --git a/chromium/cc/resources/cross_thread_shared_bitmap.h b/chromium/cc/resources/cross_thread_shared_bitmap.h new file mode 100644 index 00000000000..1a436e91b70 --- /dev/null +++ b/chromium/cc/resources/cross_thread_shared_bitmap.h @@ -0,0 +1,51 @@ +// 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_RESOURCES_CROSS_THREAD_SHARED_BITMAP_H_ +#define CC_RESOURCES_CROSS_THREAD_SHARED_BITMAP_H_ + +#include <memory> + +#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 "ui/gfx/geometry/size.h" + +namespace cc { + +// This class holds ownership of a base::SharedMemory segment for use as a +// composited resource, and is refcounted in order to share ownership with the +// LayerTreeHost, via TextureLayer, which needs access to the base::SharedMemory +// from the compositor thread. +// Because all the fields exposed are const, they can be used from any thread +// without conflict, as they only read existing states. +class CC_EXPORT CrossThreadSharedBitmap + : public base::RefCountedThreadSafe<CrossThreadSharedBitmap> { + public: + CrossThreadSharedBitmap(const viz::SharedBitmapId& id, + std::unique_ptr<base::SharedMemory> memory, + const gfx::Size& size, + viz::ResourceFormat format); + + const viz::SharedBitmapId& id() const { return id_; } + const base::SharedMemory* shared_memory() const { return memory_.get(); } + const gfx::Size& size() const { return size_; } + viz::ResourceFormat format() const { return format_; } + + private: + friend base::RefCountedThreadSafe<CrossThreadSharedBitmap>; + + ~CrossThreadSharedBitmap(); + + const viz::SharedBitmapId id_; + const std::unique_ptr<const base::SharedMemory> memory_; + const gfx::Size size_; + const viz::ResourceFormat format_; +}; + +} // namespace cc + +#endif // CC_RESOURCES_CROSS_THREAD_SHARED_BITMAP_H_ diff --git a/chromium/cc/resources/display_resource_provider.cc b/chromium/cc/resources/display_resource_provider.cc index fe60bcc9fea..c48d3ff6768 100644 --- a/chromium/cc/resources/display_resource_provider.cc +++ b/chromium/cc/resources/display_resource_provider.cc @@ -80,6 +80,12 @@ void DisplayResourceProvider::SendPromotionHints( continue; const viz::internal::Resource* resource = LockForRead(id); + // TODO(ericrk): We should never fail LockForRead, but we appear to be + // doing so on Android in rare cases. Handle this gracefully until a better + // solution can be found. https://crbug.com/811858 + if (!resource) + return; + DCHECK(resource->wants_promotion_hint); // Insist that this is backed by a GPU texture. @@ -121,8 +127,11 @@ size_t DisplayResourceProvider::CountPromotionHintRequestsForTesting() { #endif bool DisplayResourceProvider::IsOverlayCandidate(viz::ResourceId id) { - viz::internal::Resource* resource = GetResource(id); - return resource->is_overlay_candidate; + viz::internal::Resource* resource = TryGetResource(id); + // TODO(ericrk): We should never fail TryGetResource, but we appear to + // be doing so on Android in rare cases. Handle this gracefully until a + // better solution can be found. https://crbug.com/811858 + return resource && resource->is_overlay_candidate; } viz::ResourceType DisplayResourceProvider::GetResourceType(viz::ResourceId id) { @@ -135,7 +144,12 @@ gfx::BufferFormat DisplayResourceProvider::GetBufferFormat(viz::ResourceId id) { } void DisplayResourceProvider::WaitSyncToken(viz::ResourceId id) { - viz::internal::Resource* resource = GetResource(id); + viz::internal::Resource* resource = TryGetResource(id); + // TODO(ericrk): We should never fail TryGetResource, but we appear to + // be doing so on Android in rare cases. Handle this gracefully until a + // better solution can be found. https://crbug.com/811858 + if (!resource) + return; WaitSyncTokenInternal(resource); #if defined(OS_ANDROID) // Now that the resource is synced, we may send it a promotion hint. We could @@ -254,7 +268,8 @@ void DisplayResourceProvider::DeleteAndReturnUnusedResourcesToChild( bool is_lost = resource.lost || (resource.is_gpu_resource_type() && lost_context_provider_); - if (resource.exported_count > 0 || resource.lock_for_read_count > 0) { + if (resource.exported_count > 0 || resource.lock_for_read_count > 0 || + resource.locked_for_external_use) { if (style != FOR_SHUTDOWN) { // Defer this resource deletion. resource.marked_for_deletion = true; @@ -378,11 +393,12 @@ void DisplayResourceProvider::ReceiveFromChild( viz::ResourceId local_id = next_id_++; viz::internal::Resource* resource = nullptr; if (it->is_software) { + DCHECK(IsBitmapFormatSupported(it->format)); resource = InsertResource( local_id, viz::internal::Resource(it->size, viz::internal::Resource::DELEGATED, viz::ResourceTextureHint::kDefault, - viz::ResourceType::kBitmap, viz::RGBA_8888, + viz::ResourceType::kBitmap, it->format, it->color_space)); resource->has_shared_bitmap_id = true; resource->shared_bitmap_id = it->mailbox_holder.mailbox; @@ -452,7 +468,12 @@ GLenum DisplayResourceProvider::BindForSampling(viz::ResourceId resource_id, DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); GLES2Interface* gl = ContextGL(); ResourceMap::iterator it = resources_.find(resource_id); - DCHECK(it != resources_.end()); + // TODO(ericrk): We should never fail to find resource_id, but we appear to + // be doing so on Android in rare cases. Handle this gracefully until a + // better solution can be found. https://crbug.com/811858 + if (it == resources_.end()) + return GL_TEXTURE_2D; + viz::internal::Resource* resource = &it->second; DCHECK(resource->lock_for_read_count); // TODO(xing.xu): remove locked_for_write. @@ -492,7 +513,8 @@ GLenum DisplayResourceProvider::BindForSampling(viz::ResourceId resource_id, bool DisplayResourceProvider::InUse(viz::ResourceId id) { viz::internal::Resource* resource = GetResource(id); - return resource->lock_for_read_count > 0 || resource->lost; + return resource->lock_for_read_count > 0 || resource->lost || + resource->locked_for_external_use; } DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL( @@ -501,6 +523,12 @@ DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL( : resource_provider_(resource_provider), resource_id_(resource_id) { const viz::internal::Resource* resource = resource_provider->LockForRead(resource_id); + // TODO(ericrk): We should never fail LockForRead, but we appear to be + // doing so on Android in rare cases. Handle this gracefully until a better + // solution can be found. https://crbug.com/811858 + if (!resource) + return; + texture_id_ = resource->gl_id; target_ = resource->target; size_ = resource->size; @@ -509,7 +537,13 @@ DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL( const viz::internal::Resource* DisplayResourceProvider::LockForRead( viz::ResourceId id) { - viz::internal::Resource* resource = GetResource(id); + // TODO(ericrk): We should never fail TryGetResource, but we appear to be + // doing so on Android in rare cases. Handle this gracefully until a better + // solution can be found. https://crbug.com/811858 + viz::internal::Resource* resource = TryGetResource(id); + if (!resource) + return nullptr; + // TODO(xing.xu): remove locked_for_write. DCHECK(!resource->locked_for_write) << "locked for write: " << resource->locked_for_write; @@ -537,7 +571,7 @@ const viz::internal::Resource* DisplayResourceProvider::LockForRead( shared_bitmap_manager_) { std::unique_ptr<viz::SharedBitmap> bitmap = shared_bitmap_manager_->GetSharedBitmapFromId( - resource->size, resource->shared_bitmap_id); + resource->size, resource->format, resource->shared_bitmap_id); if (bitmap) { resource->SetSharedBitmap(bitmap.get()); resource->owned_shared_bitmap = std::move(bitmap); @@ -557,13 +591,84 @@ const viz::internal::Resource* DisplayResourceProvider::LockForRead( void DisplayResourceProvider::UnlockForRead(viz::ResourceId id) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); + // TODO(ericrk): We should never fail to find id, but we appear to be + // doing so on Android in rare cases. Handle this gracefully until a better + // solution can be found. https://crbug.com/811858 + if (it == resources_.end()) + return; viz::internal::Resource* resource = &it->second; DCHECK_GT(resource->lock_for_read_count, 0); DCHECK_EQ(resource->exported_count, 0); resource->lock_for_read_count--; - if (resource->marked_for_deletion && !resource->lock_for_read_count) { + TryReleaseResource(it); +} + +viz::ResourceMetadata DisplayResourceProvider::LockForExternalUse( + viz::ResourceId id) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + ResourceMap::iterator it = resources_.find(id); + DCHECK(it != resources_.end()); + + viz::internal::Resource* resource = &it->second; + viz::ResourceMetadata metadata; + // TODO(xing.xu): remove locked_for_write. + DCHECK(!resource->locked_for_write) + << "locked for write: " << resource->locked_for_write; + DCHECK_EQ(resource->exported_count, 0); + // Uninitialized! Call SetPixels or LockForWrite first. + DCHECK(resource->allocated); + // Make sure there is no outstanding LockForExternalUse without calling + // UnlockForExternalUse. + DCHECK(!resource->locked_for_external_use); + // TODO(penghuang): support software resource. + DCHECK(resource->is_gpu_resource_type()); + + metadata.mailbox = resource->mailbox; + metadata.backend_format = GrBackendFormat::MakeGL( + TextureStorageFormat(resource->format), resource->target); + metadata.size = resource->size; + metadata.mip_mapped = GrMipMapped::kNo; + metadata.origin = kTopLeft_GrSurfaceOrigin; + metadata.color_type = ResourceFormatToClosestSkColorType(resource->format); + metadata.alpha_type = kPremul_SkAlphaType; + metadata.color_space = nullptr; + metadata.sync_token = resource->sync_token(); + + resource->locked_for_external_use = true; + return metadata; +} + +void DisplayResourceProvider::UnlockForExternalUse( + viz::ResourceId id, + const gpu::SyncToken& sync_token) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + ResourceMap::iterator it = resources_.find(id); + DCHECK(it != resources_.end()); + DCHECK(sync_token.verified_flush()); + + viz::internal::Resource* resource = &it->second; + DCHECK(resource->locked_for_external_use); + // TODO(penghuang): support software resource. + DCHECK(resource->is_gpu_resource_type()); + + // Update the resource sync token to |sync_token|. When the next frame is + // being composited, the DeclareUsedResourcesFromChild() will be called with + // resources belong to every child for the next frame. If the resource is not + // used by the next frame, the resource will be returned to a child which + // owns it with the |sync_token|. The child is responsible for issuing a + // WaitSyncToken GL command with the |sync_token| before reusing it. + resource->UpdateSyncToken(sync_token); + resource->locked_for_external_use = false; + + TryReleaseResource(it); +} + +void DisplayResourceProvider::TryReleaseResource(ResourceMap::iterator it) { + viz::ResourceId id = it->first; + viz::internal::Resource* resource = &it->second; + if (resource->marked_for_deletion && !resource->lock_for_read_count && + !resource->locked_for_external_use) { if (!resource->child_id) { // The resource belongs to this ResourceProvider, so it can be destroyed. #if defined(OS_ANDROID) @@ -617,6 +722,7 @@ DisplayResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage( : resource_provider_(resource_provider), resource_id_(resource_id) { const viz::internal::Resource* resource = resource_provider->LockForRead(resource_id); + DCHECK(resource); if (resource_provider_->resource_sk_image_.find(resource_id) != resource_provider_->resource_sk_image_.end()) { // Use cached sk_image. @@ -661,6 +767,7 @@ DisplayResourceProvider::ScopedReadLockSoftware::ScopedReadLockSoftware( : resource_provider_(resource_provider), resource_id_(resource_id) { const viz::internal::Resource* resource = resource_provider->LockForRead(resource_id); + DCHECK(resource); resource_provider->PopulateSkBitmapWithResource(&sk_bitmap_, resource); } @@ -668,6 +775,30 @@ DisplayResourceProvider::ScopedReadLockSoftware::~ScopedReadLockSoftware() { resource_provider_->UnlockForRead(resource_id_); } +DisplayResourceProvider::LockSetForExternalUse::LockSetForExternalUse( + DisplayResourceProvider* resource_provider) + : resource_provider_(resource_provider) {} + +DisplayResourceProvider::LockSetForExternalUse::~LockSetForExternalUse() { + DCHECK(resources_.empty()); +} + +viz::ResourceMetadata +DisplayResourceProvider::LockSetForExternalUse::LockResource( + viz::ResourceId id) { + DCHECK(std::find(resources_.begin(), resources_.end(), id) == + resources_.end()); + resources_.push_back(id); + return resource_provider_->LockForExternalUse(id); +} + +void DisplayResourceProvider::LockSetForExternalUse::UnlockResources( + const gpu::SyncToken& sync_token) { + for (const auto& id : resources_) + resource_provider_->UnlockForExternalUse(id, sync_token); + resources_.clear(); +} + DisplayResourceProvider::SynchronousFence::SynchronousFence( gpu::gles2::GLES2Interface* gl) : gl_(gl), has_synchronized_(true) {} diff --git a/chromium/cc/resources/display_resource_provider.h b/chromium/cc/resources/display_resource_provider.h index 7f09f5299d8..5c6baef797e 100644 --- a/chromium/cc/resources/display_resource_provider.h +++ b/chromium/cc/resources/display_resource_provider.h @@ -9,6 +9,7 @@ #include "cc/output/overlay_candidate.h" #include "cc/resources/resource_provider.h" #include "components/viz/common/resources/resource_fence.h" +#include "components/viz/common/resources/resource_metadata.h" namespace viz { class SharedBitmapManager; @@ -74,8 +75,8 @@ class CC_EXPORT DisplayResourceProvider : public ResourceProvider { DisplayResourceProvider* const resource_provider_; const viz::ResourceId resource_id_; - GLuint texture_id_; - GLenum target_; + GLuint texture_id_ = 0; + GLenum target_ = GL_TEXTURE_2D; gfx::Size size_; gfx::ColorSpace color_space_; @@ -146,6 +147,27 @@ class CC_EXPORT DisplayResourceProvider : public ResourceProvider { DISALLOW_COPY_AND_ASSIGN(ScopedReadLockSoftware); }; + // Maintains set of lock for external use. + class CC_EXPORT LockSetForExternalUse { + public: + explicit LockSetForExternalUse(DisplayResourceProvider* resource_provider); + ~LockSetForExternalUse(); + + // Lock a resource for external use. + viz::ResourceMetadata LockResource(viz::ResourceId resource_id); + + // Unlock all locked resources with a |sync_token|. + // See UnlockForExternalUse for the detail. All resources must be unlocked + // before destroying this class. + void UnlockResources(const gpu::SyncToken& sync_token); + + private: + DisplayResourceProvider* const resource_provider_; + std::vector<viz::ResourceId> resources_; + + DISALLOW_COPY_AND_ASSIGN(LockSetForExternalUse); + }; + // All resources that are returned to children while an instance of this // class exists will be stored and returned when the instance is destroyed. class CC_EXPORT ScopedBatchReturnResources { @@ -224,10 +246,20 @@ class CC_EXPORT DisplayResourceProvider : public ResourceProvider { const viz::ResourceIdSet& resources_from_child); private: - friend class ScopedBatchReturnResources; - const viz::internal::Resource* LockForRead(viz::ResourceId id); void UnlockForRead(viz::ResourceId id); + + // Lock a resource for external use. + viz::ResourceMetadata LockForExternalUse(viz::ResourceId id); + + // Unlock a resource which locked by LockForExternalUse. + // The |sync_token| should be waited on before reusing the resouce's backing + // to ensure that any external use of it is completed. This |sync_token| + // should have been verified. + void UnlockForExternalUse(viz::ResourceId id, + const gpu::SyncToken& sync_token); + + void TryReleaseResource(ResourceMap::iterator it); // Binds the given GL resource to a texture target for sampling using the // specified filter for both minification and magnification. Returns the // texture target used. The resource must be locked for reading. diff --git a/chromium/cc/resources/display_resource_provider_unittest.cc b/chromium/cc/resources/display_resource_provider_unittest.cc new file mode 100644 index 00000000000..b8e3e5647bd --- /dev/null +++ b/chromium/cc/resources/display_resource_provider_unittest.cc @@ -0,0 +1,548 @@ +// 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/resources/display_resource_provider.h" +#include "cc/resources/layer_tree_resource_provider.h" + +#include <stddef.h> +#include <stdint.h> + +#include <algorithm> +#include <map> +#include <memory> +#include <set> +#include <unordered_map> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "cc/test/render_pass_test_utils.h" +#include "cc/test/resource_provider_test_utils.h" +#include "components/viz/common/resources/resource_format_utils.h" +#include "components/viz/common/resources/returned_resource.h" +#include "components/viz/common/resources/shared_bitmap_manager.h" +#include "components/viz/common/resources/single_release_callback.h" +#include "components/viz/test/test_context_provider.h" +#include "components/viz/test/test_gpu_memory_buffer_manager.h" +#include "components/viz/test/test_shared_bitmap_manager.h" +#include "components/viz/test/test_texture.h" +#include "components/viz/test/test_web_graphics_context_3d.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/khronos/GLES2/gl2.h" +#include "third_party/khronos/GLES2/gl2ext.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/gpu_memory_buffer.h" + +namespace cc { +namespace { + +static const bool kUseGpuMemoryBufferResources = false; + +MATCHER_P(MatchesSyncToken, sync_token, "") { + gpu::SyncToken other; + memcpy(&other, arg, sizeof(other)); + return other == sync_token; +} + +static void ReleaseSharedBitmapCallback( + std::unique_ptr<viz::SharedBitmap> shared_bitmap, + bool* release_called, + gpu::SyncToken* release_sync_token, + bool* lost_resource_result, + const gpu::SyncToken& sync_token, + bool lost_resource) { + *release_called = true; + *release_sync_token = sync_token; + *lost_resource_result = lost_resource; +} + +static std::unique_ptr<viz::SharedBitmap> CreateAndFillSharedBitmap( + viz::SharedBitmapManager* manager, + const gfx::Size& size, + viz::ResourceFormat format, + uint32_t value) { + std::unique_ptr<viz::SharedBitmap> shared_bitmap = + manager->AllocateSharedBitmap(size, format); + CHECK(shared_bitmap); + uint32_t* pixels = reinterpret_cast<uint32_t*>(shared_bitmap->pixels()); + CHECK(pixels); + std::fill_n(pixels, size.GetArea(), value); + return shared_bitmap; +} + +static viz::ResourceSettings CreateResourceSettings() { + viz::ResourceSettings resource_settings; + resource_settings.use_gpu_memory_buffer_resources = + kUseGpuMemoryBufferResources; + return resource_settings; +} + +// Shared data between multiple ResourceProviderContext. This contains mailbox +// contents as well as information about sync points. +class ContextSharedData { + public: + static std::unique_ptr<ContextSharedData> Create() { + return base::WrapUnique(new ContextSharedData()); + } + + uint32_t InsertFenceSync() { return next_fence_sync_++; } + + void GenMailbox(GLbyte* mailbox) { + memset(mailbox, 0, GL_MAILBOX_SIZE_CHROMIUM); + memcpy(mailbox, &next_mailbox_, sizeof(next_mailbox_)); + ++next_mailbox_; + } + + void ProduceTexture(const GLbyte* mailbox_name, + const gpu::SyncToken& sync_token, + scoped_refptr<viz::TestTexture> texture) { + uint32_t sync_point = static_cast<uint32_t>(sync_token.release_count()); + + unsigned mailbox = 0; + memcpy(&mailbox, mailbox_name, sizeof(mailbox)); + ASSERT_TRUE(mailbox && mailbox < next_mailbox_); + textures_[mailbox] = texture; + ASSERT_LT(sync_point_for_mailbox_[mailbox], sync_point); + sync_point_for_mailbox_[mailbox] = sync_point; + } + + scoped_refptr<viz::TestTexture> ConsumeTexture( + const GLbyte* mailbox_name, + const gpu::SyncToken& sync_token) { + unsigned mailbox = 0; + memcpy(&mailbox, mailbox_name, sizeof(mailbox)); + DCHECK(mailbox && mailbox < next_mailbox_); + + // If the latest sync point the context has waited on is before the sync + // point for when the mailbox was set, pretend we never saw that + // ProduceTexture. + if (sync_point_for_mailbox_[mailbox] > sync_token.release_count()) { + NOTREACHED(); + return scoped_refptr<viz::TestTexture>(); + } + return textures_[mailbox]; + } + + private: + ContextSharedData() : next_fence_sync_(1), next_mailbox_(1) {} + + uint64_t next_fence_sync_; + unsigned next_mailbox_; + using TextureMap = + std::unordered_map<unsigned, scoped_refptr<viz::TestTexture>>; + TextureMap textures_; + std::unordered_map<unsigned, uint32_t> sync_point_for_mailbox_; +}; + +class ResourceProviderContext : public viz::TestWebGraphicsContext3D { + public: + static std::unique_ptr<ResourceProviderContext> Create( + ContextSharedData* shared_data) { + return base::WrapUnique(new ResourceProviderContext(shared_data)); + } + + void genSyncToken(GLbyte* sync_token) override { + uint64_t fence_sync = shared_data_->InsertFenceSync(); + gpu::SyncToken sync_token_data(gpu::CommandBufferNamespace::GPU_IO, + gpu::CommandBufferId::FromUnsafeValue(0x123), + fence_sync); + sync_token_data.SetVerifyFlush(); + // Commit the ProduceTextureDirectCHROMIUM calls at this point, so that + // they're associated with the sync point. + for (const std::unique_ptr<PendingProduceTexture>& pending_texture : + pending_produce_textures_) { + shared_data_->ProduceTexture(pending_texture->mailbox, sync_token_data, + pending_texture->texture); + } + pending_produce_textures_.clear(); + memcpy(sync_token, &sync_token_data, sizeof(sync_token_data)); + } + + void waitSyncToken(const GLbyte* sync_token) override { + gpu::SyncToken sync_token_data; + if (sync_token) + memcpy(&sync_token_data, sync_token, sizeof(sync_token_data)); + + if (sync_token_data.release_count() > + last_waited_sync_token_.release_count()) { + last_waited_sync_token_ = sync_token_data; + } + } + + const gpu::SyncToken& last_waited_sync_token() const { + return last_waited_sync_token_; + } + + void texStorage2DEXT(GLenum target, + GLint levels, + GLuint internalformat, + GLint width, + GLint height) override { + CheckTextureIsBound(target); + ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target); + ASSERT_EQ(1, levels); + GLenum format = GL_RGBA; + switch (internalformat) { + case GL_RGBA8_OES: + break; + case GL_BGRA8_EXT: + format = GL_BGRA_EXT; + break; + default: + NOTREACHED(); + } + AllocateTexture(gfx::Size(width, height), format); + } + + void texImage2D(GLenum target, + GLint level, + GLenum internalformat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + const void* pixels) override { + CheckTextureIsBound(target); + ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target); + ASSERT_FALSE(level); + ASSERT_EQ(internalformat, format); + ASSERT_FALSE(border); + ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type); + AllocateTexture(gfx::Size(width, height), format); + if (pixels) + SetPixels(0, 0, width, height, pixels); + } + + void texSubImage2D(GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + const void* pixels) override { + CheckTextureIsBound(target); + ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target); + ASSERT_FALSE(level); + ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type); + { + base::AutoLock lock_for_texture_access(namespace_->lock); + ASSERT_EQ(GLDataFormat(BoundTexture(target)->format), format); + } + ASSERT_TRUE(pixels); + SetPixels(xoffset, yoffset, width, height, pixels); + } + + void genMailboxCHROMIUM(GLbyte* mailbox) override { + return shared_data_->GenMailbox(mailbox); + } + + void produceTextureDirectCHROMIUM(GLuint texture, + const GLbyte* mailbox) override { + // Delay moving the texture into the mailbox until the next + // sync token, so that it is not visible to other contexts that + // haven't waited on that sync point. + std::unique_ptr<PendingProduceTexture> pending(new PendingProduceTexture); + memcpy(pending->mailbox, mailbox, sizeof(pending->mailbox)); + base::AutoLock lock_for_texture_access(namespace_->lock); + pending->texture = UnboundTexture(texture); + pending_produce_textures_.push_back(std::move(pending)); + } + + GLuint createAndConsumeTextureCHROMIUM(const GLbyte* mailbox) override { + GLuint texture_id = createTexture(); + base::AutoLock lock_for_texture_access(namespace_->lock); + scoped_refptr<viz::TestTexture> texture = + shared_data_->ConsumeTexture(mailbox, last_waited_sync_token_); + namespace_->textures.Replace(texture_id, texture); + return texture_id; + } + + void GetPixels(const gfx::Size& size, + viz::ResourceFormat format, + uint8_t* pixels) { + CheckTextureIsBound(GL_TEXTURE_2D); + base::AutoLock lock_for_texture_access(namespace_->lock); + scoped_refptr<viz::TestTexture> texture = BoundTexture(GL_TEXTURE_2D); + ASSERT_EQ(texture->size, size); + ASSERT_EQ(texture->format, format); + memcpy(pixels, texture->data.get(), TextureSizeBytes(size, format)); + } + + protected: + explicit ResourceProviderContext(ContextSharedData* shared_data) + : shared_data_(shared_data) {} + + private: + void AllocateTexture(const gfx::Size& size, GLenum format) { + CheckTextureIsBound(GL_TEXTURE_2D); + viz::ResourceFormat texture_format = viz::RGBA_8888; + switch (format) { + case GL_RGBA: + texture_format = viz::RGBA_8888; + break; + case GL_BGRA_EXT: + texture_format = viz::BGRA_8888; + break; + } + base::AutoLock lock_for_texture_access(namespace_->lock); + BoundTexture(GL_TEXTURE_2D)->Reallocate(size, texture_format); + } + + void SetPixels(int xoffset, + int yoffset, + int width, + int height, + const void* pixels) { + CheckTextureIsBound(GL_TEXTURE_2D); + base::AutoLock lock_for_texture_access(namespace_->lock); + scoped_refptr<viz::TestTexture> texture = BoundTexture(GL_TEXTURE_2D); + ASSERT_TRUE(texture->data.get()); + ASSERT_TRUE(xoffset >= 0 && xoffset + width <= texture->size.width()); + ASSERT_TRUE(yoffset >= 0 && yoffset + height <= texture->size.height()); + ASSERT_TRUE(pixels); + size_t in_pitch = TextureSizeBytes(gfx::Size(width, 1), texture->format); + size_t out_pitch = + TextureSizeBytes(gfx::Size(texture->size.width(), 1), texture->format); + uint8_t* dest = texture->data.get() + yoffset * out_pitch + + TextureSizeBytes(gfx::Size(xoffset, 1), texture->format); + const uint8_t* src = static_cast<const uint8_t*>(pixels); + for (int i = 0; i < height; ++i) { + memcpy(dest, src, in_pitch); + dest += out_pitch; + src += in_pitch; + } + } + + struct PendingProduceTexture { + GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM]; + scoped_refptr<viz::TestTexture> texture; + }; + ContextSharedData* shared_data_; + gpu::SyncToken last_waited_sync_token_; + std::vector<std::unique_ptr<PendingProduceTexture>> pending_produce_textures_; +}; + +class DisplayResourceProviderTest : public testing::TestWithParam<bool> { + public: + explicit DisplayResourceProviderTest(bool child_needs_sync_token) + : use_gpu_(GetParam()), + child_needs_sync_token_(child_needs_sync_token), + shared_data_(ContextSharedData::Create()) { + if (use_gpu_) { + auto context3d(ResourceProviderContext::Create(shared_data_.get())); + context3d_ = context3d.get(); + context_provider_ = + viz::TestContextProvider::Create(std::move(context3d)); + context_provider_->UnboundTestContext3d() + ->set_support_texture_format_bgra8888(true); + context_provider_->BindToCurrentThread(); + + auto child_context_owned = + ResourceProviderContext::Create(shared_data_.get()); + child_context_ = child_context_owned.get(); + child_context_provider_ = + viz::TestContextProvider::Create(std::move(child_context_owned)); + child_context_provider_->UnboundTestContext3d() + ->set_support_texture_format_bgra8888(true); + child_context_provider_->BindToCurrentThread(); + gpu_memory_buffer_manager_ = + std::make_unique<viz::TestGpuMemoryBufferManager>(); + child_gpu_memory_buffer_manager_ = + gpu_memory_buffer_manager_->CreateClientGpuMemoryBufferManager(); + } else { + shared_bitmap_manager_ = std::make_unique<viz::TestSharedBitmapManager>(); + } + + resource_provider_ = std::make_unique<DisplayResourceProvider>( + context_provider_.get(), shared_bitmap_manager_.get()); + + MakeChildResourceProvider(); + } + + DisplayResourceProviderTest() : DisplayResourceProviderTest(true) {} + + bool use_gpu() const { return use_gpu_; } + + void MakeChildResourceProvider() { + child_resource_provider_ = std::make_unique<LayerTreeResourceProvider>( + child_context_provider_.get(), shared_bitmap_manager_.get(), + child_gpu_memory_buffer_manager_.get(), child_needs_sync_token_, + CreateResourceSettings()); + } + + static void CollectResources( + std::vector<viz::ReturnedResource>* array, + const std::vector<viz::ReturnedResource>& returned) { + array->insert(array->end(), returned.begin(), returned.end()); + } + + static ReturnCallback GetReturnCallback( + std::vector<viz::ReturnedResource>* array) { + return base::BindRepeating(&DisplayResourceProviderTest::CollectResources, + array); + } + + static void SetResourceFilter(DisplayResourceProvider* resource_provider, + viz::ResourceId id, + GLenum filter) { + DisplayResourceProvider::ScopedSamplerGL sampler(resource_provider, id, + GL_TEXTURE_2D, filter); + } + + ResourceProviderContext* context() { return context3d_; } + + viz::ResourceId CreateChildMailbox(gpu::SyncToken* release_sync_token, + bool* lost_resource, + bool* release_called, + gpu::SyncToken* sync_token, + viz::ResourceFormat format) { + if (use_gpu()) { + unsigned texture = child_context_->createTexture(); + gpu::Mailbox gpu_mailbox; + child_context_->genMailboxCHROMIUM(gpu_mailbox.name); + child_context_->produceTextureDirectCHROMIUM(texture, gpu_mailbox.name); + child_context_->genSyncToken(sync_token->GetData()); + EXPECT_TRUE(sync_token->HasData()); + + std::unique_ptr<viz::SharedBitmap> shared_bitmap; + std::unique_ptr<viz::SingleReleaseCallback> callback = + viz::SingleReleaseCallback::Create(base::BindRepeating( + ReleaseSharedBitmapCallback, base::Passed(&shared_bitmap), + release_called, release_sync_token, lost_resource)); + viz::TransferableResource gl_resource = viz::TransferableResource::MakeGL( + gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, *sync_token); + gl_resource.format = format; + return child_resource_provider_->ImportResource(gl_resource, + std::move(callback)); + } else { + gfx::Size size(64, 64); + std::unique_ptr<viz::SharedBitmap> shared_bitmap( + CreateAndFillSharedBitmap(shared_bitmap_manager_.get(), size, format, + 0)); + + viz::SharedBitmap* shared_bitmap_ptr = shared_bitmap.get(); + std::unique_ptr<viz::SingleReleaseCallback> callback = + viz::SingleReleaseCallback::Create(base::BindRepeating( + ReleaseSharedBitmapCallback, base::Passed(&shared_bitmap), + release_called, release_sync_token, lost_resource)); + return child_resource_provider_->ImportResource( + viz::TransferableResource::MakeSoftware( + shared_bitmap_ptr->id(), shared_bitmap_ptr->sequence_number(), + size, format), + std::move(callback)); + } + } + + viz::ResourceId MakeGpuResourceAndSendToDisplay( + char c, + GLuint filter, + GLuint target, + const gpu::SyncToken& sync_token, + DisplayResourceProvider* resource_provider) { + ReturnCallback return_callback = base::DoNothing(); + + int child = resource_provider->CreateChild(return_callback); + + gpu::Mailbox gpu_mailbox; + gpu_mailbox.name[0] = c; + gpu_mailbox.name[1] = 0; + auto resource = viz::TransferableResource::MakeGL(gpu_mailbox, GL_LINEAR, + target, sync_token); + resource.id = 11; + resource_provider->ReceiveFromChild(child, {resource}); + auto& map = resource_provider->GetChildToParentMap(child); + return map.find(resource.id)->second; + } + + protected: + const bool use_gpu_; + const bool child_needs_sync_token_; + const std::unique_ptr<ContextSharedData> shared_data_; + ResourceProviderContext* context3d_ = nullptr; + ResourceProviderContext* child_context_ = nullptr; + scoped_refptr<viz::TestContextProvider> context_provider_; + scoped_refptr<viz::TestContextProvider> child_context_provider_; + std::unique_ptr<viz::TestGpuMemoryBufferManager> gpu_memory_buffer_manager_; + std::unique_ptr<DisplayResourceProvider> resource_provider_; + std::unique_ptr<viz::TestGpuMemoryBufferManager> + child_gpu_memory_buffer_manager_; + std::unique_ptr<LayerTreeResourceProvider> child_resource_provider_; + std::unique_ptr<viz::TestSharedBitmapManager> shared_bitmap_manager_; +}; + +INSTANTIATE_TEST_CASE_P(DisplayResourceProviderTests, + DisplayResourceProviderTest, + ::testing::Values(false, true)); + +TEST_P(DisplayResourceProviderTest, LockForExternalUse) { + // TODO(penghuang): consider supporting SW mode. + if (!use_gpu()) + return; + + gfx::Size size(1, 1); + viz::ResourceFormat format = viz::RGBA_8888; + + viz::ResourceId id1 = child_resource_provider_->CreateGpuTextureResource( + size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace()); + uint8_t data1[4] = {1, 2, 3, 4}; + child_resource_provider_->CopyToResource(id1, data1, size); + std::vector<viz::ReturnedResource> returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); + + // Transfer some resources to the parent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(id1); + + std::vector<viz::TransferableResource> list; + child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer, + &list); + ASSERT_EQ(1u, list.size()); + EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1)); + + resource_provider_->ReceiveFromChild(child_id, list); + + // In DisplayResourceProvider's namespace, use the mapped resource id. + ResourceProvider::ResourceIdMap resource_map = + resource_provider_->GetChildToParentMap(child_id); + + unsigned parent_id = resource_map[list.front().id]; + + DisplayResourceProvider::LockSetForExternalUse lock_set( + resource_provider_.get()); + + viz::ResourceMetadata metadata = lock_set.LockResource(parent_id); + ASSERT_EQ(size, metadata.size); + ASSERT_FALSE(metadata.mailbox.IsZero()); + ASSERT_TRUE(metadata.sync_token.HasData()); + + resource_provider_->DeclareUsedResourcesFromChild(child_id, + viz::ResourceIdSet()); + // The resource should not be returned due to the external use lock. + EXPECT_EQ(0u, returned_to_child.size()); + + gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, + gpu::CommandBufferId::FromUnsafeValue(0x234), + 0x456); + sync_token.SetVerifyFlush(); + lock_set.UnlockResources(sync_token); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + viz::ResourceIdSet()); + // The resource should be returned after the lock is released. + EXPECT_EQ(1u, returned_to_child.size()); + EXPECT_EQ(sync_token, returned_to_child[0].sync_token); + child_resource_provider_->ReceiveReturnsFromParent(returned_to_child); + child_resource_provider_->DeleteResource(id1); + EXPECT_EQ(0u, child_resource_provider_->num_resources()); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/resources/layer_tree_resource_provider.cc b/chromium/cc/resources/layer_tree_resource_provider.cc index 38f17c03d25..44720a3ffcb 100644 --- a/chromium/cc/resources/layer_tree_resource_provider.cc +++ b/chromium/cc/resources/layer_tree_resource_provider.cc @@ -7,10 +7,10 @@ #include "base/bits.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" -#include "cc/resources/resource_util.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" +#include "components/viz/common/resources/resource_sizes.h" #include "components/viz/common/resources/shared_bitmap_manager.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/context_support.h" @@ -403,23 +403,25 @@ viz::ResourceId LayerTreeResourceProvider::CreateGpuMemoryBufferResource( viz::ResourceId LayerTreeResourceProvider::CreateBitmapResource( const gfx::Size& size, - const gfx::ColorSpace& color_space) { + const gfx::ColorSpace& color_space, + viz::ResourceFormat format) { DCHECK(!compositor_context_provider_); DCHECK(!size.IsEmpty()); + DCHECK(viz::IsBitmapFormatSupported(format)); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // TODO(danakj): Allocate this outside ResourceProvider. std::unique_ptr<viz::SharedBitmap> bitmap = - shared_bitmap_manager_->AllocateSharedBitmap(size); + shared_bitmap_manager_->AllocateSharedBitmap(size, format); DCHECK(bitmap); DCHECK(bitmap->pixels()); viz::ResourceId id = next_id_++; viz::internal::Resource* resource = InsertResource( - id, viz::internal::Resource(size, viz::internal::Resource::INTERNAL, - viz::ResourceTextureHint::kDefault, - viz::ResourceType::kBitmap, viz::RGBA_8888, - color_space)); + id, + viz::internal::Resource(size, viz::internal::Resource::INTERNAL, + viz::ResourceTextureHint::kDefault, + viz::ResourceType::kBitmap, format, color_space)); resource->SetSharedBitmap(bitmap.get()); resource->owned_shared_bitmap = std::move(bitmap); return id; @@ -502,7 +504,7 @@ void LayerTreeResourceProvider::CopyToResource(viz::ResourceId id, if (resource->format == viz::ETC1) { DCHECK_EQ(resource->target, static_cast<GLenum>(GL_TEXTURE_2D)); int image_bytes = - ResourceUtil::CheckedSizeInBytes<int>(image_size, viz::ETC1); + viz::ResourceSizes::CheckedSizeInBytes<int>(image_size, viz::ETC1); gl->CompressedTexImage2D(resource->target, 0, GLInternalFormat(viz::ETC1), image_size.width(), image_size.height(), 0, image_bytes, image); @@ -790,6 +792,7 @@ LayerTreeResourceProvider::ScopedWriteLockGpu::ScopedWriteLockGpu( DCHECK_EQ(resource->type, viz::ResourceType::kTexture); resource_provider->CreateTexture(resource); size_ = resource->size; + usage_ = resource->usage; format_ = resource->format; color_space_ = resource_provider_->GetResourceColorSpaceForRaster(resource); texture_id_ = resource->gl_id; @@ -892,7 +895,8 @@ GLuint LayerTreeResourceProvider::ScopedWriteLockRaster::ConsumeTexture( DCHECK(ri); DCHECK(!mailbox_.IsZero()); - GLuint texture_id = ri->CreateAndConsumeTextureCHROMIUM(mailbox_.name); + GLuint texture_id = + ri->CreateAndConsumeTexture(is_overlay_, usage_, format_, mailbox_.name); DCHECK(texture_id); LazyAllocate(ri, texture_id); @@ -911,13 +915,10 @@ void LayerTreeResourceProvider::ScopedWriteLockRaster::LazyAllocate( return; allocated_ = true; - ri->BindTexture(target_, texture_id); - ri->TexStorageForRaster( - target_, format_, size_.width(), size_.height(), - is_overlay_ ? gpu::raster::kOverlay : gpu::raster::kNone); + ri->TexStorage2D(texture_id, 1, size_.width(), size_.height()); if (is_overlay_ && color_space_.IsValid()) { - ri->SetColorSpaceMetadataCHROMIUM( - texture_id, reinterpret_cast<GLColorSpace>(&color_space_)); + ri->SetColorSpaceMetadata(texture_id, + reinterpret_cast<GLColorSpace>(&color_space_)); } } @@ -987,7 +988,6 @@ LayerTreeResourceProvider::ScopedSkSurface::ScopedSkSurface( GLenum texture_target, const gfx::Size& size, viz::ResourceFormat format, - bool use_distance_field_text, bool can_use_lcd_text, int msaa_sample_count) { GrGLTextureInfo texture_info; @@ -996,15 +996,7 @@ LayerTreeResourceProvider::ScopedSkSurface::ScopedSkSurface( texture_info.fFormat = TextureStorageFormat(format); GrBackendTexture backend_texture(size.width(), size.height(), GrMipMapped::kNo, texture_info); - uint32_t flags = - use_distance_field_text ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 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); - } + SkSurfaceProps surface_props = ComputeSurfaceProps(can_use_lcd_text); surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget( gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, msaa_sample_count, ResourceFormatToClosestSkColorType(format), nullptr, &surface_props); @@ -1015,6 +1007,19 @@ LayerTreeResourceProvider::ScopedSkSurface::~ScopedSkSurface() { 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); diff --git a/chromium/cc/resources/layer_tree_resource_provider.h b/chromium/cc/resources/layer_tree_resource_provider.h index 92ae354a77f..07a85f50a70 100644 --- a/chromium/cc/resources/layer_tree_resource_provider.h +++ b/chromium/cc/resources/layer_tree_resource_provider.h @@ -71,7 +71,8 @@ class CC_EXPORT LayerTreeResourceProvider : public ResourceProvider { gfx::BufferUsage usage, const gfx::ColorSpace& color_space); viz::ResourceId CreateBitmapResource(const gfx::Size& size, - const gfx::ColorSpace& color_space); + const gfx::ColorSpace& color_space, + viz::ResourceFormat format); void DeleteResource(viz::ResourceId id); @@ -185,6 +186,7 @@ class CC_EXPORT LayerTreeResourceProvider : public ResourceProvider { // The following are copied from the resource. gfx::Size size_; + gfx::BufferUsage usage_; viz::ResourceFormat format_; gfx::ColorSpace color_space_; GLuint texture_id_; @@ -285,13 +287,14 @@ class CC_EXPORT LayerTreeResourceProvider : public ResourceProvider { GLenum texture_target, const gfx::Size& size, viz::ResourceFormat format, - bool use_distance_field_text, 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_; diff --git a/chromium/cc/resources/layer_tree_resource_provider_unittest.cc b/chromium/cc/resources/layer_tree_resource_provider_unittest.cc index 4069c9b256f..b0a47f2a805 100644 --- a/chromium/cc/resources/layer_tree_resource_provider_unittest.cc +++ b/chromium/cc/resources/layer_tree_resource_provider_unittest.cc @@ -358,8 +358,8 @@ TEST_P(LayerTreeResourceProviderTest, gfx::Size(3, 4), viz::ResourceTextureHint::kDefault, viz::RGBA_8888, gfx::ColorSpace()); } else { - norm_id = - provider().CreateBitmapResource(gfx::Size(3, 4), gfx::ColorSpace()); + norm_id = provider().CreateBitmapResource( + gfx::Size(3, 4), gfx::ColorSpace(), viz::RGBA_8888); } provider().AllocateForTesting(norm_id); diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc index c12c9550e44..659d5743972 100644 --- a/chromium/cc/resources/resource_pool.cc +++ b/chromium/cc/resources/resource_pool.cc @@ -20,7 +20,7 @@ #include "build/build_config.h" #include "cc/base/container_util.h" #include "cc/resources/layer_tree_resource_provider.h" -#include "cc/resources/resource_util.h" +#include "components/viz/common/resources/resource_sizes.h" using base::trace_event::MemoryAllocatorDump; using base::trace_event::MemoryDumpLevelOfDetail; @@ -124,8 +124,9 @@ ResourcePool::PoolResource* ResourcePool::ReuseResource( // Transfer resource to |in_use_resources_|. in_use_resources_[resource->unique_id()] = std::move(*it); unused_resources_.erase(it); - in_use_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( - resource->size(), resource->format()); + in_use_memory_usage_bytes_ += + viz::ResourceSizes::UncheckedSizeInBytes<size_t>(resource->size(), + resource->format()); return resource; } return nullptr; @@ -135,19 +136,19 @@ ResourcePool::PoolResource* ResourcePool::CreateResource( const gfx::Size& size, viz::ResourceFormat format, const gfx::ColorSpace& color_space) { - DCHECK(ResourceUtil::VerifySizeInBytes<size_t>(size, format)); + DCHECK(viz::ResourceSizes::VerifySizeInBytes<size_t>(size, format)); auto pool_resource = std::make_unique<PoolResource>( next_resource_unique_id_++, size, format, color_space); total_memory_usage_bytes_ += - ResourceUtil::UncheckedSizeInBytes<size_t>(size, format); + viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format); ++total_resource_count_; PoolResource* resource = pool_resource.get(); in_use_resources_[resource->unique_id()] = std::move(pool_resource); in_use_memory_usage_bytes_ += - ResourceUtil::UncheckedSizeInBytes<size_t>(size, format); + viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format); return resource; } @@ -231,8 +232,9 @@ ResourcePool::TryAcquireResourceForPartialRaster( in_use_resources_[resource->unique_id()] = std::move(*iter_resource_to_return); unused_resources_.erase(iter_resource_to_return); - in_use_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( - resource->size(), resource->format()); + in_use_memory_usage_bytes_ += + viz::ResourceSizes::UncheckedSizeInBytes<size_t>(resource->size(), + resource->format()); *total_invalidated_rect = resource->invalidated_rect(); // Clear the invalidated rect and content ID on the resource being retunred. @@ -282,9 +284,9 @@ void ResourcePool::OnResourceReleased(size_t unique_id, void ResourcePool::PrepareForExport(const InUsePoolResource& resource) { // Exactly one of gpu or software backing should exist. DCHECK(resource.resource_->gpu_backing() || - resource.resource_->shared_bitmap()); + resource.resource_->software_backing()); DCHECK(!resource.resource_->gpu_backing() || - !resource.resource_->shared_bitmap()); + !resource.resource_->software_backing()); viz::TransferableResource transferable; if (resource.resource_->gpu_backing()) { transferable = viz::TransferableResource::MakeGLOverlay( @@ -297,9 +299,11 @@ void ResourcePool::PrepareForExport(const InUsePoolResource& resource) { resource.resource_->gpu_backing()->wait_on_fence_required; } else { transferable = viz::TransferableResource::MakeSoftware( - resource.resource_->shared_bitmap()->id(), - resource.resource_->shared_bitmap()->sequence_number(), - resource.resource_->size()); + resource.resource_->software_backing()->shared_bitmap_id, + // Not needed since this software resource's SharedBitmapId was + // notified to the display compositor through the CompositorFrameSink. + /*sequence_number=*/0, resource.resource_->size(), + resource.resource_->format()); } transferable.format = resource.resource_->format(); transferable.buffer_format = viz::BufferFormat(transferable.format); @@ -362,8 +366,9 @@ void ResourcePool::ReleaseResource(InUsePoolResource in_use_resource) { CHECK(it->second.get()); pool_resource->set_last_usage(base::TimeTicks::Now()); - in_use_memory_usage_bytes_ -= ResourceUtil::UncheckedSizeInBytes<size_t>( - pool_resource->size(), pool_resource->format()); + in_use_memory_usage_bytes_ -= + viz::ResourceSizes::UncheckedSizeInBytes<size_t>(pool_resource->size(), + pool_resource->format()); // Save the ResourceId since the |pool_resource| can be deleted in the next // step. @@ -434,7 +439,7 @@ bool ResourcePool::ResourceUsageTooHigh() { } void ResourcePool::DeleteResource(std::unique_ptr<PoolResource> resource) { - size_t resource_bytes = ResourceUtil::UncheckedSizeInBytes<size_t>( + size_t resource_bytes = viz::ResourceSizes::UncheckedSizeInBytes<size_t>( resource->size(), resource->format()); total_memory_usage_bytes_ -= resource_bytes; --total_resource_count_; @@ -569,12 +574,11 @@ void ResourcePool::PoolResource::OnMemoryDump( bool is_free) const { base::UnguessableToken shm_guid; base::trace_event::MemoryAllocatorDumpGuid backing_guid; - if (shared_bitmap_) { - // Software resources are in shared memory but are kept closed to avoid - // holding an fd open. So there is no SharedMemoryHandle to get a guid - // from. - DCHECK(!shared_bitmap_->GetSharedMemoryHandle().IsValid()); - backing_guid = viz::GetSharedBitmapGUIDForTracing(shared_bitmap_->id()); + if (software_backing_) { + // Software resources are allocated in shared memory for use cross-process + // in the display compositor. So we use the guid for the shared memory to + // identify them in tracing in all processes. + shm_guid = software_backing_->SharedMemoryGuid(); } else if (gpu_backing_) { // We prefer the SharedMemoryGuid() if it exists, if the resource is backed // by shared memory. @@ -610,7 +614,7 @@ void ResourcePool::PoolResource::OnMemoryDump( } uint64_t total_bytes = - ResourceUtil::UncheckedSizeInBytesAligned<size_t>(size_, format_); + viz::ResourceSizes::UncheckedSizeInBytesAligned<size_t>(size_, format_); dump->AddScalar(MemoryAllocatorDump::kNameSize, MemoryAllocatorDump::kUnitsBytes, total_bytes); diff --git a/chromium/cc/resources/resource_pool.h b/chromium/cc/resources/resource_pool.h index 659abb85549..b55cf829297 100644 --- a/chromium/cc/resources/resource_pool.h +++ b/chromium/cc/resources/resource_pool.h @@ -14,7 +14,6 @@ #include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/memory/memory_coordinator_client.h" -#include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/trace_event/memory_allocator_dump_guid.h" @@ -51,6 +50,15 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, public: virtual ~GpuBacking() = default; + // Guids for for memory dumps. This guid will be valid once the GpuBacking + // has memory allocated. Called on the compositor thread. + virtual base::trace_event::MemoryAllocatorDumpGuid MemoryDumpGuid( + uint64_t tracing_process_id) = 0; + // Some gpu resources can be shared memory-backed, and this guid should be + // prefered in that case. But if not then this will be empty. Called on the + // compositor thread. + virtual base::UnguessableToken SharedMemoryGuid() = 0; + gpu::Mailbox mailbox; gpu::SyncToken mailbox_sync_token; GLenum texture_target = 0; @@ -61,18 +69,23 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, bool wait_on_fence_required = false; // Set by the ResourcePool when a resource is returned from the display - // compositor. The client of ResourcePool needs to wait on this token, if it - // exists, before using a resource handed out by the ResourcePool. + // compositor, or when the resource texture and mailbox are created for the + // first time, if the resource is shared with another context. The client of + // ResourcePool needs to wait on this token if it exists, before using a + // resource handed out by the ResourcePool. gpu::SyncToken returned_sync_token; + }; - // Guids for for memory dumps. This guid will be valid once the GpuBacking - // has memory allocated. Called on the compositor thread. - virtual base::trace_event::MemoryAllocatorDumpGuid MemoryDumpGuid( - uint64_t tracing_process_id) = 0; - // Some gpu resources can be shared memory-backed, and this guid should be - // prefered in that case. But if not then this will be empty. Called on the - // compositor thread. + // A base class to hold ownership of software backed PoolResources. Allows the + // client to define destruction semantics. + class SoftwareBacking { + public: + virtual ~SoftwareBacking() = default; + + // Return the guid for this resource, based on the shared memory backing it. virtual base::UnguessableToken SharedMemoryGuid() = 0; + + viz::SharedBitmapId shared_bitmap_id; }; // Scoped move-only object returned when getting a resource from the pool. @@ -125,15 +138,13 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, } // Only valid when the ResourcePool is vending software-backed resources. - viz::SharedBitmap* shared_bitmap() const { + SoftwareBacking* software_backing() const { DCHECK(!is_gpu_); - return resource_->shared_bitmap(); + return resource_->software_backing(); } - void set_shared_bitmap( - std::unique_ptr<viz::SharedBitmap> shared_bitmap) const { + void set_software_backing(std::unique_ptr<SoftwareBacking> software) const { DCHECK(!is_gpu_); - DCHECK(!resource_->shared_bitmap()); - resource_->set_shared_bitmap(std::move(shared_bitmap)); + resource_->set_software_backing(std::move(software)); } // Production code should not be built around these ids, but tests use them @@ -248,9 +259,11 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, gpu_backing_ = std::move(gpu); } - viz::SharedBitmap* shared_bitmap() const { return shared_bitmap_.get(); } - void set_shared_bitmap(std::unique_ptr<viz::SharedBitmap> shared_bitmap) { - shared_bitmap_ = std::move(shared_bitmap); + SoftwareBacking* software_backing() const { + return software_backing_.get(); + } + void set_software_backing(std::unique_ptr<SoftwareBacking> software) { + software_backing_ = std::move(software); } uint64_t content_id() const { return content_id_; } @@ -297,7 +310,7 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider, // The backing for software resources. Initially null for resources given // out by ResourcePool, to be filled in by the client. Is destroyed on the // compositor thread. - std::unique_ptr<viz::SharedBitmap> shared_bitmap_; + std::unique_ptr<SoftwareBacking> software_backing_; }; // Callback from the ResourceProvider to notify when an exported PoolResource diff --git a/chromium/cc/resources/resource_pool_unittest.cc b/chromium/cc/resources/resource_pool_unittest.cc index 0aa7a5ce8d2..cf233dc40b4 100644 --- a/chromium/cc/resources/resource_pool_unittest.cc +++ b/chromium/cc/resources/resource_pool_unittest.cc @@ -9,8 +9,8 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "cc/resources/resource_util.h" #include "cc/test/fake_resource_provider.h" +#include "components/viz/common/resources/resource_sizes.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" @@ -82,7 +82,7 @@ TEST_F(ResourcePoolTest, AccountingSingleResource) { viz::ResourceFormat format = viz::RGBA_8888; gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB(); size_t resource_bytes = - ResourceUtil::UncheckedSizeInBytes<size_t>(size, format); + viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format); ResourcePool::InUsePoolResource resource = resource_pool_->AcquireResource(size, format, color_space); diff --git a/chromium/cc/resources/resource_provider.cc b/chromium/cc/resources/resource_provider.cc index e6eb1222e65..fcc3b6158e7 100644 --- a/chromium/cc/resources/resource_provider.cc +++ b/chromium/cc/resources/resource_provider.cc @@ -23,8 +23,8 @@ #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" -#include "cc/resources/resource_util.h" #include "components/viz/common/gpu/context_provider.h" +#include "components/viz/common/resources/resource_sizes.h" #include "components/viz/common/resources/returned_resource.h" #include "components/viz/common/resources/transferable_resource.h" #include "gpu/command_buffer/client/context_support.h" @@ -149,21 +149,31 @@ viz::internal::Resource* ResourceProvider::InsertResource( viz::internal::Resource* ResourceProvider::GetResource(viz::ResourceId id) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - // TODO(ericrk): Changing the DCHECKs in this function to CHECKs to debug - // https://crbug.com/811858. - CHECK(id); + DCHECK(id); ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); + DCHECK(it != resources_.end()); + return &it->second; +} + +viz::internal::Resource* ResourceProvider::TryGetResource(viz::ResourceId id) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (!id) + return nullptr; + ResourceMap::iterator it = resources_.find(id); + if (it == resources_.end()) + return nullptr; return &it->second; } void ResourceProvider::PopulateSkBitmapWithResource( SkBitmap* sk_bitmap, const viz::internal::Resource* resource) { - DCHECK_EQ(viz::RGBA_8888, resource->format); + DCHECK(IsBitmapFormatSupported(resource->format)); SkImageInfo info = SkImageInfo::MakeN32Premul(resource->size.width(), resource->size.height()); - sk_bitmap->installPixels(info, resource->pixels, info.minRowBytes()); + bool pixels_installed = + sk_bitmap->installPixels(info, resource->pixels, info.minRowBytes()); + DCHECK(pixels_installed); } void ResourceProvider::WaitSyncTokenInternal( @@ -207,7 +217,7 @@ bool ResourceProvider::OnMemoryDump( backing_memory_allocated = !!resource.gl_id; break; case viz::ResourceType::kBitmap: - backing_memory_allocated = resource.has_shared_bitmap_id; + backing_memory_allocated = !!resource.shared_bitmap; break; } @@ -224,11 +234,16 @@ bool ResourceProvider::OnMemoryDump( base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); - uint64_t total_bytes = ResourceUtil::UncheckedSizeInBytesAligned<size_t>( - resource.size, resource.format); - dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, - base::trace_event::MemoryAllocatorDump::kUnitsBytes, - static_cast<uint64_t>(total_bytes)); + // Texture resources may not come with a size, in which case don't report + // one. + if (!resource.size.IsEmpty()) { + uint64_t total_bytes = + viz::ResourceSizes::UncheckedSizeInBytesAligned<size_t>( + resource.size, resource.format); + dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, + base::trace_event::MemoryAllocatorDump::kUnitsBytes, + static_cast<uint64_t>(total_bytes)); + } // Resources may be shared across processes and require a shared GUID to // prevent double counting the memory. @@ -236,10 +251,16 @@ bool ResourceProvider::OnMemoryDump( base::UnguessableToken shared_memory_guid; switch (resource.type) { case viz::ResourceType::kGpuMemoryBuffer: - guid = - resource.gpu_memory_buffer->GetGUIDForTracing(tracing_process_id); + // GpuMemoryBuffers may be backed by shared memory, and in that case we + // use the guid from there to attribute for the global shared memory + // dumps. Otherwise, they may be backed by native structures, and we + // fall back to that with GetGUIDForTracing. shared_memory_guid = resource.gpu_memory_buffer->GetHandle().handle.GetGUID(); + if (shared_memory_guid.is_empty()) { + guid = + resource.gpu_memory_buffer->GetGUIDForTracing(tracing_process_id); + } break; case viz::ResourceType::kTexture: DCHECK(resource.gl_id); @@ -249,24 +270,28 @@ bool ResourceProvider::OnMemoryDump( resource.gl_id); break; case viz::ResourceType::kBitmap: - DCHECK(resource.has_shared_bitmap_id); - guid = viz::GetSharedBitmapGUIDForTracing(resource.shared_bitmap_id); - if (resource.shared_bitmap) { - shared_memory_guid = - resource.shared_bitmap->GetSharedMemoryHandle().GetGUID(); - } + // If the resource comes from out of process, it will have this id, + // which we prefer. Otherwise, we fall back to the SharedBitmapGUID + // which can be generated for in-process bitmaps. + shared_memory_guid = resource.shared_bitmap->GetCrossProcessGUID(); + if (shared_memory_guid.is_empty()) + guid = viz::GetSharedBitmapGUIDForTracing(resource.shared_bitmap_id); break; } - DCHECK(!guid.empty()); + DCHECK(!shared_memory_guid.is_empty() || !guid.empty()); - const int kImportance = 2; + const int kImportanceForInteral = 2; + const int kImportanceForExternal = 1; + int importance = resource.origin == viz::internal::Resource::INTERNAL + ? kImportanceForInteral + : kImportanceForExternal; if (!shared_memory_guid.is_empty()) { pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid, - kImportance); + importance); } else { pmd->CreateSharedGlobalAllocatorDump(guid); - pmd->AddOwnershipEdge(dump->guid(), guid, kImportance); + pmd->AddOwnershipEdge(dump->guid(), guid, importance); } } diff --git a/chromium/cc/resources/resource_provider.h b/chromium/cc/resources/resource_provider.h index fa07aae6595..31e46d23620 100644 --- a/chromium/cc/resources/resource_provider.h +++ b/chromium/cc/resources/resource_provider.h @@ -92,6 +92,11 @@ class CC_EXPORT ResourceProvider viz::internal::Resource resource); viz::internal::Resource* GetResource(viz::ResourceId id); + // TODO(ericrk): TryGetResource is part of a temporary workaround for cases + // where resources which should be available are missing. This version may + // return nullptr if a resource is not found. https://crbug.com/811858 + viz::internal::Resource* TryGetResource(viz::ResourceId id); + void PopulateSkBitmapWithResource(SkBitmap* sk_bitmap, const viz::internal::Resource* resource); diff --git a/chromium/cc/resources/resource_provider_unittest.cc b/chromium/cc/resources/resource_provider_unittest.cc index 90cef4cadad..b569bcdd8a7 100644 --- a/chromium/cc/resources/resource_provider_unittest.cc +++ b/chromium/cc/resources/resource_provider_unittest.cc @@ -81,9 +81,10 @@ static void ReleaseSharedBitmapCallback( static std::unique_ptr<viz::SharedBitmap> CreateAndFillSharedBitmap( viz::SharedBitmapManager* manager, const gfx::Size& size, + viz::ResourceFormat format, uint32_t value) { std::unique_ptr<viz::SharedBitmap> shared_bitmap = - manager->AllocateSharedBitmap(size); + manager->AllocateSharedBitmap(size, format); CHECK(shared_bitmap); uint32_t* pixels = reinterpret_cast<uint32_t*>(shared_bitmap->pixels()); CHECK(pixels); @@ -469,7 +470,8 @@ class ResourceProviderTest : public testing::TestWithParam<bool> { viz::ResourceId CreateChildMailbox(gpu::SyncToken* release_sync_token, bool* lost_resource, bool* release_called, - gpu::SyncToken* sync_token) { + gpu::SyncToken* sync_token, + viz::ResourceFormat format) { if (use_gpu()) { unsigned texture = child_context_->createTexture(); gpu::Mailbox gpu_mailbox; @@ -483,14 +485,16 @@ class ResourceProviderTest : public testing::TestWithParam<bool> { viz::SingleReleaseCallback::Create(base::Bind( ReleaseSharedBitmapCallback, base::Passed(&shared_bitmap), release_called, release_sync_token, lost_resource)); - return child_resource_provider_->ImportResource( - viz::TransferableResource::MakeGL(gpu_mailbox, GL_LINEAR, - GL_TEXTURE_2D, *sync_token), - std::move(callback)); + viz::TransferableResource gl_resource = viz::TransferableResource::MakeGL( + gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, *sync_token); + gl_resource.format = format; + return child_resource_provider_->ImportResource(gl_resource, + std::move(callback)); } else { gfx::Size size(64, 64); std::unique_ptr<viz::SharedBitmap> shared_bitmap( - CreateAndFillSharedBitmap(shared_bitmap_manager_.get(), size, 0)); + CreateAndFillSharedBitmap(shared_bitmap_manager_.get(), size, format, + 0)); viz::SharedBitmap* shared_bitmap_ptr = shared_bitmap.get(); std::unique_ptr<viz::SingleReleaseCallback> callback = @@ -500,7 +504,7 @@ class ResourceProviderTest : public testing::TestWithParam<bool> { return child_resource_provider_->ImportResource( viz::TransferableResource::MakeSoftware( shared_bitmap_ptr->id(), shared_bitmap_ptr->sequence_number(), - size), + size, format), std::move(callback)); } } @@ -552,8 +556,8 @@ TEST_P(ResourceProviderTest, Basic) { viz::ResourceId id; if (!use_gpu()) { - id = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + id = child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace(), + format); } else { id = child_resource_provider_->CreateGpuTextureResource( size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace()); @@ -606,10 +610,10 @@ TEST_P(ResourceProviderTest, SimpleUpload) { size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace()); } else { - id1 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); - id2 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + id1 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); + id2 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); } uint8_t image[16] = {0}; @@ -1415,13 +1419,13 @@ TEST_P(ResourceProviderTest, TransferSoftwareResources) { size_t pixel_size = TextureSizeBytes(size, format); ASSERT_EQ(4U, pixel_size); - viz::ResourceId id1 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + viz::ResourceId id1 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); uint8_t data1[4] = { 1, 2, 3, 4 }; child_resource_provider_->CopyToResource(id1, data1, size); - viz::ResourceId id2 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + viz::ResourceId id2 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); uint8_t data2[4] = { 5, 5, 5, 5 }; child_resource_provider_->CopyToResource(id2, data2, size); @@ -1641,8 +1645,8 @@ TEST_P(ResourceProviderTest, TransferInvalidSoftware) { size_t pixel_size = TextureSizeBytes(size, format); ASSERT_EQ(4U, pixel_size); - viz::ResourceId id1 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + viz::ResourceId id1 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); uint8_t data1[4] = { 1, 2, 3, 4 }; child_resource_provider_->CopyToResource(id1, data1, size); @@ -1697,10 +1701,10 @@ TEST_P(ResourceProviderTest, DeleteExportedResources) { id2 = child_resource_provider_->CreateGpuTextureResource( size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace()); } else { - id1 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); - id2 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + id1 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); + id2 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); } uint8_t data1[4] = { 1, 2, 3, 4 }; @@ -1763,10 +1767,10 @@ TEST_P(ResourceProviderTest, DestroyChildWithExportedResources) { id2 = child_resource_provider_->CreateGpuTextureResource( size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace()); } else { - id1 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); - id2 = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + id1 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); + id2 = child_resource_provider_->CreateBitmapResource( + size, gfx::ColorSpace(), format); } uint8_t data1[4] = {1, 2, 3, 4}; @@ -1820,8 +1824,8 @@ TEST_P(ResourceProviderTest, DeleteTransferredResources) { id = child_resource_provider_->CreateGpuTextureResource( size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace()); } else { - id = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + id = child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace(), + format); } uint8_t data[4] = { 1, 2, 3, 4 }; @@ -2149,8 +2153,8 @@ TEST_P(ResourceProviderTest, LostResourceInParent) { id = child_resource_provider_->CreateGpuTextureResource( size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace()); } else { - id = - child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace()); + id = child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace(), + format); } child_resource_provider_->AllocateForTesting(id); @@ -2208,8 +2212,9 @@ TEST_P(ResourceProviderTest, LostMailboxInParent) { bool lost_resource = false; bool release_called = false; gpu::SyncToken sync_token; - viz::ResourceId resource = CreateChildMailbox( - &release_sync_token, &lost_resource, &release_called, &sync_token); + viz::ResourceId resource = + CreateChildMailbox(&release_sync_token, &lost_resource, &release_called, + &sync_token, viz::RGBA_8888); std::vector<viz::ReturnedResource> returned_to_child; int child_id = @@ -2283,8 +2288,9 @@ TEST_P(ResourceProviderTest, Shutdown) { bool lost_resource = false; bool release_called = false; gpu::SyncToken sync_token; - viz::ResourceId id = CreateChildMailbox(&release_sync_token, &lost_resource, - &release_called, &sync_token); + viz::ResourceId id = + CreateChildMailbox(&release_sync_token, &lost_resource, &release_called, + &sync_token, viz::RGBA_8888); if (i == kShutdownAfterExport || i == kShutdownAfterExportAndReturn || i == kShutdownAfterExportAndReturnWithLostResource || @@ -2566,9 +2572,10 @@ TEST_P(ResourceProviderTest, ImportedResource_SharedMemory) { return; gfx::Size size(64, 64); + viz::ResourceFormat format = viz::RGBA_8888; const uint32_t kBadBeef = 0xbadbeef; - std::unique_ptr<viz::SharedBitmap> shared_bitmap( - CreateAndFillSharedBitmap(shared_bitmap_manager_.get(), size, kBadBeef)); + std::unique_ptr<viz::SharedBitmap> shared_bitmap(CreateAndFillSharedBitmap( + shared_bitmap_manager_.get(), size, format, kBadBeef)); auto resource_provider(std::make_unique<DisplayResourceProvider>( nullptr, shared_bitmap_manager_.get())); @@ -2583,7 +2590,7 @@ TEST_P(ResourceProviderTest, ImportedResource_SharedMemory) { viz::SingleReleaseCallback::Create( base::Bind(&ReleaseCallback, &release_sync_token, &lost_resource)); auto resource = viz::TransferableResource::MakeSoftware( - shared_bitmap->id(), shared_bitmap->sequence_number(), size); + shared_bitmap->id(), shared_bitmap->sequence_number(), size, format); viz::ResourceId resource_id = child_resource_provider->ImportResource(resource, std::move(callback)); diff --git a/chromium/cc/resources/resource_util.h b/chromium/cc/resources/resource_util.h deleted file mode 100644 index e263aa67e70..00000000000 --- a/chromium/cc/resources/resource_util.h +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_RESOURCES_RESOURCE_UTIL_H_ -#define CC_RESOURCES_RESOURCE_UTIL_H_ - -#include <stddef.h> - -#include <limits> - -#include "base/macros.h" -#include "base/numerics/safe_math.h" -#include "cc/base/math_util.h" -#include "cc/cc_export.h" -#include "components/viz/common/resources/resource_format.h" -#include "components/viz/common/resources/resource_format_utils.h" -#include "ui/gfx/geometry/size.h" - -namespace cc { - -class CC_EXPORT ResourceUtil { - public: - // Returns true if the width is valid and fits in bytes, false otherwise. - template <typename T> - static bool VerifyWidthInBytes(int width, viz::ResourceFormat format); - // Returns true if the size is valid and fits in bytes, false otherwise. - template <typename T> - static bool VerifySizeInBytes(const gfx::Size& size, - viz::ResourceFormat format); - - // Dies with a CRASH() if the width can not be represented as a positive - // number of bytes. - template <typename T> - static T CheckedWidthInBytes(int width, viz::ResourceFormat format); - // Dies with a CRASH() if the size can not be represented as a positive - // number of bytes. - template <typename T> - static T CheckedSizeInBytes(const gfx::Size& size, - viz::ResourceFormat format); - - // Returns the width in bytes but may overflow or return 0. Only do this for - // computing widths for sizes that have already been checked. - template <typename T> - static T UncheckedWidthInBytes(int width, viz::ResourceFormat format); - // Returns the size in bytes but may overflow or return 0. Only do this for - // sizes that have already been checked. - template <typename T> - static T UncheckedSizeInBytes(const gfx::Size& size, - viz::ResourceFormat format); - // Returns the width in bytes aligned but may overflow or return 0. Only do - // this for computing widths for sizes that have already been checked. - template <typename T> - static T UncheckedWidthInBytesAligned(int width, viz::ResourceFormat format); - // Returns the size in bytes aligned but may overflow or return 0. Only do - // this for sizes that have already been checked. - template <typename T> - static T UncheckedSizeInBytesAligned(const gfx::Size& size, - viz::ResourceFormat format); - - private: - template <typename T> - static inline void VerifyType(); - - template <typename T> - static bool VerifyFitsInBytesInternal(int width, - int height, - viz::ResourceFormat format, - bool verify_size, - bool aligned); - - template <typename T> - static T BytesInternal(int width, - int height, - viz::ResourceFormat format, - bool verify_size, - bool aligned); - - DISALLOW_COPY_AND_ASSIGN(ResourceUtil); -}; - -template <typename T> -bool ResourceUtil::VerifyWidthInBytes(int width, viz::ResourceFormat format) { - VerifyType<T>(); - return VerifyFitsInBytesInternal<T>(width, 0, format, false, false); -} - -template <typename T> -bool ResourceUtil::VerifySizeInBytes(const gfx::Size& size, - viz::ResourceFormat format) { - VerifyType<T>(); - return VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, - false); -} - -template <typename T> -T ResourceUtil::CheckedWidthInBytes(int width, viz::ResourceFormat format) { - VerifyType<T>(); - DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, false)); - base::CheckedNumeric<T> checked_value = BitsPerPixel(format); - checked_value *= width; - checked_value = MathUtil::CheckedRoundUp<T>(checked_value.ValueOrDie(), 8); - checked_value /= 8; - return checked_value.ValueOrDie(); -} - -template <typename T> -T ResourceUtil::CheckedSizeInBytes(const gfx::Size& size, - viz::ResourceFormat format) { - VerifyType<T>(); - DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, - false)); - base::CheckedNumeric<T> checked_value = BitsPerPixel(format); - checked_value *= size.width(); - checked_value = MathUtil::CheckedRoundUp<T>(checked_value.ValueOrDie(), 8); - checked_value /= 8; - checked_value *= size.height(); - return checked_value.ValueOrDie(); -} - -template <typename T> -T ResourceUtil::UncheckedWidthInBytes(int width, viz::ResourceFormat format) { - VerifyType<T>(); - DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, false)); - return BytesInternal<T>(width, 0, format, false, false); -} - -template <typename T> -T ResourceUtil::UncheckedSizeInBytes(const gfx::Size& size, - viz::ResourceFormat format) { - VerifyType<T>(); - DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, - false)); - return BytesInternal<T>(size.width(), size.height(), format, true, false); -} - -template <typename T> -T ResourceUtil::UncheckedWidthInBytesAligned(int width, - viz::ResourceFormat format) { - VerifyType<T>(); - DCHECK(VerifyFitsInBytesInternal<T>(width, 0, format, false, true)); - return BytesInternal<T>(width, 0, format, false, true); -} - -template <typename T> -T ResourceUtil::UncheckedSizeInBytesAligned(const gfx::Size& size, - viz::ResourceFormat format) { - VerifyType<T>(); - DCHECK(VerifyFitsInBytesInternal<T>(size.width(), size.height(), format, true, - true)); - return BytesInternal<T>(size.width(), size.height(), format, true, true); -} - -template <typename T> -void ResourceUtil::VerifyType() { - static_assert( - std::numeric_limits<T>::is_integer && !std::is_same<T, bool>::value, - "T must be non-bool integer type. Preferred type is size_t."); -} - -template <typename T> -bool ResourceUtil::VerifyFitsInBytesInternal(int width, - int height, - viz::ResourceFormat format, - bool verify_size, - bool aligned) { - base::CheckedNumeric<T> checked_value = BitsPerPixel(format); - checked_value *= width; - if (!checked_value.IsValid()) - return false; - - // Roundup bits to byte (8 bits) boundary. If width is 3 and BitsPerPixel is - // 4, then it should return 16, so that row pixels do not get truncated. - checked_value = MathUtil::UncheckedRoundUp<T>(checked_value.ValueOrDie(), 8); - - // If aligned is true, bytes are aligned on 4-bytes boundaries for upload - // performance, assuming that GL_PACK_ALIGNMENT or GL_UNPACK_ALIGNMENT have - // not changed from default. - if (aligned) { - checked_value /= 8; - if (!checked_value.IsValid()) - return false; - checked_value = - MathUtil::UncheckedRoundUp<T>(checked_value.ValueOrDie(), 4); - checked_value *= 8; - } - - if (verify_size) - checked_value *= height; - if (!checked_value.IsValid()) - return false; - T value = checked_value.ValueOrDie(); - if ((value % 8) != 0) - return false; - return true; -} - -template <typename T> -T ResourceUtil::BytesInternal(int width, - int height, - viz::ResourceFormat format, - bool verify_size, - bool aligned) { - T bytes = BitsPerPixel(format); - bytes *= width; - bytes = MathUtil::UncheckedRoundUp<T>(bytes, 8); - bytes /= 8; - if (aligned) - bytes = MathUtil::UncheckedRoundUp<T>(bytes, 4); - if (verify_size) - bytes *= height; - - return bytes; -} - -} // namespace cc - -#endif // CC_RESOURCES_RESOURCE_UTIL_H_ diff --git a/chromium/cc/resources/resource_util_unittest.cc b/chromium/cc/resources/resource_util_unittest.cc index 8bffa6b192e..4680c0ca312 100644 --- a/chromium/cc/resources/resource_util_unittest.cc +++ b/chromium/cc/resources/resource_util_unittest.cc @@ -5,7 +5,7 @@ #include <stddef.h> #include "base/logging.h" -#include "cc/resources/resource_util.h" +#include "components/viz/common/resources/resource_sizes.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -24,14 +24,14 @@ class ResourceUtilTest : public testing::Test { public: void TestVerifyWidthInBytes(int width, const TestFormat* test_formats) { for (int i = 0; i < kTestFormats; ++i) { - EXPECT_TRUE(ResourceUtil::VerifyWidthInBytes<size_t>( + EXPECT_TRUE(viz::ResourceSizes::VerifyWidthInBytes<size_t>( width, test_formats[i].format)); } } void TestCheckedWidthInBytes(int width, const TestFormat* test_formats) { for (int i = 0; i < kTestFormats; ++i) { - size_t bytes = ResourceUtil::CheckedWidthInBytes<size_t>( + size_t bytes = viz::ResourceSizes::CheckedWidthInBytes<size_t>( width, test_formats[i].format); EXPECT_EQ(bytes, test_formats[i].expected_bytes); } @@ -39,7 +39,7 @@ class ResourceUtilTest : public testing::Test { void TestUncheckedWidthInBytes(int width, const TestFormat* test_formats) { for (int i = 0; i < kTestFormats; ++i) { - size_t bytes = ResourceUtil::UncheckedWidthInBytes<size_t>( + size_t bytes = viz::ResourceSizes::UncheckedWidthInBytes<size_t>( width, test_formats[i].format); EXPECT_EQ(bytes, test_formats[i].expected_bytes); } @@ -48,7 +48,7 @@ class ResourceUtilTest : public testing::Test { void TestUncheckedWidthInBytesAligned(int width, const TestFormat* test_formats) { for (int i = 0; i < kTestFormats; ++i) { - size_t bytes = ResourceUtil::UncheckedWidthInBytesAligned<size_t>( + size_t bytes = viz::ResourceSizes::UncheckedWidthInBytesAligned<size_t>( width, test_formats[i].format); EXPECT_EQ(bytes, test_formats[i].expected_bytes_aligned); } @@ -57,7 +57,7 @@ class ResourceUtilTest : public testing::Test { void TestVerifySizeInBytes(const gfx::Size& size, const TestFormat* test_formats) { for (int i = 0; i < kTestFormats; ++i) { - EXPECT_TRUE(ResourceUtil::VerifySizeInBytes<size_t>( + EXPECT_TRUE(viz::ResourceSizes::VerifySizeInBytes<size_t>( size, test_formats[i].format)); } } @@ -65,7 +65,7 @@ class ResourceUtilTest : public testing::Test { void TestCheckedSizeInBytes(const gfx::Size& size, const TestFormat* test_formats) { for (int i = 0; i < kTestFormats; ++i) { - size_t bytes = ResourceUtil::CheckedSizeInBytes<size_t>( + size_t bytes = viz::ResourceSizes::CheckedSizeInBytes<size_t>( size, test_formats[i].format); EXPECT_EQ(bytes, test_formats[i].expected_bytes); } @@ -74,7 +74,7 @@ class ResourceUtilTest : public testing::Test { void TestUncheckedSizeInBytes(const gfx::Size& size, const TestFormat* test_formats) { for (int i = 0; i < kTestFormats; ++i) { - size_t bytes = ResourceUtil::UncheckedSizeInBytes<size_t>( + size_t bytes = viz::ResourceSizes::UncheckedSizeInBytes<size_t>( size, test_formats[i].format); EXPECT_EQ(bytes, test_formats[i].expected_bytes); } @@ -83,7 +83,7 @@ class ResourceUtilTest : public testing::Test { void TestUncheckedSizeInBytesAligned(const gfx::Size& size, const TestFormat* test_formats) { for (int i = 0; i < kTestFormats; ++i) { - size_t bytes = ResourceUtil::UncheckedSizeInBytesAligned<size_t>( + size_t bytes = viz::ResourceSizes::UncheckedSizeInBytesAligned<size_t>( size, test_formats[i].format); EXPECT_EQ(bytes, test_formats[i].expected_bytes_aligned); } @@ -153,18 +153,18 @@ TEST_F(ResourceUtilTest, SizeInBytes) { TEST_F(ResourceUtilTest, WidthInBytesOverflow) { int width = 10; // 10 * 16 = 160 bits, overflows in char, but fits in unsigned char. - EXPECT_FALSE( - ResourceUtil::VerifyWidthInBytes<signed char>(width, viz::RGBA_4444)); - EXPECT_TRUE( - ResourceUtil::VerifyWidthInBytes<unsigned char>(width, viz::RGBA_4444)); + EXPECT_FALSE(viz::ResourceSizes::VerifyWidthInBytes<signed char>( + width, viz::RGBA_4444)); + EXPECT_TRUE(viz::ResourceSizes::VerifyWidthInBytes<unsigned char>( + width, viz::RGBA_4444)); } TEST_F(ResourceUtilTest, SizeInBytesOverflow) { gfx::Size size(10, 10); // 10 * 16 * 10 = 1600 bits, overflows in char, but fits in int. EXPECT_FALSE( - ResourceUtil::VerifySizeInBytes<signed char>(size, viz::RGBA_4444)); - EXPECT_TRUE(ResourceUtil::VerifySizeInBytes<int>(size, viz::RGBA_4444)); + viz::ResourceSizes::VerifySizeInBytes<signed char>(size, viz::RGBA_4444)); + EXPECT_TRUE(viz::ResourceSizes::VerifySizeInBytes<int>(size, viz::RGBA_4444)); } } // namespace diff --git a/chromium/cc/resources/shared_bitmap_id_registrar.cc b/chromium/cc/resources/shared_bitmap_id_registrar.cc new file mode 100644 index 00000000000..8ab38867b4a --- /dev/null +++ b/chromium/cc/resources/shared_bitmap_id_registrar.cc @@ -0,0 +1,35 @@ +// 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/resources/shared_bitmap_id_registrar.h" + +#include "cc/layers/texture_layer.h" + +namespace cc { + +SharedBitmapIdRegistration::SharedBitmapIdRegistration() = default; + +SharedBitmapIdRegistration::SharedBitmapIdRegistration( + base::WeakPtr<TextureLayer> layer_ptr, + const viz::SharedBitmapId& id) + : layer_ptr_(std::move(layer_ptr)), id_(id) {} + +SharedBitmapIdRegistration::~SharedBitmapIdRegistration() { + if (layer_ptr_) + layer_ptr_->UnregisterSharedBitmapId(id_); +} + +SharedBitmapIdRegistration::SharedBitmapIdRegistration( + SharedBitmapIdRegistration&&) = default; + +SharedBitmapIdRegistration& SharedBitmapIdRegistration::operator=( + SharedBitmapIdRegistration&& other) { + if (layer_ptr_) + layer_ptr_->UnregisterSharedBitmapId(id_); + layer_ptr_ = std::move(other.layer_ptr_); + id_ = std::move(other.id_); + return *this; +} + +} // namespace cc diff --git a/chromium/cc/resources/shared_bitmap_id_registrar.h b/chromium/cc/resources/shared_bitmap_id_registrar.h new file mode 100644 index 00000000000..8586a2408e7 --- /dev/null +++ b/chromium/cc/resources/shared_bitmap_id_registrar.h @@ -0,0 +1,72 @@ +// 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_RESOURCES_SHARED_BITMAP_ID_REGISTRAR_H_ +#define CC_RESOURCES_SHARED_BITMAP_ID_REGISTRAR_H_ + +#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" + +namespace cc { +class CrossThreadSharedBitmap; +class SharedBitmapIdRegistration; +class TextureLayer; + +// An interface exposed to clients of TextureLayer for registering +// SharedBitmapIds that they will be using in viz::TransferableResources given +// to the TextureLayer. SharedBitmapId-SharedMemory pairs registered as such are +// then given to the display compositor, and the mapping between the pair is +// kept valid while the returned SharedBitmapIdRegistration is kept alive. +// +// These mappings are per-layer-tree. So if a client has multiple TextureLayers +// in the same tree, and wants to use the SharedBitmapId in more than one of +// them over time, it still only should register with a single TextureLayer. But +// if the TextureLayer is removed from the tree, they would need to be +// registered with another TextureLayer that is in each tree where they are +// being used. +class CC_EXPORT SharedBitmapIdRegistrar { + public: + virtual ~SharedBitmapIdRegistrar() = default; + virtual SharedBitmapIdRegistration RegisterSharedBitmapId( + const viz::SharedBitmapId& id, + scoped_refptr<CrossThreadSharedBitmap> bitmap) = 0; +}; + +// A scoped object that maintains a mapping of SharedBitmapId to SharedMemory +// that was registered through SharedBitmapIdRegistrar for the display +// compositor. Keep this object alive while the SharedBitmapId may be used +// in viz::TransferableResources given to the TextureLayer. Typically that means +// as long as the client keeps the SharedMemory alive with a reference to the +// CrossThreadSharedBitmap, which it should keep alive at least until the +// TextureLayer calls back to the ReleaseCallback indicating the display +// compositor is no longer using the resource. When this object is destroyed, or +// assigned to, then the mapping registration will be dropped from the display +// compositor, and the SharedBitmapId will no longer be able to be used in the +// TextureLayer. +class CC_EXPORT SharedBitmapIdRegistration { + public: + SharedBitmapIdRegistration(); + ~SharedBitmapIdRegistration(); + + SharedBitmapIdRegistration(SharedBitmapIdRegistration&&); + SharedBitmapIdRegistration& operator=(SharedBitmapIdRegistration&&); + + private: + // Constructed by TextureLayer only, then held by the client as long + // as they wish to + friend TextureLayer; + SharedBitmapIdRegistration(base::WeakPtr<TextureLayer> layer_ptr, + const viz::SharedBitmapId& id); + + base::WeakPtr<TextureLayer> layer_ptr_; + viz::SharedBitmapId id_; + + DISALLOW_COPY_AND_ASSIGN(SharedBitmapIdRegistration); +}; + +} // namespace cc + +#endif // CC_RESOURCES_SHARED_BITMAP_ID_REGISTRAR_H_ diff --git a/chromium/cc/resources/video_resource_updater.cc b/chromium/cc/resources/video_resource_updater.cc index dcf6ea012ca..6b3b19215d7 100644 --- a/chromium/cc/resources/video_resource_updater.cc +++ b/chromium/cc/resources/video_resource_updater.cc @@ -8,20 +8,31 @@ #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 "cc/resources/resource_util.h" +#include "cc/trees/layer_tree_frame_sink.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 "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" @@ -31,12 +42,16 @@ #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/trace_util.h" namespace cc { - namespace { -VideoFrameExternalResources::ResourceType ExternalResourceTypeForHardwarePlanes( +// 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, @@ -51,21 +66,21 @@ VideoFrameExternalResources::ResourceType ExternalResourceTypeForHardwarePlanes( switch (target) { case GL_TEXTURE_EXTERNAL_OES: if (use_stream_video_draw_quad) - return VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE; + return VideoFrameResourceType::STREAM_TEXTURE; FALLTHROUGH; case GL_TEXTURE_2D: return (format == media::PIXEL_FORMAT_XRGB) - ? VideoFrameExternalResources::RGB_RESOURCE - : VideoFrameExternalResources::RGBA_PREMULTIPLIED_RESOURCE; + ? VideoFrameResourceType::RGB + : VideoFrameResourceType::RGBA_PREMULTIPLIED; case GL_TEXTURE_RECTANGLE_ARB: - return VideoFrameExternalResources::RGB_RESOURCE; + return VideoFrameResourceType::RGB; default: NOTREACHED(); break; } break; case media::PIXEL_FORMAT_I420: - return VideoFrameExternalResources::YUV_RESOURCE; + return VideoFrameResourceType::YUV; case media::PIXEL_FORMAT_NV12: DCHECK(target == GL_TEXTURE_EXTERNAL_OES || target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE_ARB) @@ -73,10 +88,10 @@ VideoFrameExternalResources::ResourceType ExternalResourceTypeForHardwarePlanes( << target; // Single plane textures can be sampled as RGB. if (num_textures > 1) - return VideoFrameExternalResources::YUV_RESOURCE; + return VideoFrameResourceType::YUV; *buffer_format = gfx::BufferFormat::YUV_420_BIPLANAR; - return VideoFrameExternalResources::RGB_RESOURCE; + return VideoFrameResourceType::RGB; case media::PIXEL_FORMAT_YV12: case media::PIXEL_FORMAT_I422: case media::PIXEL_FORMAT_I444: @@ -99,7 +114,7 @@ VideoFrameExternalResources::ResourceType ExternalResourceTypeForHardwarePlanes( case media::PIXEL_FORMAT_UNKNOWN: break; } - return VideoFrameExternalResources::NONE; + return VideoFrameResourceType::NONE; } class SyncTokenClientImpl : public media::VideoFrame::SyncTokenClient { @@ -138,162 +153,11 @@ void GenerateCompositorSyncToken(gpu::gles2::GLES2Interface* gl, gl->GenUnverifiedSyncTokenCHROMIUM(sync_token->GetData()); } -} // namespace - -VideoResourceUpdater::PlaneResource::PlaneResource( - unsigned int resource_id, - const gfx::Size& resource_size, - viz::ResourceFormat resource_format, - gpu::Mailbox mailbox) - : resource_id_(resource_id), - resource_size_(resource_size), - resource_format_(resource_format), - mailbox_(mailbox) {} - -VideoResourceUpdater::PlaneResource::PlaneResource(const PlaneResource& other) = - default; - -bool VideoResourceUpdater::PlaneResource::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; -} - -void VideoResourceUpdater::PlaneResource::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; -} - -VideoFrameExternalResources::VideoFrameExternalResources() = default; -VideoFrameExternalResources::~VideoFrameExternalResources() = default; - -VideoFrameExternalResources::VideoFrameExternalResources( - VideoFrameExternalResources&& other) = default; -VideoFrameExternalResources& VideoFrameExternalResources::operator=( - VideoFrameExternalResources&& other) = default; - -VideoResourceUpdater::VideoResourceUpdater( - viz::ContextProvider* context_provider, - LayerTreeResourceProvider* resource_provider, - bool use_stream_video_draw_quad) - : context_provider_(context_provider), - resource_provider_(resource_provider), - use_stream_video_draw_quad_(use_stream_video_draw_quad), - weak_ptr_factory_(this) {} - -VideoResourceUpdater::~VideoResourceUpdater() { - for (const PlaneResource& plane_resource : all_resources_) - resource_provider_->DeleteResource(plane_resource.resource_id()); -} - -VideoResourceUpdater::ResourceList::iterator -VideoResourceUpdater::RecycleOrAllocateResource( - const gfx::Size& resource_size, - viz::ResourceFormat resource_format, - const gfx::ColorSpace& color_space, - bool software_resource, - int unique_id, - int plane_index) { - ResourceList::iterator recyclable_resource = all_resources_.end(); - for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) { - // 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 && it->Matches(unique_id, plane_index)) { - DCHECK(it->resource_size() == resource_size); - DCHECK(it->resource_format() == resource_format); - DCHECK(it->mailbox().IsZero() == software_resource); - return it; - } - - // 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. - // Resources backed by SharedMemory are not ref-counted, unlike mailboxes, - // so the definition of |in_use| must take this into account. Full - // discussion in codereview.chromium.org/145273021. - const bool in_use = - it->has_refs() || - (software_resource && - resource_provider_->InUseByConsumer(it->resource_id())); - - if (!in_use && it->resource_size() == resource_size && - it->resource_format() == resource_format && - it->mailbox().IsZero() == software_resource) { - recyclable_resource = it; - } - } - - if (recyclable_resource != all_resources_.end()) - return recyclable_resource; - - // There was nothing available to reuse or recycle. Allocate a new resource. - return AllocateResource(resource_size, resource_format, color_space, - software_resource); -} - -VideoResourceUpdater::ResourceList::iterator -VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size, - viz::ResourceFormat format, - const gfx::ColorSpace& color_space, - bool software_resource) { - viz::ResourceId resource_id; - gpu::Mailbox mailbox; - - // TODO(danakj): Abstract out hw/sw resource create/delete from - // ResourceProvider and stop using ResourceProvider in this class. - if (software_resource) { - DCHECK_EQ(format, viz::RGBA_8888); - resource_id = - resource_provider_->CreateBitmapResource(plane_size, color_space); - } else { - DCHECK(context_provider_); - - resource_id = resource_provider_->CreateGpuTextureResource( - plane_size, viz::ResourceTextureHint::kDefault, format, color_space); - - gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); - - gl->GenMailboxCHROMIUM(mailbox.name); - LayerTreeResourceProvider::ScopedWriteLockGL lock(resource_provider_, - resource_id); - gl->ProduceTextureDirectCHROMIUM( - lock.GetTexture(), - mailbox.name); - } - all_resources_.push_front( - PlaneResource(resource_id, plane_size, format, mailbox)); - return all_resources_.begin(); -} - -void VideoResourceUpdater::DeleteResource(ResourceList::iterator resource_it) { - DCHECK(!resource_it->has_refs()); - resource_provider_->DeleteResource(resource_it->resource_id()); - all_resources_.erase(resource_it); -} - -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)); -} - // For frames that we receive in software format, determine the dimensions of // each plane in the frame. -static gfx::Size SoftwarePlaneDimension(media::VideoFrame* input_frame, - bool software_compositor, - size_t plane_index) { +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; @@ -305,283 +169,197 @@ static gfx::Size SoftwarePlaneDimension(media::VideoFrame* input_frame, return gfx::Size(plane_width, plane_height); } -VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( - scoped_refptr<media::VideoFrame> video_frame) { - TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes"); - const media::VideoPixelFormat input_frame_format = video_frame->format(); +} // namespace - size_t bits_per_channel = video_frame->BitDepth(); +VideoFrameExternalResources::VideoFrameExternalResources() = default; +VideoFrameExternalResources::~VideoFrameExternalResources() = default; - // Only YUV and Y16 software video frames are supported. - DCHECK(media::IsYuvPlanar(input_frame_format) || - input_frame_format == media::PIXEL_FORMAT_Y16); +VideoFrameExternalResources::VideoFrameExternalResources( + VideoFrameExternalResources&& other) = default; +VideoFrameExternalResources& VideoFrameExternalResources::operator=( + VideoFrameExternalResources&& other) = default; - const bool software_compositor = context_provider_ == nullptr; +// 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; + } - 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 { - // Can be composited directly from yuv planes. - output_resource_format = - resource_provider_->YuvResourceFormat(bits_per_channel); + // 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; } - // 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; + // 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_; } - size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_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; } - // 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; + 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); +}; - // 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(); +class VideoResourceUpdater::SoftwarePlaneResource + : public VideoResourceUpdater::PlaneResource { + public: + SoftwarePlaneResource(uint32_t plane_resource_id, + const gfx::Size& size, + LayerTreeFrameSink* layer_tree_frame_sink) + : PlaneResource(plane_resource_id, + size, + viz::ResourceFormat::RGBA_8888, + /*is_software=*/true), + layer_tree_frame_sink_(layer_tree_frame_sink), + shared_bitmap_id_(viz::SharedBitmap::GenerateId()) { + DCHECK(layer_tree_frame_sink_); + + // 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); + layer_tree_frame_sink_->DidAllocateSharedBitmap(std::move(handle), + shared_bitmap_id_); } - - // Drop recycled resources that are the wrong format. - for (auto it = all_resources_.begin(); it != all_resources_.end();) { - if (!it->has_refs() && it->resource_format() != output_resource_format) - DeleteResource(it++); - else - ++it; + ~SoftwarePlaneResource() override { + layer_tree_frame_sink_->DidDeleteSharedBitmap(shared_bitmap_id_); } - const int max_resource_size = resource_provider_->max_texture_size(); - std::vector<ResourceList::iterator> plane_resources; - for (size_t i = 0; i < output_plane_count; ++i) { - gfx::Size output_plane_resource_size = - SoftwarePlaneDimension(video_frame.get(), software_compositor, i); - 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. Clean up and return an empty - // external resources. - for (ResourceList::iterator resource_it : plane_resources) - resource_it->remove_ref(); - return VideoFrameExternalResources(); - } - - ResourceList::iterator resource_it = RecycleOrAllocateResource( - output_plane_resource_size, output_resource_format, output_color_space, - software_compositor, video_frame->unique_id(), i); - - resource_it->add_ref(); - plane_resources.push_back(resource_it); + const viz::SharedBitmapId& shared_bitmap_id() const { + return shared_bitmap_id_; } + void* pixels() { return shared_memory_->memory(); } - 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); - DCHECK(!software_compositor || - plane_resource.resource_id() > viz::kInvalidResourceId); - DCHECK_EQ(software_compositor, plane_resource.mailbox().IsZero()); - - 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_.reset(new media::PaintCanvasVideoRenderer); - - LayerTreeResourceProvider::ScopedWriteLockSoftware lock( - resource_provider_, plane_resource.resource_id()); - SkiaPaintCanvas canvas(lock.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 { - size_t bytes_per_row = ResourceUtil::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) - upload_pixels_.resize(needed_size); - - media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels( - video_frame.get(), &upload_pixels_[0], bytes_per_row); - - resource_provider_->CopyToResource(plane_resource.resource_id(), - &upload_pixels_[0], - plane_resource.resource_size()); - } - plane_resource.SetUniqueId(video_frame->unique_id(), 0); - } + // Returns a memory dump GUID consistent across processes. + base::UnguessableToken GetSharedMemoryGuid() const { + return shared_memory_->mapped_id(); + } - if (software_compositor) { - external_resources.software_resource = plane_resource.resource_id(); - external_resources.software_release_callback = - base::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(), - plane_resource.resource_id()); - external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; - } else { - gpu::SyncToken sync_token; - GenerateCompositorSyncToken(context_provider_->ContextGL(), &sync_token); + private: + LayerTreeFrameSink* const layer_tree_frame_sink_; + const viz::SharedBitmapId shared_bitmap_id_; + std::unique_ptr<base::SharedMemory> shared_memory_; - GLuint target = resource_provider_->GetResourceTextureTarget( - plane_resource.resource_id()); - auto transfer_resource = viz::TransferableResource::MakeGL( - plane_resource.mailbox(), GL_LINEAR, target, sync_token); - transfer_resource.color_space = output_color_space; + DISALLOW_COPY_AND_ASSIGN(SoftwarePlaneResource); +}; - external_resources.resources.push_back(std::move(transfer_resource)); - external_resources.release_callbacks.push_back( - base::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(), - plane_resource.resource_id())); - external_resources.type = VideoFrameExternalResources::RGBA_RESOURCE; - } - return external_resources; +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); } - - const viz::ResourceFormat yuv_resource_format = - resource_provider_->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; + ~HardwarePlaneResource() override { + context_provider_->ContextGL()->DeleteTextures(1, &allocation_.texture_id); } - // We need to transfer data from |video_frame| to the plane resources. - for (size_t i = 0; i < plane_resources.size(); ++i) { - PlaneResource& plane_resource = *plane_resources[i]; - // 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 = ResourceUtil::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)); + 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; } - // 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) - upload_pixels_.resize(needed_size); + private: + viz::ContextProvider* const context_provider_; + const gpu::Mailbox mailbox_; + const viz::TextureAllocation allocation_; - 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()); - } + DISALLOW_COPY_AND_ASSIGN(HardwarePlaneResource); +}; - pixels = &upload_pixels_[0]; - } +VideoResourceUpdater::SoftwarePlaneResource* +VideoResourceUpdater::PlaneResource::AsSoftware() { + DCHECK(is_software_); + return static_cast<SoftwarePlaneResource*>(this); +} - resource_provider_->CopyToResource(plane_resource.resource_id(), pixels, - resource_size_pixels); - plane_resource.SetUniqueId(video_frame->unique_id(), i); - } +VideoResourceUpdater::HardwarePlaneResource* +VideoResourceUpdater::PlaneResource::AsHardware() { + DCHECK(!is_software_); + return static_cast<HardwarePlaneResource*>(this); +} - // Set the sync token otherwise resource is assumed to be synchronized. - gpu::SyncToken sync_token; - GenerateCompositorSyncToken(context_provider_->ContextGL(), &sync_token); +VideoResourceUpdater::VideoResourceUpdater( + viz::ContextProvider* context_provider, + LayerTreeFrameSink* layer_tree_frame_sink, + LayerTreeResourceProvider* resource_provider, + bool use_stream_video_draw_quad, + bool use_gpu_memory_buffer_resources) + : context_provider_(context_provider), + layer_tree_frame_sink_(layer_tree_frame_sink), + resource_provider_(resource_provider), + use_stream_video_draw_quad_(use_stream_video_draw_quad), + use_gpu_memory_buffer_resources_(use_gpu_memory_buffer_resources), + tracing_id_(g_next_video_resource_updater_id.GetNext()), + weak_ptr_factory_(this) { + DCHECK(context_provider_ || layer_tree_frame_sink_); - for (size_t i = 0; i < plane_resources.size(); ++i) { - PlaneResource& plane_resource = *plane_resources[i]; - GLuint target = resource_provider_->GetResourceTextureTarget( - plane_resource.resource_id()); - auto transfer_resource = viz::TransferableResource::MakeGL( - plane_resource.mailbox(), GL_LINEAR, target, sync_token); - transfer_resource.color_space = output_color_space; - external_resources.resources.push_back(std::move(transfer_resource)); - external_resources.release_callbacks.push_back( - base::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(), - plane_resource.resource_id())); - } + base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( + this, "cc::VideoResourceUpdater", base::ThreadTaskRunnerHandle::Get()); +} - external_resources.type = VideoFrameExternalResources::YUV_RESOURCE; - return external_resources; +VideoResourceUpdater::~VideoResourceUpdater() { + base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( + this); } void VideoResourceUpdater::ObtainFrameResources( @@ -590,43 +368,31 @@ void VideoResourceUpdater::ObtainFrameResources( CreateExternalResourcesFromVideoFrame(video_frame); frame_resource_type_ = external_resources.type; - if (external_resources.type == - VideoFrameExternalResources::SOFTWARE_RESOURCE) { - DCHECK_GT(external_resources.software_resource, viz::kInvalidResourceId); - software_resource_ = external_resources.software_resource; - software_release_callback_ = - std::move(external_resources.software_release_callback); - } else { + 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()); - ResourceProvider::ResourceIdArray resource_ids; - resource_ids.reserve(external_resources.resources.size()); - for (size_t i = 0; i < external_resources.resources.size(); ++i) { - unsigned resource_id = resource_provider_->ImportResource( - external_resources.resources[i], - viz::SingleReleaseCallback::Create( - std::move(external_resources.release_callbacks[i]))); - frame_resources_.push_back( - FrameResource(resource_id, external_resources.resources[i].size)); - resource_ids.push_back(resource_id); - } + 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() { - if (frame_resource_type_ == VideoFrameExternalResources::SOFTWARE_RESOURCE) { - DCHECK_GT(software_resource_, viz::kInvalidResourceId); - std::move(software_release_callback_).Run(gpu::SyncToken(), false); - software_resource_ = viz::kInvalidResourceId; - } else { - for (size_t i = 0; i < frame_resources_.size(); ++i) - resource_provider_->RemoveImportedResource(frame_resources_[i].id); - frame_resources_.clear(); - } + for (auto& frame_resource : frame_resources_) + resource_provider_->RemoveImportedResource(frame_resource.id); + frame_resources_.clear(); } void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass, @@ -660,28 +426,7 @@ void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass, static_cast<float>(visible_rect.height()) / coded_size.height(); switch (frame_resource_type_) { - // TODO(danakj): Remove this, hide it in the hardware path. - case VideoFrameExternalResources::SOFTWARE_RESOURCE: { - DCHECK_EQ(frame_resources_.size(), 0u); - DCHECK_GT(software_resource_, viz::kInvalidResourceId); - bool premultiplied_alpha = true; - 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, - software_resource_, premultiplied_alpha, uv_top_left, uv_bottom_right, - SK_ColorTRANSPARENT, opacity, flipped, nearest_neighbor, false); - for (viz::ResourceId resource_id : texture_quad->resources) { - resource_provider_->ValidateResource(resource_id); - } - break; - } - case VideoFrameExternalResources::YUV_RESOURCE: { + case VideoFrameResourceType::YUV: { const gfx::Size ya_tex_size = coded_size; int u_width = media::VideoFrame::Columns( @@ -730,23 +475,22 @@ void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass, 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->require_overlay = + frame->metadata()->IsTrue(media::VideoFrameMetadata::REQUIRE_OVERLAY); for (viz::ResourceId resource_id : yuv_video_quad->resources) { resource_provider_->ValidateResource(resource_id); } break; } - case VideoFrameExternalResources::RGBA_RESOURCE: - case VideoFrameExternalResources::RGBA_PREMULTIPLIED_RESOURCE: - case VideoFrameExternalResources::RGB_RESOURCE: { + 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_ == - VideoFrameExternalResources::RGBA_PREMULTIPLIED_RESOURCE; + 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}; @@ -765,7 +509,7 @@ void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass, } break; } - case VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE: { + case VideoFrameResourceType::STREAM_TEXTURE: { DCHECK_EQ(frame_resources_.size(), 1u); if (frame_resources_.size() < 1u) break; @@ -781,30 +525,94 @@ void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass, } break; } - case VideoFrameExternalResources::NONE: + case VideoFrameResourceType::NONE: NOTIMPLEMENTED(); break; } } -// static -void VideoResourceUpdater::ReturnTexture( - base::WeakPtr<VideoResourceUpdater> updater, - const scoped_refptr<media::VideoFrame>& video_frame, - const gpu::SyncToken& sync_token, - bool lost_resource) { - // TODO(dshwang) this case should be forwarded to the decoder as lost - // resource. - if (lost_resource || !updater.get()) - return; - // The video frame will insert a wait on the previous release sync token. - SyncTokenClientImpl client(updater->context_provider_->ContextGL(), - sync_token); - video_frame->UpdateReleaseSyncToken(&client); +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)); +} + +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, layer_tree_frame_sink_)); + } 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(); } -// Create a copy of a texture-backed source video frame in a new GL_TEXTURE_2D -// texture. void VideoResourceUpdater::CopyHardwarePlane( media::VideoFrame* video_frame, const gfx::ColorSpace& resource_color_space, @@ -813,21 +621,19 @@ void VideoResourceUpdater::CopyHardwarePlane( 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. - const viz::ResourceFormat copy_target_format = viz::ResourceFormat::RGBA_8888; + 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. - VideoResourceUpdater::ResourceList::iterator resource = - RecycleOrAllocateResource(output_plane_resource_size, copy_target_format, - resource_color_space, false, no_unique_id, - no_plane_index); - resource->add_ref(); - - LayerTreeResourceProvider::ScopedWriteLockGL lock(resource_provider_, - resource->resource_id()); - DCHECK_EQ( - resource_provider_->GetResourceTextureTarget(resource->resource_id()), - (GLenum)GL_TEXTURE_2D); + 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(); @@ -835,23 +641,25 @@ void VideoResourceUpdater::CopyHardwarePlane( uint32_t src_texture_id = gl->CreateAndConsumeTextureCHROMIUM(mailbox_holder.mailbox.name); gl->CopySubTextureCHROMIUM( - src_texture_id, 0, GL_TEXTURE_2D, lock.GetTexture(), 0, 0, 0, 0, 0, - output_plane_resource_size.width(), output_plane_resource_size.height(), - false, false, false); + 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 transfer_resource = viz::TransferableResource::MakeGL( - resource->mailbox(), GL_LINEAR, GL_TEXTURE_2D, sync_token); - transfer_resource.color_space = resource_color_space; - external_resources->resources.push_back(std::move(transfer_resource)); + 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::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(), - resource->resource_id())); + external_resources->release_callbacks.push_back(base::BindOnce( + &VideoResourceUpdater::RecycleResource, weak_ptr_factory_.GetWeakPtr(), + hardware_resource->plane_resource_id())); } VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( @@ -876,15 +684,14 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( external_resources.type = ExternalResourceTypeForHardwarePlanes( video_frame->format(), target, video_frame->NumTextures(), &buffer_format, use_stream_video_draw_quad_); - if (external_resources.type == VideoFrameExternalResources::NONE) { + if (external_resources.type == VideoFrameResourceType::NONE) { DLOG(ERROR) << "Unsupported Texture format" << media::VideoPixelFormatToString(video_frame->format()); return external_resources; } - if (external_resources.type == VideoFrameExternalResources::RGB_RESOURCE || - external_resources.type == VideoFrameExternalResources::RGBA_RESOURCE || - external_resources.type == - VideoFrameExternalResources::RGBA_PREMULTIPLIED_RESOURCE) { + if (external_resources.type == VideoFrameResourceType::RGB || + external_resources.type == VideoFrameResourceType::RGBA || + external_resources.type == VideoFrameResourceType::RGBA_PREMULTIPLIED) { resource_color_space = resource_color_space.GetAsFullRangeRGB(); } @@ -916,44 +723,407 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( media::VideoFrameMetadata::WANTS_PROMOTION_HINT); #endif external_resources.resources.push_back(std::move(transfer_resource)); - external_resources.release_callbacks.push_back(base::Bind( - &ReturnTexture, weak_ptr_factory_.GetWeakPtr(), video_frame)); + external_resources.release_callbacks.push_back( + base::BindOnce(&VideoResourceUpdater::ReturnTexture, + weak_ptr_factory_.GetWeakPtr(), video_frame)); } } return external_resources; } -// static -void VideoResourceUpdater::RecycleResource( - base::WeakPtr<VideoResourceUpdater> updater, - viz::ResourceId resource_id, +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 { + // Can be composited directly from yuv planes. + output_resource_format = + resource_provider_->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(); + } + + // Drop recycled resources that are the wrong format. + auto can_delete_resource_fn = + [output_resource_format](const std::unique_ptr<PlaneResource>& resource) { + return !resource->has_refs() && + resource->resource_format() != output_resource_format; + }; + base::EraseIf(all_resources_, can_delete_resource_fn); + // TODO(kylechar): Delete resources that are the wrong size for all output + // planes. + + const int max_resource_size = resource_provider_->max_texture_size(); + std::vector<PlaneResource*> plane_resources; + for (size_t i = 0; i < output_plane_count; ++i) { + gfx::Size output_plane_resource_size = + SoftwarePlaneDimension(video_frame.get(), software_compositor(), i); + 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. Clean up and return an empty + // external resources. + for (auto* plane_resource : plane_resources) + plane_resource->remove_ref(); + return VideoFrameExternalResources(); + } + + PlaneResource* plane_resource = RecycleOrAllocateResource( + output_plane_resource_size, output_resource_format, output_color_space, + video_frame->unique_id(), i); + + plane_resource->add_ref(); + plane_resources.push_back(plane_resource); + } + + 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(), 0 /* sequence_number */, + 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 = + resource_provider_->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) { - if (!updater.get()) { - // Resource was already deleted. + // TODO(dshwang): Forward to the decoder as a lost resource. + if (lost_resource) return; - } - const ResourceList::iterator resource_it = std::find_if( - updater->all_resources_.begin(), updater->all_resources_.end(), - [resource_id](const PlaneResource& plane_resource) { - return plane_resource.resource_id() == resource_id; - }); - if (resource_it == updater->all_resources_.end()) + + // 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; - viz::ContextProvider* context_provider = updater->context_provider_; - if (context_provider && sync_token.HasData()) { - context_provider->ContextGL()->WaitSyncTokenCHROMIUM( + if (context_provider_ && sync_token.HasData()) { + context_provider_->ContextGL()->WaitSyncTokenCHROMIUM( sync_token.GetConstData()); } if (lost_resource) { - resource_it->clear_refs(); - updater->DeleteResource(resource_it); - return; + 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); + } } - resource_it->remove_ref(); + return true; } } // namespace cc diff --git a/chromium/cc/resources/video_resource_updater.h b/chromium/cc/resources/video_resource_updater.h index c422902acfe..6d152ef2d85 100644 --- a/chromium/cc/resources/video_resource_updater.h +++ b/chromium/cc/resources/video_resource_updater.h @@ -16,6 +16,7 @@ #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" @@ -40,31 +41,26 @@ class RenderPass; } namespace cc { +class LayerTreeFrameSink; 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: - // Specifies what type of data is contained in the mailboxes, as well as how - // many mailboxes will be present. - enum ResourceType { - NONE, - YUV_RESOURCE, - RGB_RESOURCE, - RGBA_PREMULTIPLIED_RESOURCE, - RGBA_RESOURCE, - STREAM_TEXTURE_RESOURCE, - - SOFTWARE_RESOURCE - }; - ResourceType type = NONE; + VideoFrameResourceType type = VideoFrameResourceType::NONE; std::vector<viz::TransferableResource> resources; std::vector<viz::ReleaseCallback> release_callbacks; - // TODO(danakj): Remove these and use TransferableResources to send - // software things too. - unsigned software_resource = viz::kInvalidResourceId; - viz::ReleaseCallback software_release_callback; - // 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; @@ -79,20 +75,30 @@ class CC_EXPORT VideoFrameExternalResources { // VideoResourceUpdater is used by the video system to produce frame content as // resources consumable by the compositor. -class CC_EXPORT VideoResourceUpdater { +class CC_EXPORT VideoResourceUpdater + : public base::trace_event::MemoryDumpProvider { public: + // For GPU compositing |context_provider| should be provided and for software + // compositing |layer_tree_frame_sink| should be provided. If there is a + // non-null |context_provider| we assume GPU compositing. + // TODO(kylechar): Don't use LayerTreeFrameSink for the software compositing + // path. The UseSurfaceLayerForVideo path isn't compatible with this. We can + // maybe use mojom::CompositorFrameSink instead. VideoResourceUpdater(viz::ContextProvider* context_provider, + LayerTreeFrameSink* layer_tree_frame_sink, LayerTreeResourceProvider* resource_provider, - bool use_stream_video_draw_quad); - ~VideoResourceUpdater(); - - VideoFrameExternalResources CreateExternalResourcesFromVideoFrame( - scoped_refptr<media::VideoFrame> video_frame); - - void SetUseR16ForTesting(bool use_r16_for_testing) { - use_r16_for_testing_ = use_r16_for_testing; - } - + bool use_stream_video_draw_quad, + bool use_gpu_memory_buffer_resources); + + ~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 SharedBitmap + // 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, @@ -107,56 +113,23 @@ class CC_EXPORT VideoResourceUpdater { 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); + private: - class PlaneResource { - public: - PlaneResource(unsigned resource_id, - const gfx::Size& resource_size, - viz::ResourceFormat resource_format, - gpu::Mailbox mailbox); - PlaneResource(const PlaneResource& other); - - // Returns true if this resource matches the unique identifiers of another - // VideoFrame resource. - bool Matches(int unique_frame_id, size_t 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); - - // Accessors for resource identifiers provided at construction time. - unsigned resource_id() const { return resource_id_; } - const gfx::Size& resource_size() const { return resource_size_; } - viz::ResourceFormat resource_format() const { return resource_format_; } - const gpu::Mailbox& mailbox() const { return mailbox_; } - - // 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: - // The balance between the number of times this resource has been returned - // from CreateForSoftwarePlanes vs released in RecycleResource. - 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; - - const unsigned resource_id_; - const gfx::Size resource_size_; - const viz::ResourceFormat resource_format_; - const gpu::Mailbox mailbox_; + 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; }; - // This needs to be a container where iterators can be erased without - // invalidating other iterators. - typedef std::list<PlaneResource> ResourceList; + 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 @@ -166,64 +139,72 @@ class CC_EXPORT VideoResourceUpdater { // used for reading, and so is returned even if it is still referenced. // Passing -1 for |plane_index| avoids returning referenced // resources. - ResourceList::iterator RecycleOrAllocateResource( - const gfx::Size& resource_size, - viz::ResourceFormat resource_format, - const gfx::ColorSpace& color_space, - bool software_resource, - int unique_id, - int plane_index); - ResourceList::iterator AllocateResource(const gfx::Size& plane_size, - viz::ResourceFormat format, - const gfx::ColorSpace& color_space, - bool software_resource); - void DeleteResource(ResourceList::iterator resource_it); + 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); - static void RecycleResource(base::WeakPtr<VideoResourceUpdater> updater, - unsigned resource_id, - const gpu::SyncToken& sync_token, - bool lost_resource); - static void ReturnTexture(base::WeakPtr<VideoResourceUpdater> updater, - const scoped_refptr<media::VideoFrame>& video_frame, - const gpu::SyncToken& sync_token, - bool lost_resource); - - viz::ContextProvider* context_provider_; - LayerTreeResourceProvider* resource_provider_; + 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_; + LayerTreeFrameSink* const layer_tree_frame_sink_; + LayerTreeResourceProvider* const resource_provider_; const bool use_stream_video_draw_quad_; + const bool use_gpu_memory_buffer_resources_; + 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_; - bool use_r16_for_testing_ = false; - VideoFrameExternalResources::ResourceType frame_resource_type_; - // TODO(danakj): Remove these, use TransferableResource for software path too. - unsigned software_resource_ = viz::kInvalidResourceId; - // Called once for |software_resource_|. - viz::ReleaseCallback software_release_callback_; + VideoFrameResourceType frame_resource_type_; float frame_resource_offset_; float frame_resource_multiplier_; uint32_t frame_bits_per_channel_; - struct FrameResource { - FrameResource(viz::ResourceId id, gfx::Size size_in_pixels) - : id(id), size_in_pixels(size_in_pixels) {} - viz::ResourceId id; - gfx::Size size_in_pixels; - }; + // Resources that will be placed into quads by the next call to + // AppendDrawQuads(). std::vector<FrameResource> frame_resources_; - // Recycle resources so that we can reduce the number of allocations and - // data transfers. - ResourceList all_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_; diff --git a/chromium/cc/resources/video_resource_updater_unittest.cc b/chromium/cc/resources/video_resource_updater_unittest.cc index acc9ae843e6..17b44064587 100644 --- a/chromium/cc/resources/video_resource_updater_unittest.cc +++ b/chromium/cc/resources/video_resource_updater_unittest.cc @@ -8,12 +8,11 @@ #include <stdint.h> #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "cc/resources/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_shared_bitmap_manager.h" #include "components/viz/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "media/base/video_frame.h" @@ -69,22 +68,6 @@ class WebGraphicsContext3DUploadCounter : public viz::TestWebGraphicsContext3D { int created_texture_count_; }; -class SharedBitmapManagerAllocationCounter - : public viz::TestSharedBitmapManager { - public: - std::unique_ptr<viz::SharedBitmap> AllocateSharedBitmap( - const gfx::Size& size) override { - ++allocation_count_; - return TestSharedBitmapManager::AllocateSharedBitmap(size); - } - - int AllocationCount() { return allocation_count_; } - void ResetAllocationCount() { allocation_count_ = 0; } - - private: - int allocation_count_; -}; - class VideoResourceUpdaterTest : public testing::Test { protected: VideoResourceUpdaterTest() { @@ -98,17 +81,31 @@ class VideoResourceUpdaterTest : public testing::Test { context_provider_->BindToCurrentThread(); } + // testing::Test implementation. void SetUp() override { testing::Test::SetUp(); - shared_bitmap_manager_.reset(new SharedBitmapManagerAllocationCounter()); + layer_tree_frame_sink_software_ = FakeLayerTreeFrameSink::CreateSoftware(); resource_provider3d_ = FakeResourceProvider::CreateLayerTreeResourceProvider( - context_provider_.get(), shared_bitmap_manager_.get(), nullptr, - high_bit_for_testing_); + context_provider_.get(), nullptr, nullptr, high_bit_for_testing_); resource_provider_software_ = FakeResourceProvider::CreateLayerTreeResourceProvider( - nullptr, shared_bitmap_manager_.get(), nullptr, - high_bit_for_testing_); + nullptr, nullptr, nullptr, high_bit_for_testing_); + } + + 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); + } + + 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); } scoped_refptr<media::VideoFrame> CreateTestYUVVideoFrame() { @@ -245,7 +242,7 @@ class VideoResourceUpdaterTest : public testing::Test { WebGraphicsContext3DUploadCounter* context3d_; scoped_refptr<viz::TestContextProvider> context_provider_; - std::unique_ptr<SharedBitmapManagerAllocationCounter> shared_bitmap_manager_; + 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_; @@ -258,27 +255,21 @@ const gpu::SyncToken VideoResourceUpdaterTest::kMailboxSyncToken = 7); TEST_F(VideoResourceUpdaterTest, SoftwareFrame) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + updater->CreateExternalResourcesFromVideoFrame(video_frame); + EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); } TEST_F(VideoResourceUpdaterTest, HighBitFrameNoF16) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestHighBitFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + updater->CreateExternalResourcesFromVideoFrame(video_frame); + EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); } class VideoResourceUpdaterTestWithF16 : public VideoResourceUpdaterTest { @@ -289,23 +280,20 @@ class VideoResourceUpdaterTestWithF16 : public VideoResourceUpdaterTest { }; TEST_F(VideoResourceUpdaterTestWithF16, HighBitFrame) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestHighBitFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + 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(VideoFrameExternalResources::YUV_RESOURCE, resources2.type); + 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); } @@ -319,16 +307,12 @@ class VideoResourceUpdaterTestWithR16 : public VideoResourceUpdaterTest { }; TEST_F(VideoResourceUpdaterTestWithR16, HighBitFrame) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); - updater.SetUseR16ForTesting(true); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestHighBitFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + 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; @@ -338,62 +322,51 @@ TEST_F(VideoResourceUpdaterTestWithR16, HighBitFrame) { // Create the resource again, to test the path where the // resources are cached. VideoFrameExternalResources resources2 = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources2.type); + 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) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(nullptr, resource_provider_software_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestHighBitFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type); + updater->CreateExternalResourcesFromVideoFrame(video_frame); + EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); } TEST_F(VideoResourceUpdaterTest, WonkySoftwareFrame) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); scoped_refptr<media::VideoFrame> video_frame = CreateWonkyTestYUVVideoFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + updater->CreateExternalResourcesFromVideoFrame(video_frame); + EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); } TEST_F(VideoResourceUpdaterTest, WonkySoftwareFrameSoftwareCompositor) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(nullptr, resource_provider_software_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); scoped_refptr<media::VideoFrame> video_frame = CreateWonkyTestYUVVideoFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type); + updater->CreateExternalResourcesFromVideoFrame(video_frame); + EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); } TEST_F(VideoResourceUpdaterTest, ReuseResource) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + 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. context3d_->ResetUploadCount(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + updater->CreateExternalResourcesFromVideoFrame(video_frame); + EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); EXPECT_EQ(3u, resources.resources.size()); EXPECT_EQ(3u, resources.release_callbacks.size()); - EXPECT_EQ(viz::kInvalidResourceId, resources.software_resource); // Expect exactly three texture uploads, one for each plane. EXPECT_EQ(3, context3d_->UploadCount()); @@ -404,8 +377,8 @@ TEST_F(VideoResourceUpdaterTest, ReuseResource) { // Allocate resources for the same frame. context3d_->ResetUploadCount(); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + 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. @@ -413,28 +386,24 @@ TEST_F(VideoResourceUpdaterTest, ReuseResource) { } TEST_F(VideoResourceUpdaterTest, ReuseResourceNoDelete) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + 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. context3d_->ResetUploadCount(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + updater->CreateExternalResourcesFromVideoFrame(video_frame); + EXPECT_EQ(VideoFrameResourceType::YUV, resources.type); EXPECT_EQ(3u, resources.resources.size()); EXPECT_EQ(3u, resources.release_callbacks.size()); - EXPECT_EQ(viz::kInvalidResourceId, resources.software_resource); // Expect exactly three texture uploads, one for each plane. EXPECT_EQ(3, context3d_->UploadCount()); // Allocate resources for the same frame. context3d_->ResetUploadCount(); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + 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. @@ -442,103 +411,87 @@ TEST_F(VideoResourceUpdaterTest, ReuseResourceNoDelete) { } TEST_F(VideoResourceUpdaterTest, SoftwareFrameSoftwareCompositor) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(nullptr, resource_provider_software_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForSoftware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type); + updater->CreateExternalResourcesFromVideoFrame(video_frame); + EXPECT_EQ(VideoFrameResourceType::RGBA_PREMULTIPLIED, resources.type); } TEST_F(VideoResourceUpdaterTest, ReuseResourceSoftwareCompositor) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(nullptr, resource_provider_software_.get(), - use_stream_video_draw_quad); + 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. - shared_bitmap_manager_->ResetAllocationCount(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type); - EXPECT_EQ(0u, resources.resources.size()); - EXPECT_EQ(0u, resources.release_callbacks.size()); - EXPECT_LT(viz::kInvalidResourceId, resources.software_resource); + 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(1, shared_bitmap_manager_->AllocationCount()); + 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.software_release_callback).Run(gpu::SyncToken(), false); + std::move(resources.release_callbacks[0]).Run(gpu::SyncToken(), false); // Allocate resources for the same frame. - shared_bitmap_manager_->ResetAllocationCount(); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type); - EXPECT_EQ(0u, resources.resources.size()); - EXPECT_EQ(0u, resources.release_callbacks.size()); - EXPECT_LT(viz::kInvalidResourceId, resources.software_resource); - // The data should be reused so expect no new allocations. - EXPECT_EQ(0, shared_bitmap_manager_->AllocationCount()); + 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) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(nullptr, resource_provider_software_.get(), - use_stream_video_draw_quad); + 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. - shared_bitmap_manager_->ResetAllocationCount(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type); - EXPECT_EQ(0u, resources.resources.size()); - EXPECT_EQ(0u, resources.release_callbacks.size()); - EXPECT_LT(viz::kInvalidResourceId, resources.software_resource); + 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(1, shared_bitmap_manager_->AllocationCount()); + 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. - shared_bitmap_manager_->ResetAllocationCount(); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type); - EXPECT_EQ(0u, resources.resources.size()); - EXPECT_EQ(0u, resources.release_callbacks.size()); - EXPECT_NE(viz::kInvalidResourceId, resources.software_resource); - // The data should be reused so expect no new allocations. - EXPECT_EQ(0, shared_bitmap_manager_->AllocationCount()); + 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, CreateForHardwarePlanes) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestRGBAHardwareVideoFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::RGBA_PREMULTIPLIED_RESOURCE, - resources.type); + 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_EQ(viz::kInvalidResourceId, resources.software_resource); video_frame = CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_I420, 3, GL_TEXTURE_RECTANGLE_ARB); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + 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_EQ(viz::kInvalidResourceId, resources.software_resource); 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); @@ -548,74 +501,62 @@ TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes) { video_frame->metadata()->SetBoolean( media::VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); + 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) { - bool use_stream_video_draw_quad = true; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + // Note that |use_stream_video_draw_quad| is true for this test. + std::unique_ptr<VideoResourceUpdater> updater = + CreateUpdaterForHardware(true); context3d_->ResetTextureCreationCount(); scoped_refptr<media::VideoFrame> video_frame = CreateTestStreamTextureHardwareVideoFrame(false); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE, - resources.type); + 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(viz::kInvalidResourceId, resources.software_resource); EXPECT_EQ(0, context3d_->TextureCreationCount()); // A copied stream texture should return an RGBA resource in a new // GL_TEXTURE_2D texture. context3d_->ResetTextureCreationCount(); video_frame = CreateTestStreamTextureHardwareVideoFrame(true); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::RGBA_PREMULTIPLIED_RESOURCE, - resources.type); + 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(viz::kInvalidResourceId, resources.software_resource); EXPECT_EQ(1, context3d_->TextureCreationCount()); } TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_TextureQuad) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); context3d_->ResetTextureCreationCount(); scoped_refptr<media::VideoFrame> video_frame = CreateTestStreamTextureHardwareVideoFrame(false); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::RGBA_PREMULTIPLIED_RESOURCE, - resources.type); + 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(viz::kInvalidResourceId, resources.software_resource); EXPECT_EQ(0, context3d_->TextureCreationCount()); } // Passthrough the sync token returned by the compositor if we don't have an // existing release sync token. TEST_F(VideoResourceUpdaterTest, PassReleaseSyncToken) { - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - false /* use_stream_video_draw_quad */); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); const gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(0x123), @@ -626,7 +567,7 @@ TEST_F(VideoResourceUpdaterTest, PassReleaseSyncToken) { CreateTestRGBAHardwareVideoFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); + updater->CreateExternalResourcesFromVideoFrame(video_frame); ASSERT_EQ(resources.release_callbacks.size(), 1u); std::move(resources.release_callbacks[0]).Run(sync_token, false); @@ -637,9 +578,7 @@ TEST_F(VideoResourceUpdaterTest, PassReleaseSyncToken) { // Generate new sync token because video frame has an existing sync token. TEST_F(VideoResourceUpdaterTest, GenerateReleaseSyncToken) { - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - false /* use_stream_video_draw_quad */); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); const gpu::SyncToken sync_token1(gpu::CommandBufferNamespace::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(0x123), @@ -654,12 +593,12 @@ TEST_F(VideoResourceUpdaterTest, GenerateReleaseSyncToken) { CreateTestRGBAHardwareVideoFrame(); VideoFrameExternalResources resources1 = - updater.CreateExternalResourcesFromVideoFrame(video_frame); + 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); + updater->CreateExternalResourcesFromVideoFrame(video_frame); ASSERT_EQ(resources2.release_callbacks.size(), 1u); std::move(resources2.release_callbacks[0]).Run(sync_token2, false); } @@ -672,15 +611,13 @@ TEST_F(VideoResourceUpdaterTest, GenerateReleaseSyncToken) { // 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) { - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - false /* use_stream_video_draw_quad */); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestRGBAHardwareVideoFrame(); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); + updater->CreateExternalResourcesFromVideoFrame(video_frame); ASSERT_EQ(resources.resources.size(), 1u); EXPECT_TRUE(resources.resources[0].mailbox_holder.sync_token.HasData()); @@ -690,15 +627,13 @@ TEST_F(VideoResourceUpdaterTest, PassMailboxSyncToken) { // Generate new sync token for compositor when copying the texture. TEST_F(VideoResourceUpdaterTest, GenerateSyncTokenOnTextureCopy) { - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - false /* use_stream_video_draw_quad */); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); scoped_refptr<media::VideoFrame> video_frame = CreateTestStreamTextureHardwareVideoFrame(true /* needs_copy */); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); + updater->CreateExternalResourcesFromVideoFrame(video_frame); ASSERT_EQ(resources.resources.size(), 1u); EXPECT_TRUE(resources.resources[0].mailbox_holder.sync_token.HasData()); @@ -710,17 +645,14 @@ TEST_F(VideoResourceUpdaterTest, GenerateSyncTokenOnTextureCopy) { // 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) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); context3d_->ResetTextureCreationCount(); scoped_refptr<media::VideoFrame> video_frame = CreateTestHardwareVideoFrame( media::PIXEL_FORMAT_NV12, GL_TEXTURE_EXTERNAL_OES); VideoFrameExternalResources resources = - updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::RGB_RESOURCE, resources.type); + 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); @@ -729,8 +661,8 @@ TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_SingleNV12) { video_frame = CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_NV12, 1, GL_TEXTURE_RECTANGLE_ARB); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::RGB_RESOURCE, resources.type); + 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); @@ -741,21 +673,17 @@ TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_SingleNV12) { } TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_DualNV12) { - bool use_stream_video_draw_quad = false; - VideoResourceUpdater updater(context_provider_.get(), - resource_provider3d_.get(), - use_stream_video_draw_quad); + std::unique_ptr<VideoResourceUpdater> updater = CreateUpdaterForHardware(); context3d_->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(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + 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(viz::kInvalidResourceId, resources.software_resource); EXPECT_EQ((GLenum)GL_TEXTURE_EXTERNAL_OES, resources.resources[0].mailbox_holder.texture_target); // |updater| doesn't set |buffer_format| in this case. @@ -763,8 +691,8 @@ TEST_F(VideoResourceUpdaterTest, CreateForHardwarePlanes_DualNV12) { video_frame = CreateTestYuvHardwareVideoFrame(media::PIXEL_FORMAT_NV12, 2, GL_TEXTURE_RECTANGLE_ARB); - resources = updater.CreateExternalResourcesFromVideoFrame(video_frame); - EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + 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); diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index 343dd089106..352c8649bc6 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -8,7 +8,6 @@ #include "base/auto_reset.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc index 429f21b4d74..8f5409fc010 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.cc +++ b/chromium/cc/scheduler/scheduler_state_machine.cc @@ -913,11 +913,6 @@ void SchedulerStateMachine::WillInvalidateLayerTreeFrameSink() { did_invalidate_layer_tree_frame_sink_ = true; last_frame_number_invalidate_layer_tree_frame_sink_performed_ = current_frame_number_; - - // The synchronous compositor makes no guarantees about a draw coming in after - // an invalidate so clear any flags that would cause the compositor's pipeline - // to stall. - active_tree_needs_first_draw_ = false; // blocks commit if true } void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency( diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index 22c8968a58c..a4e1fddba04 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -3069,49 +3069,6 @@ TEST_F(SchedulerTest, SynchronousCompositorCommitAndVerifyBeginFrameAcks) { client_->Reset(); } -TEST_F(SchedulerTest, SynchronousCompositorDoubleCommitWithoutDraw) { - scheduler_settings_.using_synchronous_renderer_compositor = true; - SetUpScheduler(EXTERNAL_BFS); - - scheduler_->SetNeedsBeginMainFrame(); - EXPECT_ACTIONS("AddObserver(this)"); - client_->Reset(); - - // Next vsync. - AdvanceFrame(); - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - client_->Reset(); - - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); - EXPECT_NO_ACTION(); - - scheduler_->NotifyReadyToCommit(); - EXPECT_ACTIONS("ScheduledActionCommit"); - client_->Reset(); - - scheduler_->NotifyReadyToActivate(); - EXPECT_ACTIONS("ScheduledActionActivateSyncTree"); - client_->Reset(); - - // Ask for another commit. - scheduler_->SetNeedsBeginMainFrame(); - - AdvanceFrame(); - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", - "ScheduledActionInvalidateLayerTreeFrameSink"); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - client_->Reset(); - - scheduler_->NotifyBeginMainFrameStarted(now_src()->NowTicks()); - EXPECT_NO_ACTION(); - - // Allow new commit even though previous commit hasn't been drawn. - scheduler_->NotifyReadyToCommit(); - EXPECT_ACTIONS("ScheduledActionCommit"); - client_->Reset(); -} - class SchedulerClientSetNeedsPrepareTilesOnDraw : public FakeSchedulerClient { public: SchedulerClientSetNeedsPrepareTilesOnDraw() : FakeSchedulerClient() {} diff --git a/chromium/cc/tiles/checker_image_tracker.cc b/chromium/cc/tiles/checker_image_tracker.cc index 012cbb65efe..060eb020d1c 100644 --- a/chromium/cc/tiles/checker_image_tracker.cc +++ b/chromium/cc/tiles/checker_image_tracker.cc @@ -5,7 +5,6 @@ #include "cc/tiles/checker_image_tracker.h" #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" diff --git a/chromium/cc/tiles/checker_image_tracker_unittest.cc b/chromium/cc/tiles/checker_image_tracker_unittest.cc index 0c46a4b6d5d..0b4606f1463 100644 --- a/chromium/cc/tiles/checker_image_tracker_unittest.cc +++ b/chromium/cc/tiles/checker_image_tracker_unittest.cc @@ -5,7 +5,6 @@ #include "cc/tiles/checker_image_tracker.h" #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/paint/paint_image_builder.h" diff --git a/chromium/cc/tiles/eviction_tile_priority_queue.cc b/chromium/cc/tiles/eviction_tile_priority_queue.cc index 4bb198a2876..c09fc82961c 100644 --- a/chromium/cc/tiles/eviction_tile_priority_queue.cc +++ b/chromium/cc/tiles/eviction_tile_priority_queue.cc @@ -4,7 +4,6 @@ #include "cc/tiles/eviction_tile_priority_queue.h" -#include "base/memory/ptr_util.h" namespace cc { diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc index 97b242c908d..344515342af 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache.cc @@ -11,7 +11,6 @@ #include "base/hash.h" #include "base/memory/discardable_memory_allocator.h" #include "base/memory/memory_coordinator_client_registry.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_math.h" #include "base/strings/stringprintf.h" @@ -211,12 +210,16 @@ sk_sp<SkImage> TakeOwnershipOfSkImageBacking(GrContext* context, GrSurfaceOrigin origin; image->getTextureHandle(false /* flushPendingGrContextIO */, &origin); + SkColorType color_type = image->colorType(); + if (color_type == kUnknown_SkColorType) { + return nullptr; + } sk_sp<SkColorSpace> color_space = image->refColorSpace(); GrBackendTexture backend_texture; SkImage::BackendTextureReleaseProc release_proc; SkImage::MakeBackendTextureFromSkImage(context, std::move(image), &backend_texture, &release_proc); - return SkImage::MakeFromTexture(context, backend_texture, origin, + return SkImage::MakeFromTexture(context, backend_texture, origin, color_type, kPremul_SkAlphaType, std::move(color_space)); } @@ -577,22 +580,14 @@ void GpuImageDecodeCache::ImageData::ValidateBudgeted() const { GpuImageDecodeCache::GpuImageDecodeCache(viz::RasterContextProvider* context, bool use_transfer_cache, SkColorType color_type, - size_t max_working_set_bytes) + size_t max_working_set_bytes, + int max_texture_size) : color_type_(color_type), use_transfer_cache_(use_transfer_cache), context_(context), + max_texture_size_(max_texture_size), persistent_cache_(PersistentCache::NO_AUTO_EVICT), max_working_set_bytes_(max_working_set_bytes) { - // Acquire the context_lock so that we can safely retrieve - // |max_texture_size_|. - { - base::Optional<viz::RasterContextProvider::ScopedRasterContextLock> - context_lock; - if (context_->GetLock()) - context_lock.emplace(context_); - max_texture_size_ = context_->GrContext()->caps()->maxTextureSize(); - } - // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview). // Don't register a dump provider in these cases. if (base::ThreadTaskRunnerHandle::IsSet()) { @@ -1351,9 +1346,9 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, image_info.minRowBytes()); // Set |pixmap| to the desired colorspace to decode into. - if (image_data->mode == DecodedDataMode::kCpu) { - // If this is a kCpu image, we want to handle color conversion during - // decode, so set the target colorspace here. + if (!SupportsColorSpaceConversion()) { + pixmap.setColorSpace(nullptr); + } else if (image_data->mode == DecodedDataMode::kCpu) { pixmap.setColorSpace(draw_image.target_color_space().ToSkColorSpace()); } else { // For kGpu or kTransferCache images color conversion is handled during @@ -1420,7 +1415,11 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, if (!image_data->decode.image()->peekPixels(&pixmap)) return; - ClientImageTransferCacheEntry image_entry(&pixmap, nullptr); + sk_sp<SkColorSpace> color_space = + SupportsColorSpaceConversion() + ? draw_image.target_color_space().ToSkColorSpace() + : nullptr; + ClientImageTransferCacheEntry image_entry(&pixmap, color_space.get()); size_t size = image_entry.SerializedSize(); void* data = context_->ContextSupport()->MapTransferCacheEntry(size); // TODO(piman): handle error (failed to allocate/map shm) @@ -1445,11 +1444,12 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, // For kGpu, we upload and color convert (if necessary). 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 && SupportsColorSpaces() && + 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; @@ -1571,11 +1571,6 @@ void GpuImageDecodeCache::RunPendingContextThreadOperations() { context_->ContextGL()->UnlockDiscardableTextureCHROMIUM( GlIdFromSkImage(image)); } - if (images_pending_unlock_.size() > 0) { - // When we unlock images, we remove any outstanding texture bindings. We - // need to inform Skia so it will re-generate these bindings if needed. - context_->GrContext()->resetContext(kTextureBinding_GrGLBackendState); - } images_pending_unlock_.clear(); for (auto id : ids_pending_unlock_) { @@ -1763,7 +1758,7 @@ void GpuImageDecodeCache::OnPurgeMemory() { EnsureCapacity(0); } -bool GpuImageDecodeCache::SupportsColorSpaces() const { +bool GpuImageDecodeCache::SupportsColorSpaceConversion() const { switch (color_type_) { case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h index 9a32552c96b..ea4fe73dce9 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.h +++ b/chromium/cc/tiles/gpu_image_decode_cache.h @@ -105,7 +105,8 @@ class CC_EXPORT GpuImageDecodeCache explicit GpuImageDecodeCache(viz::RasterContextProvider* context, bool use_transfer_cache, SkColorType color_type, - size_t max_working_set_bytes); + size_t max_working_set_bytes, + int max_texture_size); ~GpuImageDecodeCache() override; // ImageDecodeCache overrides. @@ -144,6 +145,8 @@ class CC_EXPORT GpuImageDecodeCache DecodeTaskType task_type); void OnImageUploadTaskCompleted(const DrawImage& image); + bool SupportsColorSpaceConversion() const; + // For testing only. void SetWorkingSetLimitForTesting(size_t limit) { max_working_set_bytes_ = limit; @@ -420,8 +423,6 @@ class CC_EXPORT GpuImageDecodeCache // These including deleting, unlocking, and locking textures. void RunPendingContextThreadOperations(); - bool SupportsColorSpaces() const; - void CheckContextLockAcquiredIfNecessary(); const SkColorType color_type_; diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc index 52395c497c0..2c272ededad 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc @@ -19,6 +19,7 @@ #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/GrContext.h" namespace cc { namespace { @@ -253,15 +254,17 @@ class GpuImageDecodeCacheTest viz::RasterContextProvider::ScopedRasterContextLock context_lock( context_provider_.get()); transfer_cache_helper_.SetGrContext(context_provider_->GrContext()); + max_texture_size_ = + context_provider_->ContextCapabilities().max_texture_size; } use_transfer_cache_ = GetParam().second; color_type_ = GetParam().first; } std::unique_ptr<GpuImageDecodeCache> CreateCache() { - return base::WrapUnique( - new GpuImageDecodeCache(context_provider_.get(), use_transfer_cache_, - color_type_, kGpuMemoryLimitBytes)); + return std::make_unique<GpuImageDecodeCache>( + context_provider_.get(), use_transfer_cache_, color_type_, + kGpuMemoryLimitBytes, max_texture_size_); } GPUImageDecodeTestMockContextProvider* context_provider() { @@ -299,12 +302,23 @@ class GpuImageDecodeCacheTest return draw_image; } - private: + sk_sp<SkImage> GetLastTransferredImage() { + auto& key = transfer_cache_helper_.GetLastAddedEntry(); + ServiceTransferCacheEntry* entry = + transfer_cache_helper_.GetEntryInternal(key.first, key.second); + if (!entry) + return nullptr; + CHECK_EQ(TransferCacheEntryType::kImage, entry->Type()); + return static_cast<ServiceImageTransferCacheEntry*>(entry)->image(); + } + + protected: FakeDiscardableManager discardable_manager_; scoped_refptr<GPUImageDecodeTestMockContextProvider> context_provider_; TransferCacheTestHelper transfer_cache_helper_; bool use_transfer_cache_; SkColorType color_type_; + int max_texture_size_ = 0; }; SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { @@ -2198,13 +2212,81 @@ TEST_P(GpuImageDecodeCacheTest, DecodedDrawImage decoded_draw_image = cache->GetDecodedImageForDraw(draw_image); - sk_sp<SkImage> decoded_image = cache->GetSWImageDecodeForTesting(draw_image); - // Ensure that the "uploaded" image we get back is the same as the decoded - // image we've cached. - ExpectIfNotUsingTransferCache(decoded_image == decoded_draw_image.image()); - // Ensure that the SW decoded image had colorspace conversion applied. - ExpectIfNotUsingTransferCache(decoded_image->colorSpace() == - color_space.ToSkColorSpace().get()); + sk_sp<SkColorSpace> target_color_space = cache->SupportsColorSpaceConversion() + ? color_space.ToSkColorSpace() + : nullptr; + + if (use_transfer_cache_) { + // If using the transfer cache, the color conversion should be applied + // there as well, even if it is a software image. + sk_sp<SkImage> service_image = GetLastTransferredImage(); + ASSERT_TRUE(image); + EXPECT_FALSE(service_image->isTextureBacked()); + EXPECT_EQ(image.width(), service_image->width()); + EXPECT_EQ(image.height(), service_image->height()); + + // Color space should be logically equal to the original color space. + EXPECT_TRUE(SkColorSpace::Equals(service_image->colorSpace(), + target_color_space.get())); + } else { + sk_sp<SkImage> decoded_image = + cache->GetSWImageDecodeForTesting(draw_image); + // Ensure that the "uploaded" image we get back is the same as the decoded + // image we've cached. + EXPECT_TRUE(decoded_image == decoded_draw_image.image()); + // Ensure that the SW decoded image had colorspace conversion applied. + EXPECT_TRUE(decoded_image->colorSpace() == target_color_space.get()); + } + + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheTest, + ColorConversionDuringUploadForSmallImageNonSRGBColorSpace) { + auto cache = CreateCache(); + bool is_decomposable = true; + SkFilterQuality quality = kHigh_SkFilterQuality; + gfx::ColorSpace color_space = gfx::ColorSpace::CreateDisplayP3D65(); + + PaintImage image = CreateDiscardablePaintImage(gfx::Size(11, 12)); + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), 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()); + + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + DecodedDrawImage decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + + sk_sp<SkColorSpace> target_color_space = cache->SupportsColorSpaceConversion() + ? color_space.ToSkColorSpace() + : nullptr; + + if (use_transfer_cache_) { + // If using the transfer cache, the color conversion should be applied + // there during upload. + sk_sp<SkImage> service_image = GetLastTransferredImage(); + ASSERT_TRUE(image); + EXPECT_TRUE(service_image->isTextureBacked()); + EXPECT_EQ(image.width(), service_image->width()); + EXPECT_EQ(image.height(), service_image->height()); + + // Color space should be logically equal to the original color space. + EXPECT_TRUE(SkColorSpace::Equals(service_image->colorSpace(), + target_color_space.get())); + } else { + // Ensure that the HW uploaded image had color space conversion applied. + EXPECT_TRUE(decoded_draw_image.image()->colorSpace() == + target_color_space.get()); + } cache->DrawWithImageFinished(draw_image, decoded_draw_image); cache->UnrefImage(draw_image); diff --git a/chromium/cc/tiles/picture_layer_tiling.cc b/chromium/cc/tiles/picture_layer_tiling.cc index 6fb4812e47c..4901cdbfa38 100644 --- a/chromium/cc/tiles/picture_layer_tiling.cc +++ b/chromium/cc/tiles/picture_layer_tiling.cc @@ -13,7 +13,6 @@ #include "base/containers/flat_map.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" diff --git a/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc b/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc index 7ea10ee38bc..278f97b6f4b 100644 --- a/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc +++ b/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc @@ -7,7 +7,6 @@ #include <map> #include <vector> -#include "base/memory/ptr_util.h" #include "cc/resources/layer_tree_resource_provider.h" #include "cc/resources/resource_provider.h" #include "cc/test/fake_output_surface_client.h" diff --git a/chromium/cc/tiles/raster_tile_priority_queue_all.cc b/chromium/cc/tiles/raster_tile_priority_queue_all.cc index 96b98c5aa65..564ddfef40b 100644 --- a/chromium/cc/tiles/raster_tile_priority_queue_all.cc +++ b/chromium/cc/tiles/raster_tile_priority_queue_all.cc @@ -4,7 +4,6 @@ #include "cc/tiles/raster_tile_priority_queue_all.h" -#include "base/memory/ptr_util.h" #include "cc/tiles/tiling_set_raster_queue_all.h" namespace cc { diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc index c47c6894133..e566d3a1e1b 100644 --- a/chromium/cc/tiles/software_image_decode_cache.cc +++ b/chromium/cc/tiles/software_image_decode_cache.cc @@ -24,6 +24,24 @@ 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. // Depending on the memory state of the system, we limit the amount of items @@ -206,10 +224,7 @@ SoftwareImageDecodeCache::GetTaskForImageAndRefInternal( if (key.target_size().IsEmpty()) return TaskResult(false); - // For non-lazy images a decode isn't necessary. - // TODO(khushalsagar): If these images require color conversion, we should - // still cache that result. - if (!image.paint_image().IsLazyGenerated()) + if (!UseCacheForDrawImage(image)) return TaskResult(false); base::AutoLock lock(lock_); @@ -475,8 +490,8 @@ void SoftwareImageDecodeCache::DecodeImageIfNecessary( DecodedDrawImage SoftwareImageDecodeCache::GetDecodedImageForDraw( const DrawImage& draw_image) { - // Non-lazy generated images can be used for raster directly. - if (!draw_image.paint_image().GetSkImage()->isLazyGenerated()) { + // 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(), @@ -523,8 +538,7 @@ DecodedDrawImage SoftwareImageDecodeCache::GetDecodedImageForDrawInternal( void SoftwareImageDecodeCache::DrawWithImageFinished( const DrawImage& image, const DecodedDrawImage& decoded_image) { - // We don't cache any data for non-lazy images. - if (!image.paint_image().IsLazyGenerated()) + if (!UseCacheForDrawImage(image)) return; TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), diff --git a/chromium/cc/tiles/tile.cc b/chromium/cc/tiles/tile.cc index 1dd63678395..db0ba9f0177 100644 --- a/chromium/cc/tiles/tile.cc +++ b/chromium/cc/tiles/tile.cc @@ -12,8 +12,8 @@ #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "cc/base/math_util.h" -#include "cc/resources/resource_util.h" #include "cc/tiles/tile_manager.h" +#include "components/viz/common/resources/resource_sizes.h" #include "components/viz/common/traced_value.h" namespace cc { @@ -81,7 +81,7 @@ size_t Tile::GPUMemoryUsageInBytes() const { if (draw_info_.resource_) { // We can use UncheckedSizeInBytes, since the tile size is determined by the // compositor. - return ResourceUtil::UncheckedSizeInBytes<size_t>( + return viz::ResourceSizes::UncheckedSizeInBytes<size_t>( draw_info_.resource_size(), draw_info_.resource_format()); } return 0; diff --git a/chromium/cc/tiles/tile_draw_info.cc b/chromium/cc/tiles/tile_draw_info.cc index 0e51c2b2402..0c09fa281d7 100644 --- a/chromium/cc/tiles/tile_draw_info.cc +++ b/chromium/cc/tiles/tile_draw_info.cc @@ -22,7 +22,8 @@ void TileDrawInfo::AsValueInto(base::trace_event::TracedValue* state) const { void TileDrawInfo::SetResource(ResourcePool::InUsePoolResource resource, bool resource_is_checker_imaged, - bool contents_swizzled) { + bool contents_swizzled, + bool is_premultiplied) { DCHECK(!resource_); DCHECK(resource); @@ -30,6 +31,7 @@ void TileDrawInfo::SetResource(ResourcePool::InUsePoolResource resource, is_resource_ready_to_draw_ = false; resource_is_checker_imaged_ = resource_is_checker_imaged; contents_swizzled_ = contents_swizzled; + is_premultiplied_ = is_premultiplied; resource_ = std::move(resource); } @@ -45,6 +47,7 @@ ResourcePool::InUsePoolResource TileDrawInfo::TakeResource() { is_resource_ready_to_draw_ = false; resource_is_checker_imaged_ = false; contents_swizzled_ = false; + is_premultiplied_ = false; return std::move(resource_); } diff --git a/chromium/cc/tiles/tile_draw_info.h b/chromium/cc/tiles/tile_draw_info.h index 5ae3cbe0f62..c401258c103 100644 --- a/chromium/cc/tiles/tile_draw_info.h +++ b/chromium/cc/tiles/tile_draw_info.h @@ -73,6 +73,7 @@ class CC_EXPORT TileDrawInfo { } bool contents_swizzled() const { return contents_swizzled_; } + bool is_premultiplied() const { return is_premultiplied_; } bool requires_resource() const { return mode_ == RESOURCE_MODE || mode_ == OOM_MODE; @@ -101,7 +102,8 @@ class CC_EXPORT TileDrawInfo { void SetResource(ResourcePool::InUsePoolResource resource, bool resource_is_checker_imaged, - bool contents_swizzled); + bool contents_swizzled, + bool is_premultiplied); ResourcePool::InUsePoolResource TakeResource(); void set_resource_ready_for_draw() { @@ -120,6 +122,7 @@ class CC_EXPORT TileDrawInfo { SkColor solid_color_ = SK_ColorWHITE; ResourcePool::InUsePoolResource resource_; bool contents_swizzled_ = false; + bool is_premultiplied_ = false; bool is_resource_ready_to_draw_ = false; // Set to true if |resource_| was rasterized with checker-imaged content. The diff --git a/chromium/cc/tiles/tile_manager.cc b/chromium/cc/tiles/tile_manager.cc index f6234675743..33324a69a7f 100644 --- a/chromium/cc/tiles/tile_manager.cc +++ b/chromium/cc/tiles/tile_manager.cc @@ -15,7 +15,6 @@ #include "base/json/json_writer.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram.h" #include "base/numerics/safe_conversions.h" #include "base/optional.h" @@ -28,9 +27,9 @@ #include "cc/raster/playback_image_provider.h" #include "cc/raster/raster_buffer.h" #include "cc/raster/task_category.h" -#include "cc/resources/resource_util.h" #include "cc/tiles/frame_viewer_instrumentation.h" #include "cc/tiles/tile.h" +#include "components/viz/common/resources/resource_sizes.h" #include "ui/gfx/geometry/axis_transform2d.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -369,7 +368,7 @@ TileManager::TileManager( base::Bind(&TileManager::CheckIfMoreTilesNeedToBePrepared, base::Unretained(this))), signals_check_notifier_(task_runner_, - base::Bind(&TileManager::CheckAndIssueSignals, + base::Bind(&TileManager::FlushAndIssueSignals, base::Unretained(this))), has_scheduled_tile_tasks_(false), prepare_tiles_count_(0u), @@ -415,13 +414,11 @@ void TileManager::SetResources(ResourcePool* resource_pool, ImageDecodeCache* image_decode_cache, TaskGraphRunner* task_graph_runner, RasterBufferProvider* raster_buffer_provider, - size_t scheduled_raster_task_limit, bool use_gpu_rasterization) { DCHECK(!tile_task_manager_); DCHECK(task_graph_runner); use_gpu_rasterization_ = use_gpu_rasterization; - scheduled_raster_task_limit_ = scheduled_raster_task_limit; resource_pool_ = resource_pool; image_controller_.SetImageDecodeCache(image_decode_cache); tile_task_manager_ = TileTaskManagerImpl::Create(task_graph_runner); @@ -444,7 +441,7 @@ void TileManager::DidFinishRunningTileTasksRequiredForActivation() { ScheduledTasksStateAsValue()); // TODO(vmpstr): Temporary check to debug crbug.com/642927. CHECK(tile_task_manager_); - signals_.ready_to_activate = true; + signals_.activate_tile_tasks_completed = true; signals_check_notifier_.Schedule(); } @@ -454,7 +451,7 @@ void TileManager::DidFinishRunningTileTasksRequiredForDraw() { ScheduledTasksStateAsValue()); // TODO(vmpstr): Temporary check to debug crbug.com/642927. CHECK(tile_task_manager_); - signals_.ready_to_draw = true; + signals_.draw_tile_tasks_completed = true; signals_check_notifier_.Schedule(); } @@ -494,12 +491,12 @@ bool TileManager::PrepareTiles( return false; } - signals_.reset(); + signals_ = Signals(); global_state_ = state; // Ensure that we don't schedule any decode work for checkered images until // the raster work for visible tiles is complete. This is done in - // CheckAndIssueSignals when the ready to activate/draw signals are dispatched + // FlushAndIssueSignals when the ready to activate/draw signals are dispatched // to the client. checker_image_tracker_.SetNoDecodesAllowed(); @@ -538,7 +535,7 @@ void TileManager::CheckForCompletedTasks() { tile_task_manager_->CheckForCompletedTasks(); did_check_for_completed_tasks_since_last_schedule_tasks_ = true; - CheckPendingGpuWorkTiles(true /* issue_signals */, false /* flush */); + CheckPendingGpuWorkAndIssueSignals(); TRACE_EVENT_INSTANT1( "cc", "TileManager::CheckForCompletedTasksFinished", @@ -885,14 +882,10 @@ void TileManager::PartitionImagesForCheckering( WhichTree tree = tile->tiling()->tree(); for (const auto* original_draw_image : images_in_tile) { - size_t frame_index = original_draw_image->paint_image().frame_index(); - if (tile_manager_settings_.enable_image_animations) { - const auto& image = original_draw_image->paint_image(); - frame_index = client_->GetFrameIndexForImage(image, tree); - if (image_to_frame_index) { - (*image_to_frame_index)[image.stable_id()] = frame_index; - } - } + const auto& image = original_draw_image->paint_image(); + size_t frame_index = client_->GetFrameIndexForImage(image, tree); + if (image_to_frame_index) + (*image_to_frame_index)[image.stable_id()] = frame_index; DrawImage draw_image(*original_draw_image, tile->raster_transform().scale(), frame_index, raster_color_space); @@ -915,11 +908,8 @@ void TileManager::AddCheckeredImagesToDecodeQueue( WhichTree tree = tile->tiling()->tree(); for (const auto* original_draw_image : images_in_tile) { - size_t frame_index = original_draw_image->paint_image().frame_index(); - if (tile_manager_settings_.enable_image_animations) { - frame_index = client_->GetFrameIndexForImage( - original_draw_image->paint_image(), tree); - } + size_t frame_index = client_->GetFrameIndexForImage( + original_draw_image->paint_image(), tree); DrawImage draw_image(*original_draw_image, tile->raster_transform().scale(), frame_index, raster_color_space); if (checker_image_tracker_.ShouldCheckerImage(draw_image, tree)) { @@ -1221,7 +1211,7 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask( } void TileManager::ResetSignalsForTesting() { - signals_.reset(); + signals_ = Signals(); } void TileManager::OnRasterTaskCompleted( @@ -1279,9 +1269,11 @@ void TileManager::OnRasterTaskCompleted( TileDrawInfo& draw_info = tile->draw_info(); bool needs_swizzle = raster_buffer_provider_->IsResourceSwizzleRequired(!tile->is_opaque()); + bool is_premultiplied = + raster_buffer_provider_->IsResourcePremultiplied(!tile->is_opaque()); draw_info.SetResource(std::move(resource), raster_task_was_scheduled_with_checker_images, - needs_swizzle); + needs_swizzle, is_premultiplied); if (raster_task_was_scheduled_with_checker_images) num_of_tiles_with_checker_images_++; @@ -1359,30 +1351,34 @@ bool TileManager::IsReadyToDraw() const { RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW); } -void TileManager::CheckAndIssueSignals() { - TRACE_EVENT0("cc", "TileManager::CheckAndIssueSignals"); +void TileManager::FlushAndIssueSignals() { + TRACE_EVENT0("cc", "TileManager::FlushAndIssueSignals"); tile_task_manager_->CheckForCompletedTasks(); did_check_for_completed_tasks_since_last_schedule_tasks_ = true; - CheckPendingGpuWorkTiles(false /* issue_signals */, true /* flush */); + raster_buffer_provider_->Flush(); + CheckPendingGpuWorkAndIssueSignals(); +} +void TileManager::IssueSignals() { // Ready to activate. - if (signals_.ready_to_activate && !signals_.did_notify_ready_to_activate) { - signals_.ready_to_activate = false; + if (signals_.activate_tile_tasks_completed && + signals_.activate_gpu_work_completed && + !signals_.did_notify_ready_to_activate) { if (IsReadyToActivate()) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "TileManager::CheckAndIssueSignals - ready to activate"); + "TileManager::IssueSignals - ready to activate"); signals_.did_notify_ready_to_activate = true; client_->NotifyReadyToActivate(); } } // Ready to draw. - if (signals_.ready_to_draw && !signals_.did_notify_ready_to_draw) { - signals_.ready_to_draw = false; + if (signals_.draw_tile_tasks_completed && signals_.draw_gpu_work_completed && + !signals_.did_notify_ready_to_draw) { if (IsReadyToDraw()) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "TileManager::CheckAndIssueSignals - ready to draw"); + "TileManager::IssueSignals - ready to draw"); signals_.did_notify_ready_to_draw = true; client_->NotifyReadyToDraw(); } @@ -1391,11 +1387,9 @@ void TileManager::CheckAndIssueSignals() { // All tile tasks completed. if (signals_.all_tile_tasks_completed && !signals_.did_notify_all_tile_tasks_completed) { - signals_.all_tile_tasks_completed = false; if (!has_scheduled_tile_tasks_) { - TRACE_EVENT0( - TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "TileManager::CheckAndIssueSignals - all tile tasks completed"); + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "TileManager::IssueSignals - all tile tasks completed"); signals_.did_notify_all_tile_tasks_completed = true; client_->NotifyAllTileTasksCompleted(); } @@ -1451,8 +1445,8 @@ void TileManager::CheckIfMoreTilesNeedToBePrepared() { CHECK(tile_task_manager_); // Schedule all checks in case we're left with solid color tiles only. - signals_.ready_to_activate = true; - signals_.ready_to_draw = true; + signals_.activate_tile_tasks_completed = true; + signals_.draw_tile_tasks_completed = true; signals_.all_tile_tasks_completed = true; signals_check_notifier_.Schedule(); @@ -1533,8 +1527,10 @@ TileManager::ScheduledTasksStateAsValue() const { std::unique_ptr<base::trace_event::TracedValue> state( new base::trace_event::TracedValue()); state->BeginDictionary("tasks_pending"); - state->SetBoolean("ready_to_activate", signals_.ready_to_activate); - state->SetBoolean("ready_to_draw", signals_.ready_to_draw); + state->SetBoolean("activate_tile_tasks_completed", + signals_.activate_tile_tasks_completed); + state->SetBoolean("draw_tile_tasks_completed", + signals_.draw_tile_tasks_completed); state->SetBoolean("all_tile_tasks_completed", signals_.all_tile_tasks_completed); state->EndDictionary(); @@ -1546,15 +1542,12 @@ bool TileManager::UsePartialRaster() const { raster_buffer_provider_->CanPartialRasterIntoProvidedResource(); } -void TileManager::CheckPendingGpuWorkTiles(bool issue_signals, bool flush) { - TRACE_EVENT2("cc", "TileManager::CheckPendingGpuWorkTiles", +void TileManager::CheckPendingGpuWorkAndIssueSignals() { + TRACE_EVENT2("cc", "TileManager::CheckPendingGpuWorkAndIssueSignals", "pending_gpu_work_tiles", pending_gpu_work_tiles_.size(), "tree_priority", TreePriorityToString(global_state_.tree_priority)); - if (flush) - raster_buffer_provider_->Flush(); - std::vector<const ResourcePool::InUsePoolResource*> required_for_activation; std::vector<const ResourcePool::InUsePoolResource*> required_for_draw; @@ -1592,9 +1585,8 @@ void TileManager::CheckPendingGpuWorkTiles(bool issue_signals, bool flush) { pending_required_for_activation_callback_id_ = raster_buffer_provider_->SetReadyToDrawCallback( required_for_activation, - base::Bind(&TileManager::CheckPendingGpuWorkTiles, - ready_to_draw_callback_weak_ptr_factory_.GetWeakPtr(), - true /* issue_signals */, false /* flush */), + base::Bind(&TileManager::CheckPendingGpuWorkAndIssueSignals, + ready_to_draw_callback_weak_ptr_factory_.GetWeakPtr()), pending_required_for_activation_callback_id_); } @@ -1604,22 +1596,21 @@ void TileManager::CheckPendingGpuWorkTiles(bool issue_signals, bool flush) { pending_required_for_draw_callback_id_ = raster_buffer_provider_->SetReadyToDrawCallback( required_for_draw, - base::Bind(&TileManager::CheckPendingGpuWorkTiles, - ready_to_draw_callback_weak_ptr_factory_.GetWeakPtr(), - true /* issue_signals */, false /* flush */), + base::Bind(&TileManager::CheckPendingGpuWorkAndIssueSignals, + ready_to_draw_callback_weak_ptr_factory_.GetWeakPtr()), pending_required_for_draw_callback_id_); } // Update our signals now that we know whether we have pending resources. - signals_.ready_to_activate = + signals_.activate_gpu_work_completed = (pending_required_for_activation_callback_id_ == 0); - signals_.ready_to_draw = (pending_required_for_draw_callback_id_ == 0); - - if (issue_signals && (signals_.ready_to_activate || signals_.ready_to_draw)) - signals_check_notifier_.Schedule(); + signals_.draw_gpu_work_completed = + (pending_required_for_draw_callback_id_ == 0); // We've just updated all pending tile requirements if necessary. pending_tile_requirements_dirty_ = false; + + IssueSignals(); } // Utility function that can be used to create a "Task set finished" task that @@ -1720,8 +1711,8 @@ TileManager::MemoryUsage TileManager::MemoryUsage::FromConfig( // We can use UncheckedSizeInBytes here since this is used with a tile // size which is determined by the compositor (it's at most max texture // size). - return MemoryUsage(ResourceUtil::UncheckedSizeInBytes<size_t>(size, format), - 1); + return MemoryUsage( + viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format), 1); } // static @@ -1760,19 +1751,6 @@ bool TileManager::MemoryUsage::Exceeds(const MemoryUsage& limit) const { resource_count_ > limit.resource_count_; } -TileManager::Signals::Signals() { - reset(); -} - -void TileManager::Signals::reset() { - ready_to_activate = false; - did_notify_ready_to_activate = false; - ready_to_draw = false; - did_notify_ready_to_draw = false; - all_tile_tasks_completed = false; - did_notify_all_tile_tasks_completed = false; -} - TileManager::PrioritizedWorkToSchedule::PrioritizedWorkToSchedule() = default; TileManager::PrioritizedWorkToSchedule::PrioritizedWorkToSchedule( PrioritizedWorkToSchedule&& other) = default; diff --git a/chromium/cc/tiles/tile_manager.h b/chromium/cc/tiles/tile_manager.h index 1c4a90d631a..32e9a1ea563 100644 --- a/chromium/cc/tiles/tile_manager.h +++ b/chromium/cc/tiles/tile_manager.h @@ -156,7 +156,6 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { ImageDecodeCache* image_decode_cache, TaskGraphRunner* task_graph_runner, RasterBufferProvider* raster_buffer_provider, - size_t scheduled_raster_task_limit, bool use_gpu_rasterization); // This causes any completed raster work to finalize, so that tiles get up to @@ -205,7 +204,7 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(1), 1); } resource_pool_->PrepareForExport(resource); - draw_info.SetResource(std::move(resource), false, false); + draw_info.SetResource(std::move(resource), false, false, false); draw_info.set_resource_ready_for_draw(); } } @@ -313,16 +312,16 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { }; struct Signals { - Signals(); + bool activate_tile_tasks_completed = false; + bool draw_tile_tasks_completed = false; + bool all_tile_tasks_completed = false; - void reset(); + bool activate_gpu_work_completed = false; + bool draw_gpu_work_completed = false; - bool ready_to_activate; - bool did_notify_ready_to_activate; - bool ready_to_draw; - bool did_notify_ready_to_draw; - bool all_tile_tasks_completed; - bool did_notify_all_tile_tasks_completed; + bool did_notify_ready_to_activate = false; + bool did_notify_ready_to_draw = false; + bool did_notify_all_tile_tasks_completed = false; }; struct PrioritizedWorkToSchedule { @@ -359,7 +358,6 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { bool TilePriorityViolatesMemoryPolicy(const TilePriority& priority); bool AreRequiredTilesReadyToDraw(RasterTilePriorityQueue::Type type) const; void CheckIfMoreTilesNeedToBePrepared(); - void CheckAndIssueSignals(); void MarkTilesOutOfMemory( std::unique_ptr<RasterTilePriorityQueue> queue) const; @@ -392,7 +390,9 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { bool UsePartialRaster() const; - void CheckPendingGpuWorkTiles(bool issue_signals, bool flush); + void FlushAndIssueSignals(); + void CheckPendingGpuWorkAndIssueSignals(); + void IssueSignals(); TileManagerClient* client_; base::SequencedTaskRunner* task_runner_; @@ -435,7 +435,7 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { uint64_t pending_required_for_activation_callback_id_ = 0; uint64_t pending_required_for_draw_callback_id_ = 0; // If true, we should re-compute tile requirements in - // CheckPendingGpuWorkTiles. + // CheckPendingGpuWorkAndIssueSignals. bool pending_tile_requirements_dirty_ = false; std::unordered_map<Tile::Id, std::vector<DrawImage>> scheduled_draw_images_; diff --git a/chromium/cc/tiles/tile_manager_perftest.cc b/chromium/cc/tiles/tile_manager_perftest.cc index 421848a937b..6bb3562ab13 100644 --- a/chromium/cc/tiles/tile_manager_perftest.cc +++ b/chromium/cc/tiles/tile_manager_perftest.cc @@ -7,7 +7,6 @@ #include "base/lazy_instance.h" #include "base/location.h" -#include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "cc/base/lap_timer.h" diff --git a/chromium/cc/tiles/tile_manager_settings.h b/chromium/cc/tiles/tile_manager_settings.h index 7a3d158af07..6a7f7fb1f0c 100644 --- a/chromium/cc/tiles/tile_manager_settings.h +++ b/chromium/cc/tiles/tile_manager_settings.h @@ -13,7 +13,6 @@ struct CC_EXPORT TileManagerSettings { bool use_partial_raster = false; bool enable_checker_imaging = false; size_t min_image_bytes_to_checker = 1 * 1024 * 1024; - bool enable_image_animations = false; }; } // namespace cc diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc index e3abdb183d8..8c9a5f4f19a 100644 --- a/chromium/cc/tiles/tile_manager_unittest.cc +++ b/chromium/cc/tiles/tile_manager_unittest.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" @@ -18,7 +17,6 @@ #include "cc/raster/raster_source.h" #include "cc/raster/synchronous_task_graph_runner.h" #include "cc/resources/resource_pool.h" -#include "cc/resources/resource_util.h" #include "cc/test/fake_impl_task_runner_provider.h" #include "cc/test/fake_layer_tree_frame_sink.h" #include "cc/test/fake_layer_tree_frame_sink_client.h" @@ -40,6 +38,7 @@ #include "cc/tiles/tile_priority.h" #include "cc/tiles/tiling_set_raster_queue_all.h" #include "cc/trees/layer_tree_impl.h" +#include "components/viz/common/resources/resource_sizes.h" #include "components/viz/test/begin_frame_args_test.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -1370,8 +1369,9 @@ TEST_F(TileManagerTilePriorityQueueTest, host_impl()->resource_provider()->best_texture_format()); ManagedMemoryPolicy policy = host_impl()->ActualManagedMemoryPolicy(); - policy.bytes_limit_when_visible = ResourceUtil::UncheckedSizeInBytes<size_t>( - gfx::Size(256, 256), viz::RGBA_8888); + policy.bytes_limit_when_visible = + viz::ResourceSizes::UncheckedSizeInBytes<size_t>(gfx::Size(256, 256), + viz::RGBA_8888); host_impl()->SetMemoryPolicy(policy); EXPECT_FALSE(host_impl()->is_likely_to_require_a_draw()); @@ -1523,6 +1523,62 @@ TEST_F(TileManagerTilePriorityQueueTest, NoRasterTasksforSolidColorTiles) { } } +class TestSoftwareBacking : public ResourcePool::SoftwareBacking { + public: + // No tracing is done during these tests. + base::UnguessableToken SharedMemoryGuid() override { return {}; } + + std::unique_ptr<uint32_t[]> pixels; +}; + +// A RasterBufferProvider that allocates software backings with a standard +// array as the backing. Overrides Playback() on the RasterBuffer to raster +// into the pixels in the array. +class TestSoftwareRasterBufferProvider : public FakeRasterBufferProviderImpl { + public: + std::unique_ptr<RasterBuffer> AcquireBufferForRaster( + const ResourcePool::InUsePoolResource& resource, + uint64_t resource_content_id, + uint64_t previous_content_id) override { + if (!resource.software_backing()) { + auto backing = std::make_unique<TestSoftwareBacking>(); + backing->shared_bitmap_id = viz::SharedBitmap::GenerateId(); + backing->pixels = std::make_unique<uint32_t[]>( + viz::ResourceSizes::CheckedSizeInBytes<size_t>(resource.size(), + viz::RGBA_8888)); + resource.set_software_backing(std::move(backing)); + } + auto* backing = + static_cast<TestSoftwareBacking*>(resource.software_backing()); + return std::make_unique<TestRasterBuffer>(resource.size(), + backing->pixels.get()); + } + + private: + class TestRasterBuffer : public RasterBuffer { + public: + TestRasterBuffer(const gfx::Size& size, void* pixels) + : size_(size), pixels_(pixels) {} + + void Playback( + const RasterSource* raster_source, + const gfx::Rect& raster_full_rect, + const gfx::Rect& raster_dirty_rect, + uint64_t new_content_id, + const gfx::AxisTransform2d& transform, + const RasterSource::PlaybackSettings& playback_settings) override { + RasterBufferProvider::PlaybackToMemory( + pixels_, viz::RGBA_8888, size_, /*stride=*/0, raster_source, + raster_full_rect, /*playback_rect=*/raster_full_rect, transform, + gfx::ColorSpace(), playback_settings); + } + + private: + gfx::Size size_; + void* pixels_; + }; +}; + class TileManagerTest : public TestLayerTreeHostBase { public: // MockLayerTreeHostImpl allows us to intercept tile manager callbacks. @@ -1665,7 +1721,20 @@ TEST_F(TileManagerTest, ActivateAndDrawWhenOOM) { } } -TEST_F(TileManagerTest, LowResHasNoImage) { +class PixelInspectTileManagerTest : public TileManagerTest { + public: + void SetUp() override { + TileManagerTest::SetUp(); + // Use a RasterBufferProvider that will let us inspect pixels. + host_impl()->tile_manager()->SetRasterBufferProviderForTesting( + &raster_buffer_provider_); + } + + private: + TestSoftwareRasterBufferProvider raster_buffer_provider_; +}; + +TEST_F(PixelInspectTileManagerTest, LowResHasNoImage) { gfx::Size size(10, 12); TileResolution resolutions[] = {HIGH_RESOLUTION, LOW_RESOLUTION}; @@ -1731,10 +1800,10 @@ TEST_F(TileManagerTest, LowResHasNoImage) { resource_size.height()); // CreateLayerTreeFrameSink() sets up a software compositing, so the // tile resource will be a bitmap. - viz::SharedBitmap* shared_bitmap = - tile->draw_info().GetResource().shared_bitmap(); + auto* backing = static_cast<TestSoftwareBacking*>( + tile->draw_info().GetResource().software_backing()); SkBitmap bitmap; - bitmap.installPixels(info, shared_bitmap->pixels(), info.minRowBytes()); + bitmap.installPixels(info, backing->pixels.get(), info.minRowBytes()); for (int x = 0; x < size.width(); ++x) { for (int y = 0; y < size.height(); ++y) { @@ -1933,9 +2002,7 @@ void RunPartialRasterCheck(std::unique_ptr<LayerTreeHostImpl> host_impl, host_impl->resource_pool()->AcquireResource(kTileSize, viz::RGBA_8888, gfx::ColorSpace()); - resource.set_shared_bitmap(host_impl->layer_tree_frame_sink() - ->shared_bitmap_manager() - ->AllocateSharedBitmap(kTileSize)); + resource.set_software_backing(std::make_unique<TestSoftwareBacking>()); host_impl->resource_pool()->PrepareForExport(resource); host_impl->resource_pool()->OnContentReplaced(resource, kInvalidatedId); @@ -2106,9 +2173,8 @@ class MockReadyToDrawRasterBufferProviderImpl const ResourcePool::InUsePoolResource& resource, uint64_t resource_content_id, uint64_t previous_content_id) override { - if (!resource.shared_bitmap()) - resource.set_shared_bitmap( - shared_bitmap_manager_.AllocateSharedBitmap(resource.size())); + if (!resource.software_backing()) + resource.set_software_backing(std::make_unique<TestSoftwareBacking>()); return std::make_unique<FakeRasterBuffer>(); } @@ -2123,8 +2189,6 @@ class MockReadyToDrawRasterBufferProviderImpl const gfx::AxisTransform2d& transform, const RasterSource::PlaybackSettings& playback_settings) override {} }; - - viz::TestSharedBitmapManager shared_bitmap_manager_; }; class TileManagerReadyToDrawTest : public TileManagerTest { diff --git a/chromium/cc/trees/clip_node.cc b/chromium/cc/trees/clip_node.cc index 3c9c58fcece..c1a73a952c5 100644 --- a/chromium/cc/trees/clip_node.cc +++ b/chromium/cc/trees/clip_node.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event_argument.h" #include "cc/base/math_util.h" #include "cc/layers/layer.h" diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index bfddc4c0eb6..d3c1958a13c 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -677,6 +677,11 @@ static gfx::Rect LayerVisibleRect(PropertyTrees* property_trees, bool non_root_copy_request_or_cache_render_surface = lower_effect_closest_ancestor > EffectTree::kContentsRootNodeId; gfx::Rect layer_content_rect = gfx::Rect(layer->bounds()); + if (layer->layer_tree_impl()->IsRootLayer(layer) && + !layer->layer_tree_impl()->viewport_visible_rect().IsEmpty()) { + layer_content_rect.Intersect( + layer->layer_tree_impl()->viewport_visible_rect()); + } gfx::RectF accumulated_clip_in_root_space; if (non_root_copy_request_or_cache_render_surface) { bool include_expanding_clips = true; diff --git a/chromium/cc/trees/frame_token_allocator.cc b/chromium/cc/trees/frame_token_allocator.cc new file mode 100644 index 00000000000..4f6ec1cf91c --- /dev/null +++ b/chromium/cc/trees/frame_token_allocator.cc @@ -0,0 +1,26 @@ +// 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 new file mode 100644 index 00000000000..1bb978d631f --- /dev/null +++ b/chromium/cc/trees/frame_token_allocator.h @@ -0,0 +1,52 @@ +// 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/image_animation_controller.cc b/chromium/cc/trees/image_animation_controller.cc index 1a19cbc06ac..a781c08f8cd 100644 --- a/chromium/cc/trees/image_animation_controller.cc +++ b/chromium/cc/trees/image_animation_controller.cc @@ -21,8 +21,10 @@ const base::TimeDelta kAnimationResyncCutoff = base::TimeDelta::FromMinutes(5); ImageAnimationController::ImageAnimationController( base::SingleThreadTaskRunner* task_runner, - base::Closure invalidation_callback) - : notifier_(task_runner, invalidation_callback) {} + base::RepeatingClosure invalidation_callback, + bool enable_image_animation_resync) + : notifier_(task_runner, invalidation_callback), + enable_image_animation_resync_(enable_image_animation_resync) {} ImageAnimationController::~ImageAnimationController() = default; @@ -71,7 +73,7 @@ const PaintImageIdFlatSet& ImageAnimationController::AnimateForSyncTree( // If we were able to advance this animation, invalidate it on the sync // tree. - if (state.AdvanceFrame(now)) + if (state.AdvanceFrame(now, enable_image_animation_resync_)) images_animated_on_sync_tree_.insert(id); // Update the next invalidation time to the earliest time at which we need @@ -137,8 +139,21 @@ void ImageAnimationController::DidActivate() { DCHECK(it != animation_state_map_.end()); it->second.PushPendingToActive(); } - images_animated_on_sync_tree_.clear(); + + // We would retain state for images with no drivers (no recordings) to allow + // resuming of animations. However, since the animation will be re-started + // from the beginning after navigation, we can avoid maintaining the state. + if (did_navigate_) { + for (auto it = animation_state_map_.begin(); + it != animation_state_map_.end();) { + if (it->second.has_drivers()) + it++; + else + it = animation_state_map_.erase(it); + } + did_navigate_ = false; + } } size_t ImageAnimationController::GetFrameIndexForImage( @@ -216,7 +231,8 @@ bool ImageAnimationController::AnimationState::ShouldAnimate() const { } bool ImageAnimationController::AnimationState::AdvanceFrame( - base::TimeTicks now) { + base::TimeTicks now, + bool enable_image_animation_resync) { DCHECK(ShouldAnimate()); // Start the animation from the first frame, if not yet started. We don't need @@ -240,7 +256,8 @@ bool ImageAnimationController::AnimationState::AdvanceFrame( // up and start again from the current frame. // Note that we don't need to invalidate this image since the active tree // is already displaying the current frame. - if (now - next_desired_frame_time_ > kAnimationResyncCutoff) { + if (enable_image_animation_resync && + now - next_desired_frame_time_ > kAnimationResyncCutoff) { DCHECK_EQ(pending_index_, active_index_); next_desired_frame_time_ = now + frames_[pending_index_].duration; return false; @@ -268,8 +285,8 @@ bool ImageAnimationController::AnimationState::AdvanceFrame( // pages that try to sync an image and some other resource (e.g. audio), // especially if users switch tabs (and thus stop drawing the animation, // which will pause it) during that initial loop, then switch back later. - if (next_frame_index == 0u && repetitions_completed_ == 1 && - next_desired_frame_time <= now) { + if (enable_image_animation_resync && next_frame_index == 0u && + repetitions_completed_ == 1 && next_desired_frame_time <= now) { pending_index_ = 0u; next_desired_frame_time_ = now + frames_[0].duration; repetitions_completed_ = 0; @@ -369,7 +386,7 @@ size_t ImageAnimationController::AnimationState::NextFrameIndex() const { ImageAnimationController::DelayedNotifier::DelayedNotifier( base::SingleThreadTaskRunner* task_runner, - base::Closure closure) + base::RepeatingClosure closure) : task_runner_(task_runner), closure_(std::move(closure)), weak_factory_(this) { diff --git a/chromium/cc/trees/image_animation_controller.h b/chromium/cc/trees/image_animation_controller.h index 13a85bbc206..ec73e0419a1 100644 --- a/chromium/cc/trees/image_animation_controller.h +++ b/chromium/cc/trees/image_animation_controller.h @@ -58,8 +58,11 @@ class CC_EXPORT ImageAnimationController { // created. // |task_runner| is the thread on which the controller is used. The // invalidation_callback can only be run on this thread. + // |enable_image_animation_resync| specifies whether the animation can be + // reset to the beginning to avoid skipping many frames. ImageAnimationController(base::SingleThreadTaskRunner* task_runner, - base::Closure invalidation_callback); + base::RepeatingClosure invalidation_callback, + bool enable_image_animation_resync); ~ImageAnimationController(); // Called to update the state for a PaintImage received in a new recording. @@ -96,11 +99,17 @@ class CC_EXPORT ImageAnimationController { size_t GetFrameIndexForImage(PaintImage::Id paint_image_id, WhichTree tree) const; + void set_did_navigate() { did_navigate_ = true; }; + const base::flat_set<AnimationDriver*>& GetDriversForTesting( PaintImage::Id paint_image_id) const; size_t GetLastNumOfFramesSkippedForTesting( PaintImage::Id paint_image_id) const; + size_t animation_state_map_size_for_testing() { + return animation_state_map_.size(); + } + private: class AnimationState { public: @@ -110,7 +119,7 @@ class CC_EXPORT ImageAnimationController { ~AnimationState(); bool ShouldAnimate() const; - bool AdvanceFrame(base::TimeTicks now); + bool AdvanceFrame(base::TimeTicks now, bool enable_image_animation_resync); void UpdateMetadata(const DiscardableImageMap::AnimatedImageMetadata& data); void PushPendingToActive(); @@ -193,7 +202,7 @@ class CC_EXPORT ImageAnimationController { class DelayedNotifier { public: DelayedNotifier(base::SingleThreadTaskRunner* task_runner, - base::Closure closure); + base::RepeatingClosure closure); ~DelayedNotifier(); void Schedule(base::TimeTicks now, base::TimeTicks notification_time); @@ -204,7 +213,7 @@ class CC_EXPORT ImageAnimationController { void Notify(); base::SingleThreadTaskRunner* task_runner_; - base::Closure closure_; + base::RepeatingClosure closure_; // Set if a notification is currently pending. base::Optional<base::TimeTicks> pending_notification_time_; @@ -231,6 +240,10 @@ class CC_EXPORT ImageAnimationController { PaintImageIdFlatSet images_animated_on_sync_tree_; DelayedNotifier notifier_; + + const bool enable_image_animation_resync_; + + bool did_navigate_ = false; }; } // namespace cc diff --git a/chromium/cc/trees/image_animation_controller_unittest.cc b/chromium/cc/trees/image_animation_controller_unittest.cc index 1a4c486dc14..ea59f849c4c 100644 --- a/chromium/cc/trees/image_animation_controller_unittest.cc +++ b/chromium/cc/trees/image_animation_controller_unittest.cc @@ -78,7 +78,8 @@ class ImageAnimationControllerTest : public testing::Test { base::Bind(&ImageAnimationControllerTest::RequestInvalidation, base::Unretained(this)); controller_ = std::make_unique<ImageAnimationController>( - task_runner_.get(), invalidation_callback); + task_runner_.get(), invalidation_callback, + GetEnableImageAnimationResync()); now_ += base::TimeDelta::FromSeconds(10); } @@ -151,6 +152,8 @@ class ImageAnimationControllerTest : public testing::Test { void AdvanceNow(base::TimeDelta delta) { now_ += delta; } + virtual bool GetEnableImageAnimationResync() const { return true; } + base::TimeTicks now_; int invalidation_count_ = 0; std::unique_ptr<ImageAnimationController> controller_; @@ -734,4 +737,154 @@ TEST_F(ImageAnimationControllerTest, ResetAnimations) { controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); } +TEST_F(ImageAnimationControllerTest, ResetAnimationStateMapOnNavigation) { + std::vector<FrameMetadata> first_image_frames = { + FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), + FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; + DiscardableImageMap::AnimatedImageMetadata first_data( + PaintImage::GetNextId(), PaintImage::CompletionState::DONE, + first_image_frames, kAnimationLoopOnce, 0); + controller_->UpdateAnimatedImage(first_data); + FakeAnimationDriver first_driver; + controller_->RegisterAnimationDriver(first_data.paint_image_id, + &first_driver); + + std::vector<FrameMetadata> second_image_frames = { + FrameMetadata(true, base::TimeDelta::FromMilliseconds(5)), + FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; + DiscardableImageMap::AnimatedImageMetadata second_data( + PaintImage::GetNextId(), PaintImage::CompletionState::DONE, + second_image_frames, kAnimationLoopOnce, 0); + controller_->UpdateAnimatedImage(second_data); + FakeAnimationDriver second_driver; + controller_->RegisterAnimationDriver(second_data.paint_image_id, + &second_driver); + + controller_->AnimateForSyncTree(now_); + + controller_->UnregisterAnimationDriver(first_data.paint_image_id, + &first_driver); + EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 2u); + + // Fake navigation and activation. + controller_->set_did_navigate(); + controller_->DidActivate(); + + // Animation state map entries without drivers will be purged on navigation. + EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 1u); + + controller_->UnregisterAnimationDriver(second_data.paint_image_id, + &second_driver); + + EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 1u); +} + +class ImageAnimationControllerNoResyncTest + : public ImageAnimationControllerTest { + protected: + bool GetEnableImageAnimationResync() const override { return false; } +}; + +TEST_F(ImageAnimationControllerNoResyncTest, NoSyncCutoffAfterIdle) { + std::vector<FrameMetadata> frames = { + FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), + FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; + + DiscardableImageMap::AnimatedImageMetadata data( + PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, + kAnimationLoopInfinite, 0); + controller_->UpdateAnimatedImage(data); + FakeAnimationDriver driver; + controller_->RegisterAnimationDriver(data.paint_image_id, &driver); + controller_->UpdateStateFromDrivers(now_); + + // Advance the first frame. + task_runner_->VerifyDelay(base::TimeDelta()); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(invalidation_count_, 1); + auto animated_images = controller_->AnimateForSyncTree(now_); + EXPECT_EQ(animated_images.size(), 0u); + EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, + WhichTree::PENDING_TREE), + 0u); + EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, + WhichTree::ACTIVE_TREE), + 0u); + controller_->DidActivate(); + + // Invalidation request for the second frame. + task_runner_->VerifyDelay(frames[0].duration); + + // Advance the time by 10 min (divisible by animation duration) and first + // frame duration. + AdvanceNow(base::TimeDelta::FromMinutes(10) + frames[0].duration); + + // Animate again, it should not restart from the start. Should display second + // animation frame. + animated_images = controller_->AnimateForSyncTree(now_); + EXPECT_EQ(animated_images.size(), 1u); + EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, + WhichTree::PENDING_TREE), + 1u); + EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, + WhichTree::ACTIVE_TREE), + 0u); + controller_->DidActivate(); + + // New invalidation request since the desired invalidation time changed. + task_runner_->VerifyDelay(frames[1].duration); + invalidation_count_ = 0; + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(invalidation_count_, 1); + + controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); +} + +TEST_F(ImageAnimationControllerNoResyncTest, SkipsLoopsAfterFirstIteration) { + std::vector<FrameMetadata> frames = { + FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), + FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), + FrameMetadata(true, base::TimeDelta::FromMilliseconds(4)), + FrameMetadata(true, base::TimeDelta::FromMilliseconds(5))}; + + DiscardableImageMap::AnimatedImageMetadata data( + PaintImage::GetNextId(), PaintImage::CompletionState::PARTIALLY_DONE, + frames, kAnimationLoopInfinite, 0); + controller_->UpdateAnimatedImage(data); + FakeAnimationDriver driver; + controller_->RegisterAnimationDriver(data.paint_image_id, &driver); + controller_->UpdateStateFromDrivers(now_); + + // Perform the first loop while the image is partially loaded, until the third + // frame. + LoopOnceNoDelay(data.paint_image_id, frames, 3u, 0); + + // The invalidation has been scheduled with a delay for the third frame's + // duration. + task_runner_->VerifyDelay(frames[2].duration); + + // |now_| is set to the desired time for the fourth frame. Advance further so + // we reach the time for the second frame. + AdvanceNow(frames[3].duration + frames[0].duration); + + // Finish the image load. + data.completion_state = PaintImage::CompletionState::DONE; + controller_->UpdateAnimatedImage(data); + controller_->UpdateStateFromDrivers(now_); + + // Invalidation is scheduled immediately because we are way past the desired + // time. We skip frames even after the image is loaded. + task_runner_->VerifyDelay(base::TimeDelta()); + auto animated_images = controller_->AnimateForSyncTree(now_); + EXPECT_EQ(animated_images.size(), 1u); + EXPECT_EQ(animated_images.count(data.paint_image_id), 1u); + EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, + WhichTree::PENDING_TREE), + 1u); + EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, + WhichTree::ACTIVE_TREE), + 2u); + controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); +} + } // namespace cc diff --git a/chromium/cc/trees/latency_info_swap_promise.cc b/chromium/cc/trees/latency_info_swap_promise.cc index 3dcaf1ecf4b..6ebf5352014 100644 --- a/chromium/cc/trees/latency_info_swap_promise.cc +++ b/chromium/cc/trees/latency_info_swap_promise.cc @@ -33,7 +33,9 @@ LatencyInfoSwapPromise::LatencyInfoSwapPromise(const ui::LatencyInfo& latency) LatencyInfoSwapPromise::~LatencyInfoSwapPromise() = default; -void LatencyInfoSwapPromise::WillSwap(viz::CompositorFrameMetadata* metadata) { +void LatencyInfoSwapPromise::WillSwap( + viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator) { DCHECK(!latency_.terminated()); metadata->latency_info.push_back(latency_); } diff --git a/chromium/cc/trees/latency_info_swap_promise.h b/chromium/cc/trees/latency_info_swap_promise.h index 2b23d67dc80..bdccb3eb546 100644 --- a/chromium/cc/trees/latency_info_swap_promise.h +++ b/chromium/cc/trees/latency_info_swap_promise.h @@ -19,7 +19,8 @@ class CC_EXPORT LatencyInfoSwapPromise : public SwapPromise { ~LatencyInfoSwapPromise() override; void DidActivate() override {} - void WillSwap(viz::CompositorFrameMetadata* metadata) override; + void WillSwap(viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator) override; void DidSwap() override; DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override; void OnCommit() override; diff --git a/chromium/cc/trees/layer_tree_frame_sink.h b/chromium/cc/trees/layer_tree_frame_sink.h index c0971b59bca..f61c7a6c019 100644 --- a/chromium/cc/trees/layer_tree_frame_sink.h +++ b/chromium/cc/trees/layer_tree_frame_sink.h @@ -35,6 +35,7 @@ struct BeginFrameAck; namespace cc { class LayerTreeFrameSinkClient; +class LayerTreeHostImpl; // An interface for submitting CompositorFrames to a display compositor // which will compose frames from multiple clients to show on screen to the @@ -114,6 +115,10 @@ class CC_EXPORT LayerTreeFrameSink : public viz::ContextLostObserver { return shared_bitmap_manager_; } + // Generate hit test region list based on LayerTreeHostImpl, the data will be + // submitted with compositor frame. + virtual void UpdateHitTestData(const LayerTreeHostImpl* host_impl) {} + // If supported, this sets the viz::LocalSurfaceId the LayerTreeFrameSink will // use to submit a CompositorFrame. virtual void SetLocalSurfaceId(const viz::LocalSurfaceId& local_surface_id) {} diff --git a/chromium/cc/trees/layer_tree_frame_sink_unittest.cc b/chromium/cc/trees/layer_tree_frame_sink_unittest.cc index 7ccde476682..8ac72b45da5 100644 --- a/chromium/cc/trees/layer_tree_frame_sink_unittest.cc +++ b/chromium/cc/trees/layer_tree_frame_sink_unittest.cc @@ -4,7 +4,6 @@ #include "cc/trees/layer_tree_frame_sink.h" -#include "base/memory/ptr_util.h" #include "base/test/test_simple_task_runner.h" #include "cc/test/fake_layer_tree_frame_sink_client.h" #include "components/viz/common/quads/compositor_frame.h" diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index 7ea19e9fbd8..d8bf091317a 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -17,7 +17,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/location.h" -#include "base/memory/ptr_util.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_math.h" #include "base/single_thread_task_runner.h" @@ -28,6 +28,7 @@ #include "base/timer/elapsed_timer.h" #include "base/trace_event/trace_event.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" @@ -336,7 +337,7 @@ void LayerTreeHost::FinishCommitOnImplThread( if (did_navigate) { TRACE_EVENT0("cc,benchmark", "LayerTreeHost::DidNavigate"); proxy_->ClearHistoryOnNavigation(); - host_impl->ClearImageCacheOnNavigation(); + host_impl->DidNavigate(); } { @@ -669,12 +670,16 @@ bool LayerTreeHost::UpdateLayers() { micro_benchmark_controller_.DidUpdateLayers(); if (const char* client_name = GetClientNameForMetrics()) { + auto elapsed = timer.Elapsed().InMicroseconds(); + std::string histogram_name = - base::StringPrintf("Compositing.%s.LayersUpdateTime.%d", client_name, - GetLayersUpdateTimeHistogramBucket(NumLayers())); - base::Histogram::FactoryGet(histogram_name, 0, 10000000, 50, - base::HistogramBase::kUmaTargetedHistogramFlag) - ->Add(timer.Elapsed().InMicroseconds()); + 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; @@ -1050,11 +1055,6 @@ void LayerTreeHost::SetViewportSizeAndScale( const gfx::Size& device_viewport_size, float device_scale_factor, const viz::LocalSurfaceId& local_surface_id) { - DCHECK(!local_surface_id.is_valid() || !device_viewport_size.IsEmpty()) - << "A valid LocalSurfaceId has been provided with an empty device " - "viewport size " - << local_surface_id; - // TODO(ccameron): Add CHECKs here for surface invariants violations. if (settings_.enable_surface_synchronization) SetLocalSurfaceId(local_surface_id); @@ -1080,9 +1080,23 @@ void LayerTreeHost::SetViewportSizeAndScale( if (changed) { SetPropertyTreesNeedRebuild(); SetNeedsCommit(); +#if defined(OS_MACOSX) + // TODO(ccameron): This check is not valid on Aura or Mus yet, but should + // be. + CHECK(!has_pushed_local_surface_id_ || !local_surface_id_.is_valid()); +#endif } } +void LayerTreeHost::SetViewportVisibleRect(const gfx::Rect& visible_rect) { + if (visible_rect == viewport_visible_rect_) + return; + + viewport_visible_rect_ = visible_rect; + SetPropertyTreesNeedRebuild(); + SetNeedsCommit(); +} + void LayerTreeHost::SetBrowserControlsHeight(float top_height, float bottom_height, bool shrink) { @@ -1171,6 +1185,7 @@ void LayerTreeHost::SetLocalSurfaceId( if (local_surface_id_ == local_surface_id) return; local_surface_id_ = local_surface_id; + has_pushed_local_surface_id_ = false; UpdateDeferCommitsInternal(); SetNeedsCommit(); } @@ -1361,6 +1376,7 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) { tree_impl->set_content_source_id(content_source_id_); tree_impl->set_local_surface_id(local_surface_id_); + has_pushed_local_surface_id_ = true; if (pending_page_scale_animation_) { tree_impl->SetPendingPageScaleAnimation( @@ -1389,6 +1405,7 @@ void LayerTreeHost::PushLayerTreeHostPropertiesTo( RecordGpuRasterizationHistogram(host_impl); host_impl->SetViewportSize(device_viewport_size_); + host_impl->SetViewportVisibleRect(viewport_visible_rect_); host_impl->SetDebugState(debug_state_); } diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index 01c876d48d1..9aecadafd1e 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -296,6 +296,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { float device_scale_factor, const viz::LocalSurfaceId& local_surface_id); + void SetViewportVisibleRect(const gfx::Rect& visible_rect); + + gfx::Rect viewport_visible_rect() const { return viewport_visible_rect_; } + gfx::Size device_viewport_size() const { return device_viewport_size_; } void SetBrowserControlsHeight(float top_height, @@ -633,6 +637,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { uint32_t content_source_id_; viz::LocalSurfaceId local_surface_id_; + // Used to detect surface invariant violations. + bool has_pushed_local_surface_id_ = false; bool defer_commits_ = false; SkColor background_color_ = SK_ColorWHITE; @@ -641,6 +647,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { gfx::Size device_viewport_size_; + gfx::Rect viewport_visible_rect_; + bool have_scroll_event_handlers_ = false; EventListenerProperties event_listener_properties_[static_cast<size_t>( EventListenerClass::kNumClasses)]; diff --git a/chromium/cc/trees/layer_tree_host_common_unittest.cc b/chromium/cc/trees/layer_tree_host_common_unittest.cc index b9ad8b4a2a3..329e9cd4b7e 100644 --- a/chromium/cc/trees/layer_tree_host_common_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_common_unittest.cc @@ -9,6 +9,7 @@ #include <algorithm> #include <memory> #include <set> +#include <tuple> #include <vector> #include "base/memory/ptr_util.h" @@ -4489,7 +4490,7 @@ TEST_F(LayerTreeHostCommonTest, OpacityAnimatingOnPendingTree) { active_child->effect_tree_index())); } -using LCDTextTestParam = std::tr1::tuple<bool, bool, bool>; +using LCDTextTestParam = std::tuple<bool, bool, bool>; class LCDTextTest : public LayerTreeHostCommonTestBase, public testing::TestWithParam<LCDTextTestParam> { public: @@ -4505,8 +4506,8 @@ class LCDTextTest : public LayerTreeHostCommonTestBase, LayerTreeSettings LCDTextTestLayerTreeSettings() { LayerTreeSettings settings = VerifyTreeCalcsLayerTreeSettings(); - can_use_lcd_text_ = std::tr1::get<0>(GetParam()); - layers_always_allowed_lcd_text_ = std::tr1::get<1>(GetParam()); + can_use_lcd_text_ = std::get<0>(GetParam()); + layers_always_allowed_lcd_text_ = std::get<1>(GetParam()); settings.can_use_lcd_text = can_use_lcd_text_; settings.layers_always_allowed_lcd_text = layers_always_allowed_lcd_text_; return settings; @@ -4547,8 +4548,7 @@ class LCDTextTest : public LayerTreeHostCommonTestBase, child_->SetBounds(gfx::Size(1, 1)); grand_child_->SetBounds(gfx::Size(1, 1)); - child_->test_properties()->force_render_surface = - std::tr1::get<2>(GetParam()); + child_->test_properties()->force_render_surface = std::get<2>(GetParam()); } bool can_use_lcd_text_; @@ -4885,10 +4885,16 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHiddenWithCopyRequest) { inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); - EXPECT_TRUE(root_layer->has_copy_requests_in_target_subtree()); - EXPECT_TRUE(copy_grand_parent_layer->has_copy_requests_in_target_subtree()); - EXPECT_TRUE(copy_parent_layer->has_copy_requests_in_target_subtree()); - EXPECT_TRUE(copy_layer->has_copy_requests_in_target_subtree()); + auto& effect_tree = + root_layer->layer_tree_impl()->property_trees()->effect_tree; + EXPECT_TRUE(effect_tree.Node(root_layer->effect_tree_index()) + ->subtree_has_copy_request); + EXPECT_TRUE(effect_tree.Node(copy_grand_parent_layer->effect_tree_index()) + ->subtree_has_copy_request); + EXPECT_TRUE(effect_tree.Node(copy_parent_layer->effect_tree_index()) + ->subtree_has_copy_request); + EXPECT_TRUE(effect_tree.Node(copy_layer->effect_tree_index()) + ->subtree_has_copy_request); // We should have four render surfaces, one for the root, one for the grand // parent since it has opacity and two drawing descendants, one for the parent @@ -10483,5 +10489,28 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForTrilinearFiltering) { GetRenderSurface(parent)->DrawableContentRect()); } +TEST_F(LayerTreeHostCommonTest, VisibleRectClippedByViewport) { + FakeImplTaskRunnerProvider task_runner_provider; + TestTaskGraphRunner task_graph_runner; + FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); + + gfx::Size root_layer_size = gfx::Size(300, 500); + gfx::Size device_viewport_size = gfx::Size(300, 600); + gfx::Rect viewport_visible_rect = gfx::Rect(100, 100, 200, 200); + + host_impl.SetViewportSize(device_viewport_size); + host_impl.SetViewportVisibleRect(viewport_visible_rect); + host_impl.active_tree()->SetRootLayerForTesting( + LayerImpl::Create(host_impl.active_tree(), 1)); + + LayerImpl* root = host_impl.active_tree()->root_layer_for_testing(); + root->SetBounds(root_layer_size); + root->SetDrawsContent(true); + ExecuteCalculateDrawProperties(root); + + EXPECT_EQ(viewport_visible_rect, root->visible_layer_rect()); + EXPECT_EQ(gfx::Rect(root_layer_size), root->drawable_content_rect()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index 965f07783fa..910862ce9a8 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -65,6 +65,7 @@ #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" @@ -77,8 +78,10 @@ #include "cc/trees/single_thread_proxy.h" #include "cc/trees/transform_node.h" #include "cc/trees/tree_synchronizer.h" +#include "components/viz/common/features.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/delay_based_time_source.h" +#include "components/viz/common/gpu/texture_allocation.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/compositor_frame_metadata.h" #include "components/viz/common/quads/frame_deadline.h" @@ -86,6 +89,7 @@ #include "components/viz/common/quads/shared_quad_state.h" #include "components/viz/common/quads/solid_color_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h" +#include "components/viz/common/resources/bitmap_allocation.h" #include "components/viz/common/traced_value.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/context_support.h" @@ -98,6 +102,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/skia_util.h" namespace cc { namespace { @@ -170,16 +175,15 @@ DEFINE_SCOPED_UMA_HISTOGRAM_TIMER(PendingTreeDurationHistogramTimer, "Scheduling.%s.PendingTreeDuration"); DEFINE_SCOPED_UMA_HISTOGRAM_TIMER(PendingTreeRasterDurationHistogramTimer, "Scheduling.%s.PendingTreeRasterDuration"); -DEFINE_SCOPED_UMA_HISTOGRAM_TIMER( - ImageInvalidationUpdateDurationHistogramTimer, - "Scheduling.%s.ImageInvalidationUpdateDuration"); - -LayerTreeHostImpl::FrameData::FrameData() - : render_surface_list(nullptr), - has_no_damage(false), - may_contain_video(false) {} +LayerTreeHostImpl::FrameData::FrameData() = default; LayerTreeHostImpl::FrameData::~FrameData() = default; +LayerTreeHostImpl::UIResourceData::UIResourceData() = default; +LayerTreeHostImpl::UIResourceData::~UIResourceData() = default; +LayerTreeHostImpl::UIResourceData::UIResourceData(UIResourceData&&) noexcept = + default; +LayerTreeHostImpl::UIResourceData& LayerTreeHostImpl::UIResourceData::operator=( + UIResourceData&&) = default; std::unique_ptr<LayerTreeHostImpl> LayerTreeHostImpl::Create( const LayerTreeSettings& settings, @@ -257,6 +261,14 @@ LayerTreeHostImpl::LayerTreeHostImpl( 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_( + GetTaskRunner(), + base::BindRepeating( + &LayerTreeHostImpl::RequestInvalidationForAnimatedImages, + base::Unretained(this)), + settings_.enable_image_animation_resync), default_color_space_id_(gfx::ColorSpace::GetNextId()), default_color_space_(gfx::ColorSpace::CreateSRGB()) { DCHECK(mutator_host_); @@ -283,16 +295,6 @@ LayerTreeHostImpl::LayerTreeHostImpl( settings.top_controls_hide_threshold); tile_manager_.SetDecodedImageTracker(&decoded_image_tracker_); - - if (settings_.enable_image_animations) { - // It is safe to use base::Unretained here since we will outlive the - // ImageAnimationController. - base::Closure invalidation_callback = - base::Bind(&LayerTreeHostImpl::RequestInvalidationForAnimatedImages, - base::Unretained(this)); - image_animation_controller_.emplace(GetTaskRunner(), - std::move(invalidation_callback)); - } } LayerTreeHostImpl::~LayerTreeHostImpl() { @@ -362,11 +364,6 @@ void LayerTreeHostImpl::CommitComplete() { if (input_handler_client_ && impl_thread_phase_ == ImplThreadPhase::IDLE) input_handler_client_->DeliverInputForBeginFrame(); - UpdateSyncTreeAfterCommitOrImplSideInvalidation(); - micro_benchmark_controller_.DidCompleteCommit(); -} - -void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() { if (CommitToActiveTree()) { active_tree_->HandleScrollbarShowRequestsFromMain(); @@ -385,6 +382,11 @@ void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() { else AnimatePendingTreeAfterCommit(); + UpdateSyncTreeAfterCommitOrImplSideInvalidation(); + micro_benchmark_controller_.DidCompleteCommit(); +} + +void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() { // LayerTreeHost may have changed the GPU rasterization flags state, which // may require an update of the tree resources. UpdateTreeResourcesForGpuRasterizationIfNeeded(); @@ -410,22 +412,16 @@ void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() { // Defer invalidating images until UpdateDrawProperties is performed since // that updates whether an image should be animated based on its visibility // and the updated data for the image from the main frame. - { - ImageInvalidationUpdateDurationHistogramTimer image_invalidation_timer; PaintImageIdFlatSet images_to_invalidate = tile_manager_.TakeImagesToInvalidateOnSyncTree(); if (ukm_manager_) ukm_manager_->AddCheckerboardedImages(images_to_invalidate.size()); - if (image_animation_controller_.has_value()) { - const auto& animated_images = - image_animation_controller_.value().AnimateForSyncTree( - CurrentBeginFrameArgs().frame_time); - images_to_invalidate.insert(animated_images.begin(), - animated_images.end()); - } + const auto& animated_images = + image_animation_controller_.AnimateForSyncTree( + CurrentBeginFrameArgs().frame_time); + images_to_invalidate.insert(animated_images.begin(), animated_images.end()); sync_tree()->InvalidateRegionForImages(images_to_invalidate); - } // Note that it is important to push the state for checkerboarded and animated // images prior to PrepareTiles here when committing to the active tree. This @@ -500,14 +496,18 @@ bool LayerTreeHostImpl::CanDraw() const { } void LayerTreeHostImpl::AnimatePendingTreeAfterCommit() { - AnimateInternal(false); + // Animate the pending tree layer animations to put them at initial positions + // and starting state. There is no need to run other animations on pending + // tree because they depend on user inputs so the state is identical to what + // the active tree has. + AnimateLayers(CurrentBeginFrameArgs().frame_time, /* is_active_tree */ false); } void LayerTreeHostImpl::Animate() { - AnimateInternal(true); + AnimateInternal(); } -void LayerTreeHostImpl::AnimateInternal(bool active_tree) { +void LayerTreeHostImpl::AnimateInternal() { DCHECK(task_runner_provider_->IsImplThread()); base::TimeTicks monotonic_time = CurrentBeginFrameArgs().frame_time; @@ -532,19 +532,17 @@ void LayerTreeHostImpl::AnimateInternal(bool active_tree) { } did_animate |= AnimatePageScale(monotonic_time); - did_animate |= AnimateLayers(monotonic_time, active_tree); + did_animate |= AnimateLayers(monotonic_time, /* is_active_tree */ true); did_animate |= AnimateScrollbars(monotonic_time); did_animate |= AnimateBrowserControls(monotonic_time); - if (active_tree) { // Animating stuff can change the root scroll offset, so inform the // synchronous input handler. - UpdateRootLayerStateForSynchronousInputHandler(); - if (did_animate) { - // If the tree changed, then we want to draw at the end of the current - // frame. - SetNeedsRedraw(); - } + UpdateRootLayerStateForSynchronousInputHandler(); + if (did_animate) { + // If the tree changed, then we want to draw at the end of the current + // frame. + SetNeedsRedraw(); } } @@ -1188,6 +1186,8 @@ 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); if (!CommitToActiveTree()) CreatePendingTree(); @@ -1521,11 +1521,10 @@ void LayerTreeHostImpl::RequestImplSideInvalidationForCheckerImagedTiles() { size_t LayerTreeHostImpl::GetFrameIndexForImage(const PaintImage& paint_image, WhichTree tree) const { - DCHECK(image_animation_controller_.has_value()); if (!paint_image.ShouldAnimate()) return paint_image.frame_index(); - return image_animation_controller_->GetFrameIndexForImage( + return image_animation_controller_.GetFrameIndexForImage( paint_image.stable_id(), tree); } @@ -1843,6 +1842,11 @@ RenderFrameMetadata LayerTreeHostImpl::MakeRenderFrameMetadata() { 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; + + active_tree_->GetViewportSelection(&metadata.selection); + return metadata; } @@ -1901,7 +1905,7 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { if (active_tree_->hud_layer()) { TRACE_EVENT0("cc", "DrawLayers.UpdateHudTexture"); active_tree_->hud_layer()->UpdateHudTexture( - draw_mode, resource_provider_.get(), + draw_mode, layer_tree_frame_sink_, resource_provider_.get(), // 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, @@ -1916,12 +1920,12 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { frame->use_default_lower_bound_deadline); metadata.activation_dependencies = std::move(frame->activation_dependencies); - active_tree()->FinishSwapPromises(&metadata); + active_tree()->FinishSwapPromises(&metadata, &frame_token_allocator_); if (render_frame_metadata_observer_) { RenderFrameMetadata render_frame_metadata = MakeRenderFrameMetadata(); render_frame_metadata_observer_->OnRenderFrameSubmission( - render_frame_metadata); + std::move(render_frame_metadata)); } metadata.latency_info.emplace_back(ui::SourceEventType::FRAME); @@ -1963,8 +1967,14 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { compositor_frame.render_pass_list = std::move(frame->render_passes); // TODO(fsamuel): Once all clients get their viz::LocalSurfaceId from their // parent, the viz::LocalSurfaceId should hang off CompositorFrameMetadata. - if (settings_.enable_surface_synchronization && - active_tree()->local_surface_id().is_valid()) { + if (settings_.enable_surface_synchronization) { + // If surface synchronization is on, we should always have a valid + // LocalSurfaceId in LayerTreeImpl unless we don't have a scheduler because + // without a scheduler commits are not deferred and LayerTrees without valid + // LocalSurfaceId might slip through, but single-thread-without-scheduler + // mode is only used in tests so it doesn't matter. + CHECK(!settings_.single_thread_proxy_scheduler || + active_tree()->local_surface_id().is_valid()); layer_tree_frame_sink_->SetLocalSurfaceId( active_tree()->local_surface_id()); last_draw_local_surface_id_ = active_tree()->local_surface_id(); @@ -1977,6 +1987,10 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { base::StringPrintf("Compositing.%s.CompositorFrame.Quads", client_name), 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 @@ -2065,6 +2079,19 @@ void LayerTreeHostImpl::GetGpuRasterizationCapabilities( if (!*gpu_rasterization_enabled && !settings_.gpu_rasterization_forced) return; + if (use_oop_rasterization_) { + *gpu_rasterization_supported = true; + *supports_disable_msaa = caps.multisample_compatibility; + // For OOP raster, the gpu service side will disable msaa if the + // requested samples are not enough. GPU raster does this same + // logic below client side. + *max_msaa_samples = RequestedMSAASampleCount(); + return; + } + + if (!context_provider->ContextSupport()->HasGrContextSupport()) + return; + // Do not check GrContext above. It is lazy-created, and we only want to // create it if it might be used. GrContext* gr_context = context_provider->GrContext(); @@ -2478,8 +2505,7 @@ void LayerTreeHostImpl::ActivateSyncTree() { } void LayerTreeHostImpl::ActivateStateForImages() { - if (image_animation_controller_) - image_animation_controller_->DidActivate(); + image_animation_controller_.DidActivate(); tile_manager_.DidActivateSyncTree(); } @@ -2574,12 +2600,15 @@ void LayerTreeHostImpl::CreateTileManagerResources() { raster_buffer_provider_ = CreateRasterBufferProvider(); if (use_gpu_rasterization_) { + int max_texture_size = layer_tree_frame_sink_->context_provider() + ->ContextCapabilities() + .max_texture_size; image_decode_cache_ = std::make_unique<GpuImageDecodeCache>( layer_tree_frame_sink_->worker_context_provider(), - settings_.enable_oop_rasterization, + use_oop_rasterization_, viz::ResourceFormatToClosestSkColorType( settings_.preferred_tile_format), - settings_.decoded_image_working_set_budget_bytes); + settings_.decoded_image_working_set_budget_bytes, max_texture_size); } else { image_decode_cache_ = std::make_unique<SoftwareImageDecodeCache>( viz::ResourceFormatToClosestSkColorType( @@ -2597,12 +2626,8 @@ void LayerTreeHostImpl::CreateTileManagerResources() { task_graph_runner = single_thread_synchronous_task_graph_runner_.get(); } - // TODO(vmpstr): Initialize tile task limit at ctor time. tile_manager_.SetResources(resource_pool_.get(), image_decode_cache_.get(), task_graph_runner, raster_buffer_provider_.get(), - is_synchronous_single_threaded_ - ? std::numeric_limits<size_t>::max() - : settings_.scheduled_raster_task_limit, use_gpu_rasterization_); tile_manager_.SetCheckerImagingForceDisabled( settings_.only_checker_images_with_gpu_raster && !use_gpu_rasterization_); @@ -2615,11 +2640,8 @@ LayerTreeHostImpl::CreateRasterBufferProvider() { viz::ContextProvider* compositor_context_provider = layer_tree_frame_sink_->context_provider(); - if (!compositor_context_provider) { - return std::make_unique<BitmapRasterBufferProvider>( - resource_provider_.get(), - layer_tree_frame_sink_->shared_bitmap_manager()); - } + if (!compositor_context_provider) + return std::make_unique<BitmapRasterBufferProvider>(layer_tree_frame_sink_); viz::RasterContextProvider* worker_context_provider = layer_tree_frame_sink_->worker_context_provider(); @@ -2627,20 +2649,14 @@ LayerTreeHostImpl::CreateRasterBufferProvider() { DCHECK(worker_context_provider); int msaa_sample_count = use_msaa_ ? RequestedMSAASampleCount() : 0; - // The worker context must support oop raster to enable oop rasterization. - bool oop_raster_enabled = settings_.enable_oop_rasterization; - if (oop_raster_enabled) { - viz::RasterContextProvider::ScopedRasterContextLock hold( - worker_context_provider); - oop_raster_enabled &= - worker_context_provider->ContextCapabilities().supports_oop_raster; - } - return std::make_unique<GpuRasterBufferProvider>( compositor_context_provider, worker_context_provider, - resource_provider_.get(), settings_.use_distance_field_text, + resource_provider_.get(), settings_.resource_settings.use_gpu_memory_buffer_resources, - msaa_sample_count, settings_.preferred_tile_format, oop_raster_enabled); + msaa_sample_count, settings_.preferred_tile_format, + settings_.max_gpu_raster_tile_size, + settings_.unpremultiply_and_dither_low_bit_depth_tiles, + use_oop_rasterization_); } bool use_zero_copy = settings_.use_zero_copy; @@ -2680,6 +2696,14 @@ LayerImpl* LayerTreeHostImpl::ViewportMainScrollLayer() { return viewport()->MainScrollLayer(); } +ScrollNode* LayerTreeHostImpl::ViewportMainScrollNode() { + if (!ViewportMainScrollLayer()) + return nullptr; + + return active_tree_->property_trees()->scroll_tree.Node( + ViewportMainScrollLayer()->scroll_tree_index()); +} + void LayerTreeHostImpl::QueueImageDecode(int request_id, const PaintImage& image) { TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), @@ -2709,13 +2733,14 @@ LayerTreeHostImpl::TakeCompletedImageDecodeRequests() { return result; } -void LayerTreeHostImpl::ClearImageCacheOnNavigation() { +void LayerTreeHostImpl::DidNavigate() { // It is safe to clear the decode policy tracking on navigations since it // comes with an invalidation and the image ids are never re-used. bool can_clear_decode_policy_tracking = true; tile_manager_.ClearCheckerImageTracking(can_clear_decode_policy_tracking); if (image_decode_cache_) image_decode_cache_->ClearCache(); + image_animation_controller_.set_did_navigate(); } void LayerTreeHostImpl::DidChangeScrollbarVisibility() { @@ -2729,6 +2754,7 @@ void LayerTreeHostImpl::CleanUpTileManagerResources() { tile_manager_.FinishTasksAndCleanUp(); single_thread_synchronous_task_graph_runner_ = nullptr; image_decode_cache_ = nullptr; + raster_buffer_provider_ = nullptr; // Any resources that were allocated previously should be considered not good // for reuse, as the RasterBufferProvider will be replaced and it may choose // to allocate future resources differently. @@ -2817,6 +2843,21 @@ bool LayerTreeHostImpl::InitializeRenderer( ResourcePool::kDefaultExpirationDelay, ResourcePool::Mode::kGpu, settings_.disallow_non_exact_resource_reuse); } + if (features::IsVizHitTestingSurfaceLayerEnabled()) + layer_tree_frame_sink_->UpdateHitTestData(this); + + // 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; + } + } // Since the new context may be capable of MSAA, update status here. We don't // need to check the return value since we are recreating all resources @@ -2876,6 +2917,15 @@ gfx::Rect LayerTreeHostImpl::DeviceViewport() const { return external_viewport_; } +void LayerTreeHostImpl::SetViewportVisibleRect(const gfx::Rect& visible_rect) { + if (visible_rect == viewport_visible_rect_) + return; + + viewport_visible_rect_ = visible_rect; + SetFullViewportDamage(); + active_tree_->set_needs_update_draw_properties(); +} + const gfx::Rect LayerTreeHostImpl::ViewportRectForTilePriority() const { if (viewport_rect_for_tile_priority_.IsEmpty()) return DeviceViewport(); @@ -2927,8 +2977,10 @@ InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll( InputHandler::ScrollStatus scroll_status; scroll_status.main_thread_scrolling_reasons = MainThreadScrollingReason::kNotScrollingOnMain; - if (!!scroll_node->main_thread_scrolling_reasons) { - TRACE_EVENT0("cc", "LayerImpl::TryScroll: Failed ShouldScrollOnMainThread"); + if (scroll_node->main_thread_scrolling_reasons) { + TRACE_EVENT1("cc", "LayerImpl::TryScroll: Failed ShouldScrollOnMainThread", + "MainThreadScrollingReason", + scroll_node->main_thread_scrolling_reasons); scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD; scroll_status.main_thread_scrolling_reasons = scroll_node->main_thread_scrolling_reasons; @@ -3109,12 +3161,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBeginImpl( // If the viewport is scrolling and it cannot consume any delta hints, the // scroll event will need to get bubbled if the viewport is for a guest or // oopif. - ScrollNode* viewport_scroll_node = - viewport()->MainScrollLayer() - ? active_tree_->property_trees()->scroll_tree.Node( - viewport()->MainScrollLayer()->scroll_tree_index()) - : nullptr; - if (active_tree_->CurrentlyScrollingNode() == viewport_scroll_node && + if (active_tree_->CurrentlyScrollingNode() == ViewportMainScrollNode() && !viewport()->CanScroll(*scroll_state)) { scroll_status.bubble = true; } @@ -3659,10 +3706,7 @@ void LayerTreeHostImpl::DistributeScrollDelta(ScrollState* scroll_state) { std::list<ScrollNode*> current_scroll_chain; ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree; ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode(); - ScrollNode* viewport_scroll_node = - viewport()->MainScrollLayer() - ? scroll_tree.Node(viewport()->MainScrollLayer()->scroll_tree_index()) - : nullptr; + ScrollNode* viewport_scroll_node = ViewportMainScrollNode(); if (scroll_node) { // TODO(bokan): The loop checks for a null parent but don't we still want to // distribute to the root scroll node? @@ -3751,7 +3795,7 @@ void LayerTreeHostImpl::UpdateImageDecodingHints( void LayerTreeHostImpl::SetRenderFrameObserver( std::unique_ptr<RenderFrameMetadataObserver> observer) { render_frame_metadata_observer_ = std::move(observer); - render_frame_metadata_observer_->BindToCurrentThread(); + render_frame_metadata_observer_->BindToCurrentThread(&frame_token_allocator_); } InputHandlerScrollResult LayerTreeHostImpl::ScrollBy( @@ -3815,8 +3859,13 @@ InputHandlerScrollResult LayerTreeHostImpl::ScrollBy( accumulated_root_overscroll_.set_x(0); if (did_scroll_y) accumulated_root_overscroll_.set_y(0); - gfx::Vector2dF unused_root_delta(scroll_state->delta_x(), - scroll_state->delta_y()); + + gfx::Vector2dF unused_root_delta; + if (current_scrolling_node && + current_scrolling_node == ViewportMainScrollNode()) { + unused_root_delta = + gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y()); + } // When inner viewport is unscrollable, disable overscrolls. if (const auto* inner_viewport_scroll_node = InnerViewportScrollNode()) { @@ -3849,6 +3898,11 @@ InputHandlerScrollResult LayerTreeHostImpl::ScrollBy( UpdateRootLayerStateForSynchronousInputHandler(); } + scroll_result.current_offset = ScrollOffsetToVector2dF( + scroll_tree.current_scroll_offset(scroll_node->element_id)); + float scale_factor = active_tree()->current_page_scale_factor(); + scroll_result.current_offset.Scale(scale_factor); + // Run animations which need to respond to updated scroll offset. mutator_host_->TickScrollAnimations( CurrentBeginFrameArgs().frame_time, @@ -3886,11 +3940,12 @@ bool LayerTreeHostImpl::SnapAtScrollEnd() { gfx::ScrollOffset current_position = scroll_tree.current_scroll_offset(scroll_node->element_id); - gfx::ScrollOffset snap_position = - data.FindSnapPosition(current_position, did_scroll_x_for_scroll_gesture_, - did_scroll_y_for_scroll_gesture_); - if (snap_position == current_position) + gfx::ScrollOffset snap_position; + if (!data.FindSnapPosition(current_position, did_scroll_x_for_scroll_gesture_, + did_scroll_y_for_scroll_gesture_, + &snap_position)) { return false; + } ScrollAnimationCreate( scroll_node, ScrollOffsetToVector2dF(snap_position - current_position), @@ -3898,6 +3953,41 @@ bool LayerTreeHostImpl::SnapAtScrollEnd() { return true; } +bool LayerTreeHostImpl::GetSnapFlingInfo( + const gfx::Vector2dF& natural_displacement_in_viewport, + gfx::Vector2dF* initial_offset, + gfx::Vector2dF* target_offset) const { + const ScrollNode* scroll_node = CurrentlyScrollingNode(); + if (!scroll_node || !scroll_node->snap_container_data.has_value()) + return false; + + const SnapContainerData& data = scroll_node->snap_container_data.value(); + float scale_factor = active_tree()->current_page_scale_factor(); + gfx::Vector2dF natural_displacement_in_content = + gfx::ScaleVector2d(natural_displacement_in_viewport, 1.f / scale_factor); + + const ScrollTree& scroll_tree = active_tree()->property_trees()->scroll_tree; + *initial_offset = ScrollOffsetToVector2dF( + scroll_tree.current_scroll_offset(scroll_node->element_id)); + + bool did_scroll_x = did_scroll_x_for_scroll_gesture_ || + natural_displacement_in_content.x() != 0; + bool did_scroll_y = did_scroll_y_for_scroll_gesture_ || + 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)) { + return false; + } + + *target_offset = ScrollOffsetToVector2dF(snap_offset); + target_offset->Scale(scale_factor); + initial_offset->Scale(scale_factor); + return true; +} + void LayerTreeHostImpl::ClearCurrentlyScrollingNode() { active_tree_->ClearCurrentlyScrollingNode(); did_lock_scrolling_layer_ = false; @@ -4455,6 +4545,8 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, const gfx::Size source_size = bitmap.GetSize(); gfx::Size upload_size = bitmap.GetSize(); bool scaled = false; + // UIResources are assumed to be rastered in SRGB. + const gfx::ColorSpace& color_space = gfx::ColorSpace::CreateSRGB(); int max_texture_size = resource_provider_->max_texture_size(); if (source_size.width() > max_texture_size || @@ -4467,18 +4559,48 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, upload_size = gfx::ScaleToCeiledSize(source_size, scale, scale); } + // For gpu compositing, a texture will be allocated and the UIResource + // will be uploaded into it. + viz::TextureAllocation texture_alloc; + // For software compositing, shared memory will be allocated and the + // UIResource will be copied into it. + std::unique_ptr<base::SharedMemory> shared_memory; + viz::SharedBitmapId shared_bitmap_id; + if (layer_tree_frame_sink_->context_provider()) { - id = resource_provider_->CreateGpuTextureResource( - upload_size, viz::ResourceTextureHint::kDefault, format, - gfx::ColorSpace::CreateSRGB()); + viz::ContextProvider* context_provider = + layer_tree_frame_sink_->context_provider(); + texture_alloc = viz::TextureAllocation::MakeTextureId( + context_provider->ContextGL(), context_provider->ContextCapabilities(), + format, settings_.resource_settings.use_gpu_memory_buffer_resources, + /*for_framebuffer_attachment=*/false); } else { - DCHECK_EQ(format, viz::RGBA_8888); - id = resource_provider_->CreateBitmapResource( - upload_size, gfx::ColorSpace::CreateSRGB()); + shared_memory = + viz::bitmap_allocation::AllocateMappedBitmap(upload_size, format); + shared_bitmap_id = viz::SharedBitmap::GenerateId(); } if (!scaled) { - resource_provider_->CopyToResource(id, bitmap.GetPixels(), source_size); + // If not scaled, we can copy the pixels 1:1 from the source bitmap to our + // destination backing of a texture or shared bitmap. + if (layer_tree_frame_sink_->context_provider()) { + viz::TextureAllocation::UploadStorage( + layer_tree_frame_sink_->context_provider()->ContextGL(), + layer_tree_frame_sink_->context_provider()->ContextCapabilities(), + format, upload_size, texture_alloc, color_space, bitmap.GetPixels()); + } else { + DCHECK_EQ(bitmap.GetFormat(), UIResourceBitmap::RGBA8); + SkImageInfo src_info = + SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(source_size)); + SkImageInfo dst_info = + SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(upload_size)); + + sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect( + dst_info, shared_memory->memory(), dst_info.minRowBytes()); + surface->getCanvas()->writePixels( + src_info, const_cast<uint8_t*>(bitmap.GetPixels()), + src_info.minRowBytes(), 0, 0); + } } else { // Only support auto-resizing for N32 textures (since this is primarily for // scrollbars). Users of other types need to ensure they are not too big. @@ -4489,57 +4611,170 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, float canvas_scale_y = upload_size.height() / static_cast<float>(source_size.height()); - // Uses kPremul_SkAlphaType since that is what SkBitmap's allocN32Pixels - // makes, and we only support the RGBA8 format here. - SkImageInfo info = SkImageInfo::MakeN32( - source_size.width(), source_size.height(), kPremul_SkAlphaType); - int row_bytes = source_size.width() * 4; + // Uses N32Premul since that is what SkBitmap's allocN32Pixels makes, and we + // only support the RGBA8 format here. + SkImageInfo info = + SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(source_size)); SkBitmap source_bitmap; - source_bitmap.setInfo(info, row_bytes); + source_bitmap.setInfo(info); source_bitmap.setPixels(const_cast<uint8_t*>(bitmap.GetPixels())); - // This applies the scale to draw the |bitmap| into |scaled_bitmap|. - SkBitmap scaled_bitmap; - scaled_bitmap.allocN32Pixels(upload_size.width(), upload_size.height()); - SkCanvas scaled_canvas(scaled_bitmap); - scaled_canvas.scale(canvas_scale_x, canvas_scale_y); + // This applies the scale to draw the |bitmap| into |scaled_surface|. For + // gpu compositing, we scale into a software bitmap-backed SkSurface here, + // then upload from there into a texture. For software compositing, we scale + // directly into the shared memory backing. + sk_sp<SkSurface> scaled_surface; + if (layer_tree_frame_sink_->context_provider()) { + scaled_surface = SkSurface::MakeRasterN32Premul(upload_size.width(), + upload_size.height()); + } else { + SkImageInfo dst_info = + SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(upload_size)); + scaled_surface = SkSurface::MakeRasterDirect( + dst_info, shared_memory->memory(), dst_info.minRowBytes()); + } + SkCanvas* scaled_canvas = scaled_surface->getCanvas(); + scaled_canvas->scale(canvas_scale_x, canvas_scale_y); // The |canvas_scale_x| and |canvas_scale_y| may have some floating point // error for large enough values, causing pixels on the edge to be not // fully filled by drawBitmap(), so we ensure they start empty. (See // crbug.com/642011 for an example.) - scaled_canvas.clear(SK_ColorTRANSPARENT); - scaled_canvas.drawBitmap(source_bitmap, 0, 0); - - auto* pixels = static_cast<uint8_t*>(scaled_bitmap.getPixels()); - resource_provider_->CopyToResource(id, pixels, upload_size); + scaled_canvas->clear(SK_ColorTRANSPARENT); + scaled_canvas->drawBitmap(source_bitmap, 0, 0); + + if (layer_tree_frame_sink_->context_provider()) { + SkPixmap pixmap; + scaled_surface->peekPixels(&pixmap); + viz::TextureAllocation::UploadStorage( + layer_tree_frame_sink_->context_provider()->ContextGL(), + layer_tree_frame_sink_->context_provider()->ContextCapabilities(), + format, upload_size, texture_alloc, color_space, pixmap.addr()); + } } + // Once the backing has the UIResource inside it, we have to prepare it for + // export to the display compositor via ImportResource(). For gpu compositing, + // this requires a Mailbox+SyncToken as well. For software compositing, the + // SharedBitmapId must be notified to the LayerTreeFrameSink. The + // OnUIResourceReleased() method will be called once the resource is deleted + // and the display compositor is no longer using it, to free the memory + // allocated in this method above. + viz::TransferableResource transferable; + if (layer_tree_frame_sink_->context_provider()) { + gpu::gles2::GLES2Interface* gl = + layer_tree_frame_sink_->context_provider()->ContextGL(); + gpu::Mailbox mailbox = gpu::Mailbox::Generate(); + gl->ProduceTextureDirectCHROMIUM(texture_alloc.texture_id, mailbox.name); + gpu::SyncToken sync_token = + LayerTreeResourceProvider::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( + shared_memory.get(), upload_size, format); + layer_tree_frame_sink_->DidAllocateSharedBitmap(std::move(memory_handle), + shared_bitmap_id); + transferable = viz::TransferableResource::MakeSoftware(shared_bitmap_id, 0, + upload_size, format); + } + transferable.color_space = color_space; + id = resource_provider_->ImportResource( + transferable, + // The OnUIResourceReleased method is bound with a WeakPtr, but the + // resource backing will be deleted when the LayerTreeFrameSink is + // removed before shutdown, so nothing leaks if the WeakPtr is + // invalidated. + viz::SingleReleaseCallback::Create(base::BindOnce( + &LayerTreeHostImpl::OnUIResourceReleased, AsWeakPtr(), uid))); + UIResourceData data; - data.resource_id = id; data.opaque = bitmap.GetOpaque(); - ui_resource_map_[uid] = data; + data.format = format; + data.shared_bitmap_id = shared_bitmap_id; + data.shared_memory = std::move(shared_memory); + data.texture_id = texture_alloc.texture_id; + data.resource_id_for_export = id; + ui_resource_map_[uid] = std::move(data); MarkUIResourceNotEvicted(uid); } void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) { - viz::ResourceId id = ResourceIdForUIResource(uid); - if (id) { - if (has_valid_layer_tree_frame_sink_) - resource_provider_->DeleteResource(id); - ui_resource_map_.erase(uid); + auto it = ui_resource_map_.find(uid); + if (it != ui_resource_map_.end()) { + 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. + deleted_ui_resources_[uid] = std::move(data); + ui_resource_map_.erase(it); + + resource_provider_->RemoveImportedResource(id); } MarkUIResourceNotEvicted(uid); } +void LayerTreeHostImpl::DeleteUIResourceBacking( + UIResourceData data, + const gpu::SyncToken& sync_token) { + // Resources are either software or gpu backed, not both. + DCHECK(!(data.shared_memory && data.texture_id)); + if (data.shared_memory) + layer_tree_frame_sink_->DidDeleteSharedBitmap(data.shared_bitmap_id); + if (data.texture_id) { + gpu::gles2::GLES2Interface* gl = + layer_tree_frame_sink_->context_provider()->ContextGL(); + if (sync_token.HasData()) + gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); + gl->DeleteTextures(1, &data.texture_id); + } + // |data| goes out of scope and deletes anything it owned. +} + +void LayerTreeHostImpl::OnUIResourceReleased(UIResourceId uid, + const gpu::SyncToken& sync_token, + bool lost) { + auto it = deleted_ui_resources_.find(uid); + if (it == deleted_ui_resources_.end()) { + // Backing was already deleted, eg if the context was lost. + return; + } + UIResourceData& data = it->second; + // We don't recycle backings here, so |lost| is not relevant, we always delete + // them. + DeleteUIResourceBacking(std::move(data), sync_token); + deleted_ui_resources_.erase(it); +} + void LayerTreeHostImpl::ClearUIResources() { - for (UIResourceMap::const_iterator iter = ui_resource_map_.begin(); - iter != ui_resource_map_.end(); ++iter) { - evicted_ui_resources_.insert(iter->first); - resource_provider_->DeleteResource(iter->second.resource_id); + for (auto& pair : ui_resource_map_) { + UIResourceId uid = pair.first; + UIResourceData& data = pair.second; + 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). + DeleteUIResourceBacking(std::move(data), gpu::SyncToken()); + // This resource is not deleted, and its |uid| is still valid, so it moves + // to the evicted list, not the |deleted_ui_resources_| set. Also, its + // backing is gone, so it would not belong in |deleted_ui_resources_|. + evicted_ui_resources_.insert(uid); } ui_resource_map_.clear(); + for (auto& pair : deleted_ui_resources_) { + UIResourceData& data = pair.second; + // 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). + DeleteUIResourceBacking(std::move(data), gpu::SyncToken()); + } + deleted_ui_resources_.clear(); } void LayerTreeHostImpl::EvictAllUIResources() { @@ -4554,14 +4789,14 @@ void LayerTreeHostImpl::EvictAllUIResources() { viz::ResourceId LayerTreeHostImpl::ResourceIdForUIResource( UIResourceId uid) const { - UIResourceMap::const_iterator iter = ui_resource_map_.find(uid); + auto iter = ui_resource_map_.find(uid); if (iter != ui_resource_map_.end()) - return iter->second.resource_id; - return 0; + return iter->second.resource_id_for_export; + return viz::kInvalidResourceId; } bool LayerTreeHostImpl::IsUIResourceOpaque(UIResourceId uid) const { - UIResourceMap::const_iterator iter = ui_resource_map_.find(uid); + auto iter = ui_resource_map_.find(uid); DCHECK(iter != ui_resource_map_.end()); return iter->second.opaque; } @@ -4820,8 +5055,6 @@ void LayerTreeHostImpl::ShowScrollbarsForImplScroll(ElementId element_id) { } void LayerTreeHostImpl::RequestInvalidationForAnimatedImages() { - DCHECK(image_animation_controller_); - // If we are animating an image, we want at least one draw of the active tree // before a new tree is activated. bool needs_first_draw_on_activation = true; diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index 0547246ca33..82d50cc888b 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -35,6 +35,7 @@ #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_mutator.h" #include "cc/trees/layer_tree_settings.h" @@ -165,6 +166,56 @@ class CC_EXPORT LayerTreeHostImpl public MutatorHostClient, public base::SupportsWeakPtr<LayerTreeHostImpl> { public: + // This structure is used to build all the state required for producing a + // single CompositorFrame. The |render_passes| list becomes the set of + // RenderPasses in the quad, and the other fields are used for computation + // or become part of the CompositorFrameMetadata. + struct CC_EXPORT FrameData { + FrameData(); + ~FrameData(); + void AsValueInto(base::trace_event::TracedValue* value) const; + + std::vector<viz::SurfaceId> activation_dependencies; + base::Optional<uint32_t> deadline_in_frames; + bool use_default_lower_bound_deadline = false; + std::vector<gfx::Rect> occluding_screen_space_rects; + std::vector<gfx::Rect> non_occluding_screen_space_rects; + viz::RenderPassList render_passes; + const RenderSurfaceList* render_surface_list = nullptr; + LayerImplList will_draw_layers; + bool has_no_damage = false; + bool may_contain_video = false; + viz::BeginFrameAck begin_frame_ack; + + private: + DISALLOW_COPY_AND_ASSIGN(FrameData); + }; + + // A struct of data for a single UIResource, including the backing + // pixels, and metadata about it. + struct CC_EXPORT UIResourceData { + UIResourceData(); + ~UIResourceData(); + UIResourceData(UIResourceData&&) noexcept; + UIResourceData& operator=(UIResourceData&&); + + bool opaque; + viz::ResourceFormat format; + + // Backing for software compositing. + viz::SharedBitmapId shared_bitmap_id; + std::unique_ptr<base::SharedMemory> shared_memory; + // Backing for gpu compositing. + uint32_t texture_id; + + // The name with which to refer to the resource in frames submitted to the + // display compositor. + viz::ResourceId resource_id_for_export; + + private: + DISALLOW_COPY_AND_ASSIGN(UIResourceData); + }; + static std::unique_ptr<LayerTreeHostImpl> Create( const LayerTreeSettings& settings, LayerTreeHostImplClient* client, @@ -244,27 +295,6 @@ class CC_EXPORT LayerTreeHostImpl resourceless_software_draw_ = true; } - struct CC_EXPORT FrameData { - FrameData(); - ~FrameData(); - void AsValueInto(base::trace_event::TracedValue* value) const; - - std::vector<viz::SurfaceId> activation_dependencies; - base::Optional<uint32_t> deadline_in_frames; - bool use_default_lower_bound_deadline = false; - std::vector<gfx::Rect> occluding_screen_space_rects; - std::vector<gfx::Rect> non_occluding_screen_space_rects; - viz::RenderPassList render_passes; - const RenderSurfaceList* render_surface_list; - LayerImplList will_draw_layers; - bool has_no_damage; - bool may_contain_video; - viz::BeginFrameAck begin_frame_ack; - - private: - DISALLOW_COPY_AND_ASSIGN(FrameData); - }; - virtual void DidSendBeginMainFrame() {} virtual void BeginMainFrameAborted( CommitEarlyOutReason reason, @@ -452,9 +482,7 @@ class CC_EXPORT LayerTreeHostImpl ResourcePool* resource_pool() { return resource_pool_.get(); } ImageDecodeCache* image_decode_cache() { return image_decode_cache_.get(); } ImageAnimationController* image_animation_controller() { - if (!image_animation_controller_.has_value()) - return nullptr; - return &image_animation_controller_.value(); + return &image_animation_controller_; } virtual bool WillBeginImplFrame(const viz::BeginFrameArgs& args); @@ -505,6 +533,9 @@ class CC_EXPORT LayerTreeHostImpl void SetViewportSize(const gfx::Size& device_viewport_size); gfx::Size device_viewport_size() const { return device_viewport_size_; } + void SetViewportVisibleRect(const gfx::Rect& visible_rect); + gfx::Rect viewport_visible_rect() const { return viewport_visible_rect_; } + const gfx::Transform& DrawTransform() const; std::unique_ptr<ScrollAndScaleSet> ProcessScrollDeltas(); @@ -565,10 +596,9 @@ class CC_EXPORT LayerTreeHostImpl virtual bool IsUIResourceOpaque(UIResourceId uid) const; - struct UIResourceData { - viz::ResourceId resource_id; - bool opaque; - }; + bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement_in_viewport, + gfx::Vector2dF* initial_offset, + gfx::Vector2dF* target_offset) const override; // Returns the amount of delta that can be applied to scroll_node, taking // page scale into account. @@ -637,12 +667,18 @@ class CC_EXPORT LayerTreeHostImpl void SetLayerTreeMutator(std::unique_ptr<LayerTreeMutator> mutator); + // The viewport has two scroll nodes, corresponding to the visual and layout + // viewports. However, when we compute the scroll chain we include only one + // of these -- we call that the "main" scroll node. When scrolling it, we + // scroll using the Viewport class which knows how to distribute scroll + // between the two. LayerImpl* ViewportMainScrollLayer(); + ScrollNode* ViewportMainScrollNode(); void QueueImageDecode(int request_id, const PaintImage& image); std::vector<std::pair<int, bool>> TakeCompletedImageDecodeRequests(); - void ClearImageCacheOnNavigation(); + void DidNavigate(); bool CanConsumeDelta(const ScrollNode& scroll_node, const ScrollState& scroll_state); @@ -709,7 +745,7 @@ class CC_EXPORT LayerTreeHostImpl void ReleaseTileResources(); void RecreateTileResources(); - void AnimateInternal(bool active_tree); + void AnimateInternal(); // The function is called to update state on the sync tree after a commit // finishes or after the sync tree was created to invalidate content on the @@ -756,8 +792,22 @@ class CC_EXPORT LayerTreeHostImpl void StartScrollbarFadeRecursive(LayerImpl* layer); void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy); + // Once a resource is uploaded or deleted, it is no longer an evicted id, this + // removes it from the evicted set, and updates if we're able to draw now that + // all UIResources are valid. void MarkUIResourceNotEvicted(UIResourceId uid); + // Deletes all UIResource backings, and marks all the ids as evicted. void ClearUIResources(); + // Frees the textures/bitmaps backing the UIResource, held in the + // UIResourceData. + void DeleteUIResourceBacking(UIResourceData data, + const gpu::SyncToken& sync_token); + // Callback for when a UIResource is deleted *and* no longer in use by the + // display compositor. It will DeleteUIResourceBacking() if the backing was + // not already deleted preemptively. + void OnUIResourceReleased(UIResourceId uid, + const gpu::SyncToken& sync_token, + bool lost); void NotifySwapPromiseMonitorsOfSetNeedsRedraw(); void NotifySwapPromiseMonitorsOfForwardingToMainThread(); @@ -801,12 +851,14 @@ class CC_EXPORT LayerTreeHostImpl // active tree. void ActivateStateForImages(); - using UIResourceMap = std::unordered_map<UIResourceId, UIResourceData>; - UIResourceMap ui_resource_map_; - + 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. + std::unordered_map<UIResourceId, UIResourceData> deleted_ui_resources_; // Resources that were evicted by EvictAllUIResources. Resources are removed // from this when they are touched by a create or destroy from the UI resource - // request queue. + // request queue. The resource IDs held in here do not have any backing + // associated with them anymore, as that is freed at the time of eviction. std::set<UIResourceId> evicted_ui_resources_; LayerTreeFrameSink* layer_tree_frame_sink_; @@ -826,6 +878,7 @@ class CC_EXPORT LayerTreeHostImpl 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_; std::unique_ptr<RasterBufferProvider> raster_buffer_provider_; @@ -899,6 +952,11 @@ class CC_EXPORT LayerTreeHostImpl // overridden. gfx::Size device_viewport_size_; + // Viewport clip rect passed in from the main thrad, in physical pixels. + // This is used for out-of-process iframes whose size exceeds the window + // in order to prevent full raster. + gfx::Rect viewport_visible_rect_; + // Optional top-level constraints that can be set by the LayerTreeFrameSink. // - external_transform_ applies a transform above the root layer // - external_viewport_ is used DrawProperties, tile management and @@ -976,13 +1034,14 @@ class CC_EXPORT LayerTreeHostImpl ImplThreadPhase impl_thread_phase_; - base::Optional<ImageAnimationController> image_animation_controller_; + ImageAnimationController image_animation_controller_; std::unique_ptr<UkmManager> ukm_manager_; // 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 diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc index c44e31569ee..4c4570ee4d7 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -14,6 +14,7 @@ #include "base/command_line.h" #include "base/location.h" #include "base/memory/ptr_util.h" +#include "base/optional.h" #include "base/run_loop.h" #include "base/test/histogram_tester.h" #include "base/threading/thread_task_runner_handle.h" @@ -56,6 +57,7 @@ #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/mutator_host.h" +#include "cc/trees/render_frame_metadata_observer.h" #include "cc/trees/scroll_node.h" #include "cc/trees/single_thread_proxy.h" #include "cc/trees/transform_node.h" @@ -176,7 +178,11 @@ class LayerTreeHostImplTest : public testing::Test, animation_task_ = task; requested_animation_delay_ = delay; } - void DidActivateSyncTree() override {} + void DidActivateSyncTree() override { + // Make sure the active tree always has a valid LocalSurfaceId. + host_impl_->active_tree()->set_local_surface_id( + viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u))); + } void WillPrepareTiles() override {} void DidPrepareTiles() override {} void DidCompletePageScaleAnimationOnImplThread() override { @@ -236,6 +242,8 @@ class LayerTreeHostImplTest : public testing::Test, bool init = host_impl_->InitializeRenderer(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()->set_local_surface_id( + viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u))); // Set the viz::BeginFrameArgs so that methods which use it are able to. host_impl_->WillBeginImplFrame(viz::CreateBeginFrameArgsForTesting( BEGINFRAME_FROM_HERE, 0, 1, @@ -681,7 +689,8 @@ 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), false); + SnapAreaData area_data(SnapAxis::kBoth, gfx::ScrollOffset(50, 50), + gfx::RectF(0, 0, 300, 300), false); container_data.AddSnapAreaData(area_data); overflow->test_properties()->snap_container_data.emplace(container_data); host_impl_->active_tree()->BuildPropertyTreesForTesting(); @@ -1615,6 +1624,34 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) { EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), 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); + EXPECT_EQ( + InputHandler::SCROLL_ON_IMPL_THREAD, + host_impl_ + ->ScrollBegin(BeginState(scroll_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()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(20, 20), overflow->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(4, 4), result.current_offset); + + gfx::Vector2dF initial_offset, target_offset; + EXPECT_TRUE(host_impl_->GetSnapFlingInfo(gfx::Vector2dF(10, 10), + &initial_offset, &target_offset)); + EXPECT_VECTOR_EQ(gfx::Vector2dF(4, 4), initial_offset); + EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), target_offset); +} + TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) { LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(200, 200)); host_impl_->SetViewportSize(gfx::Size(100, 100)); @@ -3495,6 +3532,8 @@ class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest { host_impl_->active_tree()->BuildPropertyTreesForTesting(); host_impl_->active_tree()->DidBecomeActive(); host_impl_->active_tree()->HandleScrollbarShowRequestsFromMain(); + host_impl_->active_tree()->set_local_surface_id( + viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u))); DrawFrame(); // SetScrollElementId will initialize the scrollbar which will cause it to @@ -7785,6 +7824,76 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) { } } +TEST_F(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) { + const gfx::Size content_size(200, 200); + const gfx::Size viewport_size(100, 100); + + LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); + + LayerImpl* content_layer = + CreateBasicVirtualViewportLayers(viewport_size, content_size); + LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer(); + LayerImpl* scroll_layer = nullptr; + + // Initialization: Add a nested scrolling layer, simulating a scrolling div. + { + std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 11); + scroll->SetBounds(gfx::Size(400, 400)); + scroll->SetScrollable(content_size); + scroll->SetElementId(LayerIdToElementIdForTesting(scroll->id())); + scroll->SetDrawsContent(true); + + scroll_layer = scroll.get(); + + content_layer->test_properties()->AddChild(std::move(scroll)); + layer_tree_impl->BuildPropertyTreesForTesting(); + } + + InputHandlerScrollResult scroll_result; + DrawFrame(); + + // Start a scroll gesture, ensure it's scrolling the subscroller. + { + host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(), + InputHandler::TOUCHSCREEN); + host_impl_->ScrollBy( + UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get()); + + EXPECT_VECTOR_EQ(gfx::Vector2dF(100.f, 100.f), + scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0.f, 0.f), + outer_scroll_layer->CurrentScrollOffset()); + } + + // Continue the scroll. Ensure that scrolling beyond the child's extent + // doesn't consume the delta but it isn't counted as overscroll. + { + InputHandlerScrollResult result = host_impl_->ScrollBy( + UpdateState(gfx::Point(0, 0), gfx::Vector2dF(120.f, 140.f)).get()); + + EXPECT_VECTOR_EQ(gfx::Vector2dF(200.f, 200.f), + scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0.f, 0.f), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_FALSE(result.did_overscroll_root); + } + + // Continue the scroll. Ensure that scrolling beyond the child's extent + // doesn't consume the delta but it isn't counted as overscroll. + { + InputHandlerScrollResult result = host_impl_->ScrollBy( + UpdateState(gfx::Point(0, 0), gfx::Vector2dF(20.f, 40.f)).get()); + + EXPECT_VECTOR_EQ(gfx::Vector2dF(200.f, 200.f), + scroll_layer->CurrentScrollOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0.f, 0.f), + outer_scroll_layer->CurrentScrollOffset()); + EXPECT_FALSE(result.did_overscroll_root); + } + + host_impl_->ScrollEnd(EndState().get()); +} + TEST_F(LayerTreeHostImplTest, OverscrollOnMainThread) { InputHandlerScrollResult scroll_result; LayerTreeSettings settings = DefaultSettings(); @@ -8260,8 +8369,8 @@ class BlendStateCheckLayer : public LayerImpl { gfx::Size(1, 1), viz::ResourceTextureHint::kDefault, viz::RGBA_8888, gfx::ColorSpace()); } else { - resource_id_ = resource_provider->CreateBitmapResource(gfx::Size(1, 1), - gfx::ColorSpace()); + resource_id_ = resource_provider->CreateBitmapResource( + gfx::Size(1, 1), gfx::ColorSpace(), viz::RGBA_8888); } resource_provider->AllocateForTesting(resource_id_); SetBounds(gfx::Size(10, 10)); @@ -8289,7 +8398,7 @@ class BlendStateCheckLayer : public LayerImpl { test_blending_draw_quad->SetNew( shared_quad_state, quad_rect_, visible_quad_rect, needs_blending, resource_id_, gfx::RectF(0.f, 0.f, 1.f, 1.f), gfx::Size(1, 1), false, - false, false); + false, false, false); EXPECT_EQ(blend_, test_blending_draw_quad->ShouldDrawWithBlending()); EXPECT_EQ(has_render_surface_, @@ -8795,7 +8904,10 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { VerifyLayerIsLargerThanViewport(last_on_draw_render_passes_); } - void DidActivateSyncTree() override { did_activate_pending_tree_ = true; } + void DidActivateSyncTree() override { + LayerTreeHostImplTest::DidActivateSyncTree(); + did_activate_pending_tree_ = true; + } void set_gutter_quad_material(viz::DrawQuad::Material material) { gutter_quad_material_ = material; @@ -8978,6 +9090,8 @@ TEST_F(LayerTreeHostImplTest, PartialSwapReceivesDamageRect) { root->test_properties()->AddChild(std::move(child)); layer_tree_host_impl->active_tree()->SetRootLayerForTesting(std::move(root)); layer_tree_host_impl->active_tree()->BuildPropertyTreesForTesting(); + layer_tree_host_impl->active_tree()->set_local_surface_id( + viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u))); TestFrameData frame; @@ -13643,6 +13757,10 @@ TEST_F(LayerTreeHostImplTest, CheckerImagingTileInvalidation) { scoped_refptr<RasterSource> raster_source = recording_source->CreateRasterSource(); + viz::BeginFrameArgs begin_frame_args = + viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); + host_impl_->WillBeginImplFrame(begin_frame_args); + // Create the pending tree. host_impl_->BeginCommit(); LayerTreeImpl* pending_tree = host_impl_->pending_tree(); @@ -13923,5 +14041,215 @@ 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. +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 { + if (increment_counter_) + frame_token_allocator_->GetOrAllocateFrameToken(); + last_metadata_ = metadata; + } + + const base::Optional<RenderFrameMetadata>& last_metadata() const { + return last_metadata_; + } + + 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 = + SolidColorLayerImpl::Create(host_impl_->active_tree(), root_layer_id); + root->SetPosition(gfx::PointF()); + root->SetBounds(gfx::Size(10, 10)); + root->SetDrawsContent(true); + root->test_properties()->force_render_surface = true; + + host_impl_->active_tree()->SetRootLayerForTesting(std::move(root)); + host_impl_->active_tree()->BuildPropertyTreesForTesting(); + + auto observer = std::make_unique<TestRenderFrameMetadataObserver>(false); + auto* observer_ptr = observer.get(); + host_impl_->SetRenderFrameObserver(std::move(observer)); + EXPECT_FALSE(observer_ptr->last_metadata()); + + // Trigger a draw-swap sequence. + host_impl_->SetNeedsRedraw(); + TestFrameData frame; + EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); + EXPECT_TRUE(host_impl_->DrawLayers(&frame)); + host_impl_->DidDrawAllLayers(frame); + + // Ensure the selection bounds propagated to the render frame metadata + // represent an empty selection. + ASSERT_TRUE(observer_ptr->last_metadata()); + const viz::Selection<gfx::SelectionBound>& selection_1 = + observer_ptr->last_metadata()->selection; + EXPECT_EQ(gfx::SelectionBound::EMPTY, selection_1.start.type()); + EXPECT_EQ(gfx::SelectionBound::EMPTY, selection_1.end.type()); + EXPECT_EQ(gfx::PointF(), selection_1.start.edge_bottom()); + EXPECT_EQ(gfx::PointF(), selection_1.start.edge_top()); + EXPECT_FALSE(selection_1.start.visible()); + EXPECT_FALSE(selection_1.end.visible()); + + // Plumb the layer-local selection bounds. + gfx::Point selection_top(5, 0); + gfx::Point selection_bottom(5, 5); + LayerSelection selection; + selection.start.type = gfx::SelectionBound::CENTER; + selection.start.layer_id = root_layer_id; + selection.start.edge_bottom = selection_bottom; + selection.start.edge_top = selection_top; + selection.end = selection.start; + host_impl_->active_tree()->RegisterSelection(selection); + + // Trigger a draw-swap sequence. + host_impl_->SetNeedsRedraw(); + EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); + EXPECT_TRUE(host_impl_->DrawLayers(&frame)); + host_impl_->DidDrawAllLayers(frame); + + // Ensure the selection bounds have propagated to the render frame metadata. + ASSERT_TRUE(observer_ptr->last_metadata()); + const viz::Selection<gfx::SelectionBound>& selection_2 = + observer_ptr->last_metadata()->selection; + EXPECT_EQ(selection.start.type, selection_2.start.type()); + EXPECT_EQ(selection.end.type, selection_2.end.type()); + EXPECT_EQ(gfx::PointF(selection_bottom), selection_2.start.edge_bottom()); + EXPECT_EQ(gfx::PointF(selection_top), selection_2.start.edge_top()); + EXPECT_TRUE(selection_2.start.visible()); + EXPECT_TRUE(selection_2.end.visible()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc index 11c6a23a8c2..a4f98ae353a 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc @@ -4,7 +4,6 @@ #include <stddef.h> -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "cc/input/scrollbar.h" #include "cc/layers/painted_overlay_scrollbar_layer.h" @@ -52,6 +51,7 @@ class PaintedScrollbar : public Scrollbar { gfx::Rect TrackRect() const override { return rect_; } float ThumbOpacity() const override { return 1.f; } bool NeedsPaintPart(ScrollbarPart part) const override { return true; } + bool HasTickmarks() const override { return false; } void PaintPart(PaintCanvas* canvas, ScrollbarPart part, const gfx::Rect& content_rect) override { @@ -150,7 +150,7 @@ TEST_F(LayerTreeHostScrollbarsPixelTest, HugeTransformScale) { background->AddChild(layer); scoped_refptr<TestInProcessContextProvider> context( - new TestInProcessContextProvider(nullptr)); + new TestInProcessContextProvider(nullptr, 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_pixeltest_tiles.cc b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc index 7156d699a1e..bf42914b95d 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc @@ -24,6 +24,8 @@ enum RasterMode { FULL_ONE_COPY, PARTIAL_GPU, FULL_GPU, + PARTIAL_GPU_LOW_BIT_DEPTH, + FULL_GPU_LOW_BIT_DEPTH, PARTIAL_BITMAP, FULL_BITMAP, }; @@ -55,6 +57,18 @@ class LayerTreeHostTilesPixelTest : public LayerTreePixelTest { settings->gpu_rasterization_forced = true; settings->use_partial_raster = false; break; + case PARTIAL_GPU_LOW_BIT_DEPTH: + settings->gpu_rasterization_forced = true; + settings->use_partial_raster = true; + settings->preferred_tile_format = viz::RGBA_4444; + settings->unpremultiply_and_dither_low_bit_depth_tiles = true; + break; + case FULL_GPU_LOW_BIT_DEPTH: + settings->gpu_rasterization_forced = true; + settings->use_partial_raster = false; + settings->preferred_tile_format = viz::RGBA_4444; + settings->unpremultiply_and_dither_low_bit_depth_tiles = true; + break; } } @@ -81,6 +95,8 @@ class LayerTreeHostTilesPixelTest : public LayerTreePixelTest { case FULL_ONE_COPY: case PARTIAL_GPU: case FULL_GPU: + case PARTIAL_GPU_LOW_BIT_DEPTH: + case FULL_GPU_LOW_BIT_DEPTH: test_type = PIXEL_TEST_GL; break; case PARTIAL_BITMAP: @@ -120,9 +136,12 @@ class BlueYellowClient : public ContentLayerClient { PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); - flags.setColor(SK_ColorBLUE); + // Use custom colors with 0xF2 rather than the default blue/yellow (which + // use 0xFF), as the default won't show dither patterns as it exactly maps + // to a 16-bit color. + flags.setColor(SkColorSetRGB(0x00, 0x00, 0xF2)); display_list->push<DrawRectOp>(gfx::RectToSkRect(blue_rect), flags); - flags.setColor(SK_ColorYELLOW); + flags.setColor(SkColorSetRGB(0xF2, 0xF2, 0x00)); display_list->push<DrawRectOp>(gfx::RectToSkRect(yellow_rect), flags); display_list->EndPaintOfUnpaired(PaintableRegion()); @@ -247,6 +266,20 @@ TEST_F(LayerTreeHostTilesTestPartialInvalidation, base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped.png"))); } +TEST_F(LayerTreeHostTilesTestPartialInvalidation, + PartialRaster_SingleThread_GpuRaster_LowBitDepth) { + RunRasterPixelTest(false, PARTIAL_GPU_LOW_BIT_DEPTH, picture_layer_, + base::FilePath(FILE_PATH_LITERAL( + "blue_yellow_partial_flipped_dither.png"))); +} + +TEST_F(LayerTreeHostTilesTestPartialInvalidation, + FullRaster_SingleThread_GpuRaster_LowBitDepth) { + RunRasterPixelTest( + false, FULL_GPU_LOW_BIT_DEPTH, picture_layer_, + base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped_dither.png"))); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index 19d1cdab3c3..acbd577abcd 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -11,7 +11,6 @@ #include "base/auto_reset.h" #include "base/location.h" -#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" @@ -1268,12 +1267,11 @@ class LayerTreeHostTestEarlyDamageCheckStops : public LayerTreeHostTest { int damaged_frame_limit_; }; -// Flaky on Win7 Tests (dbg)(1). https://crbug.com/813578 -#if !defined(OS_WIN) // This behavior is specific to Android WebView, which only uses // multi-threaded compositor. -MULTI_THREAD_TEST_F(LayerTreeHostTestEarlyDamageCheckStops); -#endif +// Flaky on Win7 Tests (dbg)(1). https://crbug.com/813578 +// Flaky on linux_chromium_tsan_rel_ng. https://crbug.com/822473 +// MULTI_THREAD_TEST_F(LayerTreeHostTestEarlyDamageCheckStops); // Verify CanDraw() is false until first commit. class LayerTreeHostTestCantDrawBeforeCommit : public LayerTreeHostTest { @@ -5327,7 +5325,8 @@ class TestSwapPromise : public SwapPromise { result_->did_activate_called = true; } - void WillSwap(viz::CompositorFrameMetadata* metadata) override { + void WillSwap(viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator) override { base::AutoLock lock(result_->lock); EXPECT_FALSE(result_->did_swap_called); EXPECT_FALSE(result_->did_not_swap_called); @@ -8086,7 +8085,7 @@ class GpuRasterizationSucceedsWithLargeImage : public LayerTreeHostTest { GrContext* gr_context = context_provider->GrContext(); ASSERT_TRUE(gr_context); - const uint32_t max_texture_size = gr_context->caps()->maxTextureSize(); + const uint32_t max_texture_size = gr_context->maxTextureSize(); ASSERT_GT(static_cast<uint32_t>(large_image_size_.width()), max_texture_size); } @@ -8483,10 +8482,6 @@ class LayerTreeHostTestImageAnimation : public LayerTreeHostTest { public: void BeginTest() override { PostSetNeedsCommitToMainThread(); } - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_image_animations = true; - } - virtual void AddImageOp(const PaintImage& image) = 0; void SetupTree() override { @@ -8597,6 +8592,20 @@ class LayerTreeHostTestImageAnimationDrawRecordShader MULTI_THREAD_TEST_F(LayerTreeHostTestImageAnimationDrawRecordShader); +class LayerTreeHostTestImageAnimationPaintFilter + : public LayerTreeHostTestImageAnimation { + void AddImageOp(const PaintImage& image) override { + auto record = sk_make_sp<PaintOpBuffer>(); + record->push<DrawImageOp>(image, 0.f, 0.f, nullptr); + PaintFlags flags; + flags.setImageFilter( + sk_make_sp<RecordPaintFilter>(record, SkRect::MakeWH(500, 500))); + content_layer_client_.add_draw_rect(gfx::Rect(500, 500), flags); + } +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestImageAnimationPaintFilter); + class LayerTreeHostTestImageAnimationSynchronousScheduling : public LayerTreeHostTestImageAnimationDrawImage { public: diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index da9020d54e7..0a1a2b73fc7 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -26,6 +26,7 @@ #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_picture_layer.h" #include "cc/test/layer_tree_test.h" +#include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/transform_node.h" @@ -1825,6 +1826,196 @@ class LayerTreeHostAnimationTestImplSideInvalidation MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestImplSideInvalidation); +class LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit + : public LayerTreeHostAnimationTest { + public: + void SetupTree() override { + LayerTreeHostAnimationTest::SetupTree(); + layer_ = FakePictureLayer::Create(&client_); + layer_->SetBounds(gfx::Size(4, 4)); + client_.set_bounds(layer_->bounds()); + layer_tree_host()->root_layer()->AddChild(layer_); + AttachAnimationsToTimeline(); + animation_child_->AttachElement(layer_->element_id()); + num_draws_ = 0; + } + + void UpdateAnimationState(LayerTreeHostImpl* host_impl, + bool has_unfinished_animation) override { + if (!has_unfinished_animation && !did_request_impl_side_invalidation_) { + // The animation on the active tree has finished, now request an impl-side + // invalidation and verify that the final value on an impl-side pending + // tree is correct. + did_request_impl_side_invalidation_ = true; + host_impl->RequestImplSideInvalidationForCheckerImagedTiles(); + } + } + + void AfterTest() override {} + + protected: + scoped_refptr<Layer> layer_; + FakeContentLayerClient client_; + bool did_request_impl_side_invalidation_ = false; + int num_draws_; +}; + +class ImplSideInvalidationWithoutCommitTestOpacity + : public LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit { + public: + void BeginTest() override { + AddOpacityTransitionToAnimation(animation_child_.get(), 0.04, 0.2f, 0.8f, + false); + PostSetNeedsCommitToMainThread(); + } + + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + if (num_draws_++ > 0) + return; + EXPECT_EQ(0, host_impl->active_tree()->source_frame_number()); + LayerImpl* layer_impl = host_impl->active_tree()->LayerById(layer_->id()); + EXPECT_FLOAT_EQ(0.2f, layer_impl->Opacity()); + } + + void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override { + ASSERT_TRUE(did_request_impl_side_invalidation_); + EXPECT_EQ(0, host_impl->sync_tree()->source_frame_number()); + const float expected_opacity = 0.8f; + LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id()); + EXPECT_FLOAT_EQ(expected_opacity, layer_impl->Opacity()); + EndTest(); + } +}; + +MULTI_THREAD_TEST_F(ImplSideInvalidationWithoutCommitTestOpacity); + +class ImplSideInvalidationWithoutCommitTestTransform + : public LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit { + public: + void BeginTest() override { + AddAnimatedTransformToAnimation(animation_child_.get(), 0.04, 5, 5); + PostSetNeedsCommitToMainThread(); + } + + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + if (num_draws_++ > 0) + return; + EXPECT_EQ(0, host_impl->active_tree()->source_frame_number()); + LayerImpl* layer_impl = host_impl->active_tree()->LayerById(layer_->id()); + EXPECT_TRANSFORMATION_MATRIX_EQ(gfx::Transform(), + layer_impl->DrawTransform()); + } + + void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override { + ASSERT_TRUE(did_request_impl_side_invalidation_); + EXPECT_EQ(0, host_impl->sync_tree()->source_frame_number()); + gfx::Transform expected_transform; + expected_transform.Translate(5.f, 5.f); + LayerImpl* layer_impl = host_impl->sync_tree()->LayerById(layer_->id()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, + layer_impl->DrawTransform()); + EndTest(); + } +}; + +MULTI_THREAD_TEST_F(ImplSideInvalidationWithoutCommitTestTransform); + +class ImplSideInvalidationWithoutCommitTestFilter + : public LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit { + public: + void BeginTest() override { + AddAnimatedFilterToAnimation(animation_child_.get(), 0.04, 0.f, 1.f); + PostSetNeedsCommitToMainThread(); + } + + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + if (num_draws_++ > 0) + return; + EXPECT_EQ(0, host_impl->active_tree()->source_frame_number()); + EXPECT_EQ( + std::string("{\"FilterOperations\":[{\"type\":5,\"amount\":0.0}]}"), + host_impl->active_tree() + ->property_trees() + ->effect_tree.FindNodeFromElementId(layer_->element_id()) + ->filters.ToString()); + } + + void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override { + ASSERT_TRUE(did_request_impl_side_invalidation_); + EXPECT_EQ(0, host_impl->sync_tree()->source_frame_number()); + // AddAnimatedFilterToAnimation adds brightness filter which is type 5. + EXPECT_EQ( + std::string("{\"FilterOperations\":[{\"type\":5,\"amount\":1.0}]}"), + host_impl->sync_tree() + ->property_trees() + ->effect_tree.FindNodeFromElementId(layer_->element_id()) + ->filters.ToString()); + EndTest(); + } +}; + +MULTI_THREAD_TEST_F(ImplSideInvalidationWithoutCommitTestFilter); + +class ImplSideInvalidationWithoutCommitTestScroll + : public LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit { + public: + void SetupTree() override { + LayerTreeHostAnimationTestImplSideInvalidationWithoutCommit::SetupTree(); + + layer_->SetScrollable(gfx::Size(100, 100)); + layer_->SetBounds(gfx::Size(1000, 1000)); + client_.set_bounds(layer_->bounds()); + layer_->SetScrollOffset(gfx::ScrollOffset(10.f, 20.f)); + } + + void BeginTest() override { + std::unique_ptr<ScrollOffsetAnimationCurve> curve( + ScrollOffsetAnimationCurve::Create( + gfx::ScrollOffset(500.f, 550.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); + ASSERT_TRUE(proxy()->SupportsImplScrolling()); + animation_child_->AddKeyframeModel(std::move(keyframe_model)); + PostSetNeedsCommitToMainThread(); + } + + void WillCommit() override { + if (layer_tree_host()->SourceFrameNumber() == 1) { + // Block until the invalidation is done after animation finishes on the + // compositor thread. We need to make sure the pending tree has valid + // information based on invalidation only. + completion_.Wait(); + } + } + + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + if (num_draws_++ > 0) + return; + EXPECT_EQ(0, host_impl->active_tree()->source_frame_number()); + LayerImpl* layer_impl = host_impl->active_tree()->LayerById(layer_->id()); + EXPECT_VECTOR2DF_EQ(gfx::ScrollOffset(10.f, 20.f), + layer_impl->CurrentScrollOffset()); + } + + void DidInvalidateContentOnImplSide(LayerTreeHostImpl* host_impl) override { + ASSERT_TRUE(did_request_impl_side_invalidation_); + EXPECT_EQ(0, host_impl->sync_tree()->source_frame_number()); + LayerImpl* layer_impl = host_impl->pending_tree()->LayerById(layer_->id()); + EXPECT_VECTOR2DF_EQ(gfx::ScrollOffset(500.f, 550.f), + layer_impl->CurrentScrollOffset()); + completion_.Signal(); + EndTest(); + } + + private: + CompletionEvent completion_; +}; + +MULTI_THREAD_TEST_F(ImplSideInvalidationWithoutCommitTestScroll); + class LayerTreeHostAnimationTestNotifyAnimationFinished : public LayerTreeHostAnimationTest { public: diff --git a/chromium/cc/trees/layer_tree_host_unittest_context.cc b/chromium/cc/trees/layer_tree_host_unittest_context.cc index 9c7de670edb..024ca0204ab 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_context.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_context.cc @@ -5,7 +5,6 @@ #include <stddef.h> #include <stdint.h> -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "cc/layers/heads_up_display_layer.h" #include "cc/layers/layer_impl.h" diff --git a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc index 2f3eeacef91..edc62a8e36a 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc @@ -5,7 +5,6 @@ #include <stddef.h> #include "base/location.h" -#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/layers/effect_tree_layer_list_iterator.h" @@ -190,6 +189,36 @@ TEST_F(LayerTreeHostCopyRequestTestMultipleRequests, } TEST_F(LayerTreeHostCopyRequestTestMultipleRequests, + SkiaRenderer_RunSingleThread) { + use_gl_renderer_ = true; + use_skia_renderer_ = true; + RunTest(CompositorMode::SINGLE_THREADED); +} + +TEST_F(LayerTreeHostCopyRequestTestMultipleRequests, + SkiaRenderer_RunMultiThread) { + use_gl_renderer_ = true; + use_skia_renderer_ = true; + RunTest(CompositorMode::THREADED); +} + +TEST_F(LayerTreeHostCopyRequestTestMultipleRequests, + SkiaRenderer_RunSingleThread_OutOfOrderCallbacks) { + use_gl_renderer_ = true; + use_skia_renderer_ = true; + out_of_order_callbacks_ = true; + RunTest(CompositorMode::SINGLE_THREADED); +} + +TEST_F(LayerTreeHostCopyRequestTestMultipleRequests, + SkiaRenderer_RunMultiThread_OutOfOrderCallbacks) { + use_gl_renderer_ = true; + use_skia_renderer_ = true; + out_of_order_callbacks_ = true; + RunTest(CompositorMode::THREADED); +} + +TEST_F(LayerTreeHostCopyRequestTestMultipleRequests, SoftwareRenderer_RunSingleThread) { use_gl_renderer_ = false; RunTest(CompositorMode::SINGLE_THREADED); diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index babaa05fef3..68982f6f0cf 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -5,7 +5,6 @@ #include "cc/trees/layer_tree_host.h" #include "base/location.h" -#include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index 539192d8884..fa670feff83 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -505,9 +505,33 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { target_tree->has_ever_been_drawn_ = false; // Note: this needs to happen after SetPropertyTrees. + target_tree->HandleTickmarksVisibilityChange(); target_tree->HandleScrollbarShowRequestsFromMain(); } +void LayerTreeImpl::HandleTickmarksVisibilityChange() { + if (!host_impl_->ViewportMainScrollLayer()) + return; + + ScrollbarAnimationController* controller = + host_impl_->ScrollbarAnimationControllerForElementId( + OuterViewportScrollLayer()->element_id()); + + if (!controller) + return; + + for (ScrollbarLayerImplBase* scrollbar : controller->Scrollbars()) { + if (scrollbar->orientation() != VERTICAL) + continue; + + // Android Overlay Scrollbar don't have FindInPage Tickmarks. + if (scrollbar->GetScrollbarAnimator() != LayerTreeSettings::AURA_OVERLAY) + DCHECK(!scrollbar->HasFindInPageTickmarks()); + + controller->UpdateTickmarksVisibility(scrollbar->HasFindInPageTickmarks()); + } +} + void LayerTreeImpl::HandleScrollbarShowRequestsFromMain() { LayerTreeHostCommon::CallFunctionForEveryLayer(this, [this]( LayerImpl* layer) { @@ -553,6 +577,14 @@ LayerImplList::const_iterator LayerTreeImpl::end() const { return layer_list_.cend(); } +LayerImplList::const_reverse_iterator LayerTreeImpl::rbegin() const { + return layer_list_.crbegin(); +} + +LayerImplList::const_reverse_iterator LayerTreeImpl::rend() const { + return layer_list_.crend(); +} + LayerImplList::reverse_iterator LayerTreeImpl::rbegin() { return layer_list_.rbegin(); } @@ -1340,6 +1372,10 @@ TaskRunnerProvider* LayerTreeImpl::task_runner_provider() const { return host_impl_->task_runner_provider(); } +LayerTreeFrameSink* LayerTreeImpl::layer_tree_frame_sink() { + return host_impl_->layer_tree_frame_sink(); +} + const LayerTreeSettings& LayerTreeImpl::settings() const { return host_impl_->settings(); } @@ -1384,6 +1420,10 @@ gfx::Size LayerTreeImpl::device_viewport_size() const { return host_impl_->device_viewport_size(); } +gfx::Rect LayerTreeImpl::viewport_visible_rect() const { + return host_impl_->viewport_visible_rect(); +} + DebugRectHistory* LayerTreeImpl::debug_rect_history() const { return host_impl_->debug_rect_history(); } @@ -1593,11 +1633,13 @@ void LayerTreeImpl::AppendSwapPromises( new_swap_promises.clear(); } -void LayerTreeImpl::FinishSwapPromises(viz::CompositorFrameMetadata* metadata) { +void LayerTreeImpl::FinishSwapPromises( + viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator) { for (const auto& swap_promise : swap_promise_list_) - swap_promise->WillSwap(metadata); + swap_promise->WillSwap(metadata, frame_token_allocator); for (const auto& swap_promise : pinned_swap_promise_list_) - swap_promise->WillSwap(metadata); + swap_promise->WillSwap(metadata, frame_token_allocator); } void LayerTreeImpl::ClearSwapPromises() { diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index 4352049dd43..62c31dbd0a1 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -12,7 +12,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/values.h" #include "cc/base/synced_property.h" #include "cc/input/event_listener_properties.h" @@ -44,6 +43,7 @@ class HeadsUpDisplayLayerImpl; class ImageDecodeCache; class LayerTreeDebugState; class LayerTreeImpl; +class LayerTreeFrameSink; class LayerTreeResourceProvider; class LayerTreeSettings; class MemoryHistory; @@ -105,6 +105,7 @@ class CC_EXPORT LayerTreeImpl { // Methods called by the layer tree that pass-through or access LTHI. // --------------------------------------------------------------------------- + LayerTreeFrameSink* layer_tree_frame_sink(); const LayerTreeSettings& settings() const; const LayerTreeDebugState& debug_state() const; viz::ContextProvider* context_provider() const; @@ -116,6 +117,7 @@ class CC_EXPORT LayerTreeImpl { FrameRateCounter* frame_rate_counter() const; MemoryHistory* memory_history() const; gfx::Size device_viewport_size() const; + gfx::Rect viewport_visible_rect() const; DebugRectHistory* debug_rect_history() const; bool IsActiveTree() const; bool IsPendingTree() const; @@ -185,6 +187,8 @@ class CC_EXPORT LayerTreeImpl { LayerImplList::const_iterator begin() const; LayerImplList::const_iterator end() const; + LayerImplList::const_reverse_iterator rbegin() const; + LayerImplList::const_reverse_iterator rend() const; LayerImplList::reverse_iterator rbegin(); LayerImplList::reverse_iterator rend(); @@ -446,7 +450,8 @@ 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); + void FinishSwapPromises(viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator); void ClearSwapPromises(); void BreakSwapPromises(SwapPromise::DidNotSwapReason reason); @@ -550,6 +555,8 @@ class CC_EXPORT LayerTreeImpl { void ClearLayerList(); void BuildLayerListForTesting(); + + void HandleTickmarksVisibilityChange(); void HandleScrollbarShowRequestsFromMain(); void InvalidateRegionForImages( diff --git a/chromium/cc/trees/layer_tree_impl_unittest.cc b/chromium/cc/trees/layer_tree_impl_unittest.cc index 36c37b3b8b0..3856dcd0c42 100644 --- a/chromium/cc/trees/layer_tree_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_impl_unittest.cc @@ -5,7 +5,6 @@ #include "cc/trees/layer_tree_impl.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" @@ -2286,7 +2285,9 @@ class PersistentSwapPromise ~PersistentSwapPromise() override = default; void DidActivate() override {} - MOCK_METHOD1(WillSwap, void(viz::CompositorFrameMetadata* metadata)); + MOCK_METHOD2(WillSwap, + void(viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator)); MOCK_METHOD0(DidSwap, void()); DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override { @@ -2305,7 +2306,8 @@ class NotPersistentSwapPromise ~NotPersistentSwapPromise() override = default; void DidActivate() override {} - void WillSwap(viz::CompositorFrameMetadata* metadata) override {} + void WillSwap(viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator) override {} void DidSwap() override {} DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override { @@ -2343,9 +2345,9 @@ TEST_F(LayerTreeImplTest, PersistentSwapPromisesAreKeptAlive) { 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::_)); + EXPECT_CALL(*persistent_promises[i], WillSwap(testing::_, testing::_)); } - host_impl().active_tree()->FinishSwapPromises(nullptr); + host_impl().active_tree()->FinishSwapPromises(nullptr, nullptr); } TEST_F(LayerTreeImplTest, NotPersistentSwapPromisesAreDroppedWhenSwapFails) { diff --git a/chromium/cc/trees/layer_tree_settings.cc b/chromium/cc/trees/layer_tree_settings.cc index a353e68e1f9..08137e0e846 100644 --- a/chromium/cc/trees/layer_tree_settings.cc +++ b/chromium/cc/trees/layer_tree_settings.cc @@ -44,7 +44,6 @@ TileManagerSettings LayerTreeSettings::ToTileManagerSettings() const { tile_manager_settings.use_partial_raster = use_partial_raster; tile_manager_settings.enable_checker_imaging = enable_checker_imaging; tile_manager_settings.min_image_bytes_to_checker = min_image_bytes_to_checker; - tile_manager_settings.enable_image_animations = enable_image_animations; return tile_manager_settings; } diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h index c9ad35f47e6..2584543f863 100644 --- a/chromium/cc/trees/layer_tree_settings.h +++ b/chromium/cc/trees/layer_tree_settings.h @@ -42,7 +42,6 @@ class CC_EXPORT LayerTreeSettings { int damaged_frame_limit = 3; bool enable_latency_recovery = true; bool can_use_lcd_text = true; - bool use_distance_field_text = false; bool gpu_rasterization_forced = false; int gpu_rasterization_msaa_sample_count = 0; float gpu_rasterization_skewport_target_time_in_seconds = 0.2f; @@ -71,6 +70,9 @@ class CC_EXPORT LayerTreeSettings { double background_animation_rate = 1.0; gfx::Size default_tile_size; gfx::Size max_untiled_layer_size; + // If set, indicates the largest tile size we will use for GPU Raster. If not + // set, no limit is enforced. + gfx::Size max_gpu_raster_tile_size; gfx::Size minimum_occlusion_tracking_size; // 3000 pixels should give sufficient area for prepainting. // Note this value is specified with an ideal contents scale in mind. That @@ -92,6 +94,7 @@ class CC_EXPORT LayerTreeSettings { size_t decoded_image_working_set_budget_bytes = 128 * 1024 * 1024; int max_preraster_distance_in_screen_pixels = 1000; viz::ResourceFormat preferred_tile_format; + bool unpremultiply_and_dither_low_bit_depth_tiles = false; bool enable_mask_tiling = true; @@ -139,8 +142,9 @@ class CC_EXPORT LayerTreeSettings { // would have been used, out of process gpu raster will be used instead. bool enable_oop_rasterization = false; - // Whether images should be animated in the compositor. - bool enable_image_animations = false; + // Whether image animations can be reset to the beginning to avoid skipping + // many frames. + bool enable_image_animation_resync = true; // Whether to use edge anti-aliasing for all layer types that supports it. bool enable_edge_anti_aliasing = true; diff --git a/chromium/cc/trees/mutator_host.h b/chromium/cc/trees/mutator_host.h index 7594181d37d..e4aae914bae 100644 --- a/chromium/cc/trees/mutator_host.h +++ b/chromium/cc/trees/mutator_host.h @@ -8,7 +8,6 @@ #include <memory> #include "base/callback_forward.h" -#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "cc/trees/element_id.h" #include "cc/trees/layer_tree_mutator.h" diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc index afc24f39167..4aface2581b 100644 --- a/chromium/cc/trees/property_tree_builder.cc +++ b/chromium/cc/trees/property_tree_builder.cc @@ -9,7 +9,6 @@ #include <map> #include <set> -#include "base/memory/ptr_util.h" #include "cc/base/math_util.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index d81a1c10e77..4c4cdd4c632 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -10,7 +10,6 @@ #include <string> #include "base/auto_reset.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "cc/base/devtools_instrumentation.h" diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 7de104c60d8..7b7589167bb 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -7,7 +7,6 @@ #include <algorithm> #include <string> -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "cc/base/completion_event.h" diff --git a/chromium/cc/trees/render_frame_metadata.cc b/chromium/cc/trees/render_frame_metadata.cc index d20da91fd15..4466cb928c1 100644 --- a/chromium/cc/trees/render_frame_metadata.cc +++ b/chromium/cc/trees/render_frame_metadata.cc @@ -15,6 +15,15 @@ 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; +} + RenderFrameMetadata& RenderFrameMetadata::operator=( const RenderFrameMetadata&) = default; @@ -22,7 +31,10 @@ RenderFrameMetadata& RenderFrameMetadata::operator=( RenderFrameMetadata&& other) = default; bool RenderFrameMetadata::operator==(const RenderFrameMetadata& other) { - return root_scroll_offset == other.root_scroll_offset; + return root_scroll_offset == other.root_scroll_offset && + root_background_color == other.root_background_color && + is_scroll_offset_at_top == other.is_scroll_offset_at_top && + selection == other.selection; } bool RenderFrameMetadata::operator!=(const RenderFrameMetadata& other) { diff --git a/chromium/cc/trees/render_frame_metadata.h b/chromium/cc/trees/render_frame_metadata.h index 3c029591724..0a20fa8d73c 100644 --- a/chromium/cc/trees/render_frame_metadata.h +++ b/chromium/cc/trees/render_frame_metadata.h @@ -5,8 +5,12 @@ #ifndef CC_TREES_RENDER_FRAME_METADATA_H_ #define CC_TREES_RENDER_FRAME_METADATA_H_ +#include "base/optional.h" #include "cc/cc_export.h" +#include "components/viz/common/quads/selection.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/vector2d_f.h" +#include "ui/gfx/selection_bound.h" namespace cc { @@ -17,14 +21,33 @@ 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); bool operator!=(const RenderFrameMetadata& other); - // Scroll offset and scale of the root layer. This can be used for tasks - // like positioning windowed plugins. - gfx::Vector2dF root_scroll_offset; + // Indicates whether the scroll offset of the root layer is at top, i.e., + // whether scroll_offset.y() == 0. + bool is_scroll_offset_at_top = true; + + // The background color of a CompositorFrame. It can be used for filling the + // content area if the primary surface is unavailable and fallback is not + // specified. + SkColor root_background_color = SK_ColorWHITE; + + // Scroll offset of the root layer. This optional parameter is only valid + // during tests. + base::Optional<gfx::Vector2dF> root_scroll_offset; + + // Selection region relative to the current viewport. If the selection is + // empty or otherwise unused, the bound types will indicate such. + viz::Selection<gfx::SelectionBound> selection; }; } // namespace cc diff --git a/chromium/cc/trees/render_frame_metadata_observer.h b/chromium/cc/trees/render_frame_metadata_observer.h index f1055ec1a7d..90491d70ab9 100644 --- a/chromium/cc/trees/render_frame_metadata_observer.h +++ b/chromium/cc/trees/render_frame_metadata_observer.h @@ -11,6 +11,8 @@ namespace cc { +class FrameTokenAllocator; + // Observes RenderFrameMetadata associated with the submission of a frame. // LayerTreeHostImpl will create the metadata when submitting a CompositorFrame. // @@ -22,11 +24,12 @@ class CC_EXPORT RenderFrameMetadataObserver { // Binds on the current thread. This should only be called from the compositor // thread. - virtual void BindToCurrentThread() = 0; + virtual void BindToCurrentThread( + FrameTokenAllocator* frame_token_allocator) = 0; // Notification of the RendarFrameMetadata for the frame being submitted to // the display compositor. - virtual void OnRenderFrameSubmission(const RenderFrameMetadata& metadata) = 0; + virtual void OnRenderFrameSubmission(RenderFrameMetadata metadata) = 0; private: DISALLOW_COPY_AND_ASSIGN(RenderFrameMetadataObserver); diff --git a/chromium/cc/trees/swap_promise.h b/chromium/cc/trees/swap_promise.h index 81ec7cd0e36..c56b07155b3 100644 --- a/chromium/cc/trees/swap_promise.h +++ b/chromium/cc/trees/swap_promise.h @@ -12,6 +12,8 @@ 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 @@ -61,7 +63,8 @@ class CC_EXPORT SwapPromise { virtual ~SwapPromise() {} virtual void DidActivate() = 0; - virtual void WillSwap(viz::CompositorFrameMetadata* metadata) = 0; + virtual void WillSwap(viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator) = 0; virtual void DidSwap() = 0; // Return |KEEP_ACTIVE| if this promise should remain active (should not be // broken by the owner). diff --git a/chromium/cc/trees/swap_promise_manager_unittest.cc b/chromium/cc/trees/swap_promise_manager_unittest.cc index 59bd0980283..3acf5aa1f4d 100644 --- a/chromium/cc/trees/swap_promise_manager_unittest.cc +++ b/chromium/cc/trees/swap_promise_manager_unittest.cc @@ -4,7 +4,6 @@ #include "cc/trees/swap_promise_manager.h" -#include "base/memory/ptr_util.h" #include "cc/trees/swap_promise_monitor.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -31,7 +30,8 @@ class MockSwapPromise : public SwapPromise { ~MockSwapPromise() override = default; void DidActivate() override {} - void WillSwap(viz::CompositorFrameMetadata* metadata) override {} + void WillSwap(viz::CompositorFrameMetadata* metadata, + FrameTokenAllocator* frame_token_allocator) override {} void DidSwap() override {} DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override { return DidNotSwapAction::BREAK_PROMISE; |