diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-26 13:57:00 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-11-02 11:31:01 +0000 |
commit | 1943b3c2a1dcee36c233724fc4ee7613d71b9cf6 (patch) | |
tree | 8c1b5f12357025c197da5427ae02cfdc2f3570d6 /chromium/cc | |
parent | 21ba0c5d4bf8fba15dddd97cd693bad2358b77fd (diff) | |
download | qtwebengine-chromium-1943b3c2a1dcee36c233724fc4ee7613d71b9cf6.tar.gz |
BASELINE: Update Chromium to 94.0.4606.111
Change-Id: I924781584def20fc800bedf6ff41fdb96c438193
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/cc')
258 files changed, 4943 insertions, 2455 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index eaa675ba6a9..3234a484465 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -373,13 +373,17 @@ cc_component("cc") { "trees/layer_tree_settings.h", "trees/managed_memory_policy.cc", "trees/managed_memory_policy.h", + "trees/mobile_optimized_viewport_util.cc", + "trees/mobile_optimized_viewport_util.h", "trees/mutator_host.h", "trees/mutator_host_client.h", "trees/occlusion.cc", "trees/occlusion.h", "trees/occlusion_tracker.cc", "trees/occlusion_tracker.h", + "trees/paint_holding_commit_trigger.cc", "trees/paint_holding_commit_trigger.h", + "trees/paint_holding_reason.h", "trees/presentation_time_callback_buffer.cc", "trees/presentation_time_callback_buffer.h", "trees/property_animation_state.cc", diff --git a/chromium/cc/DEPS b/chromium/cc/DEPS index b694c05b487..dc197834bf6 100644 --- a/chromium/cc/DEPS +++ b/chromium/cc/DEPS @@ -37,6 +37,7 @@ include_rules = [ "+third_party/libyuv", "+third_party/skia/include", "+third_party/skia/src/core/SkRemoteGlyphCache.h", + "+third_party/skia/src/effects/imagefilters/SkRuntimeImageFilter.h", "+third_party/perfetto/protos/perfetto/trace/track_event", "+ui/events/types", "+ui/latency", diff --git a/chromium/cc/OWNERS b/chromium/cc/OWNERS index 57a11c57c6f..2d8d597dbc8 100644 --- a/chromium/cc/OWNERS +++ b/chromium/cc/OWNERS @@ -23,6 +23,7 @@ vmpstr@chromium.org sunnyps@chromium.org # math / geometry +dbaron@chromium.org flackr@chromium.org # property trees @@ -45,15 +46,15 @@ jonross@chromium.org # input, scrolling bokan@chromium.org -# scroll snap -majidvp@chromium.org - # metrics sadrul@chromium.org +behdadb@chromium.org # paint sunnyps@chromium.org vasilyt@chromium.org +reed@google.com +fmalita@chromium.org # general vmpstr@chromium.org diff --git a/chromium/cc/PRESUBMIT.py b/chromium/cc/PRESUBMIT.py index c375a37d367..a3a40eda5ef 100644 --- a/chromium/cc/PRESUBMIT.py +++ b/chromium/cc/PRESUBMIT.py @@ -9,8 +9,8 @@ for more details about the presubmit API built into depot_tools. """ import re -import string +USE_PYTHON3 = True CC_SOURCE_FILES=(r'^cc[\\/].*\.(cc|h)$',) def CheckChangeLintsClean(input_api, output_api): @@ -105,9 +105,9 @@ def CheckPassByValue(input_api, for f in input_api.AffectedSourceFiles(source_file_filter): contents = input_api.ReadFile(f, 'rb') + sep = '|' match = re.search( - r'\bconst +' + '(?P<type>(%s))&' % - string.join(pass_by_value_types, '|'), + r'\bconst +' + '(?P<type>(%s))&' % sep.join(pass_by_value_types), contents) if match: local_errors.append(output_api.PresubmitError( @@ -297,7 +297,8 @@ def CheckForDisallowMacros(input_api, output_api, allowlist=CC_SOURCE_FILES, if disallow_macro_files: return [output_api.PresubmitError( - 'The following files use DISALLOW* macros. In cc, please use deleted constructors/operators instead.', + 'The following files use DISALLOW* macros. In cc, please use deleted ' + 'constructors/operators instead.', items=disallow_macro_files)] return [] diff --git a/chromium/cc/README.md b/chromium/cc/README.md index e8ae01d7bfb..86dc284e11a 100644 --- a/chromium/cc/README.md +++ b/chromium/cc/README.md @@ -33,11 +33,10 @@ which batches up a set of DrawQuads and RenderPasses into a CompositorFrame which is sent via a CompositorFrameSink. CompositorFrames from individual compositors are sent to the -SurfaceManager (currently in the browser process). The -SurfaceAggregator combines all CompositorFrames together and asks -the Display to finally draw the frame via Renderer, which is either -a viz::GLRenderer or a SoftwareRenderer, which finally draws the entire -composited browser contents into a backbuffer or a bitmap, respectively. +SurfaceManager (which is in the GPU process). The SurfaceAggregator combines all +CompositorFrames together when asked to by the Display. These are given to the +viz::DirectRenderer, which finally draws the entire composited browser contents. +See //components/viz for more details on the display compositor. Design documents for the graphics stack can be found at [chromium-graphics](https://www.chromium.org/developers/design-documents/chromium-graphics). @@ -76,7 +75,8 @@ that is responsible for a composited animation. Some additional information in ### DirectRenderer An abstraction that provides an API for the Display to draw a fully-aggregated CompositorFrame to a physical output. Subclasses of it provide implementations -for various backends, currently GL or Software. +for various backends, currently GL, Skia, or Software. See [viz::DirectRenderer](https://codesearch.chromium.org/chromium/src/components/viz/service/display/direct_renderer.h) +for details. ### Layer A conceptual piece of content that can appear on screen and has some known @@ -102,7 +102,7 @@ and [Blink Property Trees](https://docs.google.com/presentation/u/1/d/1ak7YVrJIT ### Display A controller class that takes CompositorFrames for each surface and draws them -to a physical output. +to a physical output. See [viz::Display](https://codesearch.chromium.org/chromium/src/components/viz/service/display/display.h) for details. ### Draw Filling pixels in a physical output (technically could be to an offscreen diff --git a/chromium/cc/animation/animation.cc b/chromium/cc/animation/animation.cc index 220347a9220..34af25da509 100644 --- a/chromium/cc/animation/animation.cc +++ b/chromium/cc/animation/animation.cc @@ -10,7 +10,6 @@ #include <string> #include <utility> -#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "cc/animation/animation_delegate.h" #include "cc/animation/animation_events.h" diff --git a/chromium/cc/animation/animation_timeline.cc b/chromium/cc/animation/animation_timeline.cc index d7067c5e235..ac3a92e9c2c 100644 --- a/chromium/cc/animation/animation_timeline.cc +++ b/chromium/cc/animation/animation_timeline.cc @@ -5,7 +5,10 @@ #include "cc/animation/animation_timeline.h" #include <algorithm> +#include <utility> +#include <vector> +#include "base/time/time.h" #include "cc/animation/animation.h" #include "cc/animation/animation_host.h" #include "cc/trees/property_tree.h" diff --git a/chromium/cc/animation/animation_timeline.h b/chromium/cc/animation/animation_timeline.h index 6ff994afa56..3f23c39416c 100644 --- a/chromium/cc/animation/animation_timeline.h +++ b/chromium/cc/animation/animation_timeline.h @@ -7,10 +7,15 @@ #include <memory> #include <unordered_map> +#include <vector> #include "base/memory/ref_counted.h" #include "cc/animation/animation_export.h" +namespace base { +class TimeTicks; +} + namespace cc { class Animation; diff --git a/chromium/cc/animation/animation_unittest.cc b/chromium/cc/animation/animation_unittest.cc index 15fe8962b49..2fc379f5957 100644 --- a/chromium/cc/animation/animation_unittest.cc +++ b/chromium/cc/animation/animation_unittest.cc @@ -463,7 +463,7 @@ TEST_F(AnimationTest, ToString) { base::StringPrintf("Animation{id=%d, element_id=%s, " "keyframe_models=[KeyframeModel{id=42, " "group=73, target_property_type=1, " - "custom_property_name=, native_property_type=1, " + "custom_property_name=, native_property_type=2, " "run_state=WAITING_FOR_TARGET_AVAILABILITY, " "element_id=(0)}]}", animation_->id(), element_id_.ToString().c_str()), @@ -476,10 +476,10 @@ TEST_F(AnimationTest, ToString) { "Animation{id=%d, element_id=%s, " "keyframe_models=[KeyframeModel{id=42, " "group=73, target_property_type=1, custom_property_name=, " - "native_property_type=1, " + "native_property_type=2, " "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}, " "KeyframeModel{id=45, group=76, target_property_type=5, " - "custom_property_name=, native_property_type=1, " + "custom_property_name=, native_property_type=2, " "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}]}", animation_->id(), element_id_.ToString().c_str()), animation_->ToString()); diff --git a/chromium/cc/animation/element_animations.cc b/chromium/cc/animation/element_animations.cc index bb4fad08e90..ebe6c50fd61 100644 --- a/chromium/cc/animation/element_animations.cc +++ b/chromium/cc/animation/element_animations.cc @@ -8,8 +8,9 @@ #include <algorithm> #include <utility> +#include <vector> -#include "base/numerics/ranges.h" +#include "base/cxx17_backports.h" #include "cc/animation/animation_delegate.h" #include "cc/animation/animation_events.h" #include "cc/animation/animation_host.h" @@ -119,6 +120,12 @@ void ElementAnimations::ClearAffectedElementTypes( RemoveKeyframeEffectsFromTicking(); } +// TODO(crbug.com/1240712): the ReservedElementId should always be 'registered'. +// Instead of calling this from AnimationHost::UpdateRegisteredElementIds, we +// can ensure that the |has_element_in_active_list_| and the +// |has_element_in_pending_list_| are true for ReservedElementId, and this +// should result in animations ticking right away. With that, we do not need to +// add anything to the |keyframe_effects_list_| for ReservedElementId. void ElementAnimations::ElementIdRegistered(ElementId element_id, ElementListType list_type) { DCHECK_EQ(element_id_, element_id); @@ -224,7 +231,7 @@ void ElementAnimations::OnFloatAnimated(const float& value, target_property_id); break; case TargetProperty::OPACITY: { - float opacity = base::ClampToRange(value, 0.0f, 1.0f); + float opacity = base::clamp(value, 0.0f, 1.0f); if (KeyframeModelAffectsActiveElements(keyframe_model)) OnOpacityAnimated(ElementListType::ACTIVE, opacity, keyframe_model); if (KeyframeModelAffectsPendingElements(keyframe_model)) @@ -305,6 +312,11 @@ void ElementAnimations::InitClientAnimationState() { void ElementAnimations::UpdateClientAnimationState() { if (!element_id()) return; + // For a custom property animation, or an animation that uses paint worklet, + // it is not associated with any property node, and thus this function is not + // needed. + if (element_id().GetStableId() == ElementId::kReservedElementId) + return; DCHECK(animation_host_); if (!animation_host_->mutator_host_client()) return; diff --git a/chromium/cc/animation/keyframe_effect.cc b/chromium/cc/animation/keyframe_effect.cc index 1076d5a2e0e..99beca105a1 100644 --- a/chromium/cc/animation/keyframe_effect.cc +++ b/chromium/cc/animation/keyframe_effect.cc @@ -9,7 +9,7 @@ #include <string> #include <utility> -#include "base/stl_util.h" +#include "base/containers/cxx20_erase.h" #include "base/time/time.h" #include "cc/animation/animation.h" #include "cc/animation/animation_host.h" diff --git a/chromium/cc/animation/keyframe_model.cc b/chromium/cc/animation/keyframe_model.cc index 7f4801ea695..95d6a7a651b 100644 --- a/chromium/cc/animation/keyframe_model.cc +++ b/chromium/cc/animation/keyframe_model.cc @@ -12,7 +12,6 @@ #include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" -#include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" @@ -55,15 +54,22 @@ KeyframeModel::TargetPropertyId::TargetPropertyId( KeyframeModel::TargetPropertyId::TargetPropertyId( const TargetPropertyId& other) = default; +KeyframeModel::TargetPropertyId::TargetPropertyId(TargetPropertyId&& other) = + default; + KeyframeModel::TargetPropertyId::~TargetPropertyId() = default; +KeyframeModel::TargetPropertyId& KeyframeModel::TargetPropertyId::operator=( + TargetPropertyId&& other) = default; + std::unique_ptr<KeyframeModel> KeyframeModel::Create( std::unique_ptr<gfx::AnimationCurve> curve, int keyframe_model_id, int group_id, TargetPropertyId target_property_id) { return base::WrapUnique(new KeyframeModel(std::move(curve), keyframe_model_id, - group_id, target_property_id)); + group_id, + std::move(target_property_id))); } std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance( @@ -97,7 +103,7 @@ KeyframeModel::KeyframeModel(std::unique_ptr<gfx::AnimationCurve> curve, keyframe_model_id, target_property_id.target_property_type()), group_(group_id), - target_property_id_(target_property_id), + target_property_id_(std::move(target_property_id)), needs_synchronized_start_time_(false), received_finished_event_(false), is_controlling_instance_(false), diff --git a/chromium/cc/animation/keyframe_model.h b/chromium/cc/animation/keyframe_model.h index 8be005fd25e..2c9edb9170d 100644 --- a/chromium/cc/animation/keyframe_model.h +++ b/chromium/cc/animation/keyframe_model.h @@ -12,7 +12,6 @@ #include "cc/animation/animation_export.h" #include "cc/paint/element_id.h" #include "cc/paint/paint_worklet_input.h" -#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/animation/keyframe/keyframe_model.h" namespace cc { @@ -43,8 +42,11 @@ class CC_ANIMATION_EXPORT KeyframeModel : public gfx::KeyframeModel { int target_property_type, PaintWorkletInput::NativePropertyType native_property_type); TargetPropertyId(const TargetPropertyId&); + TargetPropertyId(TargetPropertyId&&); ~TargetPropertyId(); + TargetPropertyId& operator=(TargetPropertyId&& other); + int target_property_type() const { return target_property_type_; } const std::string& custom_property_name() const { return custom_property_name_; diff --git a/chromium/cc/animation/keyframe_model_unittest.cc b/chromium/cc/animation/keyframe_model_unittest.cc index 1e01ad2ac47..c4003ed28dd 100644 --- a/chromium/cc/animation/keyframe_model_unittest.cc +++ b/chromium/cc/animation/keyframe_model_unittest.cc @@ -1388,7 +1388,7 @@ TEST(KeyframeModelTest, ToString) { KeyframeModel::TargetPropertyId(TargetProperty::OPACITY)); EXPECT_EQ(base::StringPrintf( "KeyframeModel{id=%d, group=73, target_property_type=1, " - "custom_property_name=, native_property_type=1, " + "custom_property_name=, native_property_type=2, " "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}", keyframe_model->id()), keyframe_model->ToString()); diff --git a/chromium/cc/animation/scroll_offset_animation_curve.cc b/chromium/cc/animation/scroll_offset_animation_curve.cc index 1b4d045c74c..0ba2a8dd155 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve.cc +++ b/chromium/cc/animation/scroll_offset_animation_curve.cc @@ -9,8 +9,8 @@ #include <utility> #include "base/check_op.h" +#include "base/cxx17_backports.h" #include "base/memory/ptr_util.h" -#include "base/numerics/ranges.h" #include "ui/gfx/animation/keyframe/timing_function.h" #include "ui/gfx/animation/tween.h" @@ -56,7 +56,7 @@ static float MaximumDimension(const gfx::Vector2dF& delta) { static std::unique_ptr<TimingFunction> EaseInOutWithInitialSlope(double slope) { // Clamp slope to a sane value. - slope = base::ClampToRange(slope, -1000.0, 1000.0); + slope = base::clamp(slope, -1000.0, 1000.0); // Based on CubicBezierTimingFunction::EaseType::EASE_IN_OUT preset // with first control point scaled. @@ -193,8 +193,8 @@ base::TimeDelta ScrollOffsetAnimationCurve::EaseInOutSegmentDuration( case DurationBehavior::INVERSE_DELTA: duration = kInverseDeltaOffset + std::abs(MaximumDimension(delta)) * kInverseDeltaSlope; - duration = base::ClampToRange(duration, kInverseDeltaMinDuration, - kInverseDeltaMaxDuration); + duration = base::clamp(duration, kInverseDeltaMinDuration, + kInverseDeltaMaxDuration); break; } duration /= kDurationDivisor; @@ -268,7 +268,7 @@ base::TimeDelta ScrollOffsetAnimationCurve::ImpulseSegmentDuration( } else { double duration_in_milliseconds = kImpulseMillisecondsPerPixel * std::abs(MaximumDimension(delta)); - duration_in_milliseconds = base::ClampToRange( + duration_in_milliseconds = base::clamp( duration_in_milliseconds, kImpulseMinDurationMs, kImpulseMaxDurationMs); duration = base::TimeDelta::FromMillisecondsD(duration_in_milliseconds); } diff --git a/chromium/cc/base/devtools_instrumentation.cc b/chromium/cc/base/devtools_instrumentation.cc index 85141d8ce59..79c321ae344 100644 --- a/chromium/cc/base/devtools_instrumentation.cc +++ b/chromium/cc/base/devtools_instrumentation.cc @@ -38,6 +38,7 @@ const char kFrameId[] = "frameId"; const char kLayerId[] = "layerId"; const char kLayerTreeId[] = "layerTreeId"; const char kPixelRefId[] = "pixelRefId"; +const char kFrameSequenceNumber[] = "frameSeqId"; const char kImageUploadTask[] = "ImageUploadTask"; const char kImageDecodeTask[] = "ImageDecodeTask"; diff --git a/chromium/cc/base/devtools_instrumentation.h b/chromium/cc/base/devtools_instrumentation.h index 0b19cf006da..39a816a3ae8 100644 --- a/chromium/cc/base/devtools_instrumentation.h +++ b/chromium/cc/base/devtools_instrumentation.h @@ -14,6 +14,7 @@ #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" +#include "base/trace_event/typed_macros.h" #include "cc/base/base_export.h" namespace cc { @@ -33,6 +34,7 @@ CC_BASE_EXPORT extern const char kFrameId[]; CC_BASE_EXPORT extern const char kLayerId[]; CC_BASE_EXPORT extern const char kLayerTreeId[]; CC_BASE_EXPORT extern const char kPixelRefId[]; +CC_BASE_EXPORT extern const char kFrameSequenceNumber[]; CC_BASE_EXPORT extern const char kImageDecodeTask[]; CC_BASE_EXPORT extern const char kBeginFrame[]; @@ -142,10 +144,11 @@ class CC_BASE_EXPORT ScopedLayerTreeTask { struct CC_BASE_EXPORT ScopedCommitTrace { public: - explicit ScopedCommitTrace(int layer_tree_host_id) { - TRACE_EVENT_BEGIN1(internal::CategoryName::kTimeline, + explicit ScopedCommitTrace(int layer_tree_host_id, uint64_t sequence_number) { + TRACE_EVENT_BEGIN2(internal::CategoryName::kTimeline, internal::kCompositeLayers, internal::kLayerTreeId, - layer_tree_host_id); + layer_tree_host_id, internal::kFrameSequenceNumber, + sequence_number); } ScopedCommitTrace(const ScopedCommitTrace&) = delete; ~ScopedCommitTrace() { @@ -176,18 +179,22 @@ inline void CC_BASE_EXPORT DidActivateLayerTree(int layer_tree_host_id, internal::kFrameId, frame_id); } -inline void CC_BASE_EXPORT -DidBeginFrame(int layer_tree_host_id, base::TimeTicks begin_frame_timestamp) { - TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( - internal::CategoryName::kTimelineFrame, internal::kBeginFrame, - TRACE_EVENT_SCOPE_THREAD, begin_frame_timestamp, internal::kLayerTreeId, - layer_tree_host_id); +inline void CC_BASE_EXPORT DidBeginFrame(int layer_tree_host_id, + base::TimeTicks begin_frame_timestamp, + uint64_t sequence_number) { + TRACE_EVENT_INSTANT(internal::CategoryName::kTimelineFrame, + perfetto::StaticString(internal::kBeginFrame), + begin_frame_timestamp, internal::kLayerTreeId, + layer_tree_host_id, internal::kFrameSequenceNumber, + sequence_number); } -inline void CC_BASE_EXPORT DidDrawFrame(int layer_tree_host_id) { - TRACE_EVENT_INSTANT1(internal::CategoryName::kTimelineFrame, +inline void CC_BASE_EXPORT DidDrawFrame(int layer_tree_host_id, + uint64_t sequence_number) { + TRACE_EVENT_INSTANT2(internal::CategoryName::kTimelineFrame, internal::kDrawFrame, TRACE_EVENT_SCOPE_THREAD, - internal::kLayerTreeId, layer_tree_host_id); + internal::kLayerTreeId, layer_tree_host_id, + internal::kFrameSequenceNumber, sequence_number); } inline void CC_BASE_EXPORT DidRequestMainThreadFrame(int layer_tree_host_id) { @@ -198,11 +205,13 @@ inline void CC_BASE_EXPORT DidRequestMainThreadFrame(int layer_tree_host_id) { inline void CC_BASE_EXPORT DidDropSmoothnessFrame(int layer_tree_host_id, - base::TimeTicks dropped_frame_timestamp) { - TRACE_EVENT_INSTANT_WITH_TIMESTAMP1( - internal::CategoryName::kTimelineFrame, internal::kDroppedFrame, - TRACE_EVENT_SCOPE_THREAD, dropped_frame_timestamp, internal::kLayerTreeId, - layer_tree_host_id); + base::TimeTicks dropped_frame_timestamp, + uint64_t sequence_number) { + TRACE_EVENT_INSTANT(internal::CategoryName::kTimelineFrame, + perfetto::StaticString(internal::kDroppedFrame), + dropped_frame_timestamp, internal::kLayerTreeId, + layer_tree_host_id, internal::kFrameSequenceNumber, + sequence_number); } inline std::unique_ptr<base::trace_event::ConvertableToTraceFormat> diff --git a/chromium/cc/base/features.cc b/chromium/cc/base/features.cc index ce01099c141..eea90717aa6 100644 --- a/chromium/cc/base/features.cc +++ b/chromium/cc/base/features.cc @@ -39,20 +39,6 @@ const base::Feature kSynchronizedScrolling = { base::FEATURE_ENABLED_BY_DEFAULT}; #endif -bool IsImplLatencyRecoveryEnabled() { - // TODO(crbug.com/1142598): Latency recovery has been disabled by default - // since M87. For now, only the flag is removed. If all goes well, remove the - // code supporting latency recovery. - return false; -} - -bool IsMainLatencyRecoveryEnabled() { - // TODO(crbug.com/1142598): Latency recovery has been disabled by default - // since M87. For now, only the flag is removed. If all goes well, remove the - // code supporting latency recovery. - return false; -} - const base::Feature kRemoveMobileViewportDoubleTap{ "RemoveMobileViewportDoubleTap", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -71,4 +57,8 @@ const base::Feature kHudDisplayForPerformanceMetrics{ const base::Feature kJankInjectionAblationFeature{ "JankInjectionAblation", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kPreferNewContentForCheckerboardedScrolls{ + "PreferNewContentForCheckerboardedScrolls", + base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features diff --git a/chromium/cc/base/features.h b/chromium/cc/base/features.h index d630c265dbd..652d5f88f57 100644 --- a/chromium/cc/base/features.h +++ b/chromium/cc/base/features.h @@ -15,9 +15,6 @@ CC_BASE_EXPORT extern const base::Feature kAnimatedImageResume; CC_BASE_EXPORT extern const base::Feature kImpulseScrollAnimations; CC_BASE_EXPORT extern const base::Feature kSynchronizedScrolling; -CC_BASE_EXPORT bool IsImplLatencyRecoveryEnabled(); -CC_BASE_EXPORT bool IsMainLatencyRecoveryEnabled(); - // When enabled, the double tap to zoom will be disabled when the viewport // meta tag is properly set for mobile using content=width=device-width // or content=initial-scale=1.0 @@ -50,6 +47,12 @@ CC_BASE_EXPORT extern const base::Feature kHudDisplayForPerformanceMetrics; // When enabled, some jank is injected to the animation/scrolling pipeline. CC_BASE_EXPORT extern const base::Feature kJankInjectionAblationFeature; +// When enabled, scheduler tree priority will change to +// NEW_CONTENT_TAKES_PRIORITY if during a scrollbar scroll, CC has to +// checkerboard. +CC_BASE_EXPORT extern const base::Feature + kPreferNewContentForCheckerboardedScrolls; + } // namespace features #endif // CC_BASE_FEATURES_H_ diff --git a/chromium/cc/base/index_rect_unittest.cc b/chromium/cc/base/index_rect_unittest.cc index 1580a3947e7..d66813ed902 100644 --- a/chromium/cc/base/index_rect_unittest.cc +++ b/chromium/cc/base/index_rect_unittest.cc @@ -3,7 +3,8 @@ // found in the LICENSE file. #include "cc/base/index_rect.h" -#include "base/stl_util.h" + +#include "base/cxx17_backports.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { diff --git a/chromium/cc/base/list_container_helper.cc b/chromium/cc/base/list_container_helper.cc index 7b594b4a458..1e5a35030b3 100644 --- a/chromium/cc/base/list_container_helper.cc +++ b/chromium/cc/base/list_container_helper.cc @@ -8,6 +8,7 @@ #include <algorithm> #include <cstring> +#include <utility> #include <vector> #include "base/check_op.h" @@ -30,8 +31,14 @@ class ListContainerHelper::CharAllocator { // This class holds the raw memory chunk, as well as information about its // size and availability. struct InnerList { - InnerList(const InnerList&) = delete; - InnerList& operator=(const InnerList&) = delete; + InnerList(size_t capacity, size_t element_size, size_t alignment) + : data(static_cast<char*>( + base::AlignedAlloc(capacity * element_size, alignment))), + capacity(capacity), + size(0), + step(element_size) {} + InnerList(InnerList&& other) = default; + InnerList& operator=(InnerList&& other) = default; std::unique_ptr<char[], base::AlignedFreeDeleter> data; // The number of elements in total the memory can hold. The difference @@ -44,8 +51,6 @@ class ListContainerHelper::CharAllocator { // elements' memory locations. size_t step; - InnerList() : capacity(0), size(0), step(0) {} - void Erase(char* position) { // Confident that destructor is called by caller of this function. Since // CharAllocator does not handle construction after @@ -116,7 +121,7 @@ class ListContainerHelper::CharAllocator { DCHECK_EQ(element_size % alignment, 0u); AllocateNewList(element_count > 0 ? element_count : kDefaultNumElementTypesToReserve); - last_list_ = storage_[last_list_index_].get(); + last_list_ = &storage_[last_list_index_]; } CharAllocator(const CharAllocator&) = delete; @@ -132,7 +137,7 @@ class ListContainerHelper::CharAllocator { AllocateNewList(last_list_->capacity * 2); ++last_list_index_; - last_list_ = storage_[last_list_index_].get(); + last_list_ = &storage_[last_list_index_]; } ++size_; @@ -148,7 +153,7 @@ class ListContainerHelper::CharAllocator { size_t Capacity() const { size_t capacity_sum = 0; for (const auto& inner_list : storage_) - capacity_sum += inner_list->capacity; + capacity_sum += inner_list.capacity; return capacity_sum; } @@ -157,7 +162,7 @@ class ListContainerHelper::CharAllocator { DCHECK(!storage_.empty()); storage_.erase(storage_.begin() + 1, storage_.end()); last_list_index_ = 0; - last_list_ = storage_[0].get(); + last_list_ = &storage_[0]; last_list_->size = 0; size_ = 0; } @@ -167,7 +172,7 @@ class ListContainerHelper::CharAllocator { last_list_->RemoveLast(); if (last_list_->IsEmpty() && last_list_index_ > 0) { --last_list_index_; - last_list_ = storage_[last_list_index_].get(); + last_list_ = &storage_[last_list_index_]; // If there are now two empty inner lists, free one of them. if (last_list_index_ + 2 < storage_.size()) @@ -180,12 +185,12 @@ class ListContainerHelper::CharAllocator { DCHECK_EQ(this, position->ptr_to_container); // Update |position| to point to the element after the erased element. - InnerList* list = storage_[position->vector_index].get(); + InnerList& list = storage_[position->vector_index]; char* item_iterator = position->item_iterator; - if (item_iterator == list->LastElement()) + if (item_iterator == list.LastElement()) position->Increment(); - list->Erase(item_iterator); + list.Erase(item_iterator); // TODO(weiliangc): Free the InnerList if it is empty. --size_; } @@ -200,20 +205,20 @@ class ListContainerHelper::CharAllocator { // Set |position| to be the first inserted element. Allocate(); position->vector_index = storage_.size() - 1; - position->item_iterator = storage_[position->vector_index]->LastElement(); + position->item_iterator = storage_[position->vector_index].LastElement(); // Allocate the rest. for (size_t i = 1; i < count; ++i) Allocate(); } else { - storage_[position->vector_index]->InsertBefore( + storage_[position->vector_index].InsertBefore( alignment_, &position->item_iterator, count); size_ += count; } } - InnerList* InnerListById(size_t id) const { + const InnerList& InnerListById(size_t id) const { DCHECK_LT(id, storage_.size()); - return storage_[id].get(); + return storage_[id]; } size_t FirstInnerListId() const { @@ -221,7 +226,7 @@ class ListContainerHelper::CharAllocator { // non-empty. DCHECK_GT(size_, 0u); size_t id = 0; - while (storage_[id]->size == 0) + while (storage_[id].size == 0) ++id; return id; } @@ -231,7 +236,7 @@ class ListContainerHelper::CharAllocator { // non-empty. DCHECK_GT(size_, 0u); size_t id = storage_.size() - 1; - while (storage_[id]->size == 0) + while (storage_[id].size == 0) --id; return id; } @@ -242,16 +247,10 @@ class ListContainerHelper::CharAllocator { private: void AllocateNewList(size_t list_size) { - std::unique_ptr<InnerList> new_list(new InnerList); - new_list->capacity = list_size; - new_list->size = 0; - new_list->step = element_size_; - new_list->data.reset(static_cast<char*>( - base::AlignedAlloc(list_size * element_size_, alignment_))); - storage_.push_back(std::move(new_list)); + storage_.emplace_back(list_size, element_size_, alignment_); } - std::vector<std::unique_ptr<InnerList>> storage_; + std::vector<InnerList> storage_; const size_t alignment_; const size_t element_size_; @@ -271,6 +270,10 @@ class ListContainerHelper::CharAllocator { ListContainerHelper::PositionInCharAllocator::PositionInCharAllocator( const ListContainerHelper::PositionInCharAllocator& other) = default; +ListContainerHelper::PositionInCharAllocator& +ListContainerHelper::PositionInCharAllocator::operator=( + const ListContainerHelper::PositionInCharAllocator& other) = default; + ListContainerHelper::PositionInCharAllocator::PositionInCharAllocator( ListContainerHelper::CharAllocator* container, size_t vector_ind, @@ -293,47 +296,45 @@ bool ListContainerHelper::PositionInCharAllocator::operator!=( ListContainerHelper::PositionInCharAllocator ListContainerHelper::PositionInCharAllocator::Increment() { - CharAllocator::InnerList* list = - ptr_to_container->InnerListById(vector_index); - if (item_iterator == list->LastElement()) { + const auto& list = ptr_to_container->InnerListById(vector_index); + if (item_iterator == list.LastElement()) { ++vector_index; while (vector_index < ptr_to_container->list_count()) { - if (ptr_to_container->InnerListById(vector_index)->size != 0) + if (ptr_to_container->InnerListById(vector_index).size != 0) break; ++vector_index; } if (vector_index < ptr_to_container->list_count()) - item_iterator = ptr_to_container->InnerListById(vector_index)->Begin(); + item_iterator = ptr_to_container->InnerListById(vector_index).Begin(); else item_iterator = nullptr; } else { - item_iterator += list->step; + item_iterator += list.step; } return *this; } ListContainerHelper::PositionInCharAllocator ListContainerHelper::PositionInCharAllocator::ReverseIncrement() { - CharAllocator::InnerList* list = - ptr_to_container->InnerListById(vector_index); - if (item_iterator == list->Begin()) { + const auto& list = ptr_to_container->InnerListById(vector_index); + if (item_iterator == list.Begin()) { --vector_index; // Since |vector_index| is unsigned, we compare < list_count() instead of // comparing >= 0, as the variable will wrap around when it goes out of // range (below 0). while (vector_index < ptr_to_container->list_count()) { - if (ptr_to_container->InnerListById(vector_index)->size != 0) + if (ptr_to_container->InnerListById(vector_index).size != 0) break; --vector_index; } if (vector_index < ptr_to_container->list_count()) { item_iterator = - ptr_to_container->InnerListById(vector_index)->LastElement(); + ptr_to_container->InnerListById(vector_index).LastElement(); } else { item_iterator = nullptr; } } else { - item_iterator -= list->step; + item_iterator -= list.step; } return *this; } @@ -343,9 +344,9 @@ ListContainerHelper::PositionInCharAllocator::ReverseIncrement() { ListContainerHelper::ListContainerHelper(size_t alignment, size_t max_size_for_derived_class, size_t num_of_elements_to_reserve_for) - : data_(new CharAllocator(alignment, - max_size_for_derived_class, - num_of_elements_to_reserve_for)) {} + : data_(std::make_unique<CharAllocator>(alignment, + max_size_for_derived_class, + num_of_elements_to_reserve_for)) {} ListContainerHelper::~ListContainerHelper() = default; @@ -370,7 +371,7 @@ ListContainerHelper::ConstReverseIterator ListContainerHelper::crbegin() const { size_t id = data_->LastInnerListId(); return ConstReverseIterator(data_.get(), id, - data_->InnerListById(id)->LastElement(), 0); + data_->InnerListById(id).LastElement(), 0); } ListContainerHelper::ConstReverseIterator ListContainerHelper::crend() const { @@ -384,7 +385,7 @@ ListContainerHelper::ReverseIterator ListContainerHelper::rbegin() { size_t id = data_->LastInnerListId(); return ReverseIterator(data_.get(), id, - data_->InnerListById(id)->LastElement(), 0); + data_->InnerListById(id).LastElement(), 0); } ListContainerHelper::ReverseIterator ListContainerHelper::rend() { @@ -396,7 +397,7 @@ ListContainerHelper::ConstIterator ListContainerHelper::cbegin() const { return cend(); size_t id = data_->FirstInnerListId(); - return ConstIterator(data_.get(), id, data_->InnerListById(id)->Begin(), 0); + return ConstIterator(data_.get(), id, data_->InnerListById(id).Begin(), 0); } ListContainerHelper::ConstIterator ListContainerHelper::cend() const { @@ -412,7 +413,7 @@ ListContainerHelper::Iterator ListContainerHelper::begin() { return end(); size_t id = data_->FirstInnerListId(); - return Iterator(data_.get(), id, data_->InnerListById(id)->Begin(), 0); + return Iterator(data_.get(), id, data_->InnerListById(id).Begin(), 0); } ListContainerHelper::Iterator ListContainerHelper::end() { @@ -429,13 +430,13 @@ ListContainerHelper::ConstIterator ListContainerHelper::IteratorAt( size_t original_index = index; size_t list_index; for (list_index = 0; list_index < data_->list_count(); ++list_index) { - size_t current_size = data_->InnerListById(list_index)->size; + size_t current_size = data_->InnerListById(list_index).size; if (index < current_size) break; index -= current_size; } return ConstIterator(data_.get(), list_index, - data_->InnerListById(list_index)->ElementAt(index), + data_->InnerListById(list_index).ElementAt(index), original_index); } @@ -444,13 +445,13 @@ ListContainerHelper::Iterator ListContainerHelper::IteratorAt(size_t index) { size_t original_index = index; size_t list_index; for (list_index = 0; list_index < data_->list_count(); ++list_index) { - size_t current_size = data_->InnerListById(list_index)->size; + size_t current_size = data_->InnerListById(list_index).size; if (index < current_size) break; index -= current_size; } return Iterator(data_.get(), list_index, - data_->InnerListById(list_index)->ElementAt(index), + data_->InnerListById(list_index).ElementAt(index), original_index); } diff --git a/chromium/cc/base/list_container_helper.h b/chromium/cc/base/list_container_helper.h index 31658bc8486..7fc3bcebc56 100644 --- a/chromium/cc/base/list_container_helper.h +++ b/chromium/cc/base/list_container_helper.h @@ -41,6 +41,7 @@ class CC_BASE_EXPORT ListContainerHelper final { char* item_iterator; PositionInCharAllocator(const PositionInCharAllocator& other); + PositionInCharAllocator& operator=(const PositionInCharAllocator& other); PositionInCharAllocator(CharAllocator* container, size_t vector_ind, diff --git a/chromium/cc/base/list_container_unittest.cc b/chromium/cc/base/list_container_unittest.cc index b053305bcc7..c2975adcc49 100644 --- a/chromium/cc/base/list_container_unittest.cc +++ b/chromium/cc/base/list_container_unittest.cc @@ -9,7 +9,7 @@ #include <algorithm> #include <vector> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc index 26a7156d9b4..decdb8ca885 100644 --- a/chromium/cc/base/math_util.cc +++ b/chromium/cc/base/math_util.cc @@ -11,7 +11,7 @@ #include <xmmintrin.h> #endif -#include "base/numerics/ranges.h" +#include "base/cxx17_backports.h" #include "base/trace_event/traced_value.h" #include "base/values.h" #include "ui/gfx/geometry/angle_conversions.h" @@ -64,17 +64,21 @@ static HomogeneousCoordinate MapHomogeneousPoint( return result; } -static void homogenousLimitAtZero(SkScalar a1, - SkScalar w1, - SkScalar a2, - SkScalar w2, - float t, - float* limit) { - // This is the tolerance for detecting an eyepoint-aligned edge. - static const float kStationaryPointEplison = 0.00001f; +namespace { - if (std::abs(a1 * w2 / w1 / a2 - 1.0f) > kStationaryPointEplison) { - // We are going to explode towards an infity, but we choose the one that +// This is the tolerance for detecting an eyepoint-aligned edge. +const float kStationaryPointEpsilon = 0.00001f; + +} // namespace + +static void homogeneousLimitAtZero(SkScalar a1, + SkScalar w1, + SkScalar a2, + SkScalar w2, + float t, + float* limit) { + if (std::abs(a1 * w2 / w1 / a2 - 1.0f) > kStationaryPointEpsilon) { + // We are going to explode towards an infinity, but we choose the one that // corresponds to the one on the positive side of w. if (((1.0f - t) * a1 + t * a2) > 0) { *limit = HomogeneousCoordinate::kInfiniteCoordinate; @@ -102,16 +106,43 @@ static gfx::PointF ComputeClippedCartesianPoint2dForEdge( // i.e., either the coordinate is not moving, or is trending to one // infinity or the other. + // This assertion isn't really as strong as it looks because + // std::isfinite(h1.w()) or std::isfinite(h2.w()) might not be true + // (and they could be NaN). + // TODO(crbug.com/1219622): We should be able to assert something + // stronger here, and avoid dependencies on undefined floating point + // behavior. + DCHECK_NE(h1.w() <= 0, h2.w() <= 0); + float t = h1.w() / (h1.w() - h2.w()); float x; float y; - homogenousLimitAtZero(h1.x(), h1.w(), h2.x(), h2.w(), t, &x); - homogenousLimitAtZero(h1.y(), h1.w(), h2.y(), h2.w(), t, &y); + homogeneousLimitAtZero(h1.x(), h1.w(), h2.x(), h2.w(), t, &x); + homogeneousLimitAtZero(h1.y(), h1.w(), h2.y(), h2.w(), t, &y); return gfx::PointF(x, y); } +static void homogeneousLimitNearZero(SkScalar a1, + SkScalar w1, + SkScalar a2, + SkScalar w2, + float t, + float* limit) { + if (std::abs(a1 * w2 / w1 / a2 - 1.0f) > kStationaryPointEpsilon) { + // t has been computed so that w is near but not at zero. + *limit = ((1.0f - t) * a1 + t * a2) / ((1.0f - t) * w1 + t * w2); + // std::abs(*limit) should now be somewhere near + // HomogeneousCoordinate::kInfiniteCoordinate, preferably smaller than it, + // but there are edge cases where it will be larger (for example, if the + // point where a crosses 0 is very close to the point where w crosses 0), + // so it's hard to DCHECK() that this is the case. + } else { + *limit = a1 / w1; // (== a2 / w2) && == (1.0f - t) * a1 / w1 + t * a2 / w2 + } +} + static gfx::Point3F ComputeClippedCartesianPoint3dForEdge( const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2) { @@ -129,14 +160,47 @@ static gfx::Point3F ComputeClippedCartesianPoint3dForEdge( // i.e., either the coordinate is not moving, or is trending to one // infinity or the other. - float t = h1.w() / (h1.w() - h2.w()); + // When we clamp to HomogeneousCoordinate::kInfiniteCoordinate we want + // to keep the result in the correct plane, which we do by computing + // a t that will result in the largest (in absolute value) of x, y, or + // z being HomogeneousCoordinate::kInfiniteCoordinate + + // This assertion isn't really as strong as it looks because + // std::isfinite(h1.w()) or std::isfinite(h2.w()) might not be true + // (and they could be NaN). + // TODO(crbug.com/1219622): We should be able to assert something + // stronger here, and avoid dependencies on undefined floating point + // behavior. + DCHECK_NE(h1.w() <= 0, h2.w() <= 0); + + float w_diff = h1.w() - h2.w(); + float t = h1.w() / w_diff; + float max_numerator = std::max({std::abs((1.0f - t) * h1.x() + t * h2.x()), + std::abs((1.0f - t) * h1.y() + t * h2.y()), + std::abs((1.0f - t) * h1.z() + t * h2.z())}); + + // Shift t away from the point where w is zero, far enough so that the + // largest of the resulting x, y, and z will be about + // kInfiniteCoordinate. Add an extra epsilon() / 2.0 so that there's + // always enough movement (in case t_shift is very small, which it + // often is). + const float t_shift = + max_numerator / w_diff / HomogeneousCoordinate::kInfiniteCoordinate; + constexpr float half_epsilon = std::numeric_limits<float>::epsilon() / 2.0f; + DCHECK_EQ(w_diff > 0, t_shift > 0); + if (w_diff > 0) { + t = std::max(0.0f, t - (t_shift + half_epsilon)); + } else { + t = std::min(1.0f, t - (t_shift - half_epsilon)); + } + float x; float y; float z; - homogenousLimitAtZero(h1.x(), h1.w(), h2.x(), h2.w(), t, &x); - homogenousLimitAtZero(h1.y(), h1.w(), h2.y(), h2.w(), t, &y); - homogenousLimitAtZero(h1.z(), h1.w(), h2.z(), h2.w(), t, &z); + homogeneousLimitNearZero(h1.x(), h1.w(), h2.x(), h2.w(), t, &x); + homogeneousLimitNearZero(h1.y(), h1.w(), h2.y(), h2.w(), t, &y); + homogeneousLimitNearZero(h1.z(), h1.w(), h2.z(), h2.w(), t, &z); return gfx::Point3F(x, y, z); } @@ -175,14 +239,24 @@ static inline bool IsNearlyTheSame(const gfx::Point3F& lhs, static inline void AddVertexToClippedQuad3d(const gfx::Point3F& new_vertex, gfx::Point3F clipped_quad[6], - int* num_vertices_in_clipped_quad) { + int* num_vertices_in_clipped_quad, + bool* need_to_clamp) { if (*num_vertices_in_clipped_quad > 0 && IsNearlyTheSame(clipped_quad[*num_vertices_in_clipped_quad - 1], new_vertex)) return; + DCHECK_LT(*num_vertices_in_clipped_quad, 6); clipped_quad[*num_vertices_in_clipped_quad] = new_vertex; (*num_vertices_in_clipped_quad)++; + if (new_vertex.x() < -HomogeneousCoordinate::kInfiniteCoordinate || + new_vertex.x() > HomogeneousCoordinate::kInfiniteCoordinate || + new_vertex.y() < -HomogeneousCoordinate::kInfiniteCoordinate || + new_vertex.y() > HomogeneousCoordinate::kInfiniteCoordinate || + new_vertex.z() < -HomogeneousCoordinate::kInfiniteCoordinate || + new_vertex.z() > HomogeneousCoordinate::kInfiniteCoordinate) { + *need_to_clamp = true; + } } gfx::Rect MathUtil::MapEnclosingClippedRect(const gfx::Transform& transform, @@ -296,6 +370,8 @@ gfx::Rect MathUtil::MapEnclosedRectWith2dAxisAlignedTransform( const gfx::Transform& transform, const gfx::Rect& rect) { DCHECK(transform.Preserves2dAxisAlignment()); + DCHECK_GT(transform.matrix().get(3, 3), 0); + DCHECK(std::isnormal(transform.matrix().get(3, 3))); if (transform.IsIdentityOrIntegerTranslation()) { gfx::Vector2d offset(static_cast<int>(transform.matrix().getFloat(0, 3)), @@ -331,6 +407,11 @@ bool MathUtil::MapClippedQuad3d(const gfx::Transform& transform, const gfx::QuadF& src_quad, gfx::Point3F clipped_quad[6], int* num_vertices_in_clipped_quad) { + // This is different from the 2D version because, when we clamp + // coordinates to [-HomogeneousCoordinate::kInfiniteCoordinate, + // HomogeneousCoordinate::kInfiniteCoordinate], we need to do the + // clamping while keeping the points coplanar. + HomogeneousCoordinate h1 = MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p1())); HomogeneousCoordinate h2 = @@ -344,45 +425,50 @@ bool MathUtil::MapClippedQuad3d(const gfx::Transform& transform, // clockwise / counter-clockwise orientation is retained. *num_vertices_in_clipped_quad = 0; + bool need_to_clamp = false; if (!h1.ShouldBeClipped()) { - AddVertexToClippedQuad3d( - h1.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad); + AddVertexToClippedQuad3d(h1.CartesianPoint3dUnclamped(), clipped_quad, + num_vertices_in_clipped_quad, &need_to_clamp); } if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped()) { AddVertexToClippedQuad3d(ComputeClippedCartesianPoint3dForEdge(h1, h2), - clipped_quad, num_vertices_in_clipped_quad); + clipped_quad, num_vertices_in_clipped_quad, + &need_to_clamp); } if (!h2.ShouldBeClipped()) { - AddVertexToClippedQuad3d( - h2.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad); + AddVertexToClippedQuad3d(h2.CartesianPoint3dUnclamped(), clipped_quad, + num_vertices_in_clipped_quad, &need_to_clamp); } if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped()) { AddVertexToClippedQuad3d(ComputeClippedCartesianPoint3dForEdge(h2, h3), - clipped_quad, num_vertices_in_clipped_quad); + clipped_quad, num_vertices_in_clipped_quad, + &need_to_clamp); } if (!h3.ShouldBeClipped()) { - AddVertexToClippedQuad3d( - h3.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad); + AddVertexToClippedQuad3d(h3.CartesianPoint3dUnclamped(), clipped_quad, + num_vertices_in_clipped_quad, &need_to_clamp); } if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped()) { AddVertexToClippedQuad3d(ComputeClippedCartesianPoint3dForEdge(h3, h4), - clipped_quad, num_vertices_in_clipped_quad); + clipped_quad, num_vertices_in_clipped_quad, + &need_to_clamp); } if (!h4.ShouldBeClipped()) { - AddVertexToClippedQuad3d( - h4.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad); + AddVertexToClippedQuad3d(h4.CartesianPoint3dUnclamped(), clipped_quad, + num_vertices_in_clipped_quad, &need_to_clamp); } if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped()) { AddVertexToClippedQuad3d(ComputeClippedCartesianPoint3dForEdge(h4, h1), - clipped_quad, num_vertices_in_clipped_quad); + clipped_quad, num_vertices_in_clipped_quad, + &need_to_clamp); } if (*num_vertices_in_clipped_quad > 2 && @@ -390,6 +476,147 @@ bool MathUtil::MapClippedQuad3d(const gfx::Transform& transform, clipped_quad[*num_vertices_in_clipped_quad - 1])) *num_vertices_in_clipped_quad -= 1; + if (need_to_clamp) { + // Some of the values need to be clamped, but we need to keep them + // coplanar while doing so. + + // First, build a normal vector to the plane by averaging the + // cross products of adjacent edges. + gfx::Vector3dF normal(0.0f, 0.0f, 0.0f); + if (*num_vertices_in_clipped_quad > 2) { + gfx::Vector3dF loop_vector = + clipped_quad[0] - clipped_quad[*num_vertices_in_clipped_quad - 1]; + gfx::Vector3dF prev_vector(loop_vector); + for (int i = 1; i < *num_vertices_in_clipped_quad; ++i) { + gfx::Vector3dF cur_vector = clipped_quad[i] - clipped_quad[i - 1]; + normal += CrossProduct(prev_vector, cur_vector); + prev_vector = cur_vector; + } + normal += CrossProduct(prev_vector, loop_vector); + } + + bool clamp_by_points = false; + float length = normal.Length(); + if (std::isnormal(length)) { // exclude 0, denormals, +/- inf, NaN + normal.Scale(1.0f / length); + + // Find the vector to the point in the plane closest to (0,0,0). + gfx::Vector3dF shortest_from_zero(normal); + shortest_from_zero.Scale( + DotProduct(normal, clipped_quad[0] - gfx::Point3F(0.0f, 0.0f, 0.0f))); + + // Find the the point in the plane that is at x=0 and y=0 + float z_at_xy_zero = 0.0f; + if (shortest_from_zero.x() == 0.0f && shortest_from_zero.y() == 0.0f) { + z_at_xy_zero = shortest_from_zero.z(); + } else if (shortest_from_zero.z() != 0) { + // Compute the vector v pointing from the shortest_from_zero + // point to the point with x=0 and y=0. If both v and normal + // are projected into the x/y plane, they should point in + // opposite directions. + gfx::Vector3dF v = CrossProduct( + normal, CrossProduct(gfx::Vector3dF(0.0f, 0.0f, 1.0f), normal)); + DCHECK(std::abs(normal.x() * v.y() - normal.y() * v.x()) < 0.00001f); + // It doesn't matter whether we use x or y, unless one of them + // is zero or very close to it. + float r = std::abs(v.x()) > std::abs(v.y()) + ? shortest_from_zero.x() / v.x() + : shortest_from_zero.y() / v.y(); + z_at_xy_zero = shortest_from_zero.z() - v.z() * r; + } else { + // Plane is parallel to the z axis. This means it's not + // visible, so just fall back to clamping by points. + clamp_by_points = true; + } + + if (!clamp_by_points) { + // If z_at_xy_zero is more than 3/4 of kInfiniteCoordinate + // distance from zero, move everything in the z axis so + // z_at_xy_zero is that distance from zero, so that we don't end + // up clamping away the parts that fit within what's likely to + // be the visible area. + constexpr float max_distance = + 0.75 * HomogeneousCoordinate::kInfiniteCoordinate; + if (std::abs(z_at_xy_zero) > max_distance) { + float z_delta; + if (z_at_xy_zero > 0) { + z_delta = max_distance - z_at_xy_zero; + } else { + z_delta = -max_distance - z_at_xy_zero; + } + for (int i = 0; i < *num_vertices_in_clipped_quad; ++i) { + clipped_quad[i].set_z(clipped_quad[i].z() + z_delta); + } + z_at_xy_zero += z_delta; + } + + // Move all the points towards (0, 0, z_at_xy_zero) until all + // their coordinates are less than kInfiniteCoordinate. + for (int i = 0; i < *num_vertices_in_clipped_quad; ++i) { + gfx::Point3F& point = clipped_quad[i]; + float t = 1.0f; + + float x_abs = std::abs(point.x()); + if (x_abs > HomogeneousCoordinate::kInfiniteCoordinate) { + t = std::min(t, HomogeneousCoordinate::kInfiniteCoordinate / x_abs); + } + + float y_abs = std::abs(point.y()); + if (y_abs > HomogeneousCoordinate::kInfiniteCoordinate) { + t = std::min(t, HomogeneousCoordinate::kInfiniteCoordinate / y_abs); + } + + float z = point.z(); + if (std::abs(z) > HomogeneousCoordinate::kInfiniteCoordinate) { + // From the clamping to max_distance above, we should have + // made std::abs(z_at_xy_zero) < kInfiniteCoordinate. + // However, if it started off very large we might not have. + float z_at_xy_zero_clamped = + std::min(float{HomogeneousCoordinate::kInfiniteCoordinate}, + std::max(-HomogeneousCoordinate::kInfiniteCoordinate, + z_at_xy_zero)); + float z_offset = z - z_at_xy_zero_clamped; + float z_space = + (z > 0 ? HomogeneousCoordinate::kInfiniteCoordinate + : -HomogeneousCoordinate::kInfiniteCoordinate) - + z_at_xy_zero_clamped; + DCHECK_NE(z_offset, 0.0f); + DCHECK_NE(z_space, 0.0f); + DCHECK_EQ(z_offset > 0, z_space > 0); + t = std::min(t, z_space / z_offset); + } + + if (t != 1.0f) { + DCHECK(0.0f <= t && t < 1.0f); + point.set_x(t * point.x()); + point.set_y(t * point.y()); + point.set_z((1.0f - t) * z_at_xy_zero + t * point.z()); + } + } + } + } else { + // Our points were colinear, so there's no plane to maintain. + clamp_by_points = true; + } + + if (clamp_by_points) { + // Just clamp each point separately in each axis, just like we do + // for 2D. + for (int i = 0; i < *num_vertices_in_clipped_quad; ++i) { + gfx::Point3F& point = clipped_quad[i]; + point.set_x( + base::clamp(point.x(), -HomogeneousCoordinate::kInfiniteCoordinate, + float{HomogeneousCoordinate::kInfiniteCoordinate})); + point.set_y( + base::clamp(point.y(), -HomogeneousCoordinate::kInfiniteCoordinate, + float{HomogeneousCoordinate::kInfiniteCoordinate})); + point.set_z( + base::clamp(point.z(), -HomogeneousCoordinate::kInfiniteCoordinate, + float{HomogeneousCoordinate::kInfiniteCoordinate})); + } + } + } + DCHECK_LE(*num_vertices_in_clipped_quad, 6); return (*num_vertices_in_clipped_quad >= 4); } @@ -582,7 +809,7 @@ float MathUtil::SmallestAngleBetweenVectors(const gfx::Vector2dF& v1, const gfx::Vector2dF& v2) { double dot_product = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length(); // Clamp to compensate for rounding errors. - dot_product = base::ClampToRange(dot_product, -1.0, 1.0); + dot_product = base::clamp(dot_product, -1.0, 1.0); return static_cast<float>(gfx::RadToDeg(std::acos(dot_product))); } @@ -595,22 +822,25 @@ gfx::Vector2dF MathUtil::ProjectVector(const gfx::Vector2dF& source, } bool MathUtil::FromValue(const base::Value* raw_value, gfx::Rect* out_rect) { - const base::ListValue* value = nullptr; - if (!raw_value->GetAsList(&value)) + if (!raw_value->is_list()) return false; - if (value->GetSize() != 4) - return false; + base::Value::ConstListView list_view = raw_value->GetList(); - int x, y, w, h; - bool ok = true; - ok &= value->GetInteger(0, &x); - ok &= value->GetInteger(1, &y); - ok &= value->GetInteger(2, &w); - ok &= value->GetInteger(3, &h); - if (!ok) + if (list_view.size() != 4) return false; + for (const auto& val : list_view) { + if (!val.is_int()) { + return false; + } + } + + int x = list_view[0].GetInt(); + int y = list_view[1].GetInt(); + int w = list_view[2].GetInt(); + int h = list_view[3].GetInt(); + *out_rect = gfx::Rect(x, y, w, h); return true; } @@ -729,7 +959,7 @@ void MathUtil::AddToTracedValue(const char* name, const gfx::Transform& transform, base::trace_event::TracedValue* res) { res->BeginArray(name); - const SkMatrix44& m = transform.matrix(); + const skia::Matrix44& m = transform.matrix(); for (int row = 0; row < 4; ++row) { for (int col = 0; col < 4; ++col) res->AppendDouble(m.getDouble(row, col)); diff --git a/chromium/cc/base/math_util.h b/chromium/cc/base/math_util.h index e6940430086..b7c762b258f 100644 --- a/chromium/cc/base/math_util.h +++ b/chromium/cc/base/math_util.h @@ -10,7 +10,7 @@ #include <vector> #include "base/check.h" -#include "base/numerics/ranges.h" +#include "base/cxx17_backports.h" #include "build/build_config.h" #include "cc/base/base_export.h" #include "third_party/skia/include/core/SkM44.h" @@ -71,10 +71,10 @@ struct HomogeneousCoordinate { SkScalar inv_w = SK_Scalar1 / w(); // However, w may be close to 0 and we lose precision on our geometry // calculations if we allow scaling to extremely large values. - return gfx::PointF(base::ClampToRange(x() * inv_w, -kInfiniteCoordinate, - float{kInfiniteCoordinate}), - base::ClampToRange(y() * inv_w, -kInfiniteCoordinate, - float{kInfiniteCoordinate})); + return gfx::PointF(base::clamp(x() * inv_w, -kInfiniteCoordinate, + float{kInfiniteCoordinate}), + base::clamp(y() * inv_w, -kInfiniteCoordinate, + float{kInfiniteCoordinate})); } gfx::Point3F CartesianPoint3d() const { @@ -87,12 +87,25 @@ struct HomogeneousCoordinate { SkScalar inv_w = SK_Scalar1 / w(); // However, w may be close to 0 and we lose precision on our geometry // calculations if we allow scaling to extremely large values. - return gfx::Point3F(base::ClampToRange(x() * inv_w, -kInfiniteCoordinate, - float{kInfiniteCoordinate}), - base::ClampToRange(y() * inv_w, -kInfiniteCoordinate, - float{kInfiniteCoordinate}), - base::ClampToRange(z() * inv_w, -kInfiniteCoordinate, - float{kInfiniteCoordinate})); + return gfx::Point3F(base::clamp(x() * inv_w, -kInfiniteCoordinate, + float{kInfiniteCoordinate}), + base::clamp(y() * inv_w, -kInfiniteCoordinate, + float{kInfiniteCoordinate}), + base::clamp(z() * inv_w, -kInfiniteCoordinate, + float{kInfiniteCoordinate})); + } + + gfx::Point3F CartesianPoint3dUnclamped() const { + if (w() == SK_Scalar1) + return gfx::Point3F(x(), y(), z()); + + // For now, because this code is used privately only by MathUtil, it should + // never be called when w == 0, and we do not yet need to handle that case. + DCHECK(w()); + SkScalar inv_w = SK_Scalar1 / w(); + // However, w may be close to 0 and we lose precision on our geometry + // calculations if we allow scaling to extremely large values. + return gfx::Point3F(x() * inv_w, y() * inv_w, z() * inv_w); } SkScalar x() const { return vec[0]; } diff --git a/chromium/cc/base/math_util_unittest.cc b/chromium/cc/base/math_util_unittest.cc index 789145584b6..6931856891c 100644 --- a/chromium/cc/base/math_util_unittest.cc +++ b/chromium/cc/base/math_util_unittest.cc @@ -619,16 +619,16 @@ TEST(MathUtilTest, ApproximatePoint3F) { } // This takes a quad for which two points, (at x = -99) are behind and below -// the eyepoint and checks to make sure we build a triangle. We used to build -// a degenerate quad. +// the eyepoint and checks to make sure we build a quad that doesn't include +// anything from w<0 space. We used to build a degenerate quad. TEST(MathUtilTest, MapClippedQuadDuplicateTriangle) { gfx::Transform transform; transform.MakeIdentity(); transform.ApplyPerspectiveDepth(50.0); transform.RotateAboutYAxis(89.0); - // We are amost looking along the X-Y plane from (-50, almost 0) + // We are almost looking along the X-Y plane from (-50, almost 0) - gfx::QuadF src_quad(gfx::PointF(0.0f, 100.0f), gfx::PointF(0.0f, -100.0f), + gfx::QuadF src_quad(gfx::PointF(0.0f, -50.0f), gfx::PointF(0.0f, -100.0f), gfx::PointF(-99.0f, -300.0f), gfx::PointF(-99.0f, -100.0f)); @@ -638,21 +638,44 @@ TEST(MathUtilTest, MapClippedQuadDuplicateTriangle) { MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, &num_vertices_in_clipped_quad); + // If we include anything from w<0 space, it will produce positive y + // coordinates rather than negative ones. + for (int i = 0; i < num_vertices_in_clipped_quad; ++i) { + EXPECT_LE(clipped_quad[i].y(), 0); + } + + EXPECT_EQ(num_vertices_in_clipped_quad, 4); +} + +// This takes a quad for which two points are identical and checks to make +// sure we build a triangle. +TEST(MathUtilTest, MapClippedQuadDuplicatePoints) { + gfx::Transform transform; + transform.MakeIdentity(); + transform.RotateAboutYAxis(45.0); + + gfx::QuadF src_quad(gfx::PointF(-99.0f, -50.0f), gfx::PointF(-99.0f, -50.0f), + gfx::PointF(0.0f, 100.0f), gfx::PointF(0.0f, -100.0f)); + + gfx::Point3F clipped_quad[8]; + int num_vertices_in_clipped_quad; + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + EXPECT_EQ(num_vertices_in_clipped_quad, 3); } -// This takes a quad for which two points, (at x = -99) are behind and below -// the eyepoint and checks to make sure we build a triangle. We used to build -// a degenerate quad. The quirk here is that the two shared points are first -// and last, not sequential. -TEST(MathUtilTest, MapClippedQuadDuplicateTriangleWrapped) { +// This takes a quad for which two points are identical and checks to make +// sure we build a triangle. The quirk here is that the two shared points are +// first and last, not sequential. +TEST(MathUtilTest, MapClippedQuadDuplicatePointsWrapped) { gfx::Transform transform; transform.MakeIdentity(); - transform.ApplyPerspectiveDepth(50.0); - transform.RotateAboutYAxis(89.0); + transform.RotateAboutYAxis(45.0); - gfx::QuadF src_quad(gfx::PointF(-99.0f, -100.0f), gfx::PointF(0.0f, 100.0f), - gfx::PointF(0.0f, -100.0f), gfx::PointF(-99.0f, -300.0f)); + gfx::QuadF src_quad(gfx::PointF(-99.0f, -50.0f), gfx::PointF(0.0f, 100.0f), + gfx::PointF(0.0f, -100.0f), gfx::PointF(-99.0f, -50.0f)); gfx::Point3F clipped_quad[8]; int num_vertices_in_clipped_quad; @@ -672,7 +695,7 @@ TEST(MathUtilTest, MapClippedQuadDuplicateQuad) { transform.ApplyPerspectiveDepth(50.0); transform.RotateAboutYAxis(89.0); - gfx::QuadF src_quad(gfx::PointF(0.0f, 100.0f), gfx::PointF(400.0f, 0.0f), + gfx::QuadF src_quad(gfx::PointF(0.0f, -50.0f), gfx::PointF(400.0f, -50.0f), gfx::PointF(0.0f, -100.0f), gfx::PointF(-99.0f, -300.0f)); gfx::Point3F clipped_quad[8]; @@ -681,7 +704,306 @@ TEST(MathUtilTest, MapClippedQuadDuplicateQuad) { MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, &num_vertices_in_clipped_quad); + // If we include anything from w<0 space, it will produce positive y + // coordinates rather than negative ones. + for (int i = 0; i < num_vertices_in_clipped_quad; ++i) { + EXPECT_LE(clipped_quad[i].y(), 0); + } + + EXPECT_EQ(num_vertices_in_clipped_quad, 5); +} + +#define EXPECT_LT_LT(a, b, c) \ + do { \ + auto b_evaluated = b; \ + EXPECT_LT(a, b_evaluated); \ + EXPECT_LT(b_evaluated, c); \ + } while (0) + +#define EXPECT_LE_LT(a, b, c) \ + do { \ + auto b_evaluated = b; \ + EXPECT_LE(a, b_evaluated); \ + EXPECT_LT(b_evaluated, c); \ + } while (0) + +#define EXPECT_LT_LE(a, b, c) \ + do { \ + auto b_evaluated = b; \ + EXPECT_LT(a, b_evaluated); \ + EXPECT_LE(b_evaluated, c); \ + } while (0) + +#define EXPECT_LE_LE(a, b, c) \ + do { \ + auto b_evaluated = b; \ + EXPECT_LE(a, b_evaluated); \ + EXPECT_LE(b_evaluated, c); \ + } while (0) + +// Here we map and clip a quad with a point that disappears to infinity behind +// us while staying finite in one dimension (i.e., x goes to 0 as w goes to 0, +// and x' is constant along the edge). +TEST(MathUtilTest, MapClippedQuadInfiniteInSomeDimensions) { + gfx::Transform transform; + transform.MakeIdentity(); + transform.ApplyPerspectiveDepth(50.0); + transform.RotateAboutXAxis(89.0); + + gfx::QuadF src_quad(gfx::PointF(0.0f, 0.0f), gfx::PointF(0.0f, 100.0f), + gfx::PointF(100.0f, 100.0f), gfx::PointF(100.0f, 0.0f)); + + gfx::Point3F clipped_quad[8]; + int num_vertices_in_clipped_quad; + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + + EXPECT_EQ(num_vertices_in_clipped_quad, 4); + + EXPECT_EQ(clipped_quad[0].x(), 0.0f); + EXPECT_EQ(clipped_quad[0].y(), 0.0f); + EXPECT_EQ(clipped_quad[0].z(), 0.0f); + + EXPECT_EQ(clipped_quad[1].x(), 0.0f); + EXPECT_LT_LT(17000.0f, clipped_quad[1].y(), 18000.0f); + EXPECT_LT_LE(998000.0f, clipped_quad[1].z(), 1000000.0f); + + EXPECT_LT_LE(998000.0f, clipped_quad[2].x(), 1000000.0f); + EXPECT_LT_LT(8500.0f, clipped_quad[2].y(), 9000.0f); + EXPECT_LT_LE(499000.0f, clipped_quad[2].z(), 500000.0f); + + EXPECT_EQ(clipped_quad[3].x(), 100.0f); + EXPECT_EQ(clipped_quad[3].y(), 0.0f); + EXPECT_EQ(clipped_quad[3].z(), 0.0f); +} + +// Here we map and clip a quad with a point that disappears to infinity behind +// us while staying finite in one dimension (i.e., x goes to 0 as w goes to 0, +// and x' is constant along the edge). This differs from the previous test +// in that the edge with constant x' is at 100 rather than 0. +TEST(MathUtilTest, MapClippedQuadInfiniteInSomeDimensionsNonZero) { + gfx::Transform transform; + transform.MakeIdentity(); + transform.Translate(100.0, 0.0); + transform.ApplyPerspectiveDepth(50.0); + transform.RotateAboutXAxis(89.0); + transform.Translate(-100.0, 0.0); + + gfx::QuadF src_quad(gfx::PointF(0.0f, 0.0f), gfx::PointF(0.0f, 100.0f), + gfx::PointF(100.0f, 100.0f), gfx::PointF(100.0f, 0.0f)); + + gfx::Point3F clipped_quad[8]; + int num_vertices_in_clipped_quad; + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + + EXPECT_EQ(num_vertices_in_clipped_quad, 4); + + EXPECT_EQ(clipped_quad[0].x(), 0.0f); + EXPECT_EQ(clipped_quad[0].y(), 0.0f); + EXPECT_EQ(clipped_quad[0].z(), 0.0f); + + EXPECT_LE_LT(-1000000.0f, clipped_quad[1].x(), -998000.0f); + EXPECT_LT_LT(8500.0f, clipped_quad[1].y(), 9000.0f); + EXPECT_LT_LE(499000.0f, clipped_quad[1].z(), 500000.0f); + + EXPECT_EQ(clipped_quad[2].x(), 100.0f); + EXPECT_LT_LT(17000.0f, clipped_quad[2].y(), 18000.0f); + EXPECT_LT_LE(996000.0f, clipped_quad[2].z(), 1000000.0f); + + EXPECT_EQ(clipped_quad[3].x(), 100.0f); + EXPECT_EQ(clipped_quad[3].y(), 0.0f); + EXPECT_EQ(clipped_quad[3].z(), 0.0f); +} + +// Test that planes that are parallel to the z axis (other than those going +// through the origin!) just fall through to clipping by points. +TEST(MathUtilTest, MapClippedQuadClampInvisiblePlane) { + gfx::Transform transform; + + gfx::QuadF src_quad(gfx::PointF(0.0f, 0.0f), gfx::PointF(0.0f, 1000.0f), + gfx::PointF(1000.0f, 1000.0f), + gfx::PointF(1000.0f, 0.0f)); + + gfx::Point3F clipped_quad[8]; + int num_vertices_in_clipped_quad; + + transform.MakeIdentity(); + transform.Translate(100.0, 0.0); + transform.RotateAboutYAxis(90.0); + transform.Scale(10000.0f, 10000.0); + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + + EXPECT_EQ(num_vertices_in_clipped_quad, 4); + + EXPECT_EQ(clipped_quad[0].x(), 100.0f); + EXPECT_EQ(clipped_quad[0].y(), 0.0f); + EXPECT_EQ(clipped_quad[0].z(), 0.0f); + + EXPECT_EQ(clipped_quad[1].x(), 100.0f); + EXPECT_EQ(clipped_quad[1].y(), 1000000.0f); + EXPECT_EQ(clipped_quad[1].z(), 0.0f); + + EXPECT_EQ(clipped_quad[2].x(), 100.0f); + EXPECT_EQ(clipped_quad[2].y(), 1000000.0f); + EXPECT_EQ(clipped_quad[2].z(), -1000000.0f); + + EXPECT_EQ(clipped_quad[3].x(), 100.0f); + EXPECT_EQ(clipped_quad[3].y(), 0.0f); + EXPECT_EQ(clipped_quad[3].z(), -1000000.0f); + + transform.MakeIdentity(); + transform.Translate(0.0, -50.0); + transform.RotateAboutXAxis(-90.0); + transform.Scale(10000.0f, 10000.0); + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + + EXPECT_EQ(num_vertices_in_clipped_quad, 4); + + EXPECT_EQ(clipped_quad[0].x(), 0.0f); + EXPECT_EQ(clipped_quad[0].y(), -50.0f); + EXPECT_EQ(clipped_quad[0].z(), 0.0f); + + EXPECT_EQ(clipped_quad[1].x(), 0.0f); + EXPECT_EQ(clipped_quad[1].y(), -50.0f); + EXPECT_EQ(clipped_quad[1].z(), -1000000.0f); + + EXPECT_EQ(clipped_quad[2].x(), 1000000.0f); + EXPECT_EQ(clipped_quad[2].y(), -50.0f); + EXPECT_EQ(clipped_quad[2].z(), -1000000.0f); + + EXPECT_EQ(clipped_quad[3].x(), 1000000.0f); + EXPECT_EQ(clipped_quad[3].y(), -50.0f); + EXPECT_EQ(clipped_quad[3].z(), 0.0f); + + transform.MakeIdentity(); + transform.Translate(10.0, 10.0); + transform.Rotate(30.0); + transform.RotateAboutXAxis(90.0); + transform.Scale(10000.0, 10000.0); + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + + EXPECT_EQ(num_vertices_in_clipped_quad, 4); + + EXPECT_EQ(clipped_quad[0].x(), 10.0f); + EXPECT_EQ(clipped_quad[0].y(), 10.0f); + EXPECT_EQ(clipped_quad[0].z(), 0.0f); + + EXPECT_EQ(clipped_quad[1].x(), 10.0f); + EXPECT_EQ(clipped_quad[1].y(), 10.0f); + EXPECT_EQ(clipped_quad[1].z(), 1000000.0f); + + EXPECT_EQ(clipped_quad[2].x(), 1000000.0f); + EXPECT_EQ(clipped_quad[2].y(), 1000000.0f); + EXPECT_EQ(clipped_quad[2].z(), 1000000.0f); + + EXPECT_EQ(clipped_quad[3].x(), 1000000.0f); + EXPECT_EQ(clipped_quad[3].y(), 1000000.0f); + EXPECT_EQ(clipped_quad[3].z(), 0.0f); +} + +// Test that when the plane passes too far from the origin, we bring it closer +// before clamping coordinates. +TEST(MathUtilTest, MapClippedQuadClampWholePlane) { + gfx::Transform transform; + transform.MakeIdentity(); + transform.Scale3d(1000.0, 1000.0, 1000.0); + transform.Translate3d(0.0, 0.0, 10000.0); + transform.RotateAboutXAxis(-45.0); + + gfx::QuadF src_quad(gfx::PointF(0.0f, 0.0f), gfx::PointF(0.0f, 10000.0f), + gfx::PointF(100.0f, 10000.0f), + gfx::PointF(100.0f, -10000.0f)); + + gfx::Point3F clipped_quad[8]; + int num_vertices_in_clipped_quad; + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + EXPECT_EQ(num_vertices_in_clipped_quad, 4); + + EXPECT_EQ(clipped_quad[0].x(), 0.0f); + EXPECT_EQ(clipped_quad[0].y(), 0.0f); + EXPECT_EQ(clipped_quad[0].z(), 750000.0f); + + EXPECT_EQ(clipped_quad[1].x(), 0.0f); + EXPECT_EQ(clipped_quad[1].y(), 1000000.0f); + EXPECT_LE_LE(-250001.0f, clipped_quad[1].z(), -249999.0f); + + EXPECT_LE_LE(14100.0f, clipped_quad[2].x(), 14200.0f); + EXPECT_EQ(clipped_quad[2].y(), 1000000.0f); + EXPECT_LE_LE(-250001.0f, clipped_quad[2].z(), -249999.0f); + + EXPECT_LE_LE(3500.0f, clipped_quad[3].x(), 3600.0f); + EXPECT_LE_LE(-250001.0f, clipped_quad[3].y(), -249999.0f); + EXPECT_EQ(clipped_quad[3].z(), 1000000.0f); +} + +// Like the previous test, but with a plane with large negative z. +TEST(MathUtilTest, MapClippedQuadClampWholePlaneBelow) { + gfx::Transform transform; + transform.MakeIdentity(); + transform.Scale3d(1000.0, 1000.0, 1000.0); + transform.Translate3d(0.0, 0.0, -5000.0); + transform.RotateAboutYAxis(30.0); + + gfx::QuadF src_quad(gfx::PointF(0.0f, 0.0f), gfx::PointF(-10000.0f, 100.0f), + gfx::PointF(10000.0f, 100.0f), + gfx::PointF(10000.0f, 0.0f)); + + gfx::Point3F clipped_quad[8]; + int num_vertices_in_clipped_quad; + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + + EXPECT_EQ(num_vertices_in_clipped_quad, 4); + + EXPECT_EQ(clipped_quad[0].x(), 0.0f); + EXPECT_EQ(clipped_quad[0].y(), 0.0f); + EXPECT_LE_LE(-750001.0f, clipped_quad[0].z(), -750000.0f); + + EXPECT_EQ(clipped_quad[1].x(), -1000000.0f); + EXPECT_LE_LE(11540.0f, clipped_quad[1].y(), 11550.0f); + EXPECT_LE_LE(-172660.0f, clipped_quad[1].z(), -172640.0f); + + EXPECT_LE_LE(433000.0f, clipped_quad[2].x(), 433025.0f); + EXPECT_LT_LT(4999.9f, clipped_quad[2].y(), 5000.1f); + EXPECT_EQ(clipped_quad[2].z(), -1000000.0f); + + EXPECT_LE_LE(433000.0f, clipped_quad[3].x(), 433025.0f); + EXPECT_EQ(clipped_quad[3].y(), 0.0f); + EXPECT_EQ(clipped_quad[3].z(), -1000000.0f); +} + +TEST(MathUtilTest, MapClippedQuadInfiniteMatrix) { + // clang-format off + gfx::Transform transform( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -100.0f, 0.0f, std::numeric_limits<float>::infinity(), + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + // clang-format on + + gfx::QuadF src_quad(gfx::PointF(0.0f, 1.0f), gfx::PointF(1.0f, 1.0f), + gfx::PointF(1.0f, 2.0f), gfx::PointF(0.0f, 2.0f)); + + gfx::Point3F clipped_quad[8]; + int num_vertices_in_clipped_quad; + + MathUtil::MapClippedQuad3d(transform, src_quad, clipped_quad, + &num_vertices_in_clipped_quad); + + // Nothing to test other than we don't fail DCHECK()s. } } // namespace diff --git a/chromium/cc/base/switches.cc b/chromium/cc/base/switches.cc index 221ddad1c89..b2368ce5d07 100644 --- a/chromium/cc/base/switches.cc +++ b/chromium/cc/base/switches.cc @@ -97,6 +97,9 @@ const char kHighlightNonLCDTextLayers[] = "highlight-non-lcd-text-layers"; // Switches the ui compositor to use layer lists instead of layer trees. const char kUIEnableLayerLists[] = "ui-enable-layer-lists"; +// Enables the resume method on animated images. +const char kAnimatedImageResume[] = "animated-image-resume"; + // Allows scaling clipped images in GpuImageDecodeCache. Note that this may // cause color-bleeding. // TODO(crbug.com/1157548): Remove this workaround flag once the underlying diff --git a/chromium/cc/base/switches.h b/chromium/cc/base/switches.h index 299891a28bc..ba24bd67931 100644 --- a/chromium/cc/base/switches.h +++ b/chromium/cc/base/switches.h @@ -61,6 +61,8 @@ CC_BASE_EXPORT extern const char kUIEnableLayerLists[]; CC_BASE_EXPORT extern const char kEnableClippedImageScaling[]; +CC_BASE_EXPORT extern const char kAnimatedImageResume[]; + // Test related. CC_BASE_EXPORT extern const char kCCLayerTreeTestNoTimeout[]; CC_BASE_EXPORT extern const char kCCLayerTreeTestLongTimeout[]; diff --git a/chromium/cc/base/tiling_data.cc b/chromium/cc/base/tiling_data.cc index deb55701417..3039e863289 100644 --- a/chromium/cc/base/tiling_data.cc +++ b/chromium/cc/base/tiling_data.cc @@ -7,8 +7,8 @@ #include <algorithm> #include "base/check_op.h" +#include "base/cxx17_backports.h" #include "base/notreached.h" -#include "base/numerics/ranges.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/vector2d.h" @@ -82,7 +82,7 @@ int TilingData::TileXIndexFromSrcCoord(int src_position) const { DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0); int x = (src_position - border_texels_) / (max_texture_size_.width() - 2 * border_texels_); - return base::ClampToRange(x, 0, num_tiles_x_ - 1); + return base::clamp(x, 0, num_tiles_x_ - 1); } int TilingData::TileYIndexFromSrcCoord(int src_position) const { @@ -92,7 +92,7 @@ int TilingData::TileYIndexFromSrcCoord(int src_position) const { DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0); int y = (src_position - border_texels_) / (max_texture_size_.height() - 2 * border_texels_); - return base::ClampToRange(y, 0, num_tiles_y_ - 1); + return base::clamp(y, 0, num_tiles_y_ - 1); } int TilingData::FirstBorderTileXIndexFromSrcCoord(int src_position) const { @@ -102,7 +102,7 @@ int TilingData::FirstBorderTileXIndexFromSrcCoord(int src_position) const { DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0); int inner_tile_size = max_texture_size_.width() - 2 * border_texels_; int x = (src_position - 2 * border_texels_) / inner_tile_size; - return base::ClampToRange(x, 0, num_tiles_x_ - 1); + return base::clamp(x, 0, num_tiles_x_ - 1); } int TilingData::FirstBorderTileYIndexFromSrcCoord(int src_position) const { @@ -112,7 +112,7 @@ int TilingData::FirstBorderTileYIndexFromSrcCoord(int src_position) const { DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0); int inner_tile_size = max_texture_size_.height() - 2 * border_texels_; int y = (src_position - 2 * border_texels_) / inner_tile_size; - return base::ClampToRange(y, 0, num_tiles_y_ - 1); + return base::clamp(y, 0, num_tiles_y_ - 1); } int TilingData::LastBorderTileXIndexFromSrcCoord(int src_position) const { @@ -122,7 +122,7 @@ int TilingData::LastBorderTileXIndexFromSrcCoord(int src_position) const { DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0); int inner_tile_size = max_texture_size_.width() - 2 * border_texels_; int x = src_position / inner_tile_size; - return base::ClampToRange(x, 0, num_tiles_x_ - 1); + return base::clamp(x, 0, num_tiles_x_ - 1); } int TilingData::LastBorderTileYIndexFromSrcCoord(int src_position) const { @@ -132,7 +132,7 @@ int TilingData::LastBorderTileYIndexFromSrcCoord(int src_position) const { DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0); int inner_tile_size = max_texture_size_.height() - 2 * border_texels_; int y = src_position / inner_tile_size; - return base::ClampToRange(y, 0, num_tiles_y_ - 1); + return base::clamp(y, 0, num_tiles_y_ - 1); } IndexRect TilingData::TileAroundIndexRect(const gfx::Rect& center_rect) const { diff --git a/chromium/cc/benchmarks/invalidation_benchmark.cc b/chromium/cc/benchmarks/invalidation_benchmark.cc index 08402bdc4ac..b54ca5cbb88 100644 --- a/chromium/cc/benchmarks/invalidation_benchmark.cc +++ b/chromium/cc/benchmarks/invalidation_benchmark.cc @@ -8,6 +8,8 @@ #include <algorithm> #include <limits> +#include <memory> +#include <utility> #include "base/rand_util.h" #include "base/values.h" @@ -131,10 +133,11 @@ bool InvalidationBenchmark::ProcessMessage(std::unique_ptr<base::Value> value) { // high quality, but they need to be identical in each run. Therefore, we use a // LCG and keep the state locally in the benchmark. float InvalidationBenchmark::LCGRandom() { - const uint32_t a = 1664525; - const uint32_t c = 1013904223; + constexpr uint32_t a = 1664525; + constexpr uint32_t c = 1013904223; seed_ = a * seed_ + c; - return static_cast<float>(seed_) / std::numeric_limits<uint32_t>::max(); + return static_cast<float>(seed_) / + static_cast<float>(std::numeric_limits<uint32_t>::max()); } } // namespace cc diff --git a/chromium/cc/benchmarks/micro_benchmark_controller.cc b/chromium/cc/benchmarks/micro_benchmark_controller.cc index 3c1b166ac47..07f8a0f4ac6 100644 --- a/chromium/cc/benchmarks/micro_benchmark_controller.cc +++ b/chromium/cc/benchmarks/micro_benchmark_controller.cc @@ -8,7 +8,7 @@ #include <string> #include "base/callback.h" -#include "base/stl_util.h" +#include "base/containers/cxx20_erase.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "cc/benchmarks/invalidation_benchmark.h" diff --git a/chromium/cc/benchmarks/micro_benchmark_controller_impl.cc b/chromium/cc/benchmarks/micro_benchmark_controller_impl.cc index 56a83c7dc19..13624c1cf04 100644 --- a/chromium/cc/benchmarks/micro_benchmark_controller_impl.cc +++ b/chromium/cc/benchmarks/micro_benchmark_controller_impl.cc @@ -7,7 +7,7 @@ #include <string> #include "base/callback.h" -#include "base/stl_util.h" +#include "base/containers/cxx20_erase.h" #include "base/values.h" #include "cc/trees/layer_tree_host_impl.h" diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc index 8095225c5be..1be5bac5396 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc @@ -75,21 +75,24 @@ void RasterizeAndRecordBenchmark::DidUpdateLayers( static_cast<int>(record_results_.paint_op_memory_usage)); results_->SetInteger("paint_op_count", static_cast<int>(record_results_.paint_op_count)); - results_->SetDouble("record_time_ms", paint_benchmark_result.record_time_ms); - results_->SetDouble("record_time_caching_disabled_ms", - paint_benchmark_result.record_time_caching_disabled_ms); - results_->SetDouble( + results_->SetDoubleKey("record_time_ms", + paint_benchmark_result.record_time_ms); + results_->SetDoubleKey( + "record_time_caching_disabled_ms", + paint_benchmark_result.record_time_caching_disabled_ms); + results_->SetDoubleKey( "record_time_subsequence_caching_disabled_ms", paint_benchmark_result.record_time_subsequence_caching_disabled_ms); - results_->SetDouble( + results_->SetDoubleKey( "record_time_partial_invalidation_ms", paint_benchmark_result.record_time_partial_invalidation_ms); - results_->SetDouble("record_time_small_invalidation_ms", - paint_benchmark_result.record_time_small_invalidation_ms); - results_->SetDouble( + results_->SetDoubleKey( + "record_time_small_invalidation_ms", + paint_benchmark_result.record_time_small_invalidation_ms); + results_->SetDoubleKey( "raster_invalidation_and_convert_time_ms", paint_benchmark_result.raster_invalidation_and_convert_time_ms); - results_->SetDouble( + results_->SetDoubleKey( "paint_artifact_compositor_update_time_ms", paint_benchmark_result.paint_artifact_compositor_update_time_ms); results_->SetInteger( diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc index b4c365d128d..deebdaf6f73 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc @@ -9,6 +9,7 @@ #include <algorithm> #include <limits> #include <memory> +#include <utility> #include "base/timer/lap_timer.h" #include "base/values.h" @@ -130,8 +131,8 @@ class FixedInvalidationPictureLayerTilingClient return base_client_->ScrollInteractionInProgress(); } - bool DidCheckerboardQuad() const override { - return base_client_->DidCheckerboardQuad(); + bool CurrentScrollDidCheckerboardLargeArea() const override { + return base_client_->CurrentScrollDidCheckerboardLargeArea(); } private: @@ -166,8 +167,8 @@ void RasterizeAndRecordBenchmarkImpl::DidCompleteCommit( } std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); - result->SetDouble("rasterize_time_ms", - rasterize_results_.total_best_time.InMillisecondsF()); + result->SetDoubleKey("rasterize_time_ms", + rasterize_results_.total_best_time.InMillisecondsF()); result->SetInteger("pixels_rasterized", rasterize_results_.pixels_rasterized); result->SetInteger("pixels_rasterized_with_non_solid_color", rasterize_results_.pixels_rasterized_with_non_solid_color); diff --git a/chromium/cc/input/browser_controls_offset_manager.cc b/chromium/cc/input/browser_controls_offset_manager.cc index 65b4ec92d30..a7c2cd9746b 100644 --- a/chromium/cc/input/browser_controls_offset_manager.cc +++ b/chromium/cc/input/browser_controls_offset_manager.cc @@ -10,8 +10,8 @@ #include <utility> #include "base/check_op.h" +#include "base/cxx17_backports.h" #include "base/memory/ptr_util.h" -#include "base/numerics/ranges.h" #include "cc/input/browser_controls_offset_manager_client.h" #include "cc/trees/layer_tree_impl.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -443,7 +443,7 @@ gfx::Vector2dF BrowserControlsOffsetManager::ScrollBy( float min_ratio = base_on_top_controls ? TopControlsMinShownRatio() : BottomControlsMinShownRatio(); float normalized_shown_ratio = - (base::ClampToRange(shown_ratio, min_ratio, 1.f) - min_ratio) / + (base::clamp(shown_ratio, min_ratio, 1.f) - min_ratio) / (1.f - min_ratio); // Even though the real shown ratios (shown height / total height) of the top // and bottom controls can be different, they share the same @@ -517,9 +517,9 @@ gfx::Vector2dF BrowserControlsOffsetManager::Animate( // too low or too high |top_controls_min_height_offset_| values. So, we // should clamp it to a valid range. top_controls_min_height_offset_ = - base::ClampToRange(top_controls_min_height_offset_ + top_offset_delta, - top_min_height_offset_animation_range_->first, - top_min_height_offset_animation_range_->second); + base::clamp(top_controls_min_height_offset_ + top_offset_delta, + top_min_height_offset_animation_range_->first, + top_min_height_offset_animation_range_->second); // Ticking the animation might reset it if it's at the final value. top_min_height_change_in_progress_ = top_controls_animation_.IsInitialized(); @@ -528,10 +528,10 @@ gfx::Vector2dF BrowserControlsOffsetManager::Animate( // The change in bottom offset may be larger than the min-height, resulting // in too low or too high |bottom_controls_min_height_offset_| values. So, // we should clamp it to a valid range. - bottom_controls_min_height_offset_ = base::ClampToRange( - bottom_controls_min_height_offset_ + bottom_offset_delta, - bottom_min_height_offset_animation_range_->first, - bottom_min_height_offset_animation_range_->second); + bottom_controls_min_height_offset_ = + base::clamp(bottom_controls_min_height_offset_ + bottom_offset_delta, + bottom_min_height_offset_animation_range_->first, + bottom_min_height_offset_animation_range_->second); // Ticking the animation might reset it if it's at the final value. bottom_min_height_change_in_progress_ = bottom_controls_animation_.IsInitialized(); @@ -743,7 +743,7 @@ void BrowserControlsOffsetManager::Animation::SetBounds(float min, float max) { } absl::optional<float> BrowserControlsOffsetManager::Animation::Reset() { - auto ret = jump_to_end_on_reset_ ? absl::make_optional(base::ClampToRange( + auto ret = jump_to_end_on_reset_ ? absl::make_optional(base::clamp( stop_value_, min_value_, max_value_)) : absl::nullopt; @@ -770,7 +770,7 @@ bool BrowserControlsOffsetManager::Animation::IsComplete(float value) { } float BrowserControlsOffsetManager::Animation::FinalValue() { - return base::ClampToRange(stop_value_, min_value_, max_value_); + return base::clamp(stop_value_, min_value_, max_value_); } } // namespace cc diff --git a/chromium/cc/input/browser_controls_offset_manager.h b/chromium/cc/input/browser_controls_offset_manager.h index 1cfa756454b..8e03259efa1 100644 --- a/chromium/cc/input/browser_controls_offset_manager.h +++ b/chromium/cc/input/browser_controls_offset_manager.h @@ -12,6 +12,7 @@ #include "cc/input/browser_controls_state.h" #include "cc/layers/layer_impl.h" #include "cc/trees/browser_controls_params.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/vector2d_f.h" diff --git a/chromium/cc/input/compositor_input_interfaces.h b/chromium/cc/input/compositor_input_interfaces.h index d5bbaededda..dcbd420b2cc 100644 --- a/chromium/cc/input/compositor_input_interfaces.h +++ b/chromium/cc/input/compositor_input_interfaces.h @@ -65,6 +65,9 @@ class InputDelegateForCompositor { // completed. virtual void ScrollOffsetAnimationFinished() = 0; + // Called to inform the input handler when prefers-reduced-motion changes. + virtual void SetPrefersReducedMotion(bool prefers_reduced_motion) = 0; + // Returns true if we're currently in a "gesture" (user-initiated) scroll. // That is, between a GestureScrollBegin and a GestureScrollEnd. Note, a // GestureScrollEnd is deferred if the gesture ended but we're still diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index 5460c76c716..e5e555b7ec6 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -106,6 +106,7 @@ class CC_EXPORT InputHandlerClient { virtual void WillShutdown() = 0; virtual void Animate(base::TimeTicks time) = 0; virtual void ReconcileElasticOverscrollAndRootScroll() = 0; + virtual void SetPrefersReducedMotion(bool prefers_reduced_motion) = 0; virtual void UpdateRootLayerStateForSynchronousInputHandler( const gfx::ScrollOffset& total_scroll_offset, const gfx::ScrollOffset& max_scroll_offset, @@ -352,6 +353,7 @@ class CC_EXPORT InputHandler { EventsMetricsManager::ScopedMonitor::DoneCallback done_callback) = 0; virtual ScrollElasticityHelper* CreateScrollElasticityHelper() = 0; + virtual void DestroyScrollElasticityHelper() = 0; // Called by the single-threaded UI Compositor to get or set the scroll offset // on the impl side. Returns false if |element_id| isn't in the active tree. @@ -381,6 +383,9 @@ class CC_EXPORT InputHandler { // being handled by the InputHandler or not. virtual void NotifyInputEvent() = 0; + // Returns true if ScrollbarController is in the middle of a scroll operation. + virtual bool ScrollbarScrollIsActive() = 0; + protected: virtual ~InputHandler() = default; InputHandler() = default; diff --git a/chromium/cc/input/main_thread_scrolling_reason.cc b/chromium/cc/input/main_thread_scrolling_reason.cc index 0d0d0c2a57a..eacc3ec2838 100644 --- a/chromium/cc/input/main_thread_scrolling_reason.cc +++ b/chromium/cc/input/main_thread_scrolling_reason.cc @@ -4,7 +4,7 @@ #include "cc/input/main_thread_scrolling_reason.h" -#include "base/stl_util.h" +#include "base/containers/cxx20_erase.h" #include "base/strings/string_util.h" #include "base/trace_event/traced_value.h" diff --git a/chromium/cc/input/scroll_snap_data.cc b/chromium/cc/input/scroll_snap_data.cc index b2dc55798cb..9395015a8a4 100644 --- a/chromium/cc/input/scroll_snap_data.cc +++ b/chromium/cc/input/scroll_snap_data.cc @@ -10,8 +10,8 @@ #include <memory> #include "base/check.h" +#include "base/cxx17_backports.h" #include "base/notreached.h" -#include "base/numerics/ranges.h" #include "cc/input/snap_selection_strategy.h" #include "ui/gfx/geometry/vector2d_f.h" @@ -94,10 +94,10 @@ void SnapSearchResult::set_visible_range(const gfx::RangeF& range) { } void SnapSearchResult::Clip(float max_snap, float max_visible) { - snap_offset_ = base::ClampToRange(snap_offset_, 0.0f, max_snap); + snap_offset_ = base::clamp(snap_offset_, 0.0f, max_snap); visible_range_ = - gfx::RangeF(base::ClampToRange(visible_range_.start(), 0.0f, max_visible), - base::ClampToRange(visible_range_.end(), 0.0f, max_visible)); + gfx::RangeF(base::clamp(visible_range_.start(), 0.0f, max_visible), + base::clamp(visible_range_.end(), 0.0f, max_visible)); } void SnapSearchResult::Union(const SnapSearchResult& other) { @@ -180,7 +180,7 @@ bool SnapContainerData::FindSnapPosition( // we cannot always assume that the incoming value fits this criteria we // clamp it to the bounds to ensure this variant. SnapSearchResult initial_snap_position_y = { - base::ClampToRange(base_position.y(), 0.f, max_position_.y()), + base::clamp(base_position.y(), 0.f, max_position_.y()), gfx::RangeF(0, max_position_.x())}; selected_x = FindClosestValidArea( SearchAxis::kX, strategy, initial_snap_position_y, active_element_id); @@ -192,7 +192,7 @@ bool SnapContainerData::FindSnapPosition( DCHECK(selected_y.has_value()); } else { SnapSearchResult initial_snap_position_x = { - base::ClampToRange(base_position.x(), 0.f, max_position_.x()), + base::clamp(base_position.x(), 0.f, max_position_.x()), gfx::RangeF(0, max_position_.y())}; selected_y = FindClosestValidArea( SearchAxis::kY, strategy, initial_snap_position_x, active_element_id); diff --git a/chromium/cc/input/scroll_snap_data.h b/chromium/cc/input/scroll_snap_data.h index d067aacdbef..7cc67a6dd7e 100644 --- a/chromium/cc/input/scroll_snap_data.h +++ b/chromium/cc/input/scroll_snap_data.h @@ -230,7 +230,7 @@ class CC_EXPORT SnapContainerData { void AddSnapAreaData(SnapAreaData snap_area_data); size_t size() const { return snap_area_list_.size(); } - const SnapAreaData& at(int index) const { return snap_area_list_[index]; } + const SnapAreaData& at(size_t index) const { return snap_area_list_[index]; } void set_scroll_snap_type(ScrollSnapType type) { scroll_snap_type_ = type; } ScrollSnapType scroll_snap_type() const { return scroll_snap_type_; } diff --git a/chromium/cc/input/scroll_state_data.cc b/chromium/cc/input/scroll_state_data.cc index 3409421c5a2..97b5cf2f460 100644 --- a/chromium/cc/input/scroll_state_data.cc +++ b/chromium/cc/input/scroll_state_data.cc @@ -29,7 +29,9 @@ ScrollStateData::ScrollStateData() is_scroll_chain_cut(false), is_main_thread_hit_tested(false) {} -ScrollStateData::ScrollStateData(const ScrollStateData& other) = default; +ScrollStateData::ScrollStateData(const ScrollStateData&) = default; + +ScrollStateData& ScrollStateData::operator=(const ScrollStateData&) = default; ElementId ScrollStateData::current_native_scrolling_element() const { return current_native_scrolling_element_; diff --git a/chromium/cc/input/scroll_state_data.h b/chromium/cc/input/scroll_state_data.h index 94f1506d011..39fc235546e 100644 --- a/chromium/cc/input/scroll_state_data.h +++ b/chromium/cc/input/scroll_state_data.h @@ -17,6 +17,7 @@ class CC_EXPORT ScrollStateData { public: ScrollStateData(); ScrollStateData(const ScrollStateData& other); + ScrollStateData& operator=(const ScrollStateData& other); // Scroll delta in viewport coordinates (DIP). double delta_x; diff --git a/chromium/cc/input/scroll_utils.cc b/chromium/cc/input/scroll_utils.cc index af78599f472..c90c93a0db0 100644 --- a/chromium/cc/input/scroll_utils.cc +++ b/chromium/cc/input/scroll_utils.cc @@ -6,7 +6,6 @@ #include <algorithm> -#include "base/numerics/ranges.h" #include "ui/gfx/geometry/size_f.h" #include "ui/gfx/geometry/vector2d_f.h" diff --git a/chromium/cc/input/scrollbar_animation_controller.cc b/chromium/cc/input/scrollbar_animation_controller.cc index ec851edea31..098d0419e4e 100644 --- a/chromium/cc/input/scrollbar_animation_controller.cc +++ b/chromium/cc/input/scrollbar_animation_controller.cc @@ -8,8 +8,8 @@ #include <memory> #include "base/bind.h" +#include "base/cxx17_backports.h" #include "base/memory/ptr_util.h" -#include "base/numerics/ranges.h" #include "base/time/time.h" #include "cc/trees/layer_tree_impl.h" @@ -160,7 +160,7 @@ bool ScrollbarAnimationController::Animate(base::TimeTicks now) { float ScrollbarAnimationController::AnimationProgressAtTime( base::TimeTicks now) { const base::TimeDelta delta = now - last_awaken_time_; - return base::ClampToRange(float{delta / fade_duration_}, 0.0f, 1.0f); + return base::clamp(static_cast<float>(delta / fade_duration_), 0.0f, 1.0f); } void ScrollbarAnimationController::RunAnimationFrame(float progress) { diff --git a/chromium/cc/input/scrollbar_controller.h b/chromium/cc/input/scrollbar_controller.h index f2dc6003bf6..80f093255cb 100644 --- a/chromium/cc/input/scrollbar_controller.h +++ b/chromium/cc/input/scrollbar_controller.h @@ -12,9 +12,10 @@ #include "cc/input/scrollbar.h" #include "cc/layers/layer_impl.h" #include "cc/layers/painted_scrollbar_layer_impl.h" +#include "third_party/abseil-cpp/absl/types/optional.h" // High level documentation: -// https://source.chromium.org/chromium/chromium/src/+/master:cc/input/README.md +// https://source.chromium.org/chromium/chromium/src/+/main:cc/input/README.md // Click scrolling. // - A click is considered as a kMouseDown and a kMouseUp in quick succession. diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc index 75a458650f3..5288a8c5d3b 100644 --- a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc +++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc @@ -6,8 +6,8 @@ #include <algorithm> +#include "base/cxx17_backports.h" #include "base/memory/ptr_util.h" -#include "base/numerics/ranges.h" #include "base/time/time.h" #include "cc/input/scrollbar_animation_controller.h" #include "cc/layers/layer_impl.h" @@ -99,7 +99,8 @@ float SingleScrollbarAnimationControllerThinning::AnimationProgressAtTime( return 1.0f; const base::TimeDelta delta = now - last_awaken_time_; - return base::ClampToRange(float{delta / thinning_duration_}, 0.0f, 1.0f); + return base::clamp(static_cast<float>(delta / thinning_duration_), 0.0f, + 1.0f); } void SingleScrollbarAnimationControllerThinning::RunAnimationFrame( diff --git a/chromium/cc/input/threaded_input_handler.cc b/chromium/cc/input/threaded_input_handler.cc index cebc21a1f6a..872bf40dbc2 100644 --- a/chromium/cc/input/threaded_input_handler.cc +++ b/chromium/cc/input/threaded_input_handler.cc @@ -77,6 +77,7 @@ base::WeakPtr<InputHandler> ThreadedInputHandler::AsWeakPtr() const { void ThreadedInputHandler::BindToClient(InputHandlerClient* client) { DCHECK(input_handler_client_ == nullptr); input_handler_client_ = client; + input_handler_client_->SetPrefersReducedMotion(prefers_reduced_motion_); } InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin( @@ -871,6 +872,12 @@ ScrollElasticityHelper* ThreadedInputHandler::CreateScrollElasticityHelper() { return scroll_elasticity_helper_.get(); } +void ThreadedInputHandler::DestroyScrollElasticityHelper() { + // Remove any stretch before destroying helper. + scroll_elasticity_helper_->SetStretchAmount(gfx::Vector2dF()); + scroll_elasticity_helper_.reset(); +} + bool ThreadedInputHandler::GetScrollOffsetForLayer(ElementId element_id, gfx::ScrollOffset* offset) { ScrollTree& scroll_tree = GetScrollTree(); @@ -956,7 +963,8 @@ void ThreadedInputHandler::ScrollEndForSnapFling(bool did_finish) { scroll_node->snap_container_data.has_value()) { scroll_node->snap_container_data.value().SetTargetSnapAreaElementIds( scroll_animating_snap_target_ids_); - updated_snapped_elements_.insert(scroll_node->element_id); + updated_snapped_elements_[scroll_node->element_id] = + scroll_animating_snap_target_ids_; SetNeedsCommit(); } scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds(); @@ -981,7 +989,7 @@ void ThreadedInputHandler::ProcessCommitDeltas( InnerViewportScrollNode() ? InnerViewportScrollNode()->element_id : ElementId(); - base::flat_set<ElementId> snapped_elements; + base::flat_map<ElementId, TargetSnapAreaElementIds> snapped_elements; updated_snapped_elements_.swap(snapped_elements); // Scroll commit data is stored in the scroll tree so it has its own method @@ -1106,7 +1114,8 @@ void ThreadedInputHandler::ScrollOffsetAnimationFinished() { if (scroll_node && scroll_node->snap_container_data.has_value()) { scroll_node->snap_container_data.value().SetTargetSnapAreaElementIds( scroll_animating_snap_target_ids_); - updated_snapped_elements_.insert(scroll_node->element_id); + updated_snapped_elements_[scroll_node->element_id] = + scroll_animating_snap_target_ids_; SetNeedsCommit(); } scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds(); @@ -1119,6 +1128,16 @@ void ThreadedInputHandler::ScrollOffsetAnimationFinished() { } } +void ThreadedInputHandler::SetPrefersReducedMotion( + bool prefers_reduced_motion) { + if (prefers_reduced_motion_ == prefers_reduced_motion) + return; + prefers_reduced_motion_ = prefers_reduced_motion; + + if (input_handler_client_) + input_handler_client_->SetPrefersReducedMotion(prefers_reduced_motion_); +} + bool ThreadedInputHandler::IsCurrentlyScrolling() const { return CurrentlyScrollingNode(); } @@ -1294,7 +1313,7 @@ InputHandler::ScrollStatus ThreadedInputHandler::TryScroll( "LayerImpl::tryScroll: Failed due to no scrolling layer"); scroll_status.thread = InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD; scroll_status.main_thread_scrolling_reasons = - MainThreadScrollingReason::kNonFastScrollableRegion; + MainThreadScrollingReason::kNoScrollingLayer; return scroll_status; } @@ -2074,7 +2093,7 @@ bool ThreadedInputHandler::SnapAtScrollEnd(SnapReason reason) { // The snap target will be set when the animation is completed. scroll_animating_snap_target_ids_ = snap_target_ids; } else if (data.SetTargetSnapAreaElementIds(snap_target_ids)) { - updated_snapped_elements_.insert(scroll_node->element_id); + updated_snapped_elements_[scroll_node->element_id] = snap_target_ids; SetNeedsCommit(); } return did_animate; @@ -2185,4 +2204,8 @@ gfx::Vector2dF ThreadedInputHandler::UserScrollableDelta( return adjusted_delta; } +bool ThreadedInputHandler::ScrollbarScrollIsActive() { + return scrollbar_controller_->ScrollbarScrollIsActive(); +} + } // namespace cc diff --git a/chromium/cc/input/threaded_input_handler.h b/chromium/cc/input/threaded_input_handler.h index e327ee989d7..eb3b6d5c88b 100644 --- a/chromium/cc/input/threaded_input_handler.h +++ b/chromium/cc/input/threaded_input_handler.h @@ -91,6 +91,7 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, GetScopedEventMetricsMonitor( EventsMetricsManager::ScopedMonitor::DoneCallback done_callback) override; ScrollElasticityHelper* CreateScrollElasticityHelper() override; + void DestroyScrollElasticityHelper() override; bool GetScrollOffsetForLayer(ElementId element_id, gfx::ScrollOffset* offset) override; bool ScrollLayerTo(ElementId element_id, @@ -102,6 +103,7 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, gfx::Vector2dF* out_target_position) override; void ScrollEndForSnapFling(bool did_finish) override; void NotifyInputEvent() override; + bool ScrollbarScrollIsActive() override; // =========== InputDelegateForCompositor Interface - This section implements // the interface that LayerTreeHostImpl uses to communicate with the input @@ -117,6 +119,7 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, void DidUnregisterScrollbar(ElementId scroll_element_id, ScrollbarOrientation orientation) override; void ScrollOffsetAnimationFinished() override; + void SetPrefersReducedMotion(bool prefers_reduced_motion) override; bool IsCurrentlyScrolling() const override; ActivelyScrollingType GetActivelyScrollingType() const override; @@ -390,7 +393,7 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, // A set of elements that scroll-snapped to a new target since the last // begin main frame. The snap target ids of these elements will be sent to // the main thread in the next begin main frame. - base::flat_set<ElementId> updated_snapped_elements_; + base::flat_map<ElementId, TargetSnapAreaElementIds> updated_snapped_elements_; ElementId scroll_element_id_mouse_currently_over_; ElementId scroll_element_id_mouse_currently_captured_; @@ -443,6 +446,8 @@ class CC_EXPORT ThreadedInputHandler : public InputHandler, bool has_scrolled_by_precisiontouchpad_ = false; bool has_scrolled_by_scrollbar_ = false; + bool prefers_reduced_motion_ = false; + // Must be the last member to ensure this is destroyed first in the // destruction order and invalidates all weak pointers. base::WeakPtrFactory<ThreadedInputHandler> weak_factory_{this}; diff --git a/chromium/cc/layers/deadline_policy.cc b/chromium/cc/layers/deadline_policy.cc index 2b343da4397..65f376ddbe5 100644 --- a/chromium/cc/layers/deadline_policy.cc +++ b/chromium/cc/layers/deadline_policy.cc @@ -6,7 +6,28 @@ #include <limits> +#include "base/notreached.h" +#include "base/strings/stringprintf.h" + namespace cc { +namespace { + +const char* PolicyToString(DeadlinePolicy::Type type) { + switch (type) { + case DeadlinePolicy::Type::kUseExistingDeadline: + return "UseExistingDeadline"; + case DeadlinePolicy::Type::kUseDefaultDeadline: + return "UseDefaultDeadline"; + case DeadlinePolicy::Type::kUseSpecifiedDeadline: + return "UseSpecifiedDeadline"; + case DeadlinePolicy::Type::kUseInfiniteDeadline: + return "UseInfiniteDeadline"; + } + NOTREACHED(); + return ""; +} + +} // namespace // static DeadlinePolicy DeadlinePolicy::UseExistingDeadline() { @@ -36,4 +57,10 @@ DeadlinePolicy::DeadlinePolicy(Type policy_type, DeadlinePolicy::DeadlinePolicy(const DeadlinePolicy& other) = default; +std::string DeadlinePolicy::ToString() const { + return base::StringPrintf("DeadlinePolicy(%s, %d)", + PolicyToString(policy_type_), + deadline_in_frames_.value_or(-1)); +} + } // namespace cc diff --git a/chromium/cc/layers/deadline_policy.h b/chromium/cc/layers/deadline_policy.h index 9a62c1fe2d4..57416c66e0d 100644 --- a/chromium/cc/layers/deadline_policy.h +++ b/chromium/cc/layers/deadline_policy.h @@ -6,6 +6,7 @@ #define CC_LAYERS_DEADLINE_POLICY_H_ #include <cstdint> +#include <string> #include "base/check.h" #include "cc/cc_export.h" @@ -58,6 +59,8 @@ class CC_EXPORT DeadlinePolicy { return !(*this == other); } + std::string ToString() const; + private: explicit DeadlinePolicy( Type policy_type, diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index 289ff12cefb..e8f4bd8df42 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -55,6 +55,7 @@ #include "third_party/skia/include/core/SkFont.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/core/SkTypeface.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "ui/gfx/geometry/point.h" @@ -306,6 +307,8 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( } else if (gpu_raster) { flags |= gpu::SHARED_IMAGE_USAGE_GLES2 | gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT; + } else { + flags |= gpu::SHARED_IMAGE_USAGE_GLES2; } if (backing->overlay_candidate) flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT; @@ -379,7 +382,8 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( constexpr GLuint msaa_sample_count = -1; constexpr bool can_use_lcd_text = true; ri->BeginRasterCHROMIUM(background_color, needs_clear, msaa_sample_count, - can_use_lcd_text, gfx::ColorSpace::CreateSRGB(), + gpu::raster::kNoMSAA, can_use_lcd_text, + gfx::ColorSpace::CreateSRGB(), backing->mailbox.name); gfx::Vector2dF post_translate(0.f, 0.f); gfx::Vector2dF post_scale(1.f, 1.f); @@ -510,11 +514,23 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( render_pass->quad_list.ReplaceExistingElement<viz::TextureDrawQuad>( it); + // The acquired resource's size could be bigger than actually needed due + // to reuse. In this case, only use the part of the texture that is within + // the bounds. + gfx::PointF uv_bottom_right(1.f, 1.f); + if (in_flight_resource_.size() != internal_content_bounds_) { + uv_bottom_right.set_x( + static_cast<double>(internal_content_bounds_.width()) / + static_cast<double>(in_flight_resource_.size().width())); + uv_bottom_right.set_y( + static_cast<double>(internal_content_bounds_.height()) / + static_cast<double>(in_flight_resource_.size().height())); + } const float vertex_opacity[] = {1.f, 1.f, 1.f, 1.f}; quad->SetNew(sqs, quad_rect, visible_rect, /*needs_blending=*/true, resource_id, /*premultiplied_alpha=*/true, /*uv_top_left=*/gfx::PointF(), - /*uv_bottom_right=*/gfx::PointF(1.f, 1.f), + /*uv_bottom_right=*/uv_bottom_right, /*background_color=*/SK_ColorTRANSPARENT, vertex_opacity, /*flipped=*/false, /*nearest_neighbor=*/false, /*secure_output_only=*/false, diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index 2f6f6f8ecf3..03cb7303a34 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -349,9 +349,13 @@ void Layer::SetBounds(const gfx::Size& size) { // Rounded corner clipping, bounds clipping and mask clipping can result in // new areas of subtrees being exposed on a bounds change. Ensure the damaged - // areas are updated. + // areas are updated. Also, if the layer subtree (rooted at this layer) is + // marked as capturable (via a valid SubtreeCaptureId), then the property tree + // needs rebuild so that |EffectNode::subtree_size| is updated with the new + // size of this layer. if (!layer_tree_host_->IsUsingLayerLists()) { - if (masks_to_bounds() || mask_layer() || HasRoundedCorner()) { + if (subtree_capture_id().is_valid() || masks_to_bounds() || mask_layer() || + HasRoundedCorner()) { SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); } diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 33918090136..db032e93d52 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -16,7 +16,6 @@ #include "base/auto_reset.h" #include "base/callback.h" #include "base/memory/ref_counted.h" -#include "base/observer_list.h" #include "cc/base/region.h" #include "cc/benchmarks/micro_benchmark.h" #include "cc/cc_export.h" @@ -53,7 +52,7 @@ class PictureLayer; // For tracing and debugging. The info will be attached to this layer's tracing // output. -struct LayerDebugInfo { +struct CC_EXPORT LayerDebugInfo { LayerDebugInfo(); LayerDebugInfo(const LayerDebugInfo&); ~LayerDebugInfo(); @@ -401,11 +400,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // For layer tree mode only. // Marks this layer as being scrollable and needing an associated scroll node, - // and specifies the total size of the content to be scrolled (ie the max - // scroll offsets. The size should be a union of the layer and its subtree, as - // well as any layers for whom this layer is their scroll parent, and their - // subtrees, when they are transformed into this layer's space. Thus - // transforms of children affect the size of the |scroll_container_bounds|. + // and specifies the size of the container in which the scrolling contents are + // visible. (Use SetBounds to set the size of the content to be scrolled.) // Once scrollable, a Layer cannot become un-scrollable. void SetScrollable(const gfx::Size& scroll_container_bounds); bool scrollable() const { diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc index 9596cbf7cbe..cda060711e4 100644 --- a/chromium/cc/layers/layer_impl_unittest.cc +++ b/chromium/cc/layers/layer_impl_unittest.cc @@ -6,7 +6,6 @@ #include <algorithm> -#include "base/stl_util.h" #include "cc/layers/painted_scrollbar_layer_impl.h" #include "cc/layers/solid_color_scrollbar_layer_impl.h" #include "cc/paint/filter_operation.h" diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index 61b4890e82c..6ae8e295a23 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -1395,7 +1395,8 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) { // layer does not abort either one. std::unique_ptr<viz::CopyOutputRequest> request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&ReceiveCopyOutputResultAtomic, base::Unretained(&result_count))); layer->RequestCopyOfOutput(std::move(request)); @@ -1404,7 +1405,8 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) { CCTestSuite::RunUntilIdle(); EXPECT_EQ(0, result_count.load()); request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&ReceiveCopyOutputResultAtomic, base::Unretained(&result_count))); layer->RequestCopyOfOutput(std::move(request)); @@ -1426,7 +1428,8 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) { // the second request using |kArbitrarySourceId1| is made. int did_receive_first_result_from_this_source = 0; request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&ReceiveCopyOutputResult, &did_receive_first_result_from_this_source)); request->set_source(kArbitrarySourceId1); @@ -1438,7 +1441,8 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) { // Make a request from a different source. int did_receive_result_from_different_source = 0; request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&ReceiveCopyOutputResult, &did_receive_result_from_different_source)); request->set_source(kArbitrarySourceId2); @@ -1450,7 +1454,8 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) { // Make a request without specifying the source. int did_receive_result_from_anonymous_source = 0; request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&ReceiveCopyOutputResult, &did_receive_result_from_anonymous_source)); layer->RequestCopyOfOutput(std::move(request)); @@ -1461,7 +1466,8 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) { // Make the second request from |kArbitrarySourceId1|. int did_receive_second_result_from_this_source = 0; request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&ReceiveCopyOutputResult, &did_receive_second_result_from_this_source)); request->set_source(kArbitrarySourceId1); diff --git a/chromium/cc/layers/picture_layer.h b/chromium/cc/layers/picture_layer.h index 7b13db34e19..8cacd483c0a 100644 --- a/chromium/cc/layers/picture_layer.h +++ b/chromium/cc/layers/picture_layer.h @@ -51,7 +51,7 @@ class CC_EXPORT PictureLayer : public Layer { ContentLayerClient* client() { return picture_layer_inputs_.client; } - RecordingSource* GetRecordingSourceForTesting() { + RecordingSource* GetRecordingSourceForTesting() const { return recording_source_.get(); } diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index 767d3315604..6dceca33062 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -15,9 +15,9 @@ #include <utility> #include "base/containers/contains.h" +#include "base/cxx17_backports.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" -#include "base/numerics/ranges.h" #include "base/system/sys_info.h" #include "base/time/time.h" #include "base/trace_event/traced_value.h" @@ -207,11 +207,6 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, viz::SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); - // If did_checkerboard_quad_ is set to true, don't set to false until the - // scroll is completed. - if (!ScrollInteractionInProgress()) - SetDidCheckerboardQuad(false); - if (raster_source_->IsSolidColor()) { // TODO(979672): This is still hard-coded at 1.0. This has some history: // - for crbug.com/769319, the contents scale was allowed to change, to @@ -549,7 +544,6 @@ void PictureLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, append_quads_data->checkerboarded_no_recording_content_area += visible_geometry_area - checkerboarded_has_recording_area; - SetDidCheckerboardQuad(true); continue; } @@ -788,6 +782,8 @@ void PictureLayerImpl::UpdateRasterSource( bool could_have_tilings = CanHaveTilings(); raster_source_.swap(raster_source); + raster_source_->set_debug_name(DebugName()); + // Register images from the new raster source, if the recording was updated. // TODO(khushalsagar): UMA the number of animated images in layer? if (recording_updated) @@ -1011,8 +1007,8 @@ bool PictureLayerImpl::ScrollInteractionInProgress() const { ActivelyScrollingType::kNone; } -bool PictureLayerImpl::DidCheckerboardQuad() const { - return did_checkerboard_quad_; +bool PictureLayerImpl::CurrentScrollDidCheckerboardLargeArea() const { + return layer_tree_impl()->CurrentScrollDidCheckerboardLargeArea(); } gfx::Rect PictureLayerImpl::GetEnclosingVisibleRectInTargetSpace() const { @@ -1183,7 +1179,7 @@ float PictureLayerImpl::CalculateDirectlyCompositedImageRasterScale() const { float min_scale = MinimumContentsScale(); float clamped_ideal_source_scale = - base::ClampToRange(ideal_source_scale_key(), min_scale, max_scale); + base::clamp(ideal_source_scale_key(), min_scale, max_scale); while (adjusted_raster_scale < clamped_ideal_source_scale) adjusted_raster_scale *= 2.f; @@ -1196,7 +1192,7 @@ float PictureLayerImpl::CalculateDirectlyCompositedImageRasterScale() const { adjusted_raster_scale /= 2.f; adjusted_raster_scale = - base::ClampToRange(adjusted_raster_scale, min_scale, max_scale); + base::clamp(adjusted_raster_scale, min_scale, max_scale); return adjusted_raster_scale; } @@ -1683,7 +1679,7 @@ bool PictureLayerImpl::CalculateRasterTranslation( // a layer of size 10000px does not exceed 0.001px. static constexpr float kPixelErrorThreshold = 0.001f; static constexpr float kScaleErrorThreshold = kPixelErrorThreshold / 10000; - auto is_raster_scale = [this](const SkMatrix44& matrix) -> bool { + auto is_raster_scale = [this](const skia::Matrix44& matrix) -> bool { // The matrix has the X scale at (0,0), and the Y scale at (1,1). return std::abs(matrix.getFloat(0, 0) - raster_contents_scale_.x()) <= kScaleErrorThreshold && diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index c673c17c128..231830dec2f 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -80,7 +80,7 @@ class CC_EXPORT PictureLayerImpl const PaintWorkletRecordMap& GetPaintWorkletRecords() const override; bool IsDirectlyCompositedImage() const override; bool ScrollInteractionInProgress() const override; - bool DidCheckerboardQuad() const override; + bool CurrentScrollDidCheckerboardLargeArea() const override; // ImageAnimationController::AnimationDriver overrides. bool ShouldAnimate(PaintImage::Id paint_image_id) const override; @@ -107,10 +107,6 @@ class CC_EXPORT PictureLayerImpl void SetNearestNeighbor(bool nearest_neighbor); - void SetDidCheckerboardQuad(bool did_checkerboard_quad) { - did_checkerboard_quad_ = did_checkerboard_quad; - } - void SetDirectlyCompositedImageSize(absl::optional<gfx::Size>); size_t GPUMemoryUsageInBytes() const override; @@ -291,9 +287,6 @@ class CC_EXPORT PictureLayerImpl bool nearest_neighbor_ : 1; - // Whether the layer did not have a draw quad during last AppendQuads call. - bool did_checkerboard_quad_ : 1; - // This is set by UpdateRasterSource() on change of raster source size. It's // used to recalculate raster scale for will-chagne:transform. It's reset to // false after raster scale update. diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index 6d0922a33e9..e34ad2457d3 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -12,8 +12,8 @@ #include <set> #include <utility> +#include "base/cxx17_backports.h" #include "base/location.h" -#include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" #include "cc/animation/animation_host.h" #include "cc/base/math_util.h" @@ -2145,7 +2145,6 @@ TEST_F(LegacySWPictureLayerImplTest, AppendQuadsDataForCheckerboard) { EXPECT_EQ(17500, data.checkerboarded_no_recording_content_area); EXPECT_EQ(22500, data.checkerboarded_needs_raster_content_area); EXPECT_TRUE(active_layer()->only_used_low_res_last_append_quads()); - EXPECT_TRUE(active_layer()->DidCheckerboardQuad()); } TEST_F(LegacySWPictureLayerImplTest, HighResRequiredWhenActiveAllReady) { diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc index 3fd239a4635..4ebb9da7dfa 100644 --- a/chromium/cc/layers/render_surface_impl.cc +++ b/chromium/cc/layers/render_surface_impl.cc @@ -160,6 +160,10 @@ viz::SubtreeCaptureId RenderSurfaceImpl::SubtreeCaptureId() const { return OwningEffectNode()->subtree_capture_id; } +gfx::Size RenderSurfaceImpl::SubtreeSize() const { + return OwningEffectNode()->subtree_size; +} + bool RenderSurfaceImpl::ShouldCacheRenderSurface() const { return OwningEffectNode()->cache_render_surface; } @@ -391,6 +395,10 @@ RenderSurfaceImpl::CreateRenderPass() { pass->backdrop_filter_bounds = BackdropFilterBounds(); pass->generate_mipmap = TrilinearFiltering(); pass->subtree_capture_id = SubtreeCaptureId(); + // The subtree size may be slightly larger than our content rect during + // some animations, so we clamp it here. + pass->subtree_size = SubtreeSize(); + pass->subtree_size.SetToMin(content_rect().size()); pass->cache_render_pass = ShouldCacheRenderSurface(); pass->has_damage_from_contributing_content = HasDamageFromeContributingContent(); diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h index 7d303a2f331..234feafb687 100644 --- a/chromium/cc/layers/render_surface_impl.h +++ b/chromium/cc/layers/render_surface_impl.h @@ -20,8 +20,10 @@ #include "components/viz/common/quads/compositor_render_pass.h" #include "components/viz/common/quads/shared_quad_state.h" #include "components/viz/common/surfaces/subtree_capture_id.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" #include "ui/gfx/mask_filter_info.h" #include "ui/gfx/transform.h" @@ -176,8 +178,15 @@ class CC_EXPORT RenderSurfaceImpl { bool HasCopyRequest() const; + // The capture identifier for this render surface and its originating effect + // node. If empty, this surface has not been selected as a subtree capture and + // is either a root surface or will not be rendered separately. viz::SubtreeCaptureId SubtreeCaptureId() const; + // The size of this surface that should be used for cropping capture. If + // empty, the entire size of this surface should be used for capture. + gfx::Size SubtreeSize() const; + bool ShouldCacheRenderSurface() const; // Returns true if it's required to copy the output of this surface (i.e. when diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.cc b/chromium/cc/layers/scrollbar_layer_impl_base.cc index 3d9e48dc9a3..1a9faf0b7ee 100644 --- a/chromium/cc/layers/scrollbar_layer_impl_base.cc +++ b/chromium/cc/layers/scrollbar_layer_impl_base.cc @@ -6,7 +6,7 @@ #include <algorithm> -#include "base/numerics/ranges.h" +#include "base/cxx17_backports.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/scroll_node.h" @@ -211,10 +211,12 @@ gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRectWithThumbThicknessScale( float track_length = TrackLength(); int thumb_length = ThumbLength(); int thumb_thickness = ThumbThickness(); - float maximum = scroll_layer_length() - clip_layer_length(); + // TODO(crbug.com/1239770): This is a speculative fix. + float maximum = std::max(scroll_layer_length() - clip_layer_length(), 0.0f); + DCHECK(scroll_layer_length() >= clip_layer_length()); // With the length known, we can compute the thumb's position. - float clamped_current_pos = base::ClampToRange(current_pos(), 0.0f, maximum); + float clamped_current_pos = base::clamp(current_pos(), 0.0f, maximum); int thumb_offset = TrackStart(); if (maximum > 0) { diff --git a/chromium/cc/layers/surface_layer_impl.cc b/chromium/cc/layers/surface_layer_impl.cc index d50707ec578..76f99c951fb 100644 --- a/chromium/cc/layers/surface_layer_impl.cc +++ b/chromium/cc/layers/surface_layer_impl.cc @@ -8,7 +8,7 @@ #include <algorithm> #include <utility> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/synchronization/waitable_event.h" #include "base/trace_event/traced_value.h" #include "cc/debug/debug_colors.h" diff --git a/chromium/cc/layers/texture_layer.cc b/chromium/cc/layers/texture_layer.cc index c5872526814..a5d2b601009 100644 --- a/chromium/cc/layers/texture_layer.cc +++ b/chromium/cc/layers/texture_layer.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/cxx20_erase.h" #include "base/location.h" #include "base/sequenced_task_runner.h" #include "base/synchronization/lock.h" diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc index ebe01e4df08..7bc545b6bcd 100644 --- a/chromium/cc/layers/texture_layer_impl.cc +++ b/chromium/cc/layers/texture_layer_impl.cc @@ -11,6 +11,7 @@ #include <utility> #include <vector> +#include "base/containers/cxx20_erase.h" #include "base/logging.h" #include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_impl.h" diff --git a/chromium/cc/layers/video_layer.cc b/chromium/cc/layers/video_layer.cc index 1c656ecc11d..5ce684b2c45 100644 --- a/chromium/cc/layers/video_layer.cc +++ b/chromium/cc/layers/video_layer.cc @@ -10,13 +10,13 @@ namespace cc { scoped_refptr<VideoLayer> VideoLayer::Create( VideoFrameProvider* provider, - media::VideoRotation video_rotation) { - return base::WrapRefCounted(new VideoLayer(provider, video_rotation)); + media::VideoTransformation transform) { + return base::WrapRefCounted(new VideoLayer(provider, transform)); } VideoLayer::VideoLayer(VideoFrameProvider* provider, - media::VideoRotation video_rotation) - : provider_(provider), video_rotation_(video_rotation) { + media::VideoTransformation transform) + : provider_(provider), transform_(transform) { SetMayContainVideo(true); DCHECK(provider_); } @@ -25,7 +25,7 @@ VideoLayer::~VideoLayer() = default; std::unique_ptr<LayerImpl> VideoLayer::CreateLayerImpl( LayerTreeImpl* tree_impl) { - return VideoLayerImpl::Create(tree_impl, id(), provider_, video_rotation_); + return VideoLayerImpl::Create(tree_impl, id(), provider_, transform_); } bool VideoLayer::Update() { diff --git a/chromium/cc/layers/video_layer.h b/chromium/cc/layers/video_layer.h index 45cacd34d0f..142bf20ac9f 100644 --- a/chromium/cc/layers/video_layer.h +++ b/chromium/cc/layers/video_layer.h @@ -5,6 +5,8 @@ #ifndef CC_LAYERS_VIDEO_LAYER_H_ #define CC_LAYERS_VIDEO_LAYER_H_ +#include <memory> + #include "base/callback.h" #include "cc/cc_export.h" #include "cc/layers/layer.h" @@ -21,7 +23,7 @@ class VideoLayerImpl; class CC_EXPORT VideoLayer : public Layer { public: static scoped_refptr<VideoLayer> Create(VideoFrameProvider* provider, - media::VideoRotation video_rotation); + media::VideoTransformation transform); VideoLayer(const VideoLayer&) = delete; VideoLayer& operator=(const VideoLayer&) = delete; @@ -34,14 +36,15 @@ class CC_EXPORT VideoLayer : public Layer { void StopUsingProvider(); private: - VideoLayer(VideoFrameProvider* provider, media::VideoRotation video_rotation); + VideoLayer(VideoFrameProvider* provider, + media::VideoTransformation transform); ~VideoLayer() override; // This pointer is only for passing to VideoLayerImpl's constructor. It should // never be dereferenced by this class. VideoFrameProvider* provider_; - media::VideoRotation video_rotation_; + media::VideoTransformation transform_; }; } // namespace cc diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc index ba7d9dabd70..0c6087a4389 100644 --- a/chromium/cc/layers/video_layer_impl.cc +++ b/chromium/cc/layers/video_layer_impl.cc @@ -32,7 +32,7 @@ std::unique_ptr<VideoLayerImpl> VideoLayerImpl::Create( LayerTreeImpl* tree_impl, int id, VideoFrameProvider* provider, - media::VideoRotation video_rotation) { + media::VideoTransformation video_transform) { DCHECK(tree_impl->task_runner_provider()->IsMainThreadBlocked()); DCHECK(tree_impl->task_runner_provider()->IsImplThread()); @@ -41,18 +41,17 @@ std::unique_ptr<VideoLayerImpl> VideoLayerImpl::Create( provider, tree_impl->GetVideoFrameControllerClient()); return base::WrapUnique(new VideoLayerImpl( - tree_impl, id, std::move(provider_client_impl), video_rotation)); + tree_impl, id, std::move(provider_client_impl), video_transform)); } VideoLayerImpl::VideoLayerImpl( LayerTreeImpl* tree_impl, int id, scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl, - media::VideoRotation video_rotation) + media::VideoTransformation video_transform) : LayerImpl(tree_impl, id), provider_client_impl_(std::move(provider_client_impl)), - frame_(nullptr), - video_rotation_(video_rotation) { + video_transform_(video_transform) { set_may_contain_video(true); } @@ -72,7 +71,7 @@ VideoLayerImpl::~VideoLayerImpl() { std::unique_ptr<LayerImpl> VideoLayerImpl::CreateLayerImpl( LayerTreeImpl* tree_impl) { return base::WrapUnique(new VideoLayerImpl( - tree_impl, id(), provider_client_impl_, video_rotation_)); + tree_impl, id(), provider_client_impl_, video_transform_)); } void VideoLayerImpl::DidBecomeActive() { @@ -126,32 +125,41 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode, void VideoLayerImpl::AppendQuads(viz::CompositorRenderPass* render_pass, AppendQuadsData* append_quads_data) { - DCHECK(frame_.get()); + DCHECK(frame_); gfx::Transform transform = DrawTransform(); + // bounds() is in post-rotation space so quad rect in content space must be // in pre-rotation space gfx::Size rotated_size = bounds(); - switch (video_rotation_) { + // Prefer the frame level transform if set. + auto media_transform = + frame_->metadata().transformation.value_or(video_transform_); + switch (media_transform.rotation) { case media::VIDEO_ROTATION_90: rotated_size = gfx::Size(rotated_size.height(), rotated_size.width()); - transform.Rotate(90.0); + transform.RotateAboutZAxis(90.0); transform.Translate(0.0, -rotated_size.height()); break; case media::VIDEO_ROTATION_180: - transform.Rotate(180.0); + transform.RotateAboutZAxis(180.0); transform.Translate(-rotated_size.width(), -rotated_size.height()); break; case media::VIDEO_ROTATION_270: rotated_size = gfx::Size(rotated_size.height(), rotated_size.width()); - transform.Rotate(270.0); + transform.RotateAboutZAxis(270.0); transform.Translate(-rotated_size.width(), 0); break; case media::VIDEO_ROTATION_0: break; } + if (media_transform.mirrored) { + transform.RotateAboutYAxis(180.0); + transform.Translate(-rotated_size.width(), 0); + } + gfx::Rect quad_rect(rotated_size); Occlusion occlusion_in_video_space = draw_properties() diff --git a/chromium/cc/layers/video_layer_impl.h b/chromium/cc/layers/video_layer_impl.h index 9727fd4625b..254621aaa4b 100644 --- a/chromium/cc/layers/video_layer_impl.h +++ b/chromium/cc/layers/video_layer_impl.h @@ -5,6 +5,7 @@ #ifndef CC_LAYERS_VIDEO_LAYER_IMPL_H_ #define CC_LAYERS_VIDEO_LAYER_IMPL_H_ +#include <memory> #include <vector> #include "cc/cc_export.h" @@ -29,7 +30,7 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl { LayerTreeImpl* tree_impl, int id, VideoFrameProvider* provider, - media::VideoRotation video_rotation); + media::VideoTransformation video_transform); VideoLayerImpl(const VideoLayerImpl&) = delete; ~VideoLayerImpl() override; @@ -48,14 +49,17 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl { gfx::ContentColorUsage GetContentColorUsage() const override; void SetNeedsRedraw(); - media::VideoRotation video_rotation() const { return video_rotation_; } + + media::VideoTransformation video_transform_for_testing() const { + return video_transform_; + } private: VideoLayerImpl( LayerTreeImpl* tree_impl, int id, scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl, - media::VideoRotation video_rotation); + media::VideoTransformation video_transform); const char* LayerTypeAsString() const override; @@ -63,7 +67,7 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl { scoped_refptr<media::VideoFrame> frame_; - media::VideoRotation video_rotation_; + media::VideoTransformation video_transform_; std::unique_ptr<media::VideoResourceUpdater> updater_; }; diff --git a/chromium/cc/metrics/average_lag_tracker.cc b/chromium/cc/metrics/average_lag_tracker.cc index 05e7e41c3a1..3efacd5ed25 100644 --- a/chromium/cc/metrics/average_lag_tracker.cc +++ b/chromium/cc/metrics/average_lag_tracker.cc @@ -11,8 +11,7 @@ namespace cc { -AverageLagTracker::AverageLagTracker(FinishTimeType finish_time_type) - : finish_time_type_(finish_time_type) {} +AverageLagTracker::AverageLagTracker() = default; AverageLagTracker::~AverageLagTracker() = default; void AverageLagTracker::AddScrollEventInFrame(const EventInfo& event_info) { @@ -28,9 +27,7 @@ void AverageLagTracker::AddScrollEventInFrame(const EventInfo& event_info) { } std::string AverageLagTracker::GetAverageLagMetricName(EventType event) const { - std::string metric_name = finish_time_type_ == FinishTimeType::GpuSwapBegin - ? "AverageLag" - : "AverageLagPresentation"; + std::string metric_name = "AverageLagPresentation"; std::string event_name = event == EventType::ScrollBegin ? "ScrollBegin" : "ScrollUpdate"; @@ -191,30 +188,52 @@ void AverageLagTracker::CalculateAndReportAverageLagUma(bool send_anyway) { const float time_delta = (frame_lag.frame_time - last_reported_time_).InMillisecondsF(); - const float scaled_lag = accumulated_lag_ / time_delta; + const float scaled_lag_with_prediction = accumulated_lag_ / time_delta; + const float scaled_lag_no_prediction = + accumulated_lag_no_prediction_ / time_delta; + base::UmaHistogramCounts1000(GetAverageLagMetricName(event_type), - scaled_lag); + scaled_lag_with_prediction); + base::UmaHistogramCounts1000( + base::JoinString({GetAverageLagMetricName(event_type), "NoPrediction"}, + "."), + scaled_lag_with_prediction); + + const float lag_improvement = + scaled_lag_no_prediction - scaled_lag_with_prediction; - const float prediction_effect = - (accumulated_lag_no_prediction_ - accumulated_lag_) / time_delta; // Log positive and negative prediction effects. ScrollBegin currently // doesn't take prediction into account so don't log for it. // Positive effect means that the prediction reduced the perceived lag, // where negative means prediction made lag worse (most likely due to // misprediction). if (event_type == EventType::ScrollUpdate) { - if (prediction_effect >= 0.f) { + if (lag_improvement >= 0.f) { base::UmaHistogramCounts1000( base::JoinString( {GetAverageLagMetricName(event_type), "PredictionPositive"}, "."), - prediction_effect); + lag_improvement); } else { base::UmaHistogramCounts1000( base::JoinString( {GetAverageLagMetricName(event_type), "PredictionNegative"}, "."), - -prediction_effect); + -lag_improvement); + } + + if (scaled_lag_no_prediction > 0) { + // How much of the original lag wasn't removed by prediction. + float remaining_lag_ratio = + scaled_lag_with_prediction / scaled_lag_no_prediction; + + // Using custom bucket count for high precision on values in (0, 100). + // With 100 buckets, (0, 100) is mapped into 60 buckets. + base::UmaHistogramCustomCounts( + base::JoinString( + {GetAverageLagMetricName(event_type), "RemainingLagPercentage"}, + "."), + 100 * remaining_lag_ratio, 1, 500, 100); } } accumulated_lag_ = 0; diff --git a/chromium/cc/metrics/average_lag_tracker.h b/chromium/cc/metrics/average_lag_tracker.h index 3877a773eff..2a94b97163c 100644 --- a/chromium/cc/metrics/average_lag_tracker.h +++ b/chromium/cc/metrics/average_lag_tracker.h @@ -18,7 +18,6 @@ namespace cc { // https://docs.google.com/document/d/1e8NuzPblIv2B9bz01oSj40rmlse7_PHq5oFS3lqz6N4/ class CC_EXPORT AverageLagTracker { public: - enum class FinishTimeType { GpuSwapBegin, PresentationFeedback }; enum class EventType { ScrollBegin, ScrollUpdate }; struct EventInfo { @@ -48,7 +47,7 @@ class CC_EXPORT AverageLagTracker { EventType event_type; }; - explicit AverageLagTracker(FinishTimeType); + AverageLagTracker(); ~AverageLagTracker(); // Disallow copy and assign. @@ -107,10 +106,6 @@ class CC_EXPORT AverageLagTracker { float LagForUnfinishedFrame(float rendered_accumulated_delta); - // Time in the frame lifecycle that EventInfo::finish_timestamp represents - // and defines which metric is tracked by this AverageLagTracker. - const FinishTimeType finish_time_type_; - std::deque<LagAreaInFrame> frame_lag_infos_; // Last scroll event's timestamp in the sequence, reset on ScrollBegin. diff --git a/chromium/cc/metrics/average_lag_tracker_unittest.cc b/chromium/cc/metrics/average_lag_tracker_unittest.cc index d836f75c759..9f011221b02 100644 --- a/chromium/cc/metrics/average_lag_tracker_unittest.cc +++ b/chromium/cc/metrics/average_lag_tracker_unittest.cc @@ -27,10 +27,7 @@ class AverageLagTrackerTest : public testing::Test { const base::HistogramTester& histogram_tester() { return *histogram_tester_; } void SetUp() override { - average_lag_tracker_gpu_swap_ = std::make_unique<AverageLagTracker>( - AverageLagTracker::FinishTimeType::GpuSwapBegin); - average_lag_tracker_presentation_ = std::make_unique<AverageLagTracker>( - AverageLagTracker::FinishTimeType::PresentationFeedback); + average_lag_tracker_ = std::make_unique<AverageLagTracker>(); } void SyntheticTouchScrollBegin(base::TimeTicks event_time, @@ -41,11 +38,7 @@ class AverageLagTrackerTest : public testing::Test { 0, delta, predicted_delta != 0 ? predicted_delta : delta, event_time, AverageLagTracker::EventType::ScrollBegin); event_info.finish_timestamp = frame_time; - - // Always add the events to both and test if they have the same behavior - // regardless of the metrics names. - average_lag_tracker_gpu_swap_->AddScrollEventInFrame(event_info); - average_lag_tracker_presentation_->AddScrollEventInFrame(event_info); + average_lag_tracker_->AddScrollEventInFrame(event_info); } void SyntheticTouchScrollUpdate(base::TimeTicks event_time, @@ -56,48 +49,46 @@ class AverageLagTrackerTest : public testing::Test { 0, delta, predicted_delta != 0 ? predicted_delta : delta, event_time, AverageLagTracker::EventType::ScrollUpdate); event_info.finish_timestamp = frame_time; - - average_lag_tracker_gpu_swap_->AddScrollEventInFrame(event_info); - average_lag_tracker_presentation_->AddScrollEventInFrame(event_info); + average_lag_tracker_->AddScrollEventInFrame(event_info); } void CheckScrollBeginHistograms(int bucket_value, int count) { EXPECT_THAT(histogram_tester().GetAllSamples( - "Event.Latency.ScrollBegin.Touch.AverageLag"), - ElementsAre(Bucket(bucket_value, count))); - EXPECT_THAT(histogram_tester().GetAllSamples( "Event.Latency.ScrollBegin.Touch.AverageLagPresentation"), ElementsAre(Bucket(bucket_value, count))); + + EXPECT_THAT( + histogram_tester().GetAllSamples("Event.Latency.ScrollBegin.Touch." + "AverageLagPresentation.NoPrediction"), + ElementsAre(Bucket(bucket_value, count))); } void CheckScrollUpdateHistograms(int bucket_value, int count) { EXPECT_THAT(histogram_tester().GetAllSamples( - "Event.Latency.ScrollUpdate.Touch.AverageLag"), - ElementsAre(Bucket(bucket_value, count))); - - EXPECT_THAT(histogram_tester().GetAllSamples( "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation"), ElementsAre(Bucket(bucket_value, count))); - } - void CheckPredictionPositiveHistograms(int bucket_value, int count) { EXPECT_THAT( - histogram_tester().GetAllSamples( - "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"), + histogram_tester().GetAllSamples("Event.Latency.ScrollUpdate.Touch." + "AverageLagPresentation.NoPrediction"), ElementsAre(Bucket(bucket_value, count))); + } + void CheckPredictionPositiveHistograms(int bucket_value, int count) { EXPECT_THAT(histogram_tester().GetAllSamples( "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation." "PredictionPositive"), ElementsAre(Bucket(bucket_value, count))); } - void CheckPredictionNegativeHistograms(int bucket_value, int count) { - EXPECT_THAT( - histogram_tester().GetAllSamples( - "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative"), - ElementsAre(Bucket(bucket_value, count))); + void CheckRemainingLagPercentageHistograms(int bucket_value, int count) { + EXPECT_THAT(histogram_tester().GetAllSamples( + "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation." + "RemainingLagPercentage"), + ElementsAre(Bucket(bucket_value, count))); + } + void CheckPredictionNegativeHistograms(int bucket_value, int count) { EXPECT_THAT(histogram_tester().GetAllSamples( "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation." "PredictionNegative"), @@ -106,16 +97,14 @@ class AverageLagTrackerTest : public testing::Test { void CheckScrollUpdateHistogramsTotalCount(int count) { histogram_tester().ExpectTotalCount( - "Event.Latency.ScrollUpdate.Touch.AverageLag", count); - histogram_tester().ExpectTotalCount( "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", count); + histogram_tester().ExpectTotalCount( + "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation.NoPrediction", + count); } void CheckPredictionPositiveHistogramsTotalCount(int count) { histogram_tester().ExpectTotalCount( - "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive", - count); - histogram_tester().ExpectTotalCount( "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation." "PredictionPositive", count); @@ -123,17 +112,13 @@ class AverageLagTrackerTest : public testing::Test { void CheckPredictionNegativeHistogramsTotalCount(int count) { histogram_tester().ExpectTotalCount( - "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", - count); - histogram_tester().ExpectTotalCount( "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation." "PredictionNegative", count); } protected: - std::unique_ptr<AverageLagTracker> average_lag_tracker_gpu_swap_; - std::unique_ptr<AverageLagTracker> average_lag_tracker_presentation_; + std::unique_ptr<AverageLagTracker> average_lag_tracker_; std::unique_ptr<base::HistogramTester> histogram_tester_; }; @@ -190,6 +175,7 @@ TEST_F(AverageLagTrackerTest, OneSecondInterval) { CheckScrollUpdateHistograms(9, 1); CheckPredictionPositiveHistograms(0, 1); CheckPredictionNegativeHistogramsTotalCount(0); + CheckRemainingLagPercentageHistograms(100 - 0, 1); ResetHistograms(); @@ -202,6 +188,7 @@ TEST_F(AverageLagTrackerTest, OneSecondInterval) { CheckScrollUpdateHistograms(8, 1); CheckPredictionPositiveHistograms(0, 1); CheckPredictionNegativeHistogramsTotalCount(0); + CheckRemainingLagPercentageHistograms(100 - 0, 1); } // Test the case that event's frame swap time is later than next event's @@ -376,6 +363,7 @@ TEST_F(AverageLagTrackerTest, ScrollPrediction) { CheckScrollUpdateHistograms(4, 1); CheckPredictionPositiveHistograms(5, 1); CheckPredictionNegativeHistogramsTotalCount(0); + CheckRemainingLagPercentageHistograms(100 * 4.375 / 9.375, 1); } // Test AverageLag with imperfect scroll prediction. @@ -416,6 +404,7 @@ TEST_F(AverageLagTrackerTest, ImperfectScrollPrediction) { // Positive effect of prediction = 4.3px CheckPredictionPositiveHistograms(4, 1); CheckPredictionNegativeHistogramsTotalCount(0); + CheckRemainingLagPercentageHistograms(100 * 5.075 / 9.375, 1); } TEST_F(AverageLagTrackerTest, NegativePredictionEffect) { @@ -455,6 +444,9 @@ TEST_F(AverageLagTrackerTest, NegativePredictionEffect) { // Negative effect of prediction = 11.25 CheckPredictionPositiveHistogramsTotalCount(0); CheckPredictionNegativeHistograms(11, 1); + + // 100 * 20.625 / 9.375 = 220 is logged into bucket 219. + CheckRemainingLagPercentageHistograms(219, 1); } TEST_F(AverageLagTrackerTest, NoPredictionEffect) { diff --git a/chromium/cc/metrics/average_lag_tracking_manager.cc b/chromium/cc/metrics/average_lag_tracking_manager.cc index efca9693ca7..9dc2c4e8027 100644 --- a/chromium/cc/metrics/average_lag_tracking_manager.cc +++ b/chromium/cc/metrics/average_lag_tracking_manager.cc @@ -88,11 +88,8 @@ void AverageLagTrackingManager::DidPresentCompositorFrame( }); for (AverageLagTracker::EventInfo info : infos) { - info.finish_timestamp = frame_details.swap_timings.swap_start; - lag_tracker_gpu_swap_.AddScrollEventInFrame(info); - info.finish_timestamp = frame_details.presentation_feedback.timestamp; - lag_tracker_presentation_.AddScrollEventInFrame(info); + lag_tracker_.AddScrollEventInFrame(info); } } } diff --git a/chromium/cc/metrics/average_lag_tracking_manager.h b/chromium/cc/metrics/average_lag_tracking_manager.h index d694fe789ad..ba2e34f9b4b 100644 --- a/chromium/cc/metrics/average_lag_tracking_manager.h +++ b/chromium/cc/metrics/average_lag_tracking_manager.h @@ -48,17 +48,10 @@ class CC_EXPORT AverageLagTrackingManager { void Clear(); private: - // TODO(https://crbug.com/1101005): Remove GpuSwap implementation after M86. - // Tracker for the AverageLag metrics that uses the gpu swap begin timing as - // an approximation for the time the users sees the frame on the screen. - AverageLagTracker lag_tracker_gpu_swap_{ - AverageLagTracker::FinishTimeType::GpuSwapBegin}; - // Tracker for the AverageLagPresentation metrics that uses the presentation // feedback time as an approximation for the time the users sees the frame on // the screen. - AverageLagTracker lag_tracker_presentation_{ - AverageLagTracker::FinishTimeType::PresentationFeedback}; + AverageLagTracker lag_tracker_; // List of events (vector) per frame (uint32_t |frame_token|) to submit to the // lag trackers when DidPresentCompositorFrame is called for a |frame_token|. diff --git a/chromium/cc/metrics/average_lag_tracking_manager_unittest.cc b/chromium/cc/metrics/average_lag_tracking_manager_unittest.cc index 69931f0a010..10b296f2032 100644 --- a/chromium/cc/metrics/average_lag_tracking_manager_unittest.cc +++ b/chromium/cc/metrics/average_lag_tracking_manager_unittest.cc @@ -157,14 +157,6 @@ TEST_F(AverageLagTrackingManagerTest, OneSecondInterval) { EXPECT_EQ(gpu_swap_time, MillisecondsToTimeTicks(1030)); EXPECT_EQ(presentation_time, MillisecondsToTimeTicks(1033)); - // ScrollBegin AverageLag are the area between the event original component - // (time=15ms, delta=10px) to the gpu swap time (time=20ms, expect finger - // position at delta=15px). The AverageLag scaled to 1 second is - // (0.5*(10px+15px)*5ms)/5ms = 12.5px. - EXPECT_THAT(histogram_tester_->GetAllSamples( - "Event.Latency.ScrollBegin.Touch.AverageLag"), - ElementsAre(Bucket(12, 1))); - // Using the presentation time (25ms) instead of gpu swap (20ms) the expected // finger position is delta = 16px. Then (0.5*(10px+18px)*10ms)/10ms = 14px. // UmaHistogramCounts1000's binning will round it to 12. @@ -172,21 +164,6 @@ TEST_F(AverageLagTrackingManagerTest, OneSecondInterval) { "Event.Latency.ScrollBegin.Touch.AverageLagPresentation"), ElementsAre(Bucket(14, 1))); - // This ScrollUpdate AverageLag are calculated as the finger uniformly scroll - // 10px each frame. For scroll up/down frame, the Lag at the last frame swap - // is 5px, and Lag at this frame swap is 15px. For the one changing direction, - // the Lag is from 5 to 10 and down to 5 again. So total LagArea is 99 * 100, - // plus 75. the AverageLag in 1 second is 9.975px. - EXPECT_THAT(histogram_tester_->GetAllSamples( - "Event.Latency.ScrollUpdate.Touch.AverageLag"), - ElementsAre(Bucket(9, 1))); - EXPECT_THAT( - histogram_tester_->GetAllSamples( - "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"), - ElementsAre(Bucket(0, 1))); - histogram_tester_->ExpectTotalCount( - "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0); - // As the presentation times are at 80% of the gap between 2 scroll events, // the Lag Area between 2 frames is define by the trapezoids: (time=event-2, // delta=8px), (time=event, delta=10px), (time=event+8, delta=18). This makes @@ -209,28 +186,6 @@ TEST_F(AverageLagTrackingManagerTest, OneSecondInterval) { 0); ResetHistograms(); - - // Send another ScrollBegin to end the unfinished ScrollUpdate report. - event_time += base::TimeDelta::FromMilliseconds(10); - gpu_swap_time += base::TimeDelta::FromMilliseconds(10); - presentation_time += base::TimeDelta::FromMilliseconds(10); - evt = PrepareScrollEvent(AverageLagTracker::EventType::ScrollBegin, - event_time, 1, scroll_delta); - average_lag_tracking_manager_.CollectScrollEventsFromFrame( - frame_id, std::vector<ui::LatencyInfo>{evt}); - average_lag_tracking_manager_.DidPresentCompositorFrame( - frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time)); - - // The last ScrollUpdate's lag is 8.75px and truncated to 8. - EXPECT_THAT(histogram_tester_->GetAllSamples( - "Event.Latency.ScrollUpdate.Touch.AverageLag"), - ElementsAre(Bucket(8, 1))); - EXPECT_THAT( - histogram_tester_->GetAllSamples( - "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"), - ElementsAre(Bucket(0, 1))); - histogram_tester_->ExpectTotalCount( - "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0); } // This test creates 3 frames in order to check the submission of ScrollBegin @@ -248,13 +203,6 @@ TEST_F(AverageLagTrackingManagerTest, MultipleEventsInSameFrame) { } // As the first frame is the ScrollBegin frame, the average lag is, using the - // gpu swap time, 0.5*(10 + 40) * 30 / 30 = 25. But UmaHistogramCounts1000's - // binning will round it to 23. - EXPECT_THAT(histogram_tester_->GetAllSamples( - "Event.Latency.ScrollBegin.Touch.AverageLag"), - ElementsAre(Bucket(23, 1))); - - // As the first frame is the ScrollBegin frame, the average lag is, using the // presentation time, 0.5*(10 + 50) * 40 / 40 = 30. But // UmaHistogramCounts1000's binning will round it to 29. EXPECT_THAT(histogram_tester_->GetAllSamples( @@ -264,16 +212,6 @@ TEST_F(AverageLagTrackingManagerTest, MultipleEventsInSameFrame) { // Only the ScrollUpdate events from frame 2 are sent (as the frame 3 is // waiting for the next frame for sumission). // As there is a scroll update right at the same time as the frame submission, - // using gpu swap time, frame 2 starts with 0 lag at 0.4s and finishes with - // 100 at 1.4, thus: 0.5 * (0 + 100) / 2 = 50. It gets into the same bin as - // 47. - EXPECT_THAT(histogram_tester_->GetAllSamples( - "Event.Latency.ScrollUpdate.Touch.AverageLag"), - ElementsAre(Bucket(47, 1))); - - // Only the ScrollUpdate events from frame 2 are sent (as the frame 3 is - // waiting for the next frame for sumission). - // As there is a scroll update right at the same time as the frame submission, // using presentation time, frame 2 starts with 10 lag at 0.5s and finishes // with 110 at 1.5, thus: 0.5 * (10 + 110) / 2 = 60. EXPECT_THAT(histogram_tester_->GetAllSamples( diff --git a/chromium/cc/metrics/begin_main_frame_metrics.cc b/chromium/cc/metrics/begin_main_frame_metrics.cc index 04b6d8bbf3d..effcada55c9 100644 --- a/chromium/cc/metrics/begin_main_frame_metrics.cc +++ b/chromium/cc/metrics/begin_main_frame_metrics.cc @@ -11,4 +11,7 @@ BeginMainFrameMetrics::BeginMainFrameMetrics() = default; BeginMainFrameMetrics::BeginMainFrameMetrics( const BeginMainFrameMetrics& other) = default; +BeginMainFrameMetrics& BeginMainFrameMetrics::operator=( + const BeginMainFrameMetrics& other) = default; + } // namespace cc diff --git a/chromium/cc/metrics/begin_main_frame_metrics.h b/chromium/cc/metrics/begin_main_frame_metrics.h index 1f26384fc21..9514e622bd2 100644 --- a/chromium/cc/metrics/begin_main_frame_metrics.h +++ b/chromium/cc/metrics/begin_main_frame_metrics.h @@ -33,6 +33,7 @@ struct CC_EXPORT BeginMainFrameMetrics { BeginMainFrameMetrics(); BeginMainFrameMetrics(const BeginMainFrameMetrics& other); + BeginMainFrameMetrics& operator=(const BeginMainFrameMetrics& other); }; } // namespace cc diff --git a/chromium/cc/metrics/compositor_frame_reporter.cc b/chromium/cc/metrics/compositor_frame_reporter.cc index 7d2bd0fa57e..375696fa2bf 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.cc +++ b/chromium/cc/metrics/compositor_frame_reporter.cc @@ -9,8 +9,8 @@ #include <string> #include <utility> +#include "base/cxx17_backports.h" #include "base/metrics/histogram_macros.h" -#include "base/stl_util.h" #include "base/strings/strcat.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" @@ -147,7 +147,6 @@ constexpr const char* GetEventLatencyDispatchToCompositorBreakdownName( NOTREACHED(); return nullptr; } - break; case EventMetrics::DispatchStage::kRendererMainFinished: switch (compositor_stage) { case CompositorFrameReporter::StageType:: @@ -171,7 +170,6 @@ constexpr const char* GetEventLatencyDispatchToCompositorBreakdownName( NOTREACHED(); return nullptr; } - break; default: NOTREACHED(); return nullptr; @@ -1044,8 +1042,8 @@ void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const { return; if (IsDroppedFrameAffectingSmoothness()) { - devtools_instrumentation::DidDropSmoothnessFrame(layer_tree_host_id_, - args_.frame_time); + devtools_instrumentation::DidDropSmoothnessFrame( + layer_tree_host_id_, args_.frame_time, args_.frame_id.sequence_number); } const auto trace_track = @@ -1274,6 +1272,12 @@ bool CompositorFrameReporter::IsDroppedFrameAffectingSmoothness() const { return smooth_thread_ != SmoothThread::kSmoothNone; } + // If the frame includes new main-thread update, even if it's for an earlier + // begin-frame, then do not count it as a dropped frame affecting smoothness. + if (is_accompanied_by_main_thread_update_) { + return false; + } + // If the frame was shown, but included only partial updates, then it hurt // smoothness only if the main-thread is affecting smoothness (e.g. running an // animation, or scroll etc.). diff --git a/chromium/cc/metrics/compositor_frame_reporter.h b/chromium/cc/metrics/compositor_frame_reporter.h index 8651f314175..188de0a9e9f 100644 --- a/chromium/cc/metrics/compositor_frame_reporter.h +++ b/chromium/cc/metrics/compositor_frame_reporter.h @@ -256,7 +256,9 @@ class CC_EXPORT CompositorFrameReporter { void AddEventsMetrics(EventMetrics::List events_metrics); EventMetrics::List TakeEventsMetrics(); - int stage_history_size_for_testing() const { return stage_history_.size(); } + size_t stage_history_size_for_testing() const { + return stage_history_.size(); + } void OnFinishImplFrame(base::TimeTicks timestamp); void OnAbortBeginMainFrame(base::TimeTicks timestamp); @@ -302,6 +304,12 @@ class CC_EXPORT CompositorFrameReporter { return owned_partial_update_dependents_.size(); } + void set_is_accompanied_by_main_thread_update( + bool is_accompanied_by_main_thread_update) { + is_accompanied_by_main_thread_update_ = + is_accompanied_by_main_thread_update; + } + const viz::BeginFrameId& frame_id() const { return args_.frame_id; } // Adopts |cloned_reporter|, i.e. keeps |cloned_reporter| alive until after @@ -414,6 +422,9 @@ class CC_EXPORT CompositorFrameReporter { DroppedFrameCounter* dropped_frame_counter_ = nullptr; bool has_partial_update_ = false; + // If the submitted frame has update from main thread + bool is_accompanied_by_main_thread_update_ = false; + const SmoothThread smooth_thread_; const int layer_tree_host_id_; diff --git a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc index 63e046a5f00..0f885408da1 100644 --- a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc @@ -74,13 +74,12 @@ class CompositorFrameReporterTest : public testing::Test { std::unique_ptr<EventMetrics> CreateEventMetrics( ui::EventType type, - absl::optional<EventMetrics::ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type) { + absl::optional<EventMetrics::ScrollParams> scroll_params = + absl::nullopt) { const base::TimeTicks event_time = AdvanceNowByMs(3); AdvanceNowByMs(3); std::unique_ptr<EventMetrics> metrics = EventMetrics::CreateForTesting( - type, scroll_update_type, scroll_input_type, event_time, - &test_tick_clock_); + type, scroll_params, event_time, &test_tick_clock_); if (metrics) { AdvanceNowByMs(3); metrics->SetDispatchStageTimestamp( @@ -133,30 +132,30 @@ TEST_F(CompositorFrameReporterTest, MainFrameAbortedReportingTest) { pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, Now()); - EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); - EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); - EXPECT_EQ(3, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(3u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - EXPECT_EQ(4, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(4u, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount( @@ -178,18 +177,18 @@ TEST_F(CompositorFrameReporterTest, ReplacedByNewReporterReportingTest) { pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, Now()); - EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); - EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter, Now()); - EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0); @@ -202,18 +201,18 @@ TEST_F(CompositorFrameReporterTest, SubmittedFrameReportingTest) { pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kActivation, Now()); - EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1); @@ -238,18 +237,18 @@ TEST_F(CompositorFrameReporterTest, SubmittedDroppedFrameReportingTest) { pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); - EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, Now()); - EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); + EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount( @@ -277,9 +276,9 @@ TEST_F(CompositorFrameReporterTest, base::HistogramTester histogram_tester; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - CreateEventMetrics(ui::ET_TOUCH_PRESSED, absl::nullopt, absl::nullopt), - CreateEventMetrics(ui::ET_TOUCH_MOVED, absl::nullopt, absl::nullopt), - CreateEventMetrics(ui::ET_TOUCH_MOVED, absl::nullopt, absl::nullopt), + CreateEventMetrics(ui::ET_TOUCH_PRESSED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( @@ -329,17 +328,23 @@ TEST_F(CompositorFrameReporterTest, const base::HistogramBase::Sample latency_ms; } expected_latencies[] = { {"EventLatency.TouchPressed.TotalLatency", - (presentation_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[0]).InMicroseconds())}, {"EventLatency.TouchMoved.TotalLatency", - (presentation_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[1]).InMicroseconds())}, {"EventLatency.TouchMoved.TotalLatency", - (presentation_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[2]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[0]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[1]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[2]).InMicroseconds())}, }; for (const auto& expected_latency : expected_latencies) { histogram_tester.ExpectBucketCount(expected_latency.name, @@ -353,15 +358,24 @@ TEST_F(CompositorFrameReporterTest, EventLatencyScrollTotalForPresentedFrameReported) { base::HistogramTester histogram_tester; + const bool kIsInertial = true; + const bool kIsNotInertial = false; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, absl::nullopt, - ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, + EventMetrics::ScrollParams(ui::ScrollInputType::kWheel, + kIsNotInertial)), CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kStarted, - ui::ScrollInputType::kWheel), + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsNotInertial, + EventMetrics::ScrollUpdateType::kStarted)), CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kContinued, - ui::ScrollInputType::kWheel), + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsNotInertial, + EventMetrics::ScrollUpdateType::kContinued)), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsInertial, + EventMetrics::ScrollUpdateType::kContinued)), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( @@ -406,7 +420,10 @@ TEST_F(CompositorFrameReporterTest, 1}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", 1}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", 1}, - {"EventLatency.TotalLatency", 3}, + {"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatency", 1}, + {"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", + 1}, + {"EventLatency.TotalLatency", 4}, }; for (const auto& expected_count : expected_counts) { histogram_tester.ExpectTotalCount(expected_count.name, @@ -421,17 +438,29 @@ TEST_F(CompositorFrameReporterTest, const base::HistogramBase::Sample latency_ms; } expected_latencies[] = { {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", - (presentation_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[0]).InMicroseconds())}, {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapBegin", - (swap_begin_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (swap_begin_time - event_times[0]).InMicroseconds())}, {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency", - (presentation_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[1]).InMicroseconds())}, {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", - (swap_begin_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (swap_begin_time - event_times[1]).InMicroseconds())}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", - (presentation_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[2]).InMicroseconds())}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", - (swap_begin_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (swap_begin_time - event_times[2]).InMicroseconds())}, + {"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatency", + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[3]).InMicroseconds())}, + {"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", + static_cast<base::HistogramBase::Sample>( + (swap_begin_time - event_times[3]).InMicroseconds())}, }; for (const auto& expected_latency : expected_latencies) { histogram_tester.ExpectBucketCount(expected_latency.name, @@ -446,9 +475,9 @@ TEST_F(CompositorFrameReporterTest, base::HistogramTester histogram_tester; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - CreateEventMetrics(ui::ET_TOUCH_PRESSED, absl::nullopt, absl::nullopt), - CreateEventMetrics(ui::ET_TOUCH_MOVED, absl::nullopt, absl::nullopt), - CreateEventMetrics(ui::ET_TOUCH_MOVED, absl::nullopt, absl::nullopt), + CreateEventMetrics(ui::ET_TOUCH_PRESSED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.cc b/chromium/cc/metrics/compositor_frame_reporting_controller.cc index 5cd49b99786..8ccda7ea94e 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller.cc @@ -296,6 +296,8 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame( impl_reporter->AddEventsMetrics( std::move(events_metrics.impl_event_metrics)); impl_reporter->set_has_missing_content(has_missing_content); + impl_reporter->set_is_accompanied_by_main_thread_update( + is_activated_frame_new); submitted_compositor_frames_.emplace_back(frame_token, std::move(impl_reporter)); } diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc index 351e0e236f3..30cf759aa00 100644 --- a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc +++ b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc @@ -239,13 +239,12 @@ class CompositorFrameReportingControllerTest : public testing::Test { std::unique_ptr<EventMetrics> CreateEventMetrics( ui::EventType type, - absl::optional<EventMetrics::ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type) { + absl::optional<EventMetrics::ScrollParams> scroll_params = + absl::nullopt) { const base::TimeTicks event_time = AdvanceNowByMs(10); AdvanceNowByMs(10); std::unique_ptr<EventMetrics> metrics = EventMetrics::CreateForTesting( - type, scroll_update_type, scroll_input_type, event_time, - &test_tick_clock_); + type, scroll_params, event_time, &test_tick_clock_); if (metrics) { AdvanceNowByMs(10); metrics->SetDispatchStageTimestamp( @@ -1123,9 +1122,9 @@ TEST_F(CompositorFrameReportingControllerTest, base::HistogramTester histogram_tester; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - CreateEventMetrics(ui::ET_TOUCH_PRESSED, absl::nullopt, absl::nullopt), - CreateEventMetrics(ui::ET_TOUCH_MOVED, absl::nullopt, absl::nullopt), - CreateEventMetrics(ui::ET_TOUCH_MOVED, absl::nullopt, absl::nullopt), + CreateEventMetrics(ui::ET_TOUCH_PRESSED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( @@ -1162,17 +1161,23 @@ TEST_F(CompositorFrameReportingControllerTest, const base::HistogramBase::Sample latency_ms; } expected_latencies[] = { {"EventLatency.TouchPressed.TotalLatency", - (presentation_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[0]).InMicroseconds())}, {"EventLatency.TouchMoved.TotalLatency", - (presentation_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[1]).InMicroseconds())}, {"EventLatency.TouchMoved.TotalLatency", - (presentation_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[2]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[0]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[1]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[2]).InMicroseconds())}, }; for (const auto& expected_latency : expected_latencies) { histogram_tester.ExpectBucketCount(expected_latency.name, @@ -1186,15 +1191,24 @@ TEST_F(CompositorFrameReportingControllerTest, EventLatencyScrollTotalForPresentedFrameReported) { base::HistogramTester histogram_tester; + const bool kIsInertial = true; + const bool kIsNotInertial = false; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, absl::nullopt, - ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, + EventMetrics::ScrollParams(ui::ScrollInputType::kWheel, + kIsNotInertial)), CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kStarted, - ui::ScrollInputType::kWheel), + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsNotInertial, + EventMetrics::ScrollUpdateType::kStarted)), CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kContinued, - ui::ScrollInputType::kWheel), + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsNotInertial, + EventMetrics::ScrollUpdateType::kContinued)), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsInertial, + EventMetrics::ScrollUpdateType::kContinued)), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( @@ -1227,7 +1241,10 @@ TEST_F(CompositorFrameReportingControllerTest, 1}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", 1}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", 1}, - {"EventLatency.TotalLatency", 3}, + {"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatency", 1}, + {"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", + 1}, + {"EventLatency.TotalLatency", 4}, }; for (const auto& expected_count : expected_counts) { histogram_tester.ExpectTotalCount(expected_count.name, @@ -1242,17 +1259,29 @@ TEST_F(CompositorFrameReportingControllerTest, const base::HistogramBase::Sample latency_ms; } expected_latencies[] = { {"EventLatency.GestureScrollBegin.Wheel.TotalLatency", - (presentation_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[0]).InMicroseconds())}, {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapBegin", - (swap_begin_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (swap_begin_time - event_times[0]).InMicroseconds())}, {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency", - (presentation_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[1]).InMicroseconds())}, {"EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", - (swap_begin_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (swap_begin_time - event_times[1]).InMicroseconds())}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", - (presentation_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[2]).InMicroseconds())}, {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", - (swap_begin_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (swap_begin_time - event_times[2]).InMicroseconds())}, + {"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatency", + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[3]).InMicroseconds())}, + {"EventLatency.InertialGestureScrollUpdate.Wheel.TotalLatencyToSwapBegin", + static_cast<base::HistogramBase::Sample>( + (swap_begin_time - event_times[3]).InMicroseconds())}, }; for (const auto& expected_latency : expected_latencies) { histogram_tester.ExpectBucketCount(expected_latency.name, @@ -1267,9 +1296,9 @@ TEST_F(CompositorFrameReportingControllerTest, base::HistogramTester histogram_tester; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - CreateEventMetrics(ui::ET_TOUCH_PRESSED, absl::nullopt, absl::nullopt), - CreateEventMetrics(ui::ET_TOUCH_MOVED, absl::nullopt, absl::nullopt), - CreateEventMetrics(ui::ET_TOUCH_MOVED, absl::nullopt, absl::nullopt), + CreateEventMetrics(ui::ET_TOUCH_PRESSED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), + CreateEventMetrics(ui::ET_TOUCH_MOVED), }; EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); EventMetrics::List events_metrics( @@ -1311,17 +1340,23 @@ TEST_F(CompositorFrameReportingControllerTest, const base::HistogramBase::Sample latency_ms; } expected_latencies[] = { {"EventLatency.TouchPressed.TotalLatency", - (presentation_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[0]).InMicroseconds())}, {"EventLatency.TouchMoved.TotalLatency", - (presentation_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[1]).InMicroseconds())}, {"EventLatency.TouchMoved.TotalLatency", - (presentation_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[2]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[0]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[0]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[1]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[1]).InMicroseconds())}, {"EventLatency.TotalLatency", - (presentation_time - event_times[2]).InMicroseconds()}, + static_cast<base::HistogramBase::Sample>( + (presentation_time - event_times[2]).InMicroseconds())}, }; for (const auto& expected_latency : expected_latencies) { histogram_tester.ExpectBucketCount(expected_latency.name, @@ -1365,7 +1400,7 @@ TEST_F(CompositorFrameReportingControllerTest, // R2C has been presented, but it is blocked on R2M to know whether R2C // contains partial update, or complete updates. So it is kept alive. EXPECT_EQ(2u, dropped_counter_.total_frames()); - EXPECT_EQ(1u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(1u, dropped_counter_.total_partial()); EXPECT_EQ(1u, reporting_controller_.GetBlockingReportersCount()); EXPECT_EQ(1u, reporting_controller_.GetBlockedReportersCount()); @@ -1402,7 +1437,7 @@ TEST_F(CompositorFrameReportingControllerTest, // At this point no frame has been completed, yet. EXPECT_EQ(0u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); // Start yet another frame that has impl-thread update and submit it, but with // failed presentation. The reporter for this frame should become dependent of @@ -1427,7 +1462,7 @@ TEST_F(CompositorFrameReportingControllerTest, // At this point 1 frame has been completed and it's a dropped frame. EXPECT_EQ(1u, dropped_counter_.total_frames()); - EXPECT_EQ(1u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(1u, dropped_counter_.total_dropped()); reporting_controller_.ResetReporters(); reporting_controller_.SetDroppedFrameCounter(nullptr); @@ -1438,13 +1473,13 @@ TEST_F(CompositorFrameReportingControllerTest, // Submit and present two compositor frames. SimulatePresentCompositorFrame(); EXPECT_EQ(1u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); SimulatePresentCompositorFrame(); EXPECT_EQ(2u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); // Now skip over a few frames, and submit + present another frame. const uint32_t kSkipFrames = 5; @@ -1452,8 +1487,8 @@ TEST_F(CompositorFrameReportingControllerTest, IncrementCurrentId(); SimulatePresentCompositorFrame(); EXPECT_EQ(3u + kSkipFrames, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(kSkipFrames, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(kSkipFrames, dropped_counter_.total_dropped()); // Stop requesting frames, skip over a few frames, and submit + present // another frame. There should no new dropped frames. @@ -1463,8 +1498,8 @@ TEST_F(CompositorFrameReportingControllerTest, IncrementCurrentId(); SimulatePresentCompositorFrame(); EXPECT_EQ(1u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); reporting_controller_.ResetReporters(); reporting_controller_.SetDroppedFrameCounter(nullptr); @@ -1475,13 +1510,13 @@ TEST_F(CompositorFrameReportingControllerTest, // Submit and present two compositor frames. SimulatePresentCompositorFrame(); EXPECT_EQ(1u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); SimulatePresentCompositorFrame(); EXPECT_EQ(2u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); // Now skip over a 101 frames (It should be ignored as it more than 100) // and submit + present another frame. @@ -1491,8 +1526,8 @@ TEST_F(CompositorFrameReportingControllerTest, IncrementCurrentId(); SimulatePresentCompositorFrame(); EXPECT_EQ(3u + kSkipFramesActual, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(kSkipFramesActual, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(kSkipFramesActual, dropped_counter_.total_dropped()); } TEST_F(CompositorFrameReportingControllerTest, @@ -1512,7 +1547,7 @@ TEST_F(CompositorFrameReportingControllerTest, reporting_controller_.WillBeginImplFrame(args_1); reporting_controller_.WillBeginMainFrame(args_1); reporting_controller_.OnFinishImplFrame(current_id_1); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); reporting_controller_.DidNotProduceFrame(args_1.frame_id, FrameSkippedReason::kWaitingOnMain); @@ -1530,23 +1565,23 @@ TEST_F(CompositorFrameReportingControllerTest, EXPECT_EQ(3u, reporting_controller_.GetBlockedReportersCount()); // All frames are waiting for the main frame - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); EXPECT_EQ(0u, dropped_counter_.total_frames()); reporting_controller_.BeginMainFrameAborted( args_1.frame_id, CommitEarlyOutReason::FINISHED_NO_UPDATES); reporting_controller_.DidNotProduceFrame(args_1.frame_id, FrameSkippedReason::kNoDamage); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); // New reporters replace older reporters reporting_controller_.WillBeginImplFrame(args_4); reporting_controller_.WillBeginMainFrame(args_4); EXPECT_EQ(4u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); } TEST_F(CompositorFrameReportingControllerTest, @@ -1559,13 +1594,13 @@ TEST_F(CompositorFrameReportingControllerTest, // Submit and present two compositor frames. SimulatePresentCompositorFrame(); EXPECT_EQ(1u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); SimulatePresentCompositorFrame(); EXPECT_EQ(2u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); // Now skip over a few frames, and submit + present another frame. const uint32_t kSkipFrames_1 = 5; @@ -1573,8 +1608,8 @@ TEST_F(CompositorFrameReportingControllerTest, IncrementCurrentId(); SimulatePresentCompositorFrame(); EXPECT_EQ(3u + kSkipFrames_1, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_dropped()); EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_smoothness_dropped()); // Now skip over a few frames which are not affecting smoothness. @@ -1586,9 +1621,8 @@ TEST_F(CompositorFrameReportingControllerTest, SimulatePresentCompositorFrame(); // Present another frame. EXPECT_EQ(4u + kSkipFrames_1 + kSkipFrames_2, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2, - dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2, dropped_counter_.total_dropped()); EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_smoothness_dropped()); // Now skip over a few frames more frames which are affecting smoothness. @@ -1600,9 +1634,9 @@ TEST_F(CompositorFrameReportingControllerTest, SimulatePresentCompositorFrame(); // Present another frame. EXPECT_EQ(5u + kSkipFrames_1 + kSkipFrames_2 + kSkipFrames_3, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); EXPECT_EQ(kSkipFrames_1 + kSkipFrames_2 + kSkipFrames_3, - dropped_counter_.total_compositor_dropped()); + dropped_counter_.total_dropped()); EXPECT_EQ(kSkipFrames_1 + kSkipFrames_3, dropped_counter_.total_smoothness_dropped()); } @@ -1612,13 +1646,13 @@ TEST_F(CompositorFrameReportingControllerTest, // Submit and present two compositor frames. SimulatePresentCompositorFrame(); EXPECT_EQ(1u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); SimulatePresentCompositorFrame(); EXPECT_EQ(2u, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); // Now skip over a few frames, and submit + present another frame. const uint32_t kTotalFrames = 5; @@ -1629,9 +1663,8 @@ TEST_F(CompositorFrameReportingControllerTest, SimulatePresentCompositorFrame(); EXPECT_EQ(3u + kTotalFrames - kThrottledFrames, dropped_counter_.total_frames()); - EXPECT_EQ(0u, dropped_counter_.total_main_dropped()); - EXPECT_EQ(kTotalFrames - kThrottledFrames, - dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_partial()); + EXPECT_EQ(kTotalFrames - kThrottledFrames, dropped_counter_.total_dropped()); } TEST_F(CompositorFrameReportingControllerTest, @@ -1644,7 +1677,7 @@ TEST_F(CompositorFrameReportingControllerTest, reporting_controller_.BeginMainFrameAborted( current_id_, CommitEarlyOutReason::FINISHED_NO_UPDATES); } - EXPECT_EQ(0u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(0u, dropped_counter_.total_dropped()); // Start a few begin-main-frames, but abort the main-frames due to no damage. for (int i = 0; i < 5; ++i) { @@ -1656,7 +1689,7 @@ TEST_F(CompositorFrameReportingControllerTest, SimulateSubmitCompositorFrame({}); } SimulatePresentCompositorFrame(); - EXPECT_EQ(5u, dropped_counter_.total_compositor_dropped()); + EXPECT_EQ(5u, dropped_counter_.total_dropped()); } // Verifies that presentation feedbacks that arrive out of order are handled @@ -1680,7 +1713,7 @@ TEST_F(CompositorFrameReportingControllerTest, gfx::PresentationFeedback::kFailure}; reporting_controller_.DidPresentCompositorFrame(frame_token_2, details_2); DCHECK_EQ(1u, dropped_counter_.total_frames()); - DCHECK_EQ(1u, dropped_counter_.total_compositor_dropped()); + DCHECK_EQ(1u, dropped_counter_.total_dropped()); // Send a successful presentation feedback for frame 3. This should drop frame // 1. @@ -1688,7 +1721,53 @@ TEST_F(CompositorFrameReportingControllerTest, details_3.presentation_feedback.timestamp = AdvanceNowByMs(10); reporting_controller_.DidPresentCompositorFrame(frame_token_3, details_3); DCHECK_EQ(3u, dropped_counter_.total_frames()); - DCHECK_EQ(2u, dropped_counter_.total_compositor_dropped()); + DCHECK_EQ(2u, dropped_counter_.total_dropped()); +} + +TEST_F(CompositorFrameReportingControllerTest, + NewMainThreadUpdateNotReportedAsDropped) { + auto thread_type_main = FrameSequenceMetrics::ThreadType::kMain; + reporting_controller_.SetThreadAffectsSmoothness(thread_type_main, + /*affects_smoothness=*/true); + dropped_counter_.OnFcpReceived(); + + SimulateBeginMainFrame(); + reporting_controller_.OnFinishImplFrame(current_id_); + reporting_controller_.DidSubmitCompositorFrame(1u, current_id_, {}, {}, + /*has_missing_content=*/false); + viz::FrameTimingDetails details = {}; + details.presentation_feedback.timestamp = AdvanceNowByMs(10); + reporting_controller_.DidPresentCompositorFrame(1u, details); + // Starts a new frame and submit it prior to commit + + reporting_controller_.WillCommit(); + reporting_controller_.DidCommit(); + + const auto previous_id = current_id_; + + SimulateBeginMainFrame(); + reporting_controller_.OnFinishImplFrame(current_id_); + + // Starts a new frame and submit it prior to its commit, but the older frame + // has new updates which would be activated and submitted now. + reporting_controller_.WillActivate(); + reporting_controller_.DidActivate(); + + reporting_controller_.DidSubmitCompositorFrame( + 1u, current_id_, previous_id, {}, /*has_missing_content=*/false); + details.presentation_feedback.timestamp = AdvanceNowByMs(10); + reporting_controller_.DidPresentCompositorFrame(1u, details); + + reporting_controller_.WillCommit(); + reporting_controller_.DidCommit(); + + SimulatePresentCompositorFrame(); + + // There are two frames with partial updates + EXPECT_EQ(2u, dropped_counter_.total_partial()); + // Which one is accompanied with new main thread update so only one affects + // smoothness + EXPECT_EQ(1u, dropped_counter_.total_smoothness_dropped()); } } // namespace diff --git a/chromium/cc/metrics/compositor_timing_history.cc b/chromium/cc/metrics/compositor_timing_history.cc index e8b1969dc2f..c8c2bb6a766 100644 --- a/chromium/cc/metrics/compositor_timing_history.cc +++ b/chromium/cc/metrics/compositor_timing_history.cc @@ -10,9 +10,9 @@ #include <utility> #include <vector> +#include "base/cxx17_backports.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" #include "cc/debug/rendering_stats_instrumentation.h" @@ -39,6 +39,9 @@ class CompositorTimingHistory::UMAReporter { // Only the renderer would get the meaningful data. virtual void AddDrawIntervalWithCustomPropertyAnimations( base::TimeDelta duration) = 0; + + virtual void AddImplFrameDeadlineType( + CompositorTimingHistory::DeadlineMode deadline_mode) = 0; }; namespace { @@ -323,6 +326,12 @@ class RendererUMAReporter : public CompositorTimingHistory::UMAReporter { UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Renderer.DrawDuration", duration); } + + void AddImplFrameDeadlineType( + CompositorTimingHistory::DeadlineMode deadline_mode) override { + UMA_HISTOGRAM_ENUMERATION("Scheduling.Renderer.DeadlineMode", + deadline_mode); + } }; class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { @@ -360,6 +369,12 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Browser.DrawDuration", duration); } + + void AddImplFrameDeadlineType( + CompositorTimingHistory::DeadlineMode deadline_mode) override { + // The browser compositor scheduler is synchronous and only has None (or + // Blocked as edges cases) for deadline mode. + } }; class NullUMAReporter : public CompositorTimingHistory::UMAReporter { @@ -375,6 +390,8 @@ class NullUMAReporter : public CompositorTimingHistory::UMAReporter { base::TimeDelta duration, TreePriority priority) override {} void AddDrawDuration(base::TimeDelta duration) override {} + void AddImplFrameDeadlineType( + CompositorTimingHistory::DeadlineMode deadline_mode) override {} }; } // namespace @@ -511,15 +528,6 @@ CompositorTimingHistory::BeginMainFrameQueueToActivateCriticalEstimate() const { BeginMainFrameQueueDurationCriticalEstimate(); } -base::TimeDelta -CompositorTimingHistory::BeginMainFrameQueueToActivateNotCriticalEstimate() - const { - return BeginMainFrameStartToReadyToCommitDurationEstimate() + - CommitDurationEstimate() + CommitToReadyToActivateDurationEstimate() + - ActivateDurationEstimate() + - BeginMainFrameQueueDurationNotCriticalEstimate(); -} - void CompositorTimingHistory::WillBeginImplFrame( const viz::BeginFrameArgs& args, base::TimeTicks now) { @@ -729,12 +737,14 @@ void CompositorTimingHistory::DidDraw(bool used_new_active_tree, // Emit a trace event to highlight a long time lapse between the draw times // of back-to-back BeginImplFrames. if (draw_interval > kDrawIntervalTraceThreshold) { - TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0( + TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( "latency", "Long Draw Interval", - TRACE_ID_LOCAL(g_num_long_draw_intervals), draw_start_time_); - TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0( + TRACE_ID_WITH_SCOPE("Long Draw Interval", g_num_long_draw_intervals), + draw_start_time_); + TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( "latency", "Long Draw Interval", - TRACE_ID_LOCAL(g_num_long_draw_intervals), draw_end_time); + TRACE_ID_WITH_SCOPE("Long Draw Interval", g_num_long_draw_intervals), + draw_end_time); g_num_long_draw_intervals++; } if (has_custom_property_animations && @@ -754,6 +764,11 @@ void CompositorTimingHistory::SetTreePriority(TreePriority priority) { tree_priority_ = priority; } +void CompositorTimingHistory::RecordDeadlineMode(DeadlineMode deadline_mode) { + if (uma_reporter_) + uma_reporter_->AddImplFrameDeadlineType(deadline_mode); +} + void CompositorTimingHistory::ClearHistory() { TRACE_EVENT0("cc,benchmark", "CompositorTimingHistory::ClearHistory"); @@ -767,5 +782,4 @@ void CompositorTimingHistory::ClearHistory() { activate_duration_history_.Clear(); draw_duration_history_.Clear(); } - } // namespace cc diff --git a/chromium/cc/metrics/compositor_timing_history.h b/chromium/cc/metrics/compositor_timing_history.h index 93fc2091905..8d01636964d 100644 --- a/chromium/cc/metrics/compositor_timing_history.h +++ b/chromium/cc/metrics/compositor_timing_history.h @@ -60,7 +60,6 @@ class CC_EXPORT CompositorTimingHistory { base::TimeDelta BeginMainFrameStartToReadyToCommitCriticalEstimate() const; base::TimeDelta BeginMainFrameStartToReadyToCommitNotCriticalEstimate() const; base::TimeDelta BeginMainFrameQueueToActivateCriticalEstimate() const; - base::TimeDelta BeginMainFrameQueueToActivateNotCriticalEstimate() const; // State that affects when events should be expected/recorded/reported. void SetRecordingEnabled(bool enabled); @@ -87,18 +86,15 @@ class CC_EXPORT CompositorTimingHistory { void WillInvalidateOnImplSide(); void SetTreePriority(TreePriority priority); + // Record the scheduler's deadline mode and send to UMA. + using DeadlineMode = SchedulerStateMachine::BeginImplFrameDeadlineMode; + void RecordDeadlineMode(DeadlineMode deadline_mode); + base::TimeTicks begin_main_frame_sent_time() const { return begin_main_frame_sent_time_; } void ClearHistory(); - size_t begin_main_frame_start_to_ready_to_commit_sample_count() const { - return begin_main_frame_start_to_ready_to_commit_duration_history_ - .sample_count(); - } - size_t commit_to_ready_to_activate_sample_count() const { - return commit_to_ready_to_activate_duration_history_.sample_count(); - } protected: void DidBeginMainFrame(base::TimeTicks begin_main_frame_end_time); diff --git a/chromium/cc/metrics/dropped_frame_counter.cc b/chromium/cc/metrics/dropped_frame_counter.cc index 339a3086528..21ab611d9b1 100644 --- a/chromium/cc/metrics/dropped_frame_counter.cc +++ b/chromium/cc/metrics/dropped_frame_counter.cc @@ -98,6 +98,11 @@ void DroppedFrameCounter::AddDroppedFrame() { } void DroppedFrameCounter::ResetPendingFrames(base::TimeTicks timestamp) { + // Start with flushing the frames in frame_sorter ignoring the currently + // pending frames (In other words calling NotifyFrameResult and update + // smoothness metrics tracked for all frames that have received their ack). + frame_sorter_.Reset(); + // Before resetting the pending frames, update the measurements for the // sliding windows. if (!latest_sliding_window_start_.is_null()) { @@ -119,6 +124,15 @@ void DroppedFrameCounter::ResetPendingFrames(base::TimeTicks timestamp) { double percent_dropped_frame = std::min( (dropped_frame_count_in_window_ * 100.0) / total_frames_in_window_, 100.0); + // TODO(jonross): we have divergent calculations for the sliding window + // between here and NotifyFrameResult. We should merge them to avoid + // inconsistencies in calculations. (https://crbug.com/1225307) + if (percent_dropped_frame > sliding_window_max_percent_dropped_) { + time_max_delta_ = args.frame_time - time_fcp_received_; + sliding_window_max_percent_dropped_ = percent_dropped_frame; + } + UpdateMaxPercentDroppedFrame(percent_dropped_frame); + sliding_window_histogram_.AddPercentDroppedFrame(percent_dropped_frame, /*count=*/1); } @@ -141,7 +155,6 @@ void DroppedFrameCounter::ResetPendingFrames(base::TimeTicks timestamp) { sliding_window_ = {}; latest_sliding_window_start_ = {}; latest_sliding_window_interval_ = {}; - frame_sorter_.Reset(); } void DroppedFrameCounter::OnBeginFrame(const viz::BeginFrameArgs& args, @@ -271,6 +284,7 @@ void DroppedFrameCounter::SetUkmSmoothnessDestination( } void DroppedFrameCounter::Reset() { + frame_sorter_.Reset(); total_frames_ = 0; total_partial_ = 0; total_dropped_ = 0; @@ -285,7 +299,6 @@ void DroppedFrameCounter::Reset() { latest_sliding_window_start_ = {}; sliding_window_histogram_.Clear(); ring_buffer_.Clear(); - frame_sorter_.Reset(); time_max_delta_ = {}; last_reported_metrics_ = {}; } diff --git a/chromium/cc/metrics/dropped_frame_counter.h b/chromium/cc/metrics/dropped_frame_counter.h index 218e1cf1c17..8e403e6c14e 100644 --- a/chromium/cc/metrics/dropped_frame_counter.h +++ b/chromium/cc/metrics/dropped_frame_counter.h @@ -51,8 +51,8 @@ class CC_EXPORT DroppedFrameCounter { size_t frame_history_size() const { return ring_buffer_.BufferSize(); } size_t total_frames() const { return total_frames_; } - size_t total_compositor_dropped() const { return total_dropped_; } - size_t total_main_dropped() const { return total_partial_; } + size_t total_dropped() const { return total_dropped_; } + size_t total_partial() const { return total_partial_; } size_t total_smoothness_dropped() const { return total_smoothness_dropped_; } uint32_t GetAverageThroughput() const; diff --git a/chromium/cc/metrics/dropped_frame_counter_unittest.cc b/chromium/cc/metrics/dropped_frame_counter_unittest.cc index c0a1f24da17..2e7b9012dc1 100644 --- a/chromium/cc/metrics/dropped_frame_counter_unittest.cc +++ b/chromium/cc/metrics/dropped_frame_counter_unittest.cc @@ -58,14 +58,15 @@ class DroppedFrameCounterTestBase : public LayerTreeTest { // in slower machines, slower builds such as asan/tsan builds, etc.), since // the test does not strictly control both threads and deadlines. Therefore, // it is not possible to check for strict equality here. - EXPECT_LE(expect_.min_dropped_main, dropped_main_); - EXPECT_LE(expect_.min_dropped_compositor, dropped_compositor_); + EXPECT_LE(expect_.min_partial, partial_); + EXPECT_LE(expect_.min_dropped, dropped_); EXPECT_LE(expect_.min_dropped_smoothness, dropped_smoothness_); } // Compositor thread function overrides: void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { if (TestEnded()) return; @@ -112,8 +113,8 @@ class DroppedFrameCounterTestBase : public LayerTreeTest { DCHECK(dropped_frame_counter); total_frames_ = dropped_frame_counter->total_frames(); - dropped_main_ = dropped_frame_counter->total_main_dropped(); - dropped_compositor_ = dropped_frame_counter->total_compositor_dropped(); + partial_ = dropped_frame_counter->total_partial(); + dropped_ = dropped_frame_counter->total_dropped(); dropped_smoothness_ = dropped_frame_counter->total_smoothness_dropped(); EndTest(); } @@ -168,8 +169,8 @@ class DroppedFrameCounterTestBase : public LayerTreeTest { // remains unchanged after that. So it is safe to read these fields from // either threads. struct TestExpectation { - uint32_t min_dropped_main = 0; - uint32_t min_dropped_compositor = 0; + uint32_t min_partial = 0; + uint32_t min_dropped = 0; uint32_t min_dropped_smoothness = 0; } expect_; @@ -193,8 +194,8 @@ class DroppedFrameCounterTestBase : public LayerTreeTest { // of frames have been processed. These fields are subsequently compared // against the expectation after the test ends. uint32_t total_frames_ = 0; - uint32_t dropped_main_ = 0; - uint32_t dropped_compositor_ = 0; + uint32_t partial_ = 0; + uint32_t dropped_ = 0; uint32_t dropped_smoothness_ = 0; bool skip_main_thread_next_frame_ = false; @@ -208,8 +209,8 @@ class DroppedFrameCounterNoDropTest : public DroppedFrameCounterTestBase { config_.animation_frames = 28; config_.should_register_main_thread_animation = false; - expect_.min_dropped_main = 0; - expect_.min_dropped_compositor = 0; + expect_.min_partial = 0; + expect_.min_dropped = 0; expect_.min_dropped_smoothness = 0; } }; @@ -226,7 +227,7 @@ class DroppedFrameCounterMainDropsNoSmoothness config_.should_drop_main_every = 5; config_.should_register_main_thread_animation = false; - expect_.min_dropped_main = 5; + expect_.min_partial = 5; expect_.min_dropped_smoothness = 0; } }; @@ -244,7 +245,7 @@ class DroppedFrameCounterMainDropsSmoothnessTest config_.should_drop_main_every = 5; config_.should_register_main_thread_animation = true; - expect_.min_dropped_main = 5; + expect_.min_partial = 5; expect_.min_dropped_smoothness = 5; } }; @@ -273,6 +274,15 @@ class DroppedFrameCounterTest : public testing::Test { } } + void SimulatePendingFrame(int repeat) { + for (int i = 0; i < repeat; i++) { + viz::BeginFrameArgs args_ = SimulateBeginFrameArgs(); + dropped_frame_counter_.OnBeginFrame(args_, /*is_scroll_active=*/false); + sequence_number_++; + frame_time_ += interval_; + } + } + void AdvancetimeByIntervals(int interval_count) { frame_time_ += interval_ * interval_count; } @@ -498,5 +508,97 @@ TEST_F(DroppedFrameCounterTest, Percentile95WithIdleFramesThenHide) { EXPECT_GT(histogram->GetPercentDroppedFramePercentile(0.97), 0u); } +// Tests that when ResetPendingFrames updates the sliding window, that the max +// PercentDroppedFrames is also updated accordingly. (https://crbug.com/1225307) +TEST_F(DroppedFrameCounterTest, + ResetPendingFramesUpdatesMaxPercentDroppedFrames) { + // This tests a scenario where gaps in frame production lead to having + // leftover frames in the sliding window for calculations of + // ResetPendingFrames. + // + // Testing for when those frames are sufficient to change the current maximum + // PercentDroppedFrames. + // + // This has been first seen in GpuCrash_InfoForDualHardwareGpus which forces + // a GPU crash. Introducing long periods of idle while the Renderer waits for + // a new GPU Process. (https://crbug.com/1164647) + + // Set an interval that rounds up nicely with 1 second. + constexpr auto kInterval = base::TimeDelta::FromMilliseconds(10); + constexpr size_t kFps = base::TimeDelta::FromSeconds(1) / kInterval; + SetInterval(kInterval); + + // One good frame + SimulateFrameSequence({false}, 1); + // Advance 1s so that when we process the first window, we go from having + // enough frames in the interval, to no longer having enough. + AdvancetimeByIntervals(kFps); + + // The first frame should fill up the sliding window. It isn't dropped, so + // there should be 0 dropped frames. This will pop the first reported frame. + // The second frame is dropped, however we are now tracking less frames than + // the 1s window. So we won't use it in calculations yet. + SimulateFrameSequence({false, true}, 1); + EXPECT_EQ(dropped_frame_counter_.sliding_window_max_percent_dropped(), 0u); + + // Advance 1s so that we will attempt to update the window when resetting the + // pending frames. The pending dropped frame above should be calculated here, + // and both the max and 95th percentile should be updated. + AdvancetimeByIntervals(kFps); + dropped_frame_counter_.ResetPendingFrames(GetNextFrameTime()); + + EXPECT_EQ(dropped_frame_counter_.sliding_window_max_percent_dropped(), + dropped_frame_counter_.SlidingWindow95PercentilePercentDropped()); + EXPECT_GT(dropped_frame_counter_.sliding_window_max_percent_dropped(), 0u); +} + +TEST_F(DroppedFrameCounterTest, ResetPendingFramesAccountingForPendingFrames) { + // Set an interval that rounds up nicely with 1 second. + constexpr auto kInterval = base::TimeDelta::FromMilliseconds(10); + constexpr size_t kFps = base::TimeDelta::FromSeconds(1) / kInterval; + SetInterval(kInterval); + + // First 2 seconds with 20% dropped frames. + SimulateFrameSequence({false, false, false, false, true}, (kFps / 5) * 2); + + // Have a pending frame which would hold the frames in queue. + SimulatePendingFrame(1); + + // One second with 40% dropped frames. + SimulateFrameSequence({false, false, false, true, true}, (kFps / 5)); + + // On the first 2 seconds are accounted for and pdf is 20%. + EXPECT_EQ(MaxPercentDroppedFrame(), 20); + + dropped_frame_counter_.ResetPendingFrames(GetNextFrameTime()); + + // After resetting the pending frames, the pdf would be 40%. + EXPECT_EQ(MaxPercentDroppedFrame(), 40); +} + +TEST_F(DroppedFrameCounterTest, Reset) { + // Set an interval that rounds up nicely with 1 second. + constexpr auto kInterval = base::TimeDelta::FromMilliseconds(10); + constexpr size_t kFps = base::TimeDelta::FromSeconds(1) / kInterval; + SetInterval(kInterval); + + // First 2 seconds with 20% dropped frames. + SimulateFrameSequence({false, false, false, false, true}, (kFps / 5) * 2); + + // Have a pending frame which would hold the frames in queue. + SimulatePendingFrame(1); + + // Another 2 seconds with 40% dropped frames. + SimulateFrameSequence({false, false, false, true, true}, (kFps / 5) * 2); + + EXPECT_EQ(MaxPercentDroppedFrame(), 20u); + + dropped_frame_counter_.Reset(); // Simulating gpu thread crash + + // After reset the max percent dropped frame would be 0 and frames in queue + // behind the pending frame would not affect it. + EXPECT_EQ(MaxPercentDroppedFrame(), 0u); +} + } // namespace } // namespace cc diff --git a/chromium/cc/metrics/event_metrics.cc b/chromium/cc/metrics/event_metrics.cc index f9ecfce0a7a..1989d690ac0 100644 --- a/chromium/cc/metrics/event_metrics.cc +++ b/chromium/cc/metrics/event_metrics.cc @@ -8,9 +8,9 @@ #include <utility> #include "base/check.h" +#include "base/cxx17_backports.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "base/time/default_tick_clock.h" namespace cc { @@ -20,6 +20,7 @@ constexpr struct { EventMetrics::EventType metrics_event_type; ui::EventType ui_event_type; const char* name; + absl::optional<bool> scroll_is_inertial = absl::nullopt; absl::optional<EventMetrics::ScrollUpdateType> scroll_update_type = absl::nullopt; } kInterestingEvents[] = { @@ -33,11 +34,12 @@ constexpr struct { EVENT_TYPE(TouchPressed, ui::ET_TOUCH_PRESSED), EVENT_TYPE(TouchReleased, ui::ET_TOUCH_RELEASED), EVENT_TYPE(TouchMoved, ui::ET_TOUCH_MOVED), - EVENT_TYPE(GestureScrollBegin, ui::ET_GESTURE_SCROLL_BEGIN), + EVENT_TYPE(GestureScrollBegin, ui::ET_GESTURE_SCROLL_BEGIN, false), EVENT_TYPE(GestureScrollUpdate, ui::ET_GESTURE_SCROLL_UPDATE, + false, EventMetrics::ScrollUpdateType::kContinued), - EVENT_TYPE(GestureScrollEnd, ui::ET_GESTURE_SCROLL_END), + EVENT_TYPE(GestureScrollEnd, ui::ET_GESTURE_SCROLL_END, false), EVENT_TYPE(GestureDoubleTap, ui::ET_GESTURE_DOUBLE_TAP), EVENT_TYPE(GestureLongPress, ui::ET_GESTURE_LONG_PRESS), EVENT_TYPE(GestureLongTap, ui::ET_GESTURE_LONG_TAP), @@ -49,11 +51,16 @@ constexpr struct { EVENT_TYPE(GestureTwoFingerTap, ui::ET_GESTURE_TWO_FINGER_TAP), EVENT_TYPE(FirstGestureScrollUpdate, ui::ET_GESTURE_SCROLL_UPDATE, + false, EventMetrics::ScrollUpdateType::kStarted), EVENT_TYPE(MouseDragged, ui::ET_MOUSE_DRAGGED), EVENT_TYPE(GesturePinchBegin, ui::ET_GESTURE_PINCH_BEGIN), EVENT_TYPE(GesturePinchEnd, ui::ET_GESTURE_PINCH_END), EVENT_TYPE(GesturePinchUpdate, ui::ET_GESTURE_PINCH_UPDATE), + EVENT_TYPE(InertialGestureScrollUpdate, + ui::ET_GESTURE_SCROLL_UPDATE, + true, + EventMetrics::ScrollUpdateType::kContinued), #undef EVENT_TYPE }; static_assert(base::size(kInterestingEvents) == @@ -65,12 +72,12 @@ constexpr struct { ui::ScrollInputType ui_scroll_type; const char* name; } kScrollTypes[] = { -#define SCROLL_TYPE(name, ui_type) \ - { EventMetrics::ScrollType::k##name, ui_type, #name } - SCROLL_TYPE(Autoscroll, ui::ScrollInputType::kAutoscroll), - SCROLL_TYPE(Scrollbar, ui::ScrollInputType::kScrollbar), - SCROLL_TYPE(Touchscreen, ui::ScrollInputType::kTouchscreen), - SCROLL_TYPE(Wheel, ui::ScrollInputType::kWheel), +#define SCROLL_TYPE(name) \ + { EventMetrics::ScrollType::k##name, ui::ScrollInputType::k##name, #name } + SCROLL_TYPE(Autoscroll), + SCROLL_TYPE(Scrollbar), + SCROLL_TYPE(Touchscreen), + SCROLL_TYPE(Wheel), #undef SCROLL_TYPE }; static_assert(base::size(kScrollTypes) == @@ -79,11 +86,19 @@ static_assert(base::size(kScrollTypes) == absl::optional<EventMetrics::EventType> ToInterestingEventType( ui::EventType ui_event_type, - absl::optional<EventMetrics::ScrollUpdateType> scroll_update_type) { + const absl::optional<EventMetrics::ScrollParams>& scroll_params) { + absl::optional<EventMetrics::ScrollUpdateType> scroll_update_type; + absl::optional<bool> scroll_is_inertial; + if (scroll_params) { + scroll_update_type = scroll_params->update_type; + scroll_is_inertial = scroll_params->is_inertial; + } + for (size_t i = 0; i < base::size(kInterestingEvents); i++) { const auto& interesting_event = kInterestingEvents[i]; if (ui_event_type == interesting_event.ui_event_type && - scroll_update_type == interesting_event.scroll_update_type) { + scroll_update_type == interesting_event.scroll_update_type && + scroll_is_inertial == interesting_event.scroll_is_inertial) { EventMetrics::EventType metrics_event_type = static_cast<EventMetrics::EventType>(i); DCHECK_EQ(metrics_event_type, interesting_event.metrics_event_type); @@ -94,12 +109,12 @@ absl::optional<EventMetrics::EventType> ToInterestingEventType( } absl::optional<EventMetrics::ScrollType> ToScrollType( - const absl::optional<ui::ScrollInputType>& scroll_input_type) { - if (!scroll_input_type) + const absl::optional<EventMetrics::ScrollParams>& scroll_params) { + if (!scroll_params) return absl::nullopt; for (size_t i = 0; i < base::size(kScrollTypes); i++) { - if (*scroll_input_type == kScrollTypes[i].ui_scroll_type) { + if (scroll_params->input_type == kScrollTypes[i].ui_scroll_type) { EventMetrics::ScrollType metrics_scroll_type = static_cast<EventMetrics::ScrollType>(i); DCHECK_EQ(metrics_scroll_type, kScrollTypes[i].metrics_scroll_type); @@ -110,22 +125,49 @@ absl::optional<EventMetrics::ScrollType> ToScrollType( return absl::nullopt; } +bool IsGestureScroll(ui::EventType type) { + return type == ui::ET_GESTURE_SCROLL_BEGIN || + type == ui::ET_GESTURE_SCROLL_UPDATE || + type == ui::ET_GESTURE_SCROLL_END; +} + +bool IsGestureScrollUpdate(ui::EventType type) { + return type == ui::ET_GESTURE_SCROLL_UPDATE; +} + } // namespace +// EventMetrics::ScrollParams: + +EventMetrics::ScrollParams::ScrollParams(ui::ScrollInputType input_type, + bool is_inertial) + : input_type(input_type), is_inertial(is_inertial) {} + +EventMetrics::ScrollParams::ScrollParams(ui::ScrollInputType input_type, + bool is_inertial, + ScrollUpdateType update_type) + : input_type(input_type), + is_inertial(is_inertial), + update_type(update_type) {} + +EventMetrics::ScrollParams::ScrollParams(const ScrollParams&) = default; +EventMetrics::ScrollParams& EventMetrics::ScrollParams::operator=( + const ScrollParams&) = default; + +// EventMetrics: + // static std::unique_ptr<EventMetrics> EventMetrics::Create( ui::EventType type, - absl::optional<ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type, + absl::optional<ScrollParams> scroll_params, base::TimeTicks timestamp) { // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there // seems to be some tests that are emitting events with null timestamp. We // should investigate and try to fix those cases and add a `DCHECK` here to // assert `timestamp` is not null. - std::unique_ptr<EventMetrics> metrics = - CreateInternal(type, scroll_update_type, scroll_input_type, timestamp, - base::DefaultTickClock::GetInstance()); + std::unique_ptr<EventMetrics> metrics = CreateInternal( + type, scroll_params, timestamp, base::DefaultTickClock::GetInstance()); if (!metrics) return nullptr; @@ -137,14 +179,13 @@ std::unique_ptr<EventMetrics> EventMetrics::Create( // static std::unique_ptr<EventMetrics> EventMetrics::CreateForTesting( ui::EventType type, - absl::optional<ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type, + absl::optional<ScrollParams> scroll_params, base::TimeTicks timestamp, const base::TickClock* tick_clock) { DCHECK(!timestamp.is_null()); - std::unique_ptr<EventMetrics> metrics = CreateInternal( - type, scroll_update_type, scroll_input_type, timestamp, tick_clock); + std::unique_ptr<EventMetrics> metrics = + CreateInternal(type, scroll_params, timestamp, tick_clock); if (!metrics) return nullptr; @@ -156,12 +197,11 @@ std::unique_ptr<EventMetrics> EventMetrics::CreateForTesting( // static std::unique_ptr<EventMetrics> EventMetrics::CreateFromExisting( ui::EventType type, - absl::optional<ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type, + absl::optional<ScrollParams> scroll_params, DispatchStage last_dispatch_stage, const EventMetrics* existing) { std::unique_ptr<EventMetrics> metrics = CreateInternal( - type, scroll_update_type, scroll_input_type, base::TimeTicks(), + type, scroll_params, base::TimeTicks(), existing ? existing->tick_clock_ : base::DefaultTickClock::GetInstance()); if (!metrics) return nullptr; @@ -187,21 +227,27 @@ std::unique_ptr<EventMetrics> EventMetrics::CreateFromExisting( // static std::unique_ptr<EventMetrics> EventMetrics::CreateInternal( ui::EventType type, - absl::optional<ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type, + const absl::optional<ScrollParams>& scroll_params, base::TimeTicks timestamp, const base::TickClock* tick_clock) { - // `scroll_update_type` should be set for and only for - // `ui::ET_GESTURE_SCROLL_UPDATE`. - DCHECK(type == ui::ET_GESTURE_SCROLL_UPDATE && scroll_update_type || - type != ui::ET_GESTURE_SCROLL_UPDATE && !scroll_update_type); + // `scroll_params` should be set if and only if the event is a gesture scroll + // event. + DCHECK(IsGestureScroll(type) && scroll_params || + !IsGestureScroll(type) && !scroll_params); + + // `scroll_params->update_type` should be set if and only if the event is a + // gesture scroll update event. + DCHECK(IsGestureScrollUpdate(type) && scroll_params && + scroll_params->update_type || + !IsGestureScrollUpdate(type) && + (!scroll_params || !scroll_params->update_type)); + absl::optional<EventType> interesting_type = - ToInterestingEventType(type, scroll_update_type); + ToInterestingEventType(type, scroll_params); if (!interesting_type) return nullptr; - return base::WrapUnique(new EventMetrics(*interesting_type, - ToScrollType(scroll_input_type), - timestamp, tick_clock)); + return base::WrapUnique(new EventMetrics( + *interesting_type, ToScrollType(scroll_params), timestamp, tick_clock)); } EventMetrics::EventMetrics(EventType type, @@ -249,6 +295,7 @@ bool EventMetrics::ShouldReportScrollingTotalLatency() const { return type_ == EventType::kGestureScrollBegin || type_ == EventType::kGestureScrollEnd || type_ == EventType::kFirstGestureScrollUpdate || + type_ == EventType::kInertialGestureScrollUpdate || type_ == EventType::kGestureScrollUpdate; } diff --git a/chromium/cc/metrics/event_metrics.h b/chromium/cc/metrics/event_metrics.h index de1a3d77fbe..eceb2de2f5f 100644 --- a/chromium/cc/metrics/event_metrics.h +++ b/chromium/cc/metrics/event_metrics.h @@ -54,7 +54,8 @@ class CC_EXPORT EventMetrics { kGesturePinchBegin, kGesturePinchEnd, kGesturePinchUpdate, - kMaxValue = kGesturePinchUpdate, + kInertialGestureScrollUpdate, + kMaxValue = kInertialGestureScrollUpdate, }; // Type of scroll events. This list should be in the same order as values of @@ -86,19 +87,32 @@ class CC_EXPORT EventMetrics { kMaxValue = kRendererMainFinished, }; + // Parameters to initialize an `EventMetrics` object for a scroll event. + struct CC_EXPORT ScrollParams { + ScrollParams(ui::ScrollInputType input_type, bool is_inertial); + ScrollParams(ui::ScrollInputType input_type, + bool is_inertial, + ScrollUpdateType update_type); + + ScrollParams(const ScrollParams&); + ScrollParams& operator=(const ScrollParams&); + + ui::ScrollInputType input_type; + bool is_inertial; + absl::optional<ScrollUpdateType> update_type; + }; + // Returns a new instance if the event is of a type we are interested in. // Otherwise, returns nullptr. static std::unique_ptr<EventMetrics> Create( ui::EventType type, - absl::optional<ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type, + absl::optional<ScrollParams> scroll_params, base::TimeTicks timestamp); // Similar to `Create()` with an extra `base::TickClock` to use in tests. static std::unique_ptr<EventMetrics> CreateForTesting( ui::EventType type, - absl::optional<ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type, + absl::optional<ScrollParams> scroll_params, base::TimeTicks timestamp, const base::TickClock* tick_clock); @@ -110,8 +124,7 @@ class CC_EXPORT EventMetrics { // new event is not an interesting one, return value would be nullptr. static std::unique_ptr<EventMetrics> CreateFromExisting( ui::EventType type, - absl::optional<ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type, + absl::optional<ScrollParams> scroll_params, DispatchStage last_dispatch_stage, const EventMetrics* existing); @@ -152,8 +165,7 @@ class CC_EXPORT EventMetrics { private: static std::unique_ptr<EventMetrics> CreateInternal( ui::EventType type, - absl::optional<ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type, + const absl::optional<ScrollParams>& scroll_params, base::TimeTicks timestamp, const base::TickClock* tick_clock); diff --git a/chromium/cc/metrics/events_metrics_manager.cc b/chromium/cc/metrics/events_metrics_manager.cc index 3de83e4ee64..72993dae887 100644 --- a/chromium/cc/metrics/events_metrics_manager.cc +++ b/chromium/cc/metrics/events_metrics_manager.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "base/callback.h" #include "base/callback_helpers.h" -#include "base/stl_util.h" namespace cc { diff --git a/chromium/cc/metrics/events_metrics_manager_unittest.cc b/chromium/cc/metrics/events_metrics_manager_unittest.cc index d8fc6e47d25..0d0b6b90217 100644 --- a/chromium/cc/metrics/events_metrics_manager_unittest.cc +++ b/chromium/cc/metrics/events_metrics_manager_unittest.cc @@ -8,7 +8,7 @@ #include <vector> #include "base/bind.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/test/simple_test_tick_clock.h" #include "cc/metrics/event_metrics.h" #include "testing/gmock/include/gmock/gmock.h" @@ -56,8 +56,8 @@ class EventsMetricsManagerTest : public testing::Test { test_tick_clock_.Advance(base::TimeDelta::FromMicroseconds(10)); base::TimeTicks event_time = test_tick_clock_.NowTicks(); test_tick_clock_.Advance(base::TimeDelta::FromMicroseconds(10)); - return EventMetrics::CreateForTesting(type, absl::nullopt, absl::nullopt, - event_time, &test_tick_clock_); + return EventMetrics::CreateForTesting(type, absl::nullopt, event_time, + &test_tick_clock_); } EventsMetricsManager manager_; diff --git a/chromium/cc/metrics/frame_sequence_metrics.cc b/chromium/cc/metrics/frame_sequence_metrics.cc index 30259bd2a9c..3b92138019f 100644 --- a/chromium/cc/metrics/frame_sequence_metrics.cc +++ b/chromium/cc/metrics/frame_sequence_metrics.cc @@ -19,6 +19,36 @@ namespace cc { +bool ShouldReportForAnimation(FrameSequenceTrackerType sequence_type, + FrameSequenceMetrics::ThreadType thread_type) { + if (sequence_type == FrameSequenceTrackerType::kCompositorAnimation) + return thread_type == FrameSequenceMetrics::ThreadType::kCompositor; + + if (sequence_type == FrameSequenceTrackerType::kMainThreadAnimation || + sequence_type == FrameSequenceTrackerType::kRAF) + return thread_type == FrameSequenceMetrics::ThreadType::kMain; + + return false; +} + +bool ShouldReportForInteraction( + FrameSequenceTrackerType sequence_type, + FrameSequenceMetrics::ThreadType reporting_thread_type, + FrameSequenceMetrics::ThreadType metrics_effective_thread_type) { + // For scrollbar/touch/wheel scroll, the slower thread is the one we want to + // report. For pinch-zoom, it's the compositor-thread. + if (sequence_type == FrameSequenceTrackerType::kScrollbarScroll || + sequence_type == FrameSequenceTrackerType::kTouchScroll || + sequence_type == FrameSequenceTrackerType::kWheelScroll) + return reporting_thread_type == metrics_effective_thread_type; + + if (sequence_type == FrameSequenceTrackerType::kPinchZoom) + return reporting_thread_type == + FrameSequenceMetrics::ThreadType::kCompositor; + + return false; +} + namespace { // Avoid reporting any throughput metric for sequences that do not have a @@ -58,43 +88,6 @@ std::string GetMissedDeadlineHistogramName(FrameSequenceTrackerType type, FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)}); } -std::string GetFrameSequenceLengthHistogramName(FrameSequenceTrackerType type) { - return base::StrCat( - {"Graphics.Smoothness.FrameSequenceLength.", - FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)}); -} - -bool ShouldReportForAnimation(FrameSequenceTrackerType sequence_type, - FrameSequenceMetrics::ThreadType thread_type) { - if (sequence_type == FrameSequenceTrackerType::kCompositorAnimation) - return thread_type == FrameSequenceMetrics::ThreadType::kCompositor; - - if (sequence_type == FrameSequenceTrackerType::kCanvasAnimation || - sequence_type == FrameSequenceTrackerType::kJSAnimation || - sequence_type == FrameSequenceTrackerType::kMainThreadAnimation || - sequence_type == FrameSequenceTrackerType::kRAF) - return thread_type == FrameSequenceMetrics::ThreadType::kMain; - - return false; -} - -bool ShouldReportForInteraction(FrameSequenceMetrics* metrics, - FrameSequenceMetrics::ThreadType thread_type) { - const auto sequence_type = metrics->type(); - - // For scrollbar/touch/wheel scroll, the slower thread is the one we want to - // report. For pinch-zoom, it's the compositor-thread. - if (sequence_type == FrameSequenceTrackerType::kScrollbarScroll || - sequence_type == FrameSequenceTrackerType::kTouchScroll || - sequence_type == FrameSequenceTrackerType::kWheelScroll) - return thread_type == metrics->GetEffectiveThread(); - - if (sequence_type == FrameSequenceTrackerType::kPinchZoom) - return thread_type == FrameSequenceMetrics::ThreadType::kCompositor; - - return false; -} - bool IsInteractionType(FrameSequenceTrackerType sequence_type) { return sequence_type == FrameSequenceTrackerType::kScrollbarScroll || sequence_type == FrameSequenceTrackerType::kTouchScroll || @@ -346,12 +339,16 @@ void FrameSequenceMetrics::ReportMetrics() { if (jank_reporter_) { if (jank_reporter_->thread_type() == FrameSequenceMetrics::ThreadType::kCompositor && - impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) + impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) { + DCHECK_EQ(jank_reporter_->thread_type(), this->GetEffectiveThread()); jank_reporter_->ReportJankMetrics(impl_throughput_.frames_expected); - else if (jank_reporter_->thread_type() == - FrameSequenceMetrics::ThreadType::kMain && - main_throughput_.frames_expected >= kMinFramesForThroughputMetric) + } else if (jank_reporter_->thread_type() == + FrameSequenceMetrics::ThreadType::kMain && + main_throughput_.frames_expected >= + kMinFramesForThroughputMetric) { + DCHECK_EQ(jank_reporter_->thread_type(), this->GetEffectiveThread()); jank_reporter_->ReportJankMetrics(main_throughput_.frames_expected); + } } // Reset the metrics that reach reporting threshold. @@ -429,17 +426,6 @@ int FrameSequenceMetrics::ThroughputData::ReportDroppedFramePercentHistogram( sequence_type == FrameSequenceTrackerType::kCanvasAnimation || sequence_type == FrameSequenceTrackerType::kJSAnimation); - if (metrics->GetEffectiveThread() == thread_type) { - STATIC_HISTOGRAM_POINTER_GROUP( - GetFrameSequenceLengthHistogramName(sequence_type), - static_cast<int>(sequence_type), - static_cast<int>(FrameSequenceTrackerType::kMaxType), - Add(data.frames_expected), - base::Histogram::FactoryGet( - GetFrameSequenceLengthHistogramName(sequence_type), 1, 1000, 50, - base::HistogramBase::kUmaTargetedHistogramFlag)); - } - // Throughput means the percent of frames that was expected to show on the // screen but didn't. In other words, the lower the throughput is, the // smoother user experience. @@ -447,7 +433,8 @@ int FrameSequenceMetrics::ThroughputData::ReportDroppedFramePercentHistogram( const bool is_animation = ShouldReportForAnimation(sequence_type, thread_type); - const bool is_interaction = ShouldReportForInteraction(metrics, thread_type); + const bool is_interaction = ShouldReportForInteraction( + metrics->type(), thread_type, metrics->GetEffectiveThread()); ThroughputUkmReporter* const ukm_reporter = metrics->ukm_reporter(); @@ -518,7 +505,8 @@ int FrameSequenceMetrics::ThroughputData:: const bool is_animation = ShouldReportForAnimation(sequence_type, thread_type); - const bool is_interaction = ShouldReportForInteraction(metrics, thread_type); + const bool is_interaction = ShouldReportForInteraction( + metrics->type(), thread_type, metrics->GetEffectiveThread()); if (is_animation) { TRACE_EVENT_INSTANT2( diff --git a/chromium/cc/metrics/frame_sequence_metrics.h b/chromium/cc/metrics/frame_sequence_metrics.h index 1de8184cad1..c051a27f5f8 100644 --- a/chromium/cc/metrics/frame_sequence_metrics.h +++ b/chromium/cc/metrics/frame_sequence_metrics.h @@ -11,7 +11,6 @@ #include "base/callback.h" #include "base/trace_event/traced_value.h" #include "cc/cc_export.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace cc { class ThroughputUkmReporter; @@ -147,7 +146,7 @@ class CC_EXPORT FrameSequenceMetrics { struct CustomReportData { uint32_t frames_expected = 0; uint32_t frames_produced = 0; - uint32_t jank_count = 0; + int jank_count = 0; }; using CustomReporter = base::OnceCallback<void(const CustomReportData& data)>; // Sets reporter callback for kCustom typed sequence. @@ -230,6 +229,14 @@ class CC_EXPORT FrameSequenceMetrics { std::unique_ptr<JankMetrics> jank_reporter_; }; +bool ShouldReportForAnimation(FrameSequenceTrackerType sequence_type, + FrameSequenceMetrics::ThreadType thread_type); + +bool ShouldReportForInteraction( + FrameSequenceTrackerType sequence_type, + FrameSequenceMetrics::ThreadType reporting_thread_type, + FrameSequenceMetrics::ThreadType metrics_effective_thread_type); + } // namespace cc #endif // CC_METRICS_FRAME_SEQUENCE_METRICS_H_ diff --git a/chromium/cc/metrics/frame_sequence_metrics_unittest.cc b/chromium/cc/metrics/frame_sequence_metrics_unittest.cc index 983cc4a893c..f2bf7ee43ad 100644 --- a/chromium/cc/metrics/frame_sequence_metrics_unittest.cc +++ b/chromium/cc/metrics/frame_sequence_metrics_unittest.cc @@ -56,21 +56,6 @@ TEST(FrameSequenceMetricsTest, ScrollingThreadMergeMetrics) { } #endif // DCHECK_IS_ON() -TEST(FrameSequenceMetricsTest, VideoReportsOnImplOnly) { - base::HistogramTester histograms; - - FrameSequenceMetrics first(FrameSequenceTrackerType::kVideo, nullptr); - first.impl_throughput().frames_expected = 120; - first.impl_throughput().frames_produced = 80; - first.impl_throughput().frames_ontime = 80; - first.main_throughput().frames_expected = 0; - first.main_throughput().frames_produced = 0; - first.main_throughput().frames_ontime = 0; - first.ReportMetrics(); - histograms.ExpectTotalCount("Graphics.Smoothness.FrameSequenceLength.Video", - 1u); -} - TEST(FrameSequenceMetricsTest, AllMetricsReported) { base::HistogramTester histograms; diff --git a/chromium/cc/metrics/frame_sequence_tracker.cc b/chromium/cc/metrics/frame_sequence_tracker.cc index 474b2f0d36a..e72bd574646 100644 --- a/chromium/cc/metrics/frame_sequence_tracker.cc +++ b/chromium/cc/metrics/frame_sequence_tracker.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/containers/contains.h" +#include "base/containers/cxx20_erase.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/metrics/histogram_macros.h" diff --git a/chromium/cc/metrics/frame_sequence_tracker.h b/chromium/cc/metrics/frame_sequence_tracker.h index e76ff04f5b7..1630c711217 100644 --- a/chromium/cc/metrics/frame_sequence_tracker.h +++ b/chromium/cc/metrics/frame_sequence_tracker.h @@ -9,11 +9,9 @@ #include <sstream> #include "base/containers/circular_deque.h" -#include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "cc/cc_export.h" #include "cc/metrics/frame_sequence_metrics.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace gfx { struct PresentationFeedback; diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.cc b/chromium/cc/metrics/frame_sequence_tracker_collection.cc index ed9f7bbf691..b6394a6360d 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_collection.cc +++ b/chromium/cc/metrics/frame_sequence_tracker_collection.cc @@ -8,6 +8,7 @@ #include <vector> #include "base/containers/contains.h" +#include "base/containers/cxx20_erase.h" #include "base/memory/ptr_util.h" #include "cc/metrics/compositor_frame_reporting_controller.h" #include "cc/metrics/frame_sequence_tracker.h" diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.h b/chromium/cc/metrics/frame_sequence_tracker_collection.h index fdba36a0435..e3dc01d9af8 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_collection.h +++ b/chromium/cc/metrics/frame_sequence_tracker_collection.h @@ -13,7 +13,6 @@ #include "base/containers/flat_map.h" #include "cc/cc_export.h" #include "cc/metrics/frame_sequence_metrics.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace gfx { struct PresentationFeedback; diff --git a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc index 908b0edd950..1cfa9f309e6 100644 --- a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc +++ b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc @@ -905,13 +905,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame2) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -934,13 +928,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame3) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -963,13 +951,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame4) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1033,13 +1015,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame7) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1062,13 +1038,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame8) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1091,13 +1061,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame9) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1153,13 +1117,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame12) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1182,13 +1140,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame13) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1204,13 +1156,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame14) { // The tracker should have been removed from the removal_tracker_ list. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1240,13 +1186,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame15) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1269,13 +1209,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame16) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1298,13 +1232,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame17) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1328,13 +1256,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame18) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1357,13 +1279,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame19) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } @@ -1386,13 +1302,7 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame20) { // Now the |removal_tracker| should have been destroyed. EXPECT_EQ(NumberOfRemovalTrackers(), 0u); - std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll"; - // Impl thread reports 101 frames expected. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1); - // The main thread does not submit a report because it is not the effective - // thread. - EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0); - metric = + std::string metric = "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll"; EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1); } diff --git a/chromium/cc/metrics/jank_metrics.cc b/chromium/cc/metrics/jank_metrics.cc index 8922e14f484..3212403ab06 100644 --- a/chromium/cc/metrics/jank_metrics.cc +++ b/chromium/cc/metrics/jank_metrics.cc @@ -237,6 +237,29 @@ void JankMetrics::ReportJankMetrics(int frames_expected) { GetJankHistogramName(tracker_type_, jank_thread_name), 1, 100, 101, base::HistogramBase::kUmaTargetedHistogramFlag)); + const bool is_animation = + ShouldReportForAnimation(tracker_type_, effective_thread_); + + // Jank reporter's effective thread is guaranteed to be identical to that of + // the owning FrameSequenceMetrics instance. + const bool is_interaction = ShouldReportForInteraction( + tracker_type_, effective_thread_, effective_thread_); + + if (is_animation) { + UMA_HISTOGRAM_PERCENTAGE("Graphics.Smoothness.Jank.AllAnimations", + jank_percent); + } + + if (is_interaction) { + UMA_HISTOGRAM_PERCENTAGE("Graphics.Smoothness.Jank.AllInteractions", + jank_percent); + } + + if (is_animation || is_interaction) { + UMA_HISTOGRAM_PERCENTAGE("Graphics.Smoothness.Jank.AllSequences", + jank_percent); + } + // Report the max staleness metrics STATIC_HISTOGRAM_POINTER_GROUP( GetMaxStaleHistogramName(tracker_type_), diff --git a/chromium/cc/metrics/jank_metrics_unittest.cc b/chromium/cc/metrics/jank_metrics_unittest.cc index dbc624e1ac5..86e7c3542ff 100644 --- a/chromium/cc/metrics/jank_metrics_unittest.cc +++ b/chromium/cc/metrics/jank_metrics_unittest.cc @@ -28,6 +28,12 @@ const base::TimeDelta kDefaultFrameInterval = // This makes it easier to numerically distinguish sequence numbers versus // frame tokens, which always start at 1. const uint32_t kSequenceNumberStartsAt = 100u; + +const char* kAllSequencesMetricName = "Graphics.Smoothness.Jank.AllSequences"; +const char* kAllAnimationsMetricName = "Graphics.Smoothness.Jank.AllAnimations"; +const char* kAllInteractionsMetricName = + "Graphics.Smoothness.Jank.AllInteractions"; + } // namespace namespace cc { @@ -158,6 +164,17 @@ TEST_F(JankMetricsTest, CompositorAnimationOneJankWithMildFluctuation) { EXPECT_THAT(histogram_tester.GetAllSamples(metric), testing::ElementsAre(base::Bucket(1, 1))); + // Test all-sequence metrics. + histogram_tester.ExpectTotalCount(kAllSequencesMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllSequencesMetricName), + testing::ElementsAre(base::Bucket(1, 1))); + + histogram_tester.ExpectTotalCount(kAllAnimationsMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllSequencesMetricName), + testing::ElementsAre(base::Bucket(1, 1))); + + histogram_tester.ExpectTotalCount(kAllInteractionsMetricName, 0u); + // Stale-frame metrics const char* stale_metric = "Graphics.Smoothness.Stale.CompositorAnimation"; const char* maxstale_metric = @@ -206,6 +223,17 @@ TEST_F(JankMetricsTest, MainThreadAnimationOneJankWithNoUpdate) { // No jank is reported for "Compositor" histogram_tester.ExpectTotalCount(invalid_metric, 0u); + // Test all-sequence metrics. + histogram_tester.ExpectTotalCount(kAllSequencesMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllSequencesMetricName), + testing::ElementsAre(base::Bucket(1, 1))); + + histogram_tester.ExpectTotalCount(kAllAnimationsMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllSequencesMetricName), + testing::ElementsAre(base::Bucket(1, 1))); + + histogram_tester.ExpectTotalCount(kAllInteractionsMetricName, 0u); + // Stale-frame metrics const char* stale_metric = "Graphics.Smoothness.Stale.MainThreadAnimation"; const char* maxstale_metric = @@ -248,6 +276,11 @@ TEST_F(JankMetricsTest, VideoManyJanksOver300ExpectedFrames) { // No jank is reported for "Main" histogram_tester.ExpectTotalCount(invalid_metric, 0u); + // Test all-sequence metrics. Videos are not counted into AllSequences. + histogram_tester.ExpectTotalCount(kAllSequencesMetricName, 0u); + histogram_tester.ExpectTotalCount(kAllAnimationsMetricName, 0u); + histogram_tester.ExpectTotalCount(kAllInteractionsMetricName, 0u); + // Stale-frame metrics const char* stale_metric = "Graphics.Smoothness.Stale.Video"; const char* maxstale_metric = "Graphics.Smoothness.MaxStale.Video"; @@ -289,6 +322,17 @@ TEST_F(JankMetricsTest, WheelScrollMainThreadNoJanksWithNoUpdates) { histogram_tester.ExpectTotalCount(invalid_metric, 0u); + // Test all-sequence metrics. + histogram_tester.ExpectTotalCount(kAllSequencesMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllSequencesMetricName), + testing::ElementsAre(base::Bucket(0, 1))); + + histogram_tester.ExpectTotalCount(kAllAnimationsMetricName, 0u); + + histogram_tester.ExpectTotalCount(kAllInteractionsMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllInteractionsMetricName), + testing::ElementsAre(base::Bucket(0, 1))); + // Stale-frame metrics const char* stale_metric = "Graphics.Smoothness.Stale.WheelScroll"; const char* maxstale_metric = "Graphics.Smoothness.MaxStale.WheelScroll"; @@ -332,6 +376,17 @@ TEST_F(JankMetricsTest, WheelScrollCompositorTwoJanksWithLargeFluctuation) { // No reporting for "Main". histogram_tester.ExpectTotalCount(invalid_metric, 0u); + // Test all-sequence metrics. + histogram_tester.ExpectTotalCount(kAllSequencesMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllSequencesMetricName), + testing::ElementsAre(base::Bucket(2, 1))); + + histogram_tester.ExpectTotalCount(kAllAnimationsMetricName, 0u); + + histogram_tester.ExpectTotalCount(kAllInteractionsMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllInteractionsMetricName), + testing::ElementsAre(base::Bucket(2, 1))); + // Stale-frame metrics const char* stale_metric = "Graphics.Smoothness.Stale.WheelScroll"; const char* maxstale_metric = "Graphics.Smoothness.MaxStale.WheelScroll"; @@ -378,6 +433,17 @@ TEST_F(JankMetricsTest, TouchScrollCompositorThreadManyJanksLongLatency) { histogram_tester.ExpectTotalCount(invalid_metric, 0u); + // Test all-sequence metrics. + histogram_tester.ExpectTotalCount(kAllSequencesMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllSequencesMetricName), + testing::ElementsAre(base::Bucket(4, 1))); + + histogram_tester.ExpectTotalCount(kAllAnimationsMetricName, 0u); + + histogram_tester.ExpectTotalCount(kAllInteractionsMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllInteractionsMetricName), + testing::ElementsAre(base::Bucket(4, 1))); + // Stale-frame metrics const char* stale_metric = "Graphics.Smoothness.Stale.TouchScroll"; const char* maxstale_metric = "Graphics.Smoothness.MaxStale.TouchScroll"; @@ -436,6 +502,17 @@ TEST_F(JankMetricsTest, RAFMergeJanks) { histogram_tester.ExpectTotalCount(invalid_metric, 0u); + // Test all-sequence metrics. + histogram_tester.ExpectTotalCount(kAllSequencesMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllSequencesMetricName), + testing::ElementsAre(base::Bucket(6, 1))); + + histogram_tester.ExpectTotalCount(kAllAnimationsMetricName, 1u); + EXPECT_THAT(histogram_tester.GetAllSamples(kAllAnimationsMetricName), + testing::ElementsAre(base::Bucket(6, 1))); + + histogram_tester.ExpectTotalCount(kAllInteractionsMetricName, 0u); + // Stale-frame metrics const char* stale_metric = "Graphics.Smoothness.Stale.RAF"; const char* maxstale_metric = "Graphics.Smoothness.MaxStale.RAF"; @@ -472,6 +549,11 @@ TEST_F(JankMetricsTest, CustomNotReported) { histogram_tester.ExpectTotalCount( "Graphics.Smoothness.Jank.Compositor.Custom", 0u); + // Test all-sequence metrics. + histogram_tester.ExpectTotalCount(kAllSequencesMetricName, 0u); + histogram_tester.ExpectTotalCount(kAllAnimationsMetricName, 0u); + histogram_tester.ExpectTotalCount(kAllInteractionsMetricName, 0u); + // Stale-frame metrics histogram_tester.ExpectTotalCount("Graphics.Smoothness.Stale.Custom", 0u); histogram_tester.ExpectTotalCount("Graphics.Smoothness.MaxStale.Custom", 0u); diff --git a/chromium/cc/metrics/video_playback_roughness_reporter.cc b/chromium/cc/metrics/video_playback_roughness_reporter.cc index 33ef9c517be..abc93c368b4 100644 --- a/chromium/cc/metrics/video_playback_roughness_reporter.cc +++ b/chromium/cc/metrics/video_playback_roughness_reporter.cc @@ -7,8 +7,8 @@ #include <algorithm> #include "base/callback_helpers.h" +#include "base/cxx17_backports.h" #include "base/metrics/histogram_macros.h" -#include "base/numerics/ranges.h" #include "base/numerics/safe_conversions.h" #include "components/viz/common/quads/compositor_frame_metadata.h" @@ -60,7 +60,7 @@ void VideoPlaybackRoughnessReporter::FrameSubmitted( FrameInfo info; info.token = token; info.decode_time = frame.metadata().decode_end_time; - info.refresh_rate_hz = int{std::round(1.0 / render_interval.InSecondsF())}; + info.refresh_rate_hz = base::ClampRound(render_interval.ToHz()); info.size = frame.natural_size(); info.intended_duration = frame.metadata().wallclock_frame_duration; @@ -75,8 +75,7 @@ void VideoPlaybackRoughnessReporter::FrameSubmitted( // Adjust frame window size to fit about 1 second of playback const int win_size = base::ClampRound(info.intended_duration.value().ToHz()); - frames_window_size_ = - base::ClampToRange(win_size, kMinWindowSize, kMaxWindowSize); + frames_window_size_ = base::clamp(win_size, kMinWindowSize, kMaxWindowSize); } frames_.push_back(info); diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc index 3ac47e45d4f..f3f2fc5e416 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc @@ -199,7 +199,9 @@ void AsyncLayerTreeFrameSink::SubmitCompositorFrame( "Event.Pipeline", TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT, "step", "SubmitHitTestData"); - power_mode_voter_.OnFrameProduced(); + + power_mode_voter_.OnFrameProduced(frame.render_pass_list.back()->damage_rect, + frame.device_scale_factor()); compositor_frame_sink_ptr_->SubmitCompositorFrame( local_surface_id_, std::move(frame), std::move(hit_test_region_list), 0); diff --git a/chromium/cc/paint/decoded_draw_image.cc b/chromium/cc/paint/decoded_draw_image.cc index c570114ddf6..d03a42b2779 100644 --- a/chromium/cc/paint/decoded_draw_image.cc +++ b/chromium/cc/paint/decoded_draw_image.cc @@ -12,7 +12,7 @@ DecodedDrawImage::DecodedDrawImage(sk_sp<const SkImage> image, sk_sp<SkColorFilter> dark_mode_color_filter, const SkSize& src_rect_offset, const SkSize& scale_adjustment, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, bool is_budgeted) : image_(std::move(image)), dark_mode_color_filter_(std::move(dark_mode_color_filter)), @@ -22,7 +22,7 @@ DecodedDrawImage::DecodedDrawImage(sk_sp<const SkImage> image, is_budgeted_(is_budgeted) {} DecodedDrawImage::DecodedDrawImage(const gpu::Mailbox& mailbox, - SkFilterQuality filter_quality) + PaintFlags::FilterQuality filter_quality) : mailbox_(mailbox), src_rect_offset_(SkSize::MakeEmpty()), scale_adjustment_(SkSize::Make(1.f, 1.f)), @@ -34,7 +34,7 @@ DecodedDrawImage::DecodedDrawImage( sk_sp<SkColorFilter> dark_mode_color_filter, const SkSize& src_rect_offset, const SkSize& scale_adjustment, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, bool needs_mips, bool is_budgeted) : transfer_cache_entry_id_(transfer_cache_entry_id), @@ -50,7 +50,7 @@ DecodedDrawImage::DecodedDrawImage() nullptr, SkSize::MakeEmpty(), SkSize::Make(1.f, 1.f), - kNone_SkFilterQuality, + PaintFlags::FilterQuality::kNone, true) {} DecodedDrawImage::DecodedDrawImage(const DecodedDrawImage&) = default; diff --git a/chromium/cc/paint/decoded_draw_image.h b/chromium/cc/paint/decoded_draw_image.h index a3b0e3a9fc9..8b52faeecd0 100644 --- a/chromium/cc/paint/decoded_draw_image.h +++ b/chromium/cc/paint/decoded_draw_image.h @@ -9,10 +9,10 @@ #include <cmath> #include "cc/paint/paint_export.h" +#include "cc/paint/paint_flags.h" #include "gpu/command_buffer/common/mailbox.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkColorFilter.h" -#include "third_party/skia/include/core/SkFilterQuality.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkSize.h" @@ -30,14 +30,15 @@ class CC_PAINT_EXPORT DecodedDrawImage { sk_sp<SkColorFilter> dark_mode_color_filter, const SkSize& src_rect_offset, const SkSize& scale_adjustment, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, bool is_budgeted); - DecodedDrawImage(const gpu::Mailbox& mailbox, SkFilterQuality filter_quality); + DecodedDrawImage(const gpu::Mailbox& mailbox, + PaintFlags::FilterQuality filter_quality); DecodedDrawImage(absl::optional<uint32_t> transfer_cache_entry_id, sk_sp<SkColorFilter> dark_mode_color_filter, const SkSize& src_rect_offset, const SkSize& scale_adjustment, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, bool needs_mips, bool is_budgeted); DecodedDrawImage(const DecodedDrawImage& other); @@ -57,7 +58,7 @@ class CC_PAINT_EXPORT DecodedDrawImage { } const SkSize& src_rect_offset() const { return src_rect_offset_; } const SkSize& scale_adjustment() const { return scale_adjustment_; } - SkFilterQuality filter_quality() const { return filter_quality_; } + PaintFlags::FilterQuality filter_quality() const { return filter_quality_; } bool is_scale_adjustment_identity() const { return std::abs(scale_adjustment_.width() - 1.f) < FLT_EPSILON && std::abs(scale_adjustment_.height() - 1.f) < FLT_EPSILON; @@ -78,7 +79,7 @@ class CC_PAINT_EXPORT DecodedDrawImage { sk_sp<SkColorFilter> dark_mode_color_filter_; SkSize src_rect_offset_; SkSize scale_adjustment_; - SkFilterQuality filter_quality_; + PaintFlags::FilterQuality filter_quality_; bool transfer_cache_entry_needs_mips_ = false; bool is_budgeted_; }; diff --git a/chromium/cc/paint/discardable_image_map.cc b/chromium/cc/paint/discardable_image_map.cc index 2fd62e0aac7..72f23dcaa1c 100644 --- a/chromium/cc/paint/discardable_image_map.cc +++ b/chromium/cc/paint/discardable_image_map.cc @@ -166,7 +166,7 @@ class DiscardableImageGenerator { void AddImageFromShader(const gfx::Rect& op_rect, const PaintShader* shader, const SkM44& ctm, - SkFilterQuality filter_quality) { + PaintFlags::FilterQuality filter_quality) { if (!shader || !shader->has_discardable_images()) return; @@ -236,7 +236,7 @@ class DiscardableImageGenerator { const SkRect& src_rect, const gfx::Rect& image_rect, const SkM44& matrix, - SkFilterQuality filter_quality) { + PaintFlags::FilterQuality filter_quality) { if (paint_image.IsTextureBacked()) return; diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc index 1b217d8e9c3..6a4801872e3 100644 --- a/chromium/cc/paint/discardable_image_map_unittest.cc +++ b/chromium/cc/paint/discardable_image_map_unittest.cc @@ -373,7 +373,8 @@ TEST_F(DiscardableImageMapTest, PaintDestroyedWhileImageIsDrawn) { // Check if SkNoDrawCanvas does not crash for large layers. TEST_F(DiscardableImageMapTest, RestoreSavedBigLayers) { PaintFlags flags; - SkRect rect = SkRect::MakeWH(INT_MAX, INT_MAX); + SkRect rect = + SkRect::MakeWH(static_cast<float>(INT_MAX), static_cast<float>(INT_MAX)); scoped_refptr<DisplayItemList> display_list = new DisplayItemList; display_list->StartPaint(); display_list->push<DrawRectOp>(rect, flags); @@ -408,13 +409,13 @@ TEST_F(DiscardableImageMapTest, RestoreSavedTransformedLayers) { CreateDiscardablePaintImage(gfx::Size(25, 25)); PaintImage discardable_image3 = CreateDiscardablePaintImage(gfx::Size(25, 25)); - display_list->push<TranslateOp>(25, 25); + display_list->push<TranslateOp>(25.0f, 25.0f); display_list->push<DrawImageOp>(discardable_image1, 0.f, 0.f); display_list->push<SaveLayerOp>(nullptr, &paint); - display_list->push<TranslateOp>(100, 100); + display_list->push<TranslateOp>(100.0f, 100.0f); display_list->push<DrawImageOp>(discardable_image2, 0.f, 0.f); display_list->push<RestoreOp>(); - display_list->push<TranslateOp>(0, 100); + display_list->push<TranslateOp>(0.0f, 100.0f); display_list->push<DrawImageOp>(discardable_image3, 0.f, 0.f); display_list->EndPaintOfUnpaired(visible_rect); display_list->Finalize(); diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc index d0c4f52b0d4..932bee1c155 100644 --- a/chromium/cc/paint/display_item_list.cc +++ b/chromium/cc/paint/display_item_list.cc @@ -454,8 +454,8 @@ DisplayItemList::GetDirectlyCompositedImageResult( result.intrinsic_image_size = gfx::Size(width, height); // Ensure the layer will use nearest neighbor when drawn by the display // compositor, if required. - result.nearest_neighbor = - draw_image_rect_op->flags.getFilterQuality() == kNone_SkFilterQuality; + result.nearest_neighbor = draw_image_rect_op->flags.getFilterQuality() == + PaintFlags::FilterQuality::kNone; return result; } diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h index 498e2436969..7387417c7d4 100644 --- a/chromium/cc/paint/display_item_list.h +++ b/chromium/cc/paint/display_item_list.h @@ -18,6 +18,7 @@ #include "cc/paint/image_id.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_op_buffer.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkPicture.h" #include "ui/gfx/color_space.h" #include "ui/gfx/geometry/rect.h" diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc index 707ffc441e2..e781dfc6dc7 100644 --- a/chromium/cc/paint/display_item_list_unittest.cc +++ b/chromium/cc/paint/display_item_list_unittest.cc @@ -28,6 +28,7 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/skia_util.h" @@ -74,18 +75,13 @@ class DisplayItemListTest : public testing::Test { } }; -#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); \ +#define EXPECT_TRACED_RECT(x, y, width, height, rect_list) \ + do { \ + ASSERT_EQ(4u, rect_list->GetList().size()); \ + EXPECT_EQ(x, rect_list->GetList()[0].GetIfDouble()); \ + EXPECT_EQ(y, rect_list->GetList()[1].GetIfDouble()); \ + EXPECT_EQ(width, rect_list->GetList()[2].GetIfDouble()); \ + EXPECT_EQ(height, rect_list->GetList()[3].GetIfDouble()); \ } while (false) // AddToValue should not crash if there are different numbers of visual_rect @@ -371,7 +367,7 @@ TEST_F(DisplayItemListTest, FilterPairedRange) { // below. SkRect rect = SkRect::MakeWH(source_image.width(), source_image.height()); sk_sp<PaintFilter> image_filter = sk_make_sp<ImagePaintFilter>( - source_image, rect, rect, kHigh_SkFilterQuality); + source_image, rect, rect, PaintFlags::FilterQuality::kHigh); filters.Append(FilterOperation::CreateReferenceFilter(image_filter)); filters.Append(FilterOperation::CreateBrightnessFilter(0.5f)); gfx::RectF filter_bounds(10.f, 10.f, 50.f, 50.f); @@ -1147,30 +1143,30 @@ TEST_F(DisplayItemListTest, AreaOfDrawText) { auto text_blob2_area = text_blob2_size.width() * text_blob2_size.height(); sub_list->StartPaint(); - sub_list->push<DrawTextBlobOp>(text_blob1, 0, 0, PaintFlags()); + sub_list->push<DrawTextBlobOp>(text_blob1, 0.0f, 0.0f, PaintFlags()); sub_list->EndPaintOfUnpaired(gfx::Rect()); auto record = sub_list->ReleaseAsRecord(); list->StartPaint(); list->push<SaveOp>(); - list->push<TranslateOp>(100, 100); + list->push<TranslateOp>(100.0f, 100.0f); list->push<DrawRecordOp>(record); list->push<RestoreOp>(); list->EndPaintOfUnpaired(gfx::Rect(gfx::Point(100, 100), text_blob1_size)); list->StartPaint(); list->push<SaveOp>(); - list->push<TranslateOp>(100, 400); + list->push<TranslateOp>(100.0f, 400.0f); list->push<DrawRecordOp>(record); list->push<RestoreOp>(); list->EndPaintOfUnpaired(gfx::Rect(gfx::Point(100, 400), text_blob1_size)); list->StartPaint(); - list->push<DrawTextBlobOp>(text_blob2, 10, 20, PaintFlags()); + list->push<DrawTextBlobOp>(text_blob2, 10.0f, 20.0f, PaintFlags()); list->EndPaintOfUnpaired(gfx::Rect(text_blob2_size)); list->StartPaint(); - list->push<DrawTextBlobOp>(text_blob2, 400, 100, PaintFlags()); + list->push<DrawTextBlobOp>(text_blob2, 400.0f, 100.0f, PaintFlags()); list->EndPaintOfUnpaired(gfx::Rect(gfx::Point(400, 100), text_blob2_size)); list->StartPaint(); diff --git a/chromium/cc/paint/draw_image.cc b/chromium/cc/paint/draw_image.cc index 11b21e65077..d331bbb6fe3 100644 --- a/chromium/cc/paint/draw_image.cc +++ b/chromium/cc/paint/draw_image.cc @@ -30,7 +30,7 @@ bool ExtractScale(const SkM44& matrix, SkSize* scale) { DrawImage::DrawImage() : use_dark_mode_(false), src_rect_(SkIRect::MakeXYWH(0, 0, 0, 0)), - filter_quality_(kNone_SkFilterQuality), + filter_quality_(PaintFlags::FilterQuality::kNone), scale_(SkSize::Make(1.f, 1.f)), matrix_is_decomposable_(true) {} @@ -39,14 +39,14 @@ DrawImage::DrawImage(PaintImage image) use_dark_mode_(false), src_rect_( SkIRect::MakeXYWH(0, 0, paint_image_.width(), paint_image_.height())), - filter_quality_(kNone_SkFilterQuality), + filter_quality_(PaintFlags::FilterQuality::kNone), scale_(SkSize::Make(1.f, 1.f)), matrix_is_decomposable_(true) {} DrawImage::DrawImage(PaintImage image, bool use_dark_mode, const SkIRect& src_rect, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, const SkM44& matrix, absl::optional<size_t> frame_index, const absl::optional<gfx::ColorSpace>& color_space, diff --git a/chromium/cc/paint/draw_image.h b/chromium/cc/paint/draw_image.h index 02c13f66802..4f7d5f875c7 100644 --- a/chromium/cc/paint/draw_image.h +++ b/chromium/cc/paint/draw_image.h @@ -6,9 +6,9 @@ #define CC_PAINT_DRAW_IMAGE_H_ #include "cc/paint/paint_export.h" +#include "cc/paint/paint_flags.h" #include "cc/paint/paint_image.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/skia/include/core/SkFilterQuality.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkM44.h" #include "third_party/skia/include/core/SkRect.h" @@ -29,7 +29,7 @@ class CC_PAINT_EXPORT DrawImage { DrawImage(PaintImage image, bool use_dark_mode, const SkIRect& src_rect, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, const SkM44& matrix, absl::optional<size_t> frame_index = absl::nullopt, const absl::optional<gfx::ColorSpace>& color_space = absl::nullopt, @@ -54,7 +54,7 @@ class CC_PAINT_EXPORT DrawImage { bool use_dark_mode() const { return use_dark_mode_; } const SkSize& scale() const { return scale_; } const SkIRect& src_rect() const { return src_rect_; } - SkFilterQuality filter_quality() const { return filter_quality_; } + PaintFlags::FilterQuality filter_quality() const { return filter_quality_; } bool matrix_is_decomposable() const { return matrix_is_decomposable_; } const gfx::ColorSpace& target_color_space() const { DCHECK(target_color_space_.has_value()); @@ -73,7 +73,7 @@ class CC_PAINT_EXPORT DrawImage { PaintImage paint_image_; bool use_dark_mode_; SkIRect src_rect_; - SkFilterQuality filter_quality_; + PaintFlags::FilterQuality filter_quality_; SkSize scale_; bool matrix_is_decomposable_; absl::optional<size_t> frame_index_; diff --git a/chromium/cc/paint/filter_operation.cc b/chromium/cc/paint/filter_operation.cc index dae02259d51..82d0b5c20cb 100644 --- a/chromium/cc/paint/filter_operation.cc +++ b/chromium/cc/paint/filter_operation.cc @@ -5,11 +5,11 @@ #include <stddef.h> #include <algorithm> +#include <utility> #include "cc/paint/filter_operation.h" -#include "base/numerics/ranges.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/trace_event/traced_value.h" #include "base/values.h" #include "cc/base/math_util.h" @@ -39,6 +39,10 @@ bool FilterOperation::operator==(const FilterOperation& other) const { return shape_ == other.shape_ && amount_ == other.amount_ && outer_threshold_ == other.outer_threshold_; } + if (type_ == STRETCH) { + return amount_ == other.amount_ && + outer_threshold_ == other.outer_threshold_; + } return amount_ == other.amount_; } @@ -108,6 +112,19 @@ FilterOperation::FilterOperation(FilterType type, float amount, int inset) } FilterOperation::FilterOperation(FilterType type, + float amount, + float outer_threshold) + : type_(type), + amount_(amount), + outer_threshold_(outer_threshold), + drop_shadow_offset_(0, 0), + drop_shadow_color_(0), + zoom_inset_(0) { + DCHECK_EQ(type_, STRETCH); + memset(matrix_, 0, sizeof(matrix_)); +} + +FilterOperation::FilterOperation(FilterType type, sk_sp<PaintFilter> image_filter) : type_(type), amount_(0), @@ -187,6 +204,8 @@ static FilterOperation CreateNoOpFilter(FilterOperation::FilterType type) { case FilterOperation::ALPHA_THRESHOLD: return FilterOperation::CreateAlphaThresholdFilter( FilterOperation::ShapeRects(), 1.f, 0.f); + case FilterOperation::STRETCH: + return FilterOperation::CreateStretchFilter(0.f, 0.f); } NOTREACHED(); return FilterOperation::CreateEmptyFilter(); @@ -200,12 +219,13 @@ static float ClampAmountForFilterType(float amount, case FilterOperation::INVERT: case FilterOperation::OPACITY: case FilterOperation::ALPHA_THRESHOLD: - return base::ClampToRange(amount, 0.f, 1.f); + return base::clamp(amount, 0.f, 1.f); case FilterOperation::SATURATE: case FilterOperation::BRIGHTNESS: case FilterOperation::CONTRAST: case FilterOperation::BLUR: case FilterOperation::DROP_SHADOW: + case FilterOperation::STRETCH: return std::max(amount, 0.f); case FilterOperation::ZOOM: return std::max(amount, 1.f); @@ -331,6 +351,10 @@ void FilterOperation::AsValueInto(base::trace_event::TracedValue* value) const { } value->EndArray(); } break; + case FilterOperation::STRETCH: + value->SetDouble("amount_x", amount_); + value->SetDouble("amount_y", outer_threshold_); + break; } } diff --git a/chromium/cc/paint/filter_operation.h b/chromium/cc/paint/filter_operation.h index c989741e2dd..02002920792 100644 --- a/chromium/cc/paint/filter_operation.h +++ b/chromium/cc/paint/filter_operation.h @@ -6,6 +6,7 @@ #define CC_PAINT_FILTER_OPERATION_H_ #include <memory> +#include <utility> #include <vector> #include "base/check_op.h" @@ -46,7 +47,8 @@ class CC_PAINT_EXPORT FilterOperation { REFERENCE, SATURATING_BRIGHTNESS, // Not used in CSS/SVG. ALPHA_THRESHOLD, // Not used in CSS/SVG. - FILTER_TYPE_LAST = ALPHA_THRESHOLD + STRETCH, // Not used in CSS/SVG. + FILTER_TYPE_LAST = STRETCH }; FilterOperation(); @@ -64,7 +66,7 @@ class CC_PAINT_EXPORT FilterOperation { } float outer_threshold() const { - DCHECK_EQ(type_, ALPHA_THRESHOLD); + DCHECK(type_ == ALPHA_THRESHOLD || type_ == STRETCH); return outer_threshold_; } @@ -171,6 +173,10 @@ class CC_PAINT_EXPORT FilterOperation { outer_threshold); } + static FilterOperation CreateStretchFilter(float amount_x, float amount_y) { + return FilterOperation(STRETCH, amount_x, amount_y); + } + bool operator==(const FilterOperation& other) const; bool operator!=(const FilterOperation& other) const { @@ -191,7 +197,7 @@ class CC_PAINT_EXPORT FilterOperation { } void set_outer_threshold(float outer_threshold) { - DCHECK_EQ(type_, ALPHA_THRESHOLD); + DCHECK(type_ == ALPHA_THRESHOLD || type_ == STRETCH); outer_threshold_ = outer_threshold; } @@ -265,6 +271,8 @@ class CC_PAINT_EXPORT FilterOperation { FilterOperation(FilterType type, float amount, int inset); + FilterOperation(FilterType type, float amount, float outer_threshold); + FilterOperation(FilterType type, sk_sp<PaintFilter> image_filter); FilterOperation(FilterType type, diff --git a/chromium/cc/paint/filter_operations.cc b/chromium/cc/paint/filter_operations.cc index 2c7c2ae62e9..0e906ea8f1c 100644 --- a/chromium/cc/paint/filter_operations.cc +++ b/chromium/cc/paint/filter_operations.cc @@ -82,6 +82,7 @@ bool FilterOperations::HasFilterThatMovesPixels() const { case FilterOperation::BLUR: case FilterOperation::DROP_SHADOW: case FilterOperation::ZOOM: + case FilterOperation::STRETCH: return true; case FilterOperation::REFERENCE: // TODO(hendrikw): SkImageFilter needs a function that tells us if the @@ -128,6 +129,10 @@ float FilterOperations::MaximumPixelMovement() const { // the filter can move pixels. See crbug.com/523538 (sort of). max_movement = fmax(max_movement, 100); continue; + case FilterOperation::STRETCH: + max_movement = + fmax(max_movement, fmax(op.amount(), op.outer_threshold())); + continue; case FilterOperation::OPACITY: case FilterOperation::COLOR_MATRIX: case FilterOperation::GRAYSCALE: @@ -173,6 +178,7 @@ bool FilterOperations::HasFilterThatAffectsOpacity() const { case FilterOperation::BRIGHTNESS: case FilterOperation::CONTRAST: case FilterOperation::SATURATING_BRIGHTNESS: + case FilterOperation::STRETCH: break; } } diff --git a/chromium/cc/paint/filter_operations_unittest.cc b/chromium/cc/paint/filter_operations_unittest.cc index a51ea0f82eb..54fe83569ae 100644 --- a/chromium/cc/paint/filter_operations_unittest.cc +++ b/chromium/cc/paint/filter_operations_unittest.cc @@ -116,7 +116,7 @@ TEST(FilterOperationsTest, MapRectCombineNonCommutative) { scaleMatrix.setScale(2, 2); ops.Append( FilterOperation::CreateReferenceFilter(sk_make_sp<MatrixPaintFilter>( - scaleMatrix, kNone_SkFilterQuality, nullptr))); + scaleMatrix, PaintFlags::FilterQuality::kNone, nullptr))); EXPECT_EQ(gfx::Rect(200, 200, 20, 20), ops.MapRect(gfx::Rect(10, 10), SkMatrix::I())); @@ -135,7 +135,7 @@ TEST(FilterOperationsTest, MapRectReverseCombineNonCommutative) { scaleMatrix.setScale(2, 2); ops.Append( FilterOperation::CreateReferenceFilter(sk_make_sp<MatrixPaintFilter>( - scaleMatrix, kNone_SkFilterQuality, nullptr))); + scaleMatrix, PaintFlags::FilterQuality::kNone, nullptr))); EXPECT_EQ(gfx::Rect(10, 10), ops.MapRectReverse(gfx::Rect(200, 200, 20, 20), SkMatrix::I())); diff --git a/chromium/cc/paint/image_provider.h b/chromium/cc/paint/image_provider.h index 6657d45f5c9..7a44dd3b01b 100644 --- a/chromium/cc/paint/image_provider.h +++ b/chromium/cc/paint/image_provider.h @@ -10,7 +10,6 @@ #include "cc/paint/draw_image.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_op_buffer.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace cc { class PaintImage; diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc index cee28e58b02..841a91246cf 100644 --- a/chromium/cc/paint/oop_pixeltest.cc +++ b/chromium/cc/paint/oop_pixeltest.cc @@ -23,6 +23,7 @@ #include "cc/test/pixel_test_utils.h" #include "cc/tiles/gpu_image_decode_cache.h" #include "components/viz/test/test_in_process_context_provider.h" +#include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/raster_implementation.h" @@ -178,11 +179,16 @@ class OopPixelTest : public testing::Test, raster_implementation->WaitSyncTokenCHROMIUM( sii->GenUnverifiedSyncToken().GetConstData()); + // Assume legacy MSAA if sample count is positive. + gpu::raster::MsaaMode msaa_mode = options.msaa_sample_count > 0 + ? gpu::raster::kMSAA + : gpu::raster::kNoMSAA; + if (options.preclear) { raster_implementation->BeginRasterCHROMIUM( options.preclear_color, /*needs_clear=*/options.preclear, - options.msaa_sample_count, options.use_lcd_text, options.color_space, - mailbox.name); + options.msaa_sample_count, msaa_mode, options.use_lcd_text, + options.color_space, mailbox.name); raster_implementation->EndRasterCHROMIUM(); } @@ -192,8 +198,8 @@ class OopPixelTest : public testing::Test, // cleared, so set |needs_clear| to false here. raster_implementation->BeginRasterCHROMIUM( options.background_color, /*needs_clear=*/!options.preclear, - options.msaa_sample_count, options.use_lcd_text, options.color_space, - mailbox.name); + options.msaa_sample_count, msaa_mode, options.use_lcd_text, + options.color_space, mailbox.name); size_t max_op_size_limit = gpu::raster::RasterInterface::kDefaultMaxOpSizeHint; raster_implementation->RasterCHROMIUM( @@ -227,7 +233,10 @@ class OopPixelTest : public testing::Test, const gpu::Mailbox& mailbox, const RasterOptions& options) { // Import the texture in gl, create an fbo and bind the texture to it. - GLuint gl_texture_id = gl->CreateAndConsumeTextureCHROMIUM(mailbox.name); + GLuint gl_texture_id = + gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name); + gl->BeginSharedImageAccessDirectCHROMIUM( + gl_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); GLuint fbo_id; gl->GenFramebuffers(1, &fbo_id); gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_id); @@ -241,9 +250,11 @@ class OopPixelTest : public testing::Test, new unsigned char[width * height * 4]); gl->ReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.get()); - gl->DeleteTextures(1, &gl_texture_id); gl->DeleteFramebuffers(1, &fbo_id); + gl->EndSharedImageAccessDirectCHROMIUM(gl_texture_id); + gl->DeleteTextures(1, &gl_texture_id); + // Swizzle rgba->bgra std::vector<SkPMColor> colors; colors.reserve(width * height); @@ -415,7 +426,9 @@ class OopImagePixelTest : public OopPixelTest, public ::testing::WithParamInterface<bool> { public: bool UseTooLargeImage() { return GetParam(); } - SkFilterQuality FilterQuality() { return kNone_SkFilterQuality; } + PaintFlags::FilterQuality FilterQuality() { + return PaintFlags::FilterQuality::kNone; + } gfx::Size GetImageSize() { const int kMaxSize = 20000; @@ -1777,31 +1790,37 @@ class OopTextBlobPixelTest const int sdk = base::android::BuildInfo::GetInstance()->sdk_int(); if (sdk <= base::android::SDK_VERSION_MARSHMALLOW) { error_pixels_percentage = 10.f; - max_abs_error = 16; + max_abs_error = 20; } else { // Newer OSes occasionally have smaller flakes when using the real GPU error_pixels_percentage = 1.5f; max_abs_error = 2; } -#elif defined(OS_MAC) || defined(OS_WIN) - // Mac and Windows need very small tolerances only under complex transforms - if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kComplex) { - error_pixels_percentage = 0.2f; - max_abs_error = 2; - } #endif - // Regardless of OS, perspective triggers path rendering for each glyph, - // which produces its own set of pixel differences. - if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kPerspective) { - error_pixels_percentage = 4.f; - max_abs_error = 36; + // Many platforms need very small tolerances under complex transforms, + // and higher tolerances for perspective, since it triggers path rendering + // for each glyph. Additionally, record filters require higher tolerance + // because oop-r converts raster-at-scale to fixed-scale. + float avg_error = max_abs_error; + const bool is_record_filter = + GetTextBlobStrategy(GetParam()) == TextBlobStrategy::kRecordFilter; + if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kComplex) { + error_pixels_percentage = + std::max(is_record_filter ? 12.f : 0.2f, error_pixels_percentage); + max_abs_error = std::max(is_record_filter ? 220 : 2, max_abs_error); + avg_error = std::max(is_record_filter ? 50.f : 2.f, avg_error); + } else if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kPerspective) { + error_pixels_percentage = + std::max(is_record_filter ? 13.f : 4.f, error_pixels_percentage); + max_abs_error = std::max(is_record_filter ? 255 : 36, max_abs_error); + avg_error = std::max(is_record_filter ? 60.f : 36.f, avg_error); } FuzzyPixelComparator comparator( /*discard_alpha=*/false, /*error_pixels_percentage_limit=*/error_pixels_percentage, /*small_error_pixels_percentage_limit=*/0.0f, - /*avg_abs_error_limit=*/max_abs_error, + /*avg_abs_error_limit=*/avg_error, /*max_abs_error_limit=*/max_abs_error, /*small_error_threshold=*/0); ExpectEquals(actual, expected, comparator); @@ -1857,14 +1876,14 @@ class OopTextBlobPixelTest filter = nullptr; } if (strategy == TextBlobStrategy::kDirect) { - display_list->push<DrawTextBlobOp>(std::move(text_blob), 0u, kTextBlobY, + display_list->push<DrawTextBlobOp>(std::move(text_blob), 0.0f, kTextBlobY, text_flags); return; } // All remaining strategies add the DrawTextBlobOp to an inner paint record. auto paint_record = sk_make_sp<PaintOpBuffer>(); - paint_record->push<DrawTextBlobOp>(std::move(text_blob), 0u, kTextBlobY, + paint_record->push<DrawTextBlobOp>(std::move(text_blob), 0.0f, kTextBlobY, text_flags); if (strategy == TextBlobStrategy::kDrawRecord) { display_list->push<DrawRecordOp>(std::move(paint_record)); @@ -1900,7 +1919,7 @@ class OopTextBlobPixelTest // Use bilerp sampling with the PaintRecord to help reduce max RGB error // from pixel-snapping flakiness when using NN sampling. - record_flags.setFilterQuality(kLow_SkFilterQuality); + record_flags.setFilterQuality(PaintFlags::FilterQuality::kLow); // The text blob is embedded in a paint record, which is attached to the // paint via a shader or image filter. Just draw a rect with the paint. @@ -2025,7 +2044,7 @@ TEST_F(OopPixelTest, DrawTextMultipleRasterCHROMIUM) { PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); flags.setColor(SK_ColorGREEN); - display_item_list->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_1), 0u, + display_item_list->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_1), 0.0f, kTextBlobY, flags); display_item_list->EndPaintOfUnpaired(options.full_raster_rect); display_item_list->Finalize(); @@ -2033,7 +2052,7 @@ TEST_F(OopPixelTest, DrawTextMultipleRasterCHROMIUM) { // Create another list with a different typeface. auto display_item_list_2 = base::MakeRefCounted<DisplayItemList>(); display_item_list_2->StartPaint(); - display_item_list_2->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_2), 0u, + display_item_list_2->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_2), 0.0f, kTextBlobY, flags); display_item_list_2->EndPaintOfUnpaired(options.full_raster_rect); display_item_list_2->Finalize(); @@ -2066,7 +2085,7 @@ TEST_F(OopPixelTest, DrawTextBlobPersistentShaderCache) { PaintFlags flags; flags.setStyle(PaintFlags::kFill_Style); flags.setColor(SK_ColorGREEN); - display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0u, kTextBlobY, + display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0.0f, kTextBlobY, flags); display_item_list->EndPaintOfUnpaired(options.full_raster_rect); display_item_list->Finalize(); diff --git a/chromium/cc/paint/paint_cache.h b/chromium/cc/paint/paint_cache.h index 728df5a36f3..1d53dace843 100644 --- a/chromium/cc/paint/paint_cache.h +++ b/chromium/cc/paint/paint_cache.h @@ -7,6 +7,8 @@ #include <map> #include <set> +#include <utility> +#include <vector> #include "base/containers/mru_cache.h" #include "base/containers/stack_container.h" @@ -41,7 +43,8 @@ enum class PaintCacheEntryState : uint32_t { kEmpty, kCached, kInlined, - kLast = kInlined + kInlinedDoNotCache, + kLast = kInlinedDoNotCache }; constexpr size_t PaintCacheDataTypeCount = diff --git a/chromium/cc/paint/paint_cache_unittest.cc b/chromium/cc/paint/paint_cache_unittest.cc index 1db78de82c7..60ab43062ec 100644 --- a/chromium/cc/paint/paint_cache_unittest.cc +++ b/chromium/cc/paint/paint_cache_unittest.cc @@ -4,7 +4,6 @@ #include "cc/paint/paint_cache.h" -#include "base/stl_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h index 91626067d33..ec980047a91 100644 --- a/chromium/cc/paint/paint_canvas.h +++ b/chromium/cc/paint/paint_canvas.h @@ -12,7 +12,8 @@ #include "cc/paint/paint_export.h" #include "cc/paint/paint_image.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkTextBlob.h" + +class SkTextBlob; namespace printing { class MetafileSkia; @@ -27,6 +28,8 @@ class SkottieWrapper; class PaintFlags; class PaintOpBuffer; +enum class UsePaintCache { kDisabled = 0, kEnabled }; + using PaintRecord = PaintOpBuffer; // PaintCanvas is the cc/paint wrapper of SkCanvas. It has a more restricted @@ -109,10 +112,17 @@ class CC_PAINT_EXPORT PaintCanvas { virtual void clipPath(const SkPath& path, SkClipOp op, - bool do_anti_alias) = 0; - void clipPath(const SkPath& path, SkClipOp op) { clipPath(path, op, false); } + bool do_anti_alias, + UsePaintCache) = 0; + void clipPath(const SkPath& path, SkClipOp op, bool do_anti_alias) { + clipPath(path, op, do_anti_alias, UsePaintCache::kEnabled); + } + void clipPath(const SkPath& path, SkClipOp op) { + clipPath(path, op, /*do_anti_alias=*/false, UsePaintCache::kEnabled); + } void clipPath(const SkPath& path, bool do_anti_alias) { - clipPath(path, SkClipOp::kIntersect, do_anti_alias); + clipPath(path, SkClipOp::kIntersect, do_anti_alias, + UsePaintCache::kEnabled); } virtual SkRect getLocalClipBounds() const = 0; @@ -141,7 +151,12 @@ class CC_PAINT_EXPORT PaintCanvas { SkScalar rx, SkScalar ry, const PaintFlags& flags) = 0; - virtual void drawPath(const SkPath& path, const PaintFlags& flags) = 0; + virtual void drawPath(const SkPath& path, + const PaintFlags& flags, + UsePaintCache) = 0; + void drawPath(const SkPath& path, const PaintFlags& flags) { + drawPath(path, flags, UsePaintCache::kEnabled); + } virtual void drawImage(const PaintImage& image, SkScalar left, SkScalar top, diff --git a/chromium/cc/paint/paint_filter.cc b/chromium/cc/paint/paint_filter.cc index 2982969f573..6df6898d3a8 100644 --- a/chromium/cc/paint/paint_filter.cc +++ b/chromium/cc/paint/paint_filter.cc @@ -4,19 +4,246 @@ #include "cc/paint/paint_filter.h" +#include <string> +#include <utility> +#include <vector> + +#include "base/no_destructor.h" +#include "build/build_config.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" #include "third_party/skia/include/core/SkMath.h" +#include "third_party/skia/include/core/SkString.h" #include "third_party/skia/include/effects/SkImageFilters.h" #include "third_party/skia/include/effects/SkPerlinNoiseShader.h" +#include "third_party/skia/include/effects/SkRuntimeEffect.h" +#include "third_party/skia/src/effects/imagefilters/SkRuntimeImageFilter.h" namespace cc { namespace { const bool kHasNoDiscardableImages = false; +#if defined(OS_ANDROID) +struct StretchShaderUniforms { + // multiplier to apply to scale effect + float uMaxStretchIntensity; + + // Maximum percentage to stretch beyond bounds of target + float uStretchAffectedDistX; + float uStretchAffectedDistY; + + // Distance stretched as a function of the normalized overscroll times + // scale intensity + float uDistanceStretchedX; + float uDistanceStretchedY; + float uInverseDistanceStretchedX; + float uInverseDistanceStretchedY; + float uDistDiffX; + + // Difference between the peak stretch amount and overscroll amount normalized + float uDistDiffY; + + // Horizontal offset represented as a ratio of pixels divided by the target + // width + float uScrollX; + // Vertical offset represented as a ratio of pixels divided by the target + // height + float uScrollY; + + // Normalized overscroll amount in the horizontal direction + float uOverscrollX; + + // Normalized overscroll amount in the vertical direction + float uOverscrollY; + float viewportWidth; // target height in pixels + float viewportHeight; // target width in pixels + + // uInterpolationStrength is the intensity of the interpolation. + // if uInterpolationStrength is 0, then the stretch is constant for all the + // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch + // intensity is interpolated based on the pixel position in the + // uStretchAffectedDist area; The closer we are from the scroll anchor point, + // the more it stretches, and the other way around. + float uInterpolationStrength; +}; + +const char* kStretchShader = R"( + uniform shader uContentTexture; + + // multiplier to apply to scale effect + uniform float uMaxStretchIntensity; + + // Maximum percentage to stretch beyond bounds of target + uniform float uStretchAffectedDistX; + uniform float uStretchAffectedDistY; + + // Distance stretched as a function of the normalized overscroll times + // scale intensity + uniform float uDistanceStretchedX; + uniform float uDistanceStretchedY; + uniform float uInverseDistanceStretchedX; + uniform float uInverseDistanceStretchedY; + uniform float uDistDiffX; + + // Difference between the peak stretch amount and overscroll amount + // normalized + uniform float uDistDiffY; + + // Horizontal offset represented as a ratio of pixels divided by the target + // width + uniform float uScrollX; + // Vertical offset represented as a ratio of pixels divided by the target + // height + uniform float uScrollY; + + // Normalized overscroll amount in the horizontal direction + uniform float uOverscrollX; + + // Normalized overscroll amount in the vertical direction + uniform float uOverscrollY; + uniform float viewportWidth; // target height in pixels + uniform float viewportHeight; // target width in pixels + + // uInterpolationStrength is the intensity of the interpolation. + // if uInterpolationStrength is 0, then the stretch is constant for all the + // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch + // intensity is interpolated based on the pixel position in the + // uStretchAffectedDist area; The closer we are from the scroll anchor + // point, the more it stretches, and the other way around. + uniform float uInterpolationStrength; + + float easeInCubic(float t, float d) { + float tmp = t * d; + return tmp * tmp * tmp; + } + + float computeOverscrollStart( + float inPos, + float overscroll, + float uStretchAffectedDist, + float uInverseStretchAffectedDist, + float distanceStretched, + float interpolationStrength + ) { + float offsetPos = uStretchAffectedDist - inPos; + float posBasedVariation = mix( + 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), + interpolationStrength); + float stretchIntensity = overscroll * posBasedVariation; + return distanceStretched - (offsetPos / (1. + stretchIntensity)); + } + + float computeOverscrollEnd( + float inPos, + float overscroll, + float reverseStretchDist, + float uStretchAffectedDist, + float uInverseStretchAffectedDist, + float distanceStretched, + float interpolationStrength + ) { + float offsetPos = inPos - reverseStretchDist; + float posBasedVariation = mix( + 1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), + interpolationStrength); + float stretchIntensity = (-overscroll) * posBasedVariation; + return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity))); + } + + // Prefer usage of return values over out parameters as it enables + // SKSL to properly inline method calls and works around potential GPU + // driver issues on Wembly. See b/182566543 for details + float computeOverscroll( + float inPos, + float overscroll, + float uStretchAffectedDist, + float uInverseStretchAffectedDist, + float distanceStretched, + float distanceDiff, + float interpolationStrength + ) { + float outPos = inPos; + if (overscroll > 0) { + if (inPos <= uStretchAffectedDist) { + outPos = computeOverscrollStart( + inPos, + overscroll, + uStretchAffectedDist, + uInverseStretchAffectedDist, + distanceStretched, + interpolationStrength + ); + } else if (inPos >= distanceStretched) { + outPos = distanceDiff + inPos; + } + } + if (overscroll < 0) { + float stretchAffectedDist = 1. - uStretchAffectedDist; + if (inPos >= stretchAffectedDist) { + outPos = computeOverscrollEnd( + inPos, + overscroll, + stretchAffectedDist, + uStretchAffectedDist, + uInverseStretchAffectedDist, + distanceStretched, + interpolationStrength + ); + } else if (inPos < stretchAffectedDist) { + outPos = -distanceDiff + inPos; + } + } + return outPos; + } + + vec4 main(vec2 coord) { + // Normalize SKSL pixel coordinate into a unit vector + float inU = coord.x / viewportWidth; + float inV = coord.y / viewportHeight; + float outU; + float outV; + float stretchIntensity; + // Add the normalized scroll position within scrolling list + inU += uScrollX; + inV += uScrollY; + outU = inU; + outV = inV; + outU = computeOverscroll( + inU, + uOverscrollX, + uStretchAffectedDistX, + uInverseDistanceStretchedX, + uDistanceStretchedX, + uDistDiffX, + uInterpolationStrength + ); + outV = computeOverscroll( + inV, + uOverscrollY, + uStretchAffectedDistY, + uInverseDistanceStretchedY, + uDistanceStretchedY, + uDistDiffY, + uInterpolationStrength + ); + coord.x = outU * viewportWidth; + coord.y = outV * viewportHeight; + return sample(uContentTexture, coord); + })"; + +static const float CONTENT_DISTANCE_STRETCHED = 1.f; +static const float INTERPOLATION_STRENGTH_VALUE = 0.7f; + +sk_sp<SkRuntimeEffect> getStretchEffect() { + static base::NoDestructor<SkRuntimeEffect::Result> effect( + SkRuntimeEffect::MakeForShader(SkString(kStretchShader))); + return effect->effect; +} +#endif + bool AreFiltersEqual(const PaintFilter* one, const PaintFilter* two) { if (!one || !two) return !one && !two; @@ -107,6 +334,8 @@ std::string PaintFilter::TypeToString(Type type) { return "kLightingPoint"; case Type::kLightingSpot: return "kLightingSpot"; + case Type::kStretch: + return "kStretch"; } NOTREACHED(); return "Unknown"; @@ -219,6 +448,9 @@ bool PaintFilter::operator==(const PaintFilter& other) const { case Type::kLightingSpot: return *static_cast<const LightingSpotPaintFilter*>(this) == static_cast<const LightingSpotPaintFilter&>(other); + case Type::kStretch: + return *static_cast<const StretchPaintFilter*>(this) == + static_cast<const StretchPaintFilter&>(other); } NOTREACHED(); return true; @@ -658,7 +890,7 @@ bool DisplacementMapEffectPaintFilter::operator==( ImagePaintFilter::ImagePaintFilter(PaintImage image, const SkRect& src_rect, const SkRect& dst_rect, - SkFilterQuality filter_quality) + PaintFlags::FilterQuality filter_quality) : PaintFilter(kType, nullptr, !image.IsTextureBacked()), image_(std::move(image)), src_rect_(src_rect), @@ -709,24 +941,90 @@ bool ImagePaintFilter::operator==(const ImagePaintFilter& other) const { } RecordPaintFilter::RecordPaintFilter(sk_sp<PaintRecord> record, - const SkRect& record_bounds) - : RecordPaintFilter(std::move(record), record_bounds, nullptr) {} + const SkRect& record_bounds, + const gfx::SizeF& raster_scale, + ScalingBehavior scaling_behavior) + : RecordPaintFilter(std::move(record), + record_bounds, + raster_scale, + scaling_behavior, + nullptr) {} RecordPaintFilter::RecordPaintFilter(sk_sp<PaintRecord> record, const SkRect& record_bounds, + const gfx::SizeF& raster_scale, + ScalingBehavior scaling_behavior, ImageProvider* image_provider) : PaintFilter(kType, nullptr, record->HasDiscardableImages()), record_(std::move(record)), - record_bounds_(record_bounds) { - cached_sk_filter_ = SkImageFilters::Picture( - ToSkPicture(record_, record_bounds_, image_provider)); + record_bounds_(record_bounds), + raster_scale_(raster_scale), + scaling_behavior_(scaling_behavior) { + DCHECK(raster_scale_.width() > 0.f && raster_scale_.height() > 0.f); + + sk_sp<SkPicture> picture = + ToSkPicture(record_, record_bounds_, image_provider); + + if (scaling_behavior == ScalingBehavior::kRasterAtScale || + record_bounds_.isEmpty()) { + cached_sk_filter_ = SkImageFilters::Picture(std::move(picture)); + } else { + DCHECK(scaling_behavior == ScalingBehavior::kFixedScale); + // Convert the record to an image and then reference that in the filter DAG + int width = SkScalarCeilToInt(record_bounds.width()); + int height = SkScalarCeilToInt(record_bounds.height()); + auto image = SkImage::MakeFromPicture( + std::move(picture), SkISize::Make(width, height), nullptr, nullptr, + SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()); + + // Must account for the raster scale when drawing the picture image, + SkRect src = SkRect::MakeWH(record_bounds.width(), record_bounds.height()); + SkScalar inv_x = 1.f / raster_scale_.width(); + SkScalar inv_y = 1.f / raster_scale_.height(); + SkRect dst = {inv_x * src.fLeft, inv_y * src.fTop, inv_x * src.fRight, + inv_y * src.fBottom}; + + // Use Mitchell cubic filter, matching historic + // PaintFlags::FilterQuality::kHigh + SkSamplingOptions sampling(SkCubicResampler::Mitchell()); + cached_sk_filter_ = + SkImageFilters::Image(std::move(image), src, dst, sampling); + } } RecordPaintFilter::~RecordPaintFilter() = default; +sk_sp<RecordPaintFilter> RecordPaintFilter::CreateScaledPaintRecord( + const SkMatrix& ctm, + int max_texture_size) const { + // If this is already fixed scale, then this is already good to go, and if + // the bounds are empty the filter produces no output so keep it as-is. + if (scaling_behavior_ == ScalingBehavior::kFixedScale || + record_bounds_.isEmpty()) { + return sk_ref_sp<RecordPaintFilter>(this); + } + + // For creating a deserialized RecordPaintFilter, extract the scale factor at + // which it would have been rasterized at for the given ctm. This is modeled + // after PaintShader::CreateScaledPaintRecord. + SkRect scaled_record_bounds = + PaintRecord::GetFixedScaleBounds(ctm, record_bounds_, max_texture_size); + if (scaled_record_bounds.isEmpty()) + return nullptr; + + gfx::SizeF raster_scale = { + scaled_record_bounds.width() / record_bounds_.width(), + scaled_record_bounds.height() / record_bounds_.height()}; + + return sk_make_sp<RecordPaintFilter>(record_, scaled_record_bounds, + raster_scale, + ScalingBehavior::kFixedScale); +} + size_t RecordPaintFilter::SerializedSize() const { base::CheckedNumeric<size_t> total_size = - BaseSerializedSize() + sizeof(record_bounds_); + BaseSerializedSize() + sizeof(record_bounds_) + sizeof(raster_scale_) + + sizeof(scaling_behavior_) + sizeof(bool); total_size += PaintOpWriter::GetRecordSize(record_.get()); return total_size.ValueOrDefault(0u); } @@ -734,11 +1032,14 @@ size_t RecordPaintFilter::SerializedSize() const { sk_sp<PaintFilter> RecordPaintFilter::SnapshotWithImagesInternal( ImageProvider* image_provider) const { return sk_sp<RecordPaintFilter>( - new RecordPaintFilter(record_, record_bounds_, image_provider)); + new RecordPaintFilter(record_, record_bounds_, raster_scale_, + scaling_behavior_, image_provider)); } bool RecordPaintFilter::operator==(const RecordPaintFilter& other) const { return !!record_ == !!other.record_ && + scaling_behavior_ == other.scaling_behavior_ && + raster_scale_ == other.raster_scale_ && PaintOp::AreSkRectsEqual(record_bounds_, other.record_bounds_); } @@ -964,7 +1265,7 @@ bool TurbulencePaintFilter::operator==( ShaderPaintFilter::ShaderPaintFilter(sk_sp<PaintShader> shader, uint8_t alpha, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, SkImageFilters::Dither dither, const CropRect* crop_rect) : PaintFilter(kType, crop_rect, shader->has_discardable_images()), @@ -1028,7 +1329,7 @@ bool ShaderPaintFilter::operator==(const ShaderPaintFilter& other) const { } MatrixPaintFilter::MatrixPaintFilter(const SkMatrix& matrix, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, sk_sp<PaintFilter> input) : PaintFilter(Type::kMatrix, nullptr, HasDiscardableImages(input)), matrix_(matrix), @@ -1251,4 +1552,78 @@ bool LightingSpotPaintFilter::operator==( AreFiltersEqual(input_.get(), other.input_.get()); } +StretchPaintFilter::StretchPaintFilter(SkScalar stretch_x, + SkScalar stretch_y, + SkScalar width, + SkScalar height, + sk_sp<PaintFilter> input, + const CropRect* crop_rect) + : PaintFilter(kType, crop_rect, HasDiscardableImages(input)), + stretch_x_(stretch_x), + stretch_y_(stretch_y), + width_(width), + height_(height), + input_(std::move(input)) { +#if defined(OS_ANDROID) + float normOverScrollDistX = stretch_x_; + float normOverScrollDistY = stretch_y_; + float distanceStretchedX = + CONTENT_DISTANCE_STRETCHED / (1 + std::abs(normOverScrollDistX)); + float distanceStretchedY = + CONTENT_DISTANCE_STRETCHED / (1 + std::abs(normOverScrollDistY)); + float inverseDistanceStretchedX = 1.f / CONTENT_DISTANCE_STRETCHED; + float inverseDistanceStretchedY = 1.f / CONTENT_DISTANCE_STRETCHED; + float diffX = distanceStretchedX - CONTENT_DISTANCE_STRETCHED; + float diffY = distanceStretchedY - CONTENT_DISTANCE_STRETCHED; + StretchShaderUniforms uniforms; + + uniforms.uInterpolationStrength = INTERPOLATION_STRENGTH_VALUE; + uniforms.uStretchAffectedDistX = CONTENT_DISTANCE_STRETCHED; + uniforms.uStretchAffectedDistY = CONTENT_DISTANCE_STRETCHED; + uniforms.uDistanceStretchedX = distanceStretchedX; + uniforms.uDistanceStretchedY = distanceStretchedY; + uniforms.uInverseDistanceStretchedX = inverseDistanceStretchedX; + uniforms.uInverseDistanceStretchedY = inverseDistanceStretchedY; + uniforms.uDistDiffX = diffX; + uniforms.uDistDiffY = diffY; + uniforms.uOverscrollX = normOverScrollDistX; + uniforms.uOverscrollY = normOverScrollDistY; + uniforms.uScrollX = 0; + uniforms.uScrollY = 0; + uniforms.viewportWidth = width; + uniforms.viewportHeight = height; + sk_sp<SkData> uniformVals = SkData::MakeWithCopy(&uniforms, sizeof(uniforms)); + cached_sk_filter_ = SkMakeRuntimeImageFilter(getStretchEffect(), uniformVals, + GetSkFilter(input_.get())); +#else // defined(OS_ANDROID) + // Stretch filter is only used on android and removed from other platforms + // to reduce size. See https://crbug.com/1226170. +#endif // defined(OS_ANDROID) +} + +StretchPaintFilter::~StretchPaintFilter() = default; + +size_t StretchPaintFilter::SerializedSize() const { + base::CheckedNumeric<size_t> total_size = + BaseSerializedSize() + sizeof(stretch_x_) + sizeof(stretch_y_) + + sizeof(width_) + sizeof(height_); + total_size += GetFilterSize(input_.get()); + return total_size.ValueOrDefault(0u); +} + +sk_sp<PaintFilter> StretchPaintFilter::SnapshotWithImagesInternal( + ImageProvider* image_provider) const { + return sk_make_sp<StretchPaintFilter>(stretch_x_, stretch_y_, width_, height_, + Snapshot(input_, image_provider), + crop_rect()); +} + +bool StretchPaintFilter::operator==(const StretchPaintFilter& other) const { + return PaintOp::AreEqualEvenIfNaN(stretch_x_, other.stretch_x_) && + PaintOp::AreEqualEvenIfNaN(stretch_y_, other.stretch_y_) && + PaintOp::AreEqualEvenIfNaN(width_, other.width_) && + PaintOp::AreEqualEvenIfNaN(height_, other.height_) && + AreFiltersEqual(input_.get(), other.input_.get()); +} + } // namespace cc diff --git a/chromium/cc/paint/paint_filter.h b/chromium/cc/paint/paint_filter.h index 019811fe024..3f11012035e 100644 --- a/chromium/cc/paint/paint_filter.h +++ b/chromium/cc/paint/paint_filter.h @@ -5,6 +5,8 @@ #ifndef CC_PAINT_PAINT_FILTER_H_ #define CC_PAINT_PAINT_FILTER_H_ +#include <string> + #include "base/check_op.h" #include "base/containers/stack_container.h" #include "base/stl_util.h" @@ -56,8 +58,9 @@ class CC_PAINT_EXPORT PaintFilter : public SkRefCnt { kLightingDistant, kLightingPoint, kLightingSpot, - // Update the following if kLightingSpot is not the max anymore. - kMaxValue = kLightingSpot + kStretch, + // Update the following if kStretch is not the max anymore. + kMaxValue = kStretch }; enum class LightingType { kDiffuse, @@ -474,13 +477,13 @@ class CC_PAINT_EXPORT ImagePaintFilter final : public PaintFilter { ImagePaintFilter(PaintImage image, const SkRect& src_rect, const SkRect& dst_rect, - SkFilterQuality filter_quality); + PaintFlags::FilterQuality filter_quality); ~ImagePaintFilter() override; const PaintImage& image() const { return image_; } const SkRect& src_rect() const { return src_rect_; } const SkRect& dst_rect() const { return dst_rect_; } - SkFilterQuality filter_quality() const { return filter_quality_; } + PaintFlags::FilterQuality filter_quality() const { return filter_quality_; } size_t SerializedSize() const override; bool operator==(const ImagePaintFilter& other) const; @@ -493,17 +496,33 @@ class CC_PAINT_EXPORT ImagePaintFilter final : public PaintFilter { PaintImage image_; SkRect src_rect_; SkRect dst_rect_; - SkFilterQuality filter_quality_; + PaintFlags::FilterQuality filter_quality_; }; class CC_PAINT_EXPORT RecordPaintFilter final : public PaintFilter { public: static constexpr Type kType = Type::kPaintRecord; - RecordPaintFilter(sk_sp<PaintRecord> record, const SkRect& record_bounds); + + using ScalingBehavior = PaintShader::ScalingBehavior; + + RecordPaintFilter( + sk_sp<PaintRecord> record, + const SkRect& record_bounds, + const gfx::SizeF& raster_scale = {1.f, 1.f}, + ScalingBehavior scaling_behavior = ScalingBehavior::kRasterAtScale); ~RecordPaintFilter() override; + // Creates a fixed scale RecordPaintFilter for rasterization at the given + // |ctm|. |raster_scale| is set to the scale at which the underlying record + // should be rasterized when the paint filter is used. + // See PaintShader::CreateScaledPaintRecord. + sk_sp<RecordPaintFilter> CreateScaledPaintRecord(const SkMatrix& ctm, + int max_texture_size) const; + const sk_sp<PaintRecord>& record() const { return record_; } SkRect record_bounds() const { return record_bounds_; } + gfx::SizeF raster_scale() const { return raster_scale_; } + ScalingBehavior scaling_behavior() const { return scaling_behavior_; } size_t SerializedSize() const override; bool operator==(const RecordPaintFilter& other) const; @@ -515,10 +534,14 @@ class CC_PAINT_EXPORT RecordPaintFilter final : public PaintFilter { private: RecordPaintFilter(sk_sp<PaintRecord> record, const SkRect& record_bounds, + const gfx::SizeF& raster_scale, + ScalingBehavior scaling_behavior, ImageProvider* image_provider); sk_sp<PaintRecord> record_; SkRect record_bounds_; + gfx::SizeF raster_scale_; // ignored if scaling_behavior is kRasterAtScale + ScalingBehavior scaling_behavior_; }; class CC_PAINT_EXPORT MergePaintFilter final : public PaintFilter { @@ -679,7 +702,7 @@ class CC_PAINT_EXPORT ShaderPaintFilter final : public PaintFilter { ShaderPaintFilter(sk_sp<PaintShader> shader, uint8_t alpha, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, SkImageFilters::Dither dither, const CropRect* crop_rect = nullptr); @@ -687,7 +710,7 @@ class CC_PAINT_EXPORT ShaderPaintFilter final : public PaintFilter { const PaintShader& shader() const { return *shader_; } uint8_t alpha() const { return alpha_; } - SkFilterQuality filter_quality() const { return filter_quality_; } + PaintFlags::FilterQuality filter_quality() const { return filter_quality_; } SkImageFilters::Dither dither() const { return dither_; } size_t SerializedSize() const override; @@ -700,7 +723,7 @@ class CC_PAINT_EXPORT ShaderPaintFilter final : public PaintFilter { private: sk_sp<PaintShader> shader_; uint8_t alpha_; - SkFilterQuality filter_quality_; + PaintFlags::FilterQuality filter_quality_; SkImageFilters::Dither dither_; }; @@ -708,12 +731,12 @@ class CC_PAINT_EXPORT MatrixPaintFilter final : public PaintFilter { public: static constexpr Type kType = Type::kMatrix; MatrixPaintFilter(const SkMatrix& matrix, - SkFilterQuality filter_quality, + PaintFlags::FilterQuality filter_quality, sk_sp<PaintFilter> input); ~MatrixPaintFilter() override; const SkMatrix& matrix() const { return matrix_; } - SkFilterQuality filter_quality() const { return filter_quality_; } + PaintFlags::FilterQuality filter_quality() const { return filter_quality_; } const sk_sp<PaintFilter>& input() const { return input_; } size_t SerializedSize() const override; @@ -725,7 +748,7 @@ class CC_PAINT_EXPORT MatrixPaintFilter final : public PaintFilter { private: SkMatrix matrix_; - SkFilterQuality filter_quality_; + PaintFlags::FilterQuality filter_quality_; sk_sp<PaintFilter> input_; }; @@ -861,6 +884,39 @@ class CC_PAINT_EXPORT LightingSpotPaintFilter final : public PaintFilter { sk_sp<PaintFilter> input_; }; +class CC_PAINT_EXPORT StretchPaintFilter final : public PaintFilter { + public: + static constexpr Type kType = Type::kStretch; + StretchPaintFilter(SkScalar stretch_x, + SkScalar stretch_y, + SkScalar width, + SkScalar height, + sk_sp<PaintFilter> input, + const CropRect* crop_rect = nullptr); + ~StretchPaintFilter() override; + + const sk_sp<PaintFilter>& input() const { return input_; } + + SkScalar stretch_x() const { return stretch_x_; } + SkScalar stretch_y() const { return stretch_y_; } + SkScalar width() const { return width_; } + SkScalar height() const { return height_; } + + size_t SerializedSize() const override; + bool operator==(const StretchPaintFilter& other) const; + + protected: + sk_sp<PaintFilter> SnapshotWithImagesInternal( + ImageProvider* image_provider) const override; + + private: + SkScalar stretch_x_; + SkScalar stretch_y_; + SkScalar width_; + SkScalar height_; + sk_sp<PaintFilter> input_; +}; + } // namespace cc #endif // CC_PAINT_PAINT_FILTER_H_ diff --git a/chromium/cc/paint/paint_filter_unittest.cc b/chromium/cc/paint/paint_filter_unittest.cc index 8b84f2f53d5..ddb8ac02a52 100644 --- a/chromium/cc/paint/paint_filter_unittest.cc +++ b/chromium/cc/paint/paint_filter_unittest.cc @@ -38,7 +38,7 @@ sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, auto image_filter = sk_make_sp<ImagePaintFilter>( image, SkRect::MakeWH(100.f, 100.f), SkRect::MakeWH(100.f, 100.f), - kNone_SkFilterQuality); + PaintFlags::FilterQuality::kNone); auto record = sk_make_sp<PaintOpBuffer>(); record->push<DrawImageOp>(image, 0.f, 0.f); auto record_filter = @@ -114,12 +114,12 @@ sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, return sk_make_sp<ShaderPaintFilter>( PaintShader::MakeImage(image, SkTileMode::kClamp, SkTileMode::kClamp, nullptr), - /*alpha=*/255, kNone_SkFilterQuality, SkImageFilters::Dither::kNo, - &crop_rect); + /*alpha=*/255, PaintFlags::FilterQuality::kNone, + SkImageFilters::Dither::kNo, &crop_rect); } case PaintFilter::Type::kMatrix: - return sk_make_sp<MatrixPaintFilter>(SkMatrix::I(), kNone_SkFilterQuality, - record_filter); + return sk_make_sp<MatrixPaintFilter>( + SkMatrix::I(), PaintFlags::FilterQuality::kNone, record_filter); case PaintFilter::Type::kLightingDistant: return sk_make_sp<LightingDistantPaintFilter>( PaintFilter::LightingType::kDiffuse, SkPoint3::Make(0.1f, 0.2f, 0.3f), @@ -133,6 +133,9 @@ sk_sp<PaintFilter> CreateTestFilter(PaintFilter::Type filter_type, 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); + case PaintFilter::Type::kStretch: + return sk_make_sp<StretchPaintFilter>(0.1f, 0.2f, 100.f, 200.f, + image_filter, &crop_rect); } NOTREACHED(); return nullptr; diff --git a/chromium/cc/paint/paint_flags.cc b/chromium/cc/paint/paint_flags.cc index 5f4af712f04..8a83963b968 100644 --- a/chromium/cc/paint/paint_flags.cc +++ b/chromium/cc/paint/paint_flags.cc @@ -9,6 +9,7 @@ #include "cc/paint/paint_filter.h" #include "cc/paint/paint_op_buffer.h" #include "cc/paint/paint_op_writer.h" +#include "cc/paint/paint_shader.h" namespace { @@ -26,7 +27,8 @@ PaintFlags::PaintFlags() { bitfields_.cap_type_ = SkPaint::kDefault_Cap; bitfields_.join_type_ = SkPaint::kDefault_Join; bitfields_.style_ = SkPaint::kFill_Style; - bitfields_.filter_quality_ = SkFilterQuality::kNone_SkFilterQuality; + bitfields_.filter_quality_ = + static_cast<int>(PaintFlags::FilterQuality::kNone); static_assert(sizeof(bitfields_) <= sizeof(bitfields_uint_), "Too many bitfields"); @@ -59,6 +61,14 @@ void PaintFlags::setImageFilter(sk_sp<PaintFilter> filter) { image_filter_ = std::move(filter); } +bool PaintFlags::ShaderIsOpaque() const { + return shader_->IsOpaque(); +} + +void PaintFlags::setShader(sk_sp<PaintShader> shader) { + shader_ = std::move(shader); +} + bool PaintFlags::nothingToDraw() const { // Duplicated from SkPaint to avoid having to construct an SkPaint to // answer this question. @@ -142,20 +152,19 @@ SkPaint PaintFlags::ToSkPaint() const { paint.setStrokeCap(static_cast<SkPaint::Cap>(getStrokeCap())); paint.setStrokeJoin(static_cast<SkPaint::Join>(getStrokeJoin())); paint.setStyle(static_cast<SkPaint::Style>(getStyle())); - paint.setFilterQuality(getFilterQuality()); return paint; } SkSamplingOptions PaintFlags::FilterQualityToSkSamplingOptions( - SkFilterQuality filter_quality) { + PaintFlags::FilterQuality filter_quality) { switch (filter_quality) { - case SkFilterQuality::kHigh_SkFilterQuality: - return SkSamplingOptions(SkCubicResampler::Mitchell()); - case SkFilterQuality::kMedium_SkFilterQuality: + case PaintFlags::FilterQuality::kHigh: + return SkSamplingOptions(SkCubicResampler::CatmullRom()); + case PaintFlags::FilterQuality::kMedium: return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); - case SkFilterQuality::kLow_SkFilterQuality: + case PaintFlags::FilterQuality::kLow: return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone); - case SkFilterQuality::kNone_SkFilterQuality: + case PaintFlags::FilterQuality::kNone: return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone); default: NOTREACHED(); diff --git a/chromium/cc/paint/paint_flags.h b/chromium/cc/paint/paint_flags.h index 2e9ddc16ca1..9106bd55ef1 100644 --- a/chromium/cc/paint/paint_flags.h +++ b/chromium/cc/paint/paint_flags.h @@ -9,17 +9,18 @@ #include "base/compiler_specific.h" #include "cc/paint/paint_export.h" -#include "cc/paint/paint_shader.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkDrawLooper.h" #include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPathEffect.h" +#include "third_party/skia/include/core/SkSamplingOptions.h" class SkCanvas; namespace cc { class PaintFilter; +class PaintShader; class CC_PAINT_EXPORT PaintFlags { public: @@ -57,11 +58,19 @@ class CC_PAINT_EXPORT PaintFlags { ALWAYS_INLINE void setAntiAlias(bool aa) { bitfields_.antialias_ = aa; } ALWAYS_INLINE bool isDither() const { return bitfields_.dither_; } ALWAYS_INLINE void setDither(bool dither) { bitfields_.dither_ = dither; } - ALWAYS_INLINE void setFilterQuality(SkFilterQuality quality) { - bitfields_.filter_quality_ = quality; + + enum class FilterQuality { + kNone, + kLow, + kMedium, + kHigh, + kLast = kHigh, + }; + ALWAYS_INLINE void setFilterQuality(FilterQuality quality) { + bitfields_.filter_quality_ = static_cast<int>(quality); } - ALWAYS_INLINE SkFilterQuality getFilterQuality() const { - return static_cast<SkFilterQuality>(bitfields_.filter_quality_); + ALWAYS_INLINE FilterQuality getFilterQuality() const { + return static_cast<FilterQuality>(bitfields_.filter_quality_); } ALWAYS_INLINE bool useDarkModeForImage() const { return bitfields_.use_dark_mode_for_image_; @@ -118,11 +127,9 @@ class CC_PAINT_EXPORT PaintFlags { // Returns whether the shader is opaque. Note that it is only valid to call // this function if HasShader() returns true. - ALWAYS_INLINE bool ShaderIsOpaque() const { return shader_->IsOpaque(); } + bool ShaderIsOpaque() const; - ALWAYS_INLINE void setShader(sk_sp<PaintShader> shader) { - shader_ = std::move(shader); - } + void setShader(sk_sp<PaintShader> shader); ALWAYS_INLINE const sk_sp<SkPathEffect>& getPathEffect() const { return path_effect_; @@ -170,7 +177,7 @@ class CC_PAINT_EXPORT PaintFlags { } static SkSamplingOptions FilterQualityToSkSamplingOptions( - SkFilterQuality filter_quality); + FilterQuality filter_quality); bool IsValid() const; bool operator==(const PaintFlags& other) const; diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc index d42df94bfac..c382ee15e7d 100644 --- a/chromium/cc/paint/paint_op_buffer.cc +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -4,10 +4,12 @@ #include "cc/paint/paint_op_buffer.h" +#include <algorithm> #include <memory> #include <utility> #include <vector> +#include "base/stl_util.h" #include "build/build_config.h" #include "cc/paint/decoded_draw_image.h" #include "cc/paint/display_item_list.h" @@ -23,6 +25,7 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkRegion.h" #include "third_party/skia/include/core/SkSerialProcs.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/docs/SkPDFDocument.h" #include "third_party/skia/include/gpu/GrRecordingContext.h" #include "ui/gfx/skia_util.h" @@ -31,15 +34,17 @@ namespace cc { namespace { // In a future CL, convert DrawImage to explicitly take sampling instead of // quality -SkFilterQuality sampling_to_quality(const SkSamplingOptions& sampling) { +PaintFlags::FilterQuality sampling_to_quality( + const SkSamplingOptions& sampling) { if (sampling.useCubic) { - return kHigh_SkFilterQuality; + return PaintFlags::FilterQuality::kHigh; } if (sampling.mipmap != SkMipmapMode::kNone) { - return kMedium_SkFilterQuality; + return PaintFlags::FilterQuality::kMedium; } - return sampling.filter == SkFilterMode::kLinear ? kLow_SkFilterQuality - : kNone_SkFilterQuality; + return sampling.filter == SkFilterMode::kLinear + ? PaintFlags::FilterQuality::kLow + : PaintFlags::FilterQuality::kNone; } DrawImage CreateDrawImage(const PaintImage& image, @@ -413,7 +418,7 @@ size_t ClipPathOp::Serialize(const PaintOp* base_op, const SkM44& original_ctm) { auto* op = static_cast<const ClipPathOp*>(base_op); PaintOpWriter helper(memory, size, options); - helper.Write(op->path); + helper.Write(op->path, op->use_cache); helper.Write(op->op); helper.Write(op->antialias); return helper.size(); @@ -629,7 +634,7 @@ size_t DrawPathOp::Serialize(const PaintOp* base_op, if (!flags_to_serialize) flags_to_serialize = &op->flags; helper.Write(*flags_to_serialize, current_ctm); - helper.Write(op->path); + helper.Write(op->path, op->use_cache); helper.Write(op->sk_path_fill_type); return helper.size(); } @@ -2432,7 +2437,7 @@ gfx::Rect PaintOp::ComputePaintRect(const PaintOp* op, } else { const PaintFlags* flags = op->IsPaintOpWithFlags() - ? &static_cast<const PaintOpWithFlags*>(op)->flags + ? &(static_cast<const PaintOpWithFlags*>(op)->flags) : nullptr; SkRect paint_rect = MapRect(ctm, op_rect); if (flags) { @@ -3009,6 +3014,45 @@ sk_sp<PaintOpBuffer> PaintOpBuffer::MakeFromMemory( return buffer; } +// static +SkRect PaintOpBuffer::GetFixedScaleBounds(const SkMatrix& ctm, + const SkRect& bounds, + int max_texture_size) { + SkSize scale; + if (!ctm.decomposeScale(&scale)) { + // Decomposition failed, use an approximation. + scale.set(SkScalarSqrt(ctm.getScaleX() * ctm.getScaleX() + + ctm.getSkewX() * ctm.getSkewX()), + SkScalarSqrt(ctm.getScaleY() * ctm.getScaleY() + + ctm.getSkewY() * ctm.getSkewY())); + } + + SkScalar raster_width = bounds.width() * scale.width(); + SkScalar raster_height = bounds.height() * scale.height(); + SkScalar tile_area = raster_width * raster_height; + // Clamp the tile area to about 4M pixels, and per-dimension max texture size + // if it's provided. + static const SkScalar kMaxTileArea = 2048 * 2048; + SkScalar down_scale = 1.f; + if (tile_area > kMaxTileArea) { + down_scale = SkScalarSqrt(kMaxTileArea / tile_area); + } + if (max_texture_size > 0) { + // This only updates down_scale if the tile is larger than the texture size + // after ensuring its area is less than kMaxTileArea + down_scale = std::min( + down_scale, max_texture_size / std::max(raster_width, raster_height)); + } + + if (down_scale < 1.f) { + scale.set(down_scale * scale.width(), down_scale * scale.height()); + } + return SkRect::MakeXYWH( + bounds.fLeft * scale.width(), bounds.fTop * scale.height(), + SkScalarCeilToInt(SkScalarAbs(scale.width() * bounds.width())), + SkScalarCeilToInt(SkScalarAbs(scale.height() * bounds.height()))); +} + void PaintOpBuffer::ReallocBuffer(size_t new_size) { DCHECK_GE(new_size, used_); std::unique_ptr<char, base::AlignedFreeDeleter> new_data( diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h index aafee1295a1..777bffba80d 100644 --- a/chromium/cc/paint/paint_op_buffer.h +++ b/chromium/cc/paint/paint_op_buffer.h @@ -28,6 +28,7 @@ #include "cc/paint/paint_flags.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkScalar.h" #include "ui/gfx/geometry/rect.h" @@ -388,8 +389,15 @@ class CC_PAINT_EXPORT AnnotateOp final : public PaintOp { class CC_PAINT_EXPORT ClipPathOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::ClipPath; - ClipPathOp(SkPath path, SkClipOp op, bool antialias) - : PaintOp(kType), path(path), op(op), antialias(antialias) {} + ClipPathOp(SkPath path, + SkClipOp op, + bool antialias, + UsePaintCache use_paint_cache = UsePaintCache::kEnabled) + : PaintOp(kType), + path(path), + op(op), + antialias(antialias), + use_cache(use_paint_cache) {} static void Raster(const ClipPathOp* op, SkCanvas* canvas, const PlaybackParams& params); @@ -402,6 +410,7 @@ class CC_PAINT_EXPORT ClipPathOp final : public PaintOp { ThreadsafePath path; SkClipOp op; bool antialias; + UsePaintCache use_cache; private: ClipPathOp() : PaintOp(kType) {} @@ -684,10 +693,13 @@ class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawPath; static constexpr bool kIsDrawOp = true; - DrawPathOp(const SkPath& path, const PaintFlags& flags) + DrawPathOp(const SkPath& path, + const PaintFlags& flags, + UsePaintCache use_paint_cache = UsePaintCache::kEnabled) : PaintOpWithFlags(kType, flags), path(path), - sk_path_fill_type(static_cast<uint8_t>(path.getFillType())) {} + sk_path_fill_type(static_cast<uint8_t>(path.getFillType())), + use_cache(use_paint_cache) {} static void RasterWithFlags(const DrawPathOp* op, const PaintFlags* flags, SkCanvas* canvas, @@ -704,6 +716,7 @@ class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags { // serialize/deserialize this value and set it on the SkPath before handing it // to Skia. uint8_t sk_path_fill_type; + UsePaintCache use_cache; private: DrawPathOp() : PaintOpWithFlags(kType) {} @@ -1044,6 +1057,15 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { size_t input_size, const PaintOp::DeserializeOptions& options); + // Given the |bounds| of a PaintOpBuffer that would be transformed by |ctm| + // when rendered, compute the bounds needed to raster the buffer at a fixed + // scale into an auxiliary image instead of rasterizing at scale dynamically. + // This is used to enforce scaling decisions made pre-serialization when + // rasterizing after deserializing the buffer. + static SkRect GetFixedScaleBounds(const SkMatrix& ctm, + const SkRect& bounds, + int max_texture_size = 0); + // Returns the size of the paint op buffer. That is, the number of ops // contained in it. size_t size() const { return op_count_; } diff --git a/chromium/cc/paint/paint_op_buffer_serializer.cc b/chromium/cc/paint/paint_op_buffer_serializer.cc index 12e8f8af1d8..1dc29cc96bf 100644 --- a/chromium/cc/paint/paint_op_buffer_serializer.cc +++ b/chromium/cc/paint/paint_op_buffer_serializer.cc @@ -76,11 +76,9 @@ void PaintOpBufferSerializer::Serialize(const PaintOpBuffer* buffer) { SerializeBuffer(canvas.get(), buffer, nullptr); } -void PaintOpBufferSerializer::Serialize( - const PaintOpBuffer* buffer, - const gfx::Rect& playback_rect, - const gfx::SizeF& post_scale, - const SkMatrix& post_matrix_for_analysis) { +void PaintOpBufferSerializer::Serialize(const PaintOpBuffer* buffer, + const gfx::Rect& playback_rect, + const gfx::SizeF& post_scale) { std::unique_ptr<SkCanvas> canvas = MakeAnalysisCanvas(options_); PlaybackParams params = MakeParams(canvas.get()); @@ -97,7 +95,6 @@ void PaintOpBufferSerializer::Serialize( SerializeOp(canvas.get(), &scale_op, nullptr, params); } - canvas->concat(post_matrix_for_analysis); SerializeBuffer(canvas.get(), buffer, nullptr); } diff --git a/chromium/cc/paint/paint_op_buffer_serializer.h b/chromium/cc/paint/paint_op_buffer_serializer.h index ebd69c14ba1..9783440f25c 100644 --- a/chromium/cc/paint/paint_op_buffer_serializer.h +++ b/chromium/cc/paint/paint_op_buffer_serializer.h @@ -61,17 +61,14 @@ class CC_PAINT_EXPORT PaintOpBufferSerializer { // generally be used for internal PaintOpBuffers that want to be sent as-is. void Serialize(const PaintOpBuffer* buffer); // Serialize the buffer with a scale and a playback rect. This should - // generally be used for internal PaintOpBuffers in PaintShaders that have - // a scale and a tiling, but don't want the clearing or other complicated - // logic of the top level Serialize. - // post_matrix_for_analysis adds a scale that is not added to the serialized - // buffer, but used in analysis. This is required for cases that don't modify - // the record during serialization, but need to send resources based on the - // raster scale (mainly PaintRecord backed PaintFilters). + // generally be used for internal PaintOpBuffers in PaintShaders and + // PaintFilters that need to guarantee the nested buffer is rasterized at the + // specific scale to a separate image. This ensures that scale-dependent + // analysis made during serialization is consistent with analysis done during + // rasterization. void Serialize(const PaintOpBuffer* buffer, const gfx::Rect& playback_rect, - const gfx::SizeF& post_scale, - const SkMatrix& post_matrix_for_analysis); + const gfx::SizeF& post_scale); bool valid() const { return valid_; } diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc index a4084107699..3280a5de6a8 100644 --- a/chromium/cc/paint/paint_op_buffer_unittest.cc +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -5,9 +5,10 @@ #include "cc/paint/paint_op_buffer.h" #include <algorithm> + #include "base/bind.h" +#include "base/cxx17_backports.h" #include "base/memory/scoped_refptr.h" -#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "cc/paint/decoded_draw_image.h" @@ -287,8 +288,8 @@ TEST(PaintOpBufferTest, SaveDrawTextBlobRestore) { PaintFlags paint_flags; EXPECT_TRUE(paint_flags.SupportsFoldingAlpha()); - buffer.push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), 0, 0, - paint_flags); + buffer.push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), 0.0f, + 0.0f, paint_flags); buffer.push<RestoreOp>(); SaveCountingCanvas canvas; @@ -558,16 +559,19 @@ TEST(PaintOpBufferTest, SlowPaths) { SkPath path; path.addCircle(2, 2, 5); EXPECT_TRUE(path.isConvex()); - buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, true); + buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, /*antialias=*/true, + UsePaintCache::kDisabled); EXPECT_EQ(buffer->numSlowPaths(), 1); // Concave paths are slow only when antialiased. SkPath concave = path; concave.addCircle(3, 4, 2); EXPECT_FALSE(concave.isConvex()); - buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, true); + buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, /*antialias=*/true, + UsePaintCache::kDisabled); EXPECT_EQ(buffer->numSlowPaths(), 2); - buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, false); + buffer->push<ClipPathOp>(concave, SkClipOp::kIntersect, /*antialias=*/false, + UsePaintCache::kDisabled); EXPECT_EQ(buffer->numSlowPaths(), 2); // Drawing a record with slow paths into another adds the same @@ -608,11 +612,13 @@ TEST(PaintOpBufferTest, NonAAPaint) { path.addCircle(2, 2, 5); // ClipPathOp with AA - buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, true /* antialias */); + buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, /*antialias=*/true, + UsePaintCache::kDisabled); EXPECT_FALSE(buffer->HasNonAAPaint()); // ClipPathOp without AA - buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, false /* antialias */); + buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, /*antialias=*/false, + UsePaintCache::kDisabled); EXPECT_TRUE(buffer->HasNonAAPaint()); } @@ -641,7 +647,7 @@ TEST(PaintOpBufferTest, NonAAPaint) { SkPath path; path.addCircle(2, 2, 5); sub_buffer->push<ClipPathOp>(path, SkClipOp::kIntersect, - false /* antialias */); + /*antialias=*/false, UsePaintCache::kDisabled); EXPECT_TRUE(sub_buffer->HasNonAAPaint()); buffer->push<DrawRecordOp>(sub_buffer); @@ -1144,7 +1150,7 @@ std::vector<PaintFlags> test_flags = { flags.setStrokeCap(PaintFlags::kSquare_Cap); flags.setStrokeJoin(PaintFlags::kBevel_Join); flags.setStyle(PaintFlags::kStroke_Style); - flags.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality); + flags.setFilterQuality(PaintFlags::FilterQuality::kMedium); flags.setShader(PaintShader::MakeColor(SkColorSetARGB(1, 2, 3, 4))); return flags; }(), @@ -1158,7 +1164,7 @@ std::vector<PaintFlags> test_flags = { flags.setStrokeCap(PaintFlags::kRound_Cap); flags.setStrokeJoin(PaintFlags::kRound_Join); flags.setStyle(PaintFlags::kFill_Style); - flags.setFilterQuality(SkFilterQuality::kHigh_SkFilterQuality); + flags.setFilterQuality(PaintFlags::FilterQuality::kHigh); SkScalar intervals[] = {1.f, 1.f}; flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); @@ -1415,7 +1421,8 @@ void PushAnnotateOps(PaintOpBuffer* buffer) { void PushClipPathOps(PaintOpBuffer* buffer) { for (size_t i = 0; i < test_paths.size(); ++i) { SkClipOp op = i % 3 ? SkClipOp::kDifference : SkClipOp::kIntersect; - buffer->push<ClipPathOp>(test_paths[i], op, !!(i % 2)); + buffer->push<ClipPathOp>(test_paths[i], op, /*antialias=*/!!(i % 2), + UsePaintCache::kDisabled); } ValidateOps<ClipPathOp>(buffer); } @@ -1530,7 +1537,8 @@ void PushDrawOvalOps(PaintOpBuffer* buffer) { void PushDrawPathOps(PaintOpBuffer* buffer) { size_t len = std::min(test_paths.size(), test_flags.size()); for (size_t i = 0; i < len; ++i) - buffer->push<DrawPathOp>(test_paths[i], test_flags[i]); + buffer->push<DrawPathOp>(test_paths[i], test_flags[i], + UsePaintCache::kDisabled); ValidateOps<DrawPathOp>(buffer); } @@ -1687,7 +1695,7 @@ void PushTranslateOps(PaintOpBuffer* buffer) { void PushSetNodeIdOps(PaintOpBuffer* buffer) { for (size_t i = 0; i < test_ids.size(); i++) - buffer->push<SetNodeIdOp>(test_ids[i]); + buffer->push<SetNodeIdOp>(static_cast<int>(test_ids[i])); ValidateOps<SetNodeIdOp>(buffer); } @@ -2458,13 +2466,15 @@ TEST(PaintOpBufferTest, ValidateSkClip) { // Successful first op. SkPath path; - buffer.push<ClipPathOp>(path, SkClipOp::kMax_EnumValue, true); + buffer.push<ClipPathOp>(path, SkClipOp::kMax_EnumValue, /*antialias=*/true, + UsePaintCache::kDisabled); // Bad other ops. SkClipOp bad_clip = static_cast<SkClipOp>( static_cast<uint32_t>(SkClipOp::kMax_EnumValue) + 1); - buffer.push<ClipPathOp>(path, bad_clip, true); + buffer.push<ClipPathOp>(path, bad_clip, /*antialias=*/true, + UsePaintCache::kDisabled); buffer.push<ClipRectOp>(test_rects[0], bad_clip, true); buffer.push<ClipRRectOp>(test_rrects[0], bad_clip, false); @@ -2799,7 +2809,7 @@ class MockImageProvider : public ImageProvider { : fail_all_decodes_(fail_all_decodes) {} MockImageProvider(std::vector<SkSize> src_rect_offset, std::vector<SkSize> scale, - std::vector<SkFilterQuality> quality) + std::vector<PaintFlags::FilterQuality> quality) : src_rect_offset_(src_rect_offset), scale_(scale), quality_(quality) {} ~MockImageProvider() override = default; @@ -2826,7 +2836,7 @@ class MockImageProvider : public ImageProvider { private: std::vector<SkSize> src_rect_offset_; std::vector<SkSize> scale_; - std::vector<SkFilterQuality> quality_; + std::vector<PaintFlags::FilterQuality> quality_; size_t index_ = 0; bool fail_all_decodes_ = false; sk_sp<PaintRecord> record_; @@ -2966,7 +2976,8 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) { std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)}; - std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality}; + std::vector<PaintFlags::FilterQuality> quality = { + PaintFlags::FilterQuality::kHigh}; MockImageProvider provider(src_rect_offset, scale_adjustment, quality); provider.SetRecord(paint_worklet_buffer); @@ -2982,7 +2993,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) { testing::StrictMock<MockCanvas> canvas; testing::Sequence s; - SkSamplingOptions sampling({1.0f / 3, 1.0f / 3}); + SkSamplingOptions sampling({0, 1.0f / 2}); EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); @@ -3012,7 +3023,8 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) { std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)}; - std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality}; + std::vector<PaintFlags::FilterQuality> quality = { + PaintFlags::FilterQuality::kHigh}; MockImageProvider provider(src_rect_offset, scale_adjustment, quality); provider.SetRecord(paint_worklet_buffer); @@ -3028,7 +3040,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) { testing::StrictMock<MockCanvas> canvas; testing::Sequence s; - SkSamplingOptions sampling({1.0f / 3, 1.0f / 3}); + SkSamplingOptions sampling({0, 1.0f / 2}); EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); @@ -3061,7 +3073,8 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectClipped) { std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f)}; - std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality}; + std::vector<PaintFlags::FilterQuality> quality = { + PaintFlags::FilterQuality::kHigh}; MockImageProvider provider(src_rect_offset, scale_adjustment, quality); provider.SetRecord(paint_worklet_buffer); @@ -3077,7 +3090,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectClipped) { testing::StrictMock<MockCanvas> canvas; testing::Sequence s; - SkSamplingOptions sampling({1.0f / 3, 1.0f / 3}); + SkSamplingOptions sampling({0, 1.0f / 2}); EXPECT_CALL(canvas, willSave()).InSequence(s); EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s); @@ -3100,8 +3113,9 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProvider) { std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f), SkSize::Make(0.3f, 0.3f), SkSize::Make(0.4f, 0.4f)}; - std::vector<SkFilterQuality> quality = { - kHigh_SkFilterQuality, kMedium_SkFilterQuality, kHigh_SkFilterQuality}; + std::vector<PaintFlags::FilterQuality> quality = { + PaintFlags::FilterQuality::kHigh, PaintFlags::FilterQuality::kMedium, + PaintFlags::FilterQuality::kHigh}; MockImageProvider image_provider(src_rect_offset, scale_adjustment, quality); PaintOpBuffer buffer; @@ -3120,7 +3134,7 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProvider) { testing::StrictMock<MockCanvas> canvas; testing::Sequence s; - SkSamplingOptions sampling0({1.0f / 3, 1.0f / 3}); + SkSamplingOptions sampling0({0, 1.0f / 2}); SkSamplingOptions sampling1(SkFilterMode::kLinear, SkMipmapMode::kLinear); // Save/scale/image/restore from DrawImageop. @@ -3191,7 +3205,8 @@ TEST(PaintOpBufferTest, DrawImageRectOpWithLooperWithImageProvider) { std::vector<SkSize> src_rect_offset = {SkSize::MakeEmpty()}; std::vector<SkSize> scale_adjustment = {SkSize::Make(1.0f, 1.0f)}; - std::vector<SkFilterQuality> quality = {kHigh_SkFilterQuality}; + std::vector<PaintFlags::FilterQuality> quality = { + PaintFlags::FilterQuality::kHigh}; MockImageProvider image_provider(src_rect_offset, scale_adjustment, quality); buffer.Playback(&canvas, PlaybackParams(&image_provider)); } @@ -3285,8 +3300,8 @@ TEST_P(PaintFilterSerializationTest, Basic) { sk_sp<PaintFilter>{new TurbulencePaintFilter( TurbulencePaintFilter::TurbulenceType::kFractalNoise, 3.3f, 4.4f, 2, 123, nullptr)}, - sk_sp<PaintFilter>{ - new MatrixPaintFilter(SkMatrix::I(), kHigh_SkFilterQuality, nullptr)}, + sk_sp<PaintFilter>{new MatrixPaintFilter( + SkMatrix::I(), PaintFlags::FilterQuality::kHigh, nullptr)}, sk_sp<PaintFilter>{new LightingDistantPaintFilter( PaintFilter::LightingType::kSpecular, SkPoint3::Make(1, 2, 3), SK_ColorCYAN, 1.1f, 2.2f, 3.3f, nullptr)}, @@ -3300,7 +3315,7 @@ TEST_P(PaintFilterSerializationTest, Basic) { sk_sp<PaintFilter>{ new ImagePaintFilter(CreateDiscardablePaintImage(gfx::Size(100, 100)), SkRect::MakeWH(50, 50), SkRect::MakeWH(70, 70), - kMedium_SkFilterQuality)}}; + PaintFlags::FilterQuality::kMedium)}}; filters.emplace_back(new ComposePaintFilter(filters[0], filters[1])); filters.emplace_back( @@ -3313,6 +3328,12 @@ TEST_P(PaintFilterSerializationTest, Basic) { filters.emplace_back(new RecordPaintFilter( sk_sp<PaintRecord>{new PaintRecord}, SkRect::MakeXYWH(10, 15, 20, 25))); + // Use a non-identity ctm to confirm that RecordPaintFilters are converted + // from raster-at-scale to fixed scale properly. + float scale_x = 2.f; + float scale_y = 3.f; + SkM44 ctm = SkM44::Scale(scale_x, scale_y); + TestOptionsProvider options_provider; for (size_t i = 0; i < filters.size(); ++i) { SCOPED_TRACE(i); @@ -3327,7 +3348,7 @@ TEST_P(PaintFilterSerializationTest, Basic) { PaintOpWriter writer(memory.data(), memory.size(), options_provider.serialize_options(), GetParam()); - writer.Write(filter.get(), SkM44()); + writer.Write(filter.get(), ctm); ASSERT_GT(writer.size(), 0u) << PaintFilter::TypeToString(filter->type()); sk_sp<PaintFilter> deserialized_filter; @@ -3335,7 +3356,41 @@ TEST_P(PaintFilterSerializationTest, Basic) { options_provider.deserialize_options(), GetParam()); reader.Read(&deserialized_filter); ASSERT_TRUE(deserialized_filter); - EXPECT_TRUE(*filter == *deserialized_filter); + + if (filter->type() == PaintFilter::Type::kPaintRecord) { + // The filter's scaling behavior should be converted to kFixedScale so + // they are no longer equal. + ASSERT_EQ(deserialized_filter->type(), PaintFilter::Type::kPaintRecord); + + const RecordPaintFilter& expected = + static_cast<const RecordPaintFilter&>(*filter); + const RecordPaintFilter& actual = + static_cast<const RecordPaintFilter&>(*deserialized_filter); + + EXPECT_EQ(actual.scaling_behavior(), + RecordPaintFilter::ScalingBehavior::kFixedScale); + + SkRect expected_bounds = + SkRect::MakeXYWH(scale_x * expected.record_bounds().x(), + scale_y * expected.record_bounds().y(), + scale_x * expected.record_bounds().width(), + scale_y * expected.record_bounds().height()); + EXPECT_EQ(actual.record_bounds(), expected_bounds); + EXPECT_EQ(actual.raster_scale().width(), scale_x); + EXPECT_EQ(actual.raster_scale().height(), scale_y); + + // And the first op in the deserialized filter's record should be a + // ScaleOp containing the extracted scale factors (if there's no + // security constraints that disable record serialization) + if (!GetParam()) { + const ScaleOp* scale = actual.record()->GetOpAtForTesting<ScaleOp>(0); + ASSERT_TRUE(scale); + EXPECT_EQ(scale->sx, scale_x); + EXPECT_EQ(scale->sy, scale_y); + } + } else { + EXPECT_TRUE(*filter == *deserialized_filter); + } } } @@ -3519,7 +3574,7 @@ TEST(PaintOpBufferTest, SecurityConstrainedImageSerialization) { auto image = CreateDiscardablePaintImage(gfx::Size(10, 10)); sk_sp<PaintFilter> filter = sk_make_sp<ImagePaintFilter>( image, SkRect::MakeWH(10, 10), SkRect::MakeWH(10, 10), - kLow_SkFilterQuality); + PaintFlags::FilterQuality::kLow); const bool enable_security_constraints = true; std::unique_ptr<char, base::AlignedFreeDeleter> memory( @@ -3755,6 +3810,34 @@ TEST(PaintOpBufferTest, RecordShadersCachedSize) { EXPECT_GT(shader_size, estimated_image_size); } +TEST(PaintOpBufferTest, RecordFilterSerializeScaledImages) { + auto record_buffer = sk_make_sp<PaintOpBuffer>(); + record_buffer->push<DrawImageOp>( + CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f, 0.f); + + auto filter = + sk_make_sp<RecordPaintFilter>(record_buffer, SkRect::MakeWH(10.f, 10.f)); + auto buffer = sk_make_sp<PaintOpBuffer>(); + buffer->push<ScaleOp>(0.5f, 0.8f); + PaintFlags flags; + flags.setImageFilter(filter); + buffer->push<DrawRectOp>(SkRect::MakeWH(10.f, 10.f), flags); + + std::unique_ptr<char, base::AlignedFreeDeleter> memory( + static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize, + PaintOpBuffer::PaintOpAlign))); + TestOptionsProvider options_provider; + SimpleBufferSerializer serializer(memory.get(), + PaintOpBuffer::kInitialBufferSize, + options_provider.serialize_options()); + serializer.Serialize(buffer.get()); + + ASSERT_EQ(options_provider.decoded_images().size(), 1u); + auto scale = options_provider.decoded_images().at(0).scale(); + EXPECT_EQ(scale.width(), 0.5f); + EXPECT_EQ(scale.height(), 0.8f); +} + TEST(PaintOpBufferTest, TotalOpCount) { auto record_buffer = sk_make_sp<PaintOpBuffer>(); auto sub_record_buffer = sk_make_sp<PaintOpBuffer>(); @@ -3810,8 +3893,8 @@ TEST(PaintOpBufferTest, HasDrawOpsAndHasDrawTextOps) { buffer2->push<DrawRecordOp>(std::move(buffer1)); EXPECT_TRUE(buffer2->has_draw_ops()); EXPECT_FALSE(buffer2->has_draw_text_ops()); - buffer2->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), 0, - 0, PaintFlags()); + buffer2->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), + 0.0f, 0.0f, PaintFlags()); EXPECT_TRUE(buffer2->has_draw_ops()); EXPECT_TRUE(buffer2->has_draw_text_ops()); buffer2->push<DrawRectOp>(SkRect::MakeWH(4, 5), PaintFlags()); @@ -3849,14 +3932,14 @@ TEST(PaintOpBufferTest, HasEffectsPreventingLCDTextForSaveLayerAlpha) { TEST(PaintOpBufferTest, NeedsAdditionalInvalidationForLCDText) { auto buffer1 = sk_make_sp<PaintOpBuffer>(); - buffer1->push<SaveLayerAlphaOp>(nullptr, 100); + buffer1->push<SaveLayerAlphaOp>(nullptr, uint8_t{100}); EXPECT_FALSE(buffer1->has_draw_text_ops()); EXPECT_TRUE(buffer1->has_save_layer_alpha_ops()); EXPECT_FALSE(buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha()); auto buffer2 = sk_make_sp<PaintOpBuffer>(); - buffer2->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), 0, - 0, PaintFlags()); + buffer2->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), + 0.0f, 0.0f, PaintFlags()); buffer2->push<SaveLayerOp>(nullptr, nullptr); EXPECT_TRUE(buffer2->has_draw_ops()); EXPECT_FALSE(buffer2->has_save_layer_alpha_ops()); @@ -3879,7 +3962,7 @@ TEST(PaintOpBufferTest, NeedsAdditionalInvalidationForLCDText) { } { buffer1->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), - 0, 0, PaintFlags()); + 0.0f, 0.0f, PaintFlags()); EXPECT_TRUE(buffer1->has_draw_text_ops()); EXPECT_TRUE(buffer1->has_save_layer_alpha_ops()); EXPECT_FALSE( @@ -3917,4 +4000,41 @@ TEST(PaintOpBufferTest, SetMatrixOpWithNonIdentityPlaybackParams) { } } +TEST(PaintOpBufferTest, PathCaching) { + SkPath path; + PaintFlags flags; + + // Grow path large enough to trigger caching + path.moveTo(0, 0); + for (int x = 1; x < 100; ++x) + path.lineTo(x, x % 1); + + TestOptionsProvider options_provider; + + std::unique_ptr<char, base::AlignedFreeDeleter> memory( + static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize, + PaintOpBuffer::PaintOpAlign))); + auto buffer = sk_make_sp<PaintOpBuffer>(); + buffer->push<DrawPathOp>(path, flags, UsePaintCache::kEnabled); + SimpleBufferSerializer serializer(memory.get(), + PaintOpBuffer::kInitialBufferSize, + options_provider.serialize_options()); + serializer.Serialize(buffer.get()); + + EXPECT_TRUE(options_provider.client_paint_cache()->Get( + PaintCacheDataType::kPath, path.getGenerationID())); + + auto deserialized_buffer = + PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(), + options_provider.deserialize_options()); + ASSERT_TRUE(deserialized_buffer); + ASSERT_EQ(deserialized_buffer->size(), 1u); + ASSERT_EQ(deserialized_buffer->GetFirstOp()->GetType(), + PaintOpType::DrawPath); + + SkPath cached_path; + EXPECT_TRUE(options_provider.service_paint_cache()->GetPath( + path.getGenerationID(), &cached_path)); +} + } // namespace cc diff --git a/chromium/cc/paint/paint_op_helper_unittest.cc b/chromium/cc/paint/paint_op_helper_unittest.cc index 3710ef041fe..e7f7fc93768 100644 --- a/chromium/cc/paint/paint_op_helper_unittest.cc +++ b/chromium/cc/paint/paint_op_helper_unittest.cc @@ -6,6 +6,7 @@ #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_op_buffer.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkTextBlob.h" namespace cc { namespace { @@ -19,9 +20,12 @@ TEST(PaintOpHelper, AnnotateToString) { } TEST(PaintOpHelper, ClipPathToString) { - ClipPathOp op(SkPath(), SkClipOp::kDifference, true); + ClipPathOp op(SkPath(), SkClipOp::kDifference, true, + UsePaintCache::kDisabled); std::string str = PaintOpHelper::ToString(&op); - EXPECT_EQ(str, "ClipPathOp(path=<SkPath>, op=kDifference, antialias=true)"); + EXPECT_EQ(str, + "ClipPathOp(path=<SkPath>, op=kDifference, antialias=true, " + "use_cache=false)"); } TEST(PaintOpHelper, ClipRectToString) { @@ -156,7 +160,7 @@ TEST(PaintOpHelper, DrawOvalToString) { TEST(PaintOpHelper, DrawPathToString) { SkPath path; - DrawPathOp op(path, PaintFlags()); + DrawPathOp op(path, PaintFlags(), UsePaintCache::kDisabled); std::string str = PaintOpHelper::ToString(&op); EXPECT_EQ(str, "DrawPathOp(path=<SkPath>, flags=[color=rgba(0, 0, 0, 255), " @@ -167,7 +171,7 @@ TEST(PaintOpHelper, DrawPathToString) { "shader=(nil), hasShader=false, shaderIsOpaque=false, " "pathEffect=(nil), imageFilter=(nil), drawLooper=(nil), " "isSimpleOpacity=true, supportsFoldingAlpha=true, isValid=true, " - "hasDiscardableImages=false])"); + "hasDiscardableImages=false], use_cache=false)"); } TEST(PaintOpHelper, DrawRecordToString) { diff --git a/chromium/cc/paint/paint_op_perftest.cc b/chromium/cc/paint/paint_op_perftest.cc index 48777042fd7..9b06fc6cbc5 100644 --- a/chromium/cc/paint/paint_op_perftest.cc +++ b/chromium/cc/paint/paint_op_perftest.cc @@ -9,6 +9,7 @@ #include "base/timer/lap_timer.h" #include "cc/paint/paint_op_buffer.h" #include "cc/paint/paint_op_buffer_serializer.h" +#include "cc/paint/paint_shader.h" #include "cc/test/test_options_provider.h" #include "testing/perf/perf_result_reporter.h" #include "third_party/skia/include/core/SkMaskFilter.h" diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc index ddba7cb09f1..812e348a401 100644 --- a/chromium/cc/paint/paint_op_reader.cc +++ b/chromium/cc/paint/paint_op_reader.cc @@ -105,7 +105,7 @@ void PaintOpReader::ReadSimple(T* val) { // Align everything to 4 bytes, as the writer does. static constexpr size_t kAlign = 4; - size_t size = base::bits::Align(sizeof(T), kAlign); + size_t size = base::bits::AlignUp(sizeof(T), kAlign); if (remaining_bytes_ < size) SetInvalid(); @@ -133,7 +133,7 @@ uint8_t* PaintOpReader::CopyScratchSpace(size_t bytes) { } template <typename T> -void PaintOpReader::ReadFlattenable(sk_sp<T>* val) { +void PaintOpReader::ReadFlattenable(sk_sp<T>* val, Factory<T> factory) { size_t bytes = 0; ReadSize(&bytes); if (remaining_bytes_ < bytes) @@ -144,9 +144,7 @@ void PaintOpReader::ReadFlattenable(sk_sp<T>* val) { return; auto* scratch = CopyScratchSpace(bytes); - val->reset(static_cast<T*>( - SkFlattenable::Deserialize(T::GetFlattenableType(), scratch, bytes) - .release())); + val->reset(factory(scratch, bytes, nullptr).release()); if (!val) SetInvalid(); @@ -227,7 +225,8 @@ void PaintOpReader::Read(SkPath* path) { if (!options_.paint_cache->GetPath(path_id, path)) SetInvalid(); return; - case PaintCacheEntryState::kInlined: { + case PaintCacheEntryState::kInlined: + case PaintCacheEntryState::kInlinedDoNotCache: { size_t path_bytes = 0u; ReadSize(&path_bytes); if (path_bytes > remaining_bytes_) @@ -243,7 +242,14 @@ void PaintOpReader::Read(SkPath* path) { SetInvalid(); return; } - options_.paint_cache->PutPath(path_id, *path); + if (entry_state == PaintCacheEntryState::kInlined) { + options_.paint_cache->PutPath(path_id, *path); + } else { + // If we know that this path will only be drawn once, which is + // implied by kInlinedDoNotCache, we signal to skia that it should not + // do any caching either. + path->setIsVolatile(true); + } memory_ += path_bytes; remaining_bytes_ -= path_bytes; return; @@ -260,9 +266,9 @@ void PaintOpReader::Read(PaintFlags* flags) { ReadSimple(&flags->bitfields_uint_); - ReadFlattenable(&flags->path_effect_); - ReadFlattenable(&flags->mask_filter_); - ReadFlattenable(&flags->color_filter_); + ReadFlattenable(&flags->path_effect_, SkPathEffect::Deserialize); + ReadFlattenable(&flags->mask_filter_, SkMaskFilter::Deserialize); + ReadFlattenable(&flags->color_filter_, SkColorFilter::Deserialize); if (enable_security_constraints_) { size_t bytes = 0; @@ -272,7 +278,7 @@ void PaintOpReader::Read(PaintFlags* flags) { return; } } else { - ReadFlattenable(&flags->draw_looper_); + ReadFlattenable(&flags->draw_looper_, SkDrawLooper::Deserialize); } Read(&flags->image_filter_); @@ -725,7 +731,7 @@ void PaintOpReader::Read(scoped_refptr<SkottieWrapper>* skottie) { #endif // !defined(OS_ANDROID) void PaintOpReader::AlignMemory(size_t alignment) { - size_t padding = base::bits::Align(memory_, alignment) - memory_; + size_t padding = base::bits::AlignUp(memory_, alignment) - memory_; if (padding > remaining_bytes_) SetInvalid(); @@ -847,6 +853,9 @@ void PaintOpReader::Read(sk_sp<PaintFilter>* filter) { case PaintFilter::Type::kLightingSpot: ReadLightingSpotPaintFilter(filter, crop_rect); break; + case PaintFilter::Type::kStretch: + ReadStretchPaintFilter(filter, crop_rect); + break; } } @@ -856,7 +865,7 @@ void PaintOpReader::ReadColorFilterPaintFilter( sk_sp<SkColorFilter> color_filter; sk_sp<PaintFilter> input; - ReadFlattenable(&color_filter); + ReadFlattenable(&color_filter, SkColorFilter::Deserialize); Read(&input); if (!color_filter) SetInvalid(); @@ -1074,7 +1083,7 @@ void PaintOpReader::ReadImagePaintFilter( Read(&src_rect); SkRect dst_rect; Read(&dst_rect); - SkFilterQuality quality; + PaintFlags::FilterQuality quality; Read(&quality); if (!valid_) @@ -1086,13 +1095,37 @@ void PaintOpReader::ReadImagePaintFilter( void PaintOpReader::ReadRecordPaintFilter( sk_sp<PaintFilter>* filter, const absl::optional<PaintFilter::CropRect>& crop_rect) { - SkRect record_bounds; + bool has_filter = false; + ReadSimple(&has_filter); + if (!has_filter) { + *filter = nullptr; + return; + } + + SkRect record_bounds = SkRect::MakeEmpty(); + gfx::SizeF raster_scale = {0.f, 0.f}; + PaintShader::ScalingBehavior scaling_behavior = + PaintShader::ScalingBehavior::kRasterAtScale; sk_sp<PaintRecord> record; - Read(&record_bounds); + + ReadSimple(&record_bounds); + ReadSimple(&raster_scale); + if (raster_scale.width() <= 0.f || raster_scale.height() <= 0.f) { + SetInvalid(); + return; + } + + ReadSimple(&scaling_behavior); + if (!IsValidPaintShaderScalingBehavior(scaling_behavior)) { + SetInvalid(); + return; + } + Read(&record); if (!valid_) return; - filter->reset(new RecordPaintFilter(std::move(record), record_bounds)); + filter->reset(new RecordPaintFilter(std::move(record), record_bounds, + raster_scale, scaling_behavior)); } void PaintOpReader::ReadMergePaintFilter( @@ -1198,7 +1231,7 @@ void PaintOpReader::ReadShaderPaintFilter( sk_sp<PaintShader> shader; uint8_t alpha = 255; - SkFilterQuality quality = kNone_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kNone; Dither dither = Dither::kNo; Read(&shader); @@ -1217,7 +1250,7 @@ void PaintOpReader::ReadMatrixPaintFilter( sk_sp<PaintFilter>* filter, const absl::optional<PaintFilter::CropRect>& crop_rect) { SkMatrix matrix = SkMatrix::I(); - SkFilterQuality filter_quality = kNone_SkFilterQuality; + PaintFlags::FilterQuality filter_quality = PaintFlags::FilterQuality::kNone; sk_sp<PaintFilter> input; Read(&matrix); @@ -1312,6 +1345,28 @@ void PaintOpReader::ReadLightingSpotPaintFilter( base::OptionalOrNullptr(crop_rect))); } +void PaintOpReader::ReadStretchPaintFilter( + sk_sp<PaintFilter>* filter, + const absl::optional<PaintFilter::CropRect>& crop_rect) { + SkScalar stretch_x = 0.f; + SkScalar stretch_y = 0.f; + SkScalar width = 0.f; + SkScalar height = 0.f; + sk_sp<PaintFilter> input; + + Read(&stretch_x); + Read(&stretch_y); + Read(&width); + Read(&height); + Read(&input); + + if (!valid_) + return; + filter->reset(new StretchPaintFilter(stretch_x, stretch_y, width, height, + std::move(input), + base::OptionalOrNullptr(crop_rect))); +} + size_t PaintOpReader::Read(sk_sp<PaintRecord>* record) { size_t size_bytes = 0; ReadSize(&size_bytes); diff --git a/chromium/cc/paint/paint_op_reader.h b/chromium/cc/paint/paint_op_reader.h index eb3b7a9d5d7..7ed9abce3af 100644 --- a/chromium/cc/paint/paint_op_reader.h +++ b/chromium/cc/paint/paint_op_reader.h @@ -11,6 +11,7 @@ #include "cc/paint/paint_filter.h" #include "cc/paint/paint_op_writer.h" #include "cc/paint/transfer_cache_deserialize_helper.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace gpu { struct Mailbox; @@ -94,8 +95,9 @@ class CC_PAINT_EXPORT PaintOpReader { void Read(SkColorType* color_type) { ReadEnum<SkColorType, kLastEnum_SkColorType>(color_type); } - void Read(SkFilterQuality* quality) { - ReadEnum<SkFilterQuality, kLast_SkFilterQuality>(quality); + void Read(PaintFlags::FilterQuality* quality) { + ReadEnum<PaintFlags::FilterQuality, PaintFlags::FilterQuality::kLast>( + quality); } void Read(SkBlendMode* blend_mode) { ReadEnum<SkBlendMode, SkBlendMode::kLastMode>(blend_mode); @@ -129,7 +131,12 @@ class CC_PAINT_EXPORT PaintOpReader { void ReadSimple(T* val); template <typename T> - void ReadFlattenable(sk_sp<T>* val); + using Factory = sk_sp<T> (*)(const void* data, + size_t size, + const SkDeserialProcs* procs); + + template <typename T> + void ReadFlattenable(sk_sp<T>* val, Factory<T> factory); template <typename Enum, Enum kMaxValue = Enum::kMaxValue> void ReadEnum(Enum* enum_value) { @@ -214,6 +221,9 @@ class CC_PAINT_EXPORT PaintOpReader { void ReadLightingSpotPaintFilter( sk_sp<PaintFilter>* filter, const absl::optional<PaintFilter::CropRect>& crop_rect); + void ReadStretchPaintFilter( + sk_sp<PaintFilter>* filter, + const absl::optional<PaintFilter::CropRect>& crop_rect); // Returns the size of the read record, 0 if error. size_t Read(sk_sp<PaintRecord>* record); diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc index f473abc18fe..53b5c8b574e 100644 --- a/chromium/cc/paint/paint_op_writer.cc +++ b/chromium/cc/paint/paint_op_writer.cc @@ -103,7 +103,7 @@ void PaintOpWriter::WriteSimple(const T& val) { // to pre-align memory to the correct alignment. // TODO(enne): maybe we should do this correctly and DCHECK alignment. static constexpr size_t kAlign = 4; - size_t size = base::bits::Align(sizeof(T), kAlign); + size_t size = base::bits::AlignUp(sizeof(T), kAlign); EnsureBytes(size); if (!valid_) return; @@ -173,12 +173,15 @@ void PaintOpWriter::Write(const SkRRect& rect) { WriteSimple(rect); } -void PaintOpWriter::Write(const SkPath& path) { +void PaintOpWriter::Write(const SkPath& path, UsePaintCache use_paint_cache) { auto id = path.getGenerationID(); if (!options_.for_identifiability_study) Write(id); - if (options_.paint_cache->Get(PaintCacheDataType::kPath, id)) { + DCHECK(use_paint_cache == UsePaintCache::kEnabled || + !options_.paint_cache->Get(PaintCacheDataType::kPath, id)); + if (use_paint_cache == UsePaintCache::kEnabled && + options_.paint_cache->Get(PaintCacheDataType::kPath, id)) { Write(static_cast<uint32_t>(PaintCacheEntryState::kCached)); return; } @@ -190,7 +193,11 @@ void PaintOpWriter::Write(const SkPath& path) { return; } - Write(static_cast<uint32_t>(PaintCacheEntryState::kInlined)); + if (use_paint_cache == UsePaintCache::kEnabled) { + Write(static_cast<uint32_t>(PaintCacheEntryState::kInlined)); + } else { + Write(static_cast<uint32_t>(PaintCacheEntryState::kInlinedDoNotCache)); + } uint64_t* bytes_to_skip = WriteSize(0u); if (!valid_) return; @@ -201,7 +208,9 @@ void PaintOpWriter::Write(const SkPath& path) { } size_t bytes_written = path.writeToMemory(memory_); DCHECK_EQ(bytes_written, bytes_required); - options_.paint_cache->Put(PaintCacheDataType::kPath, id, bytes_written); + if (use_paint_cache == UsePaintCache::kEnabled) { + options_.paint_cache->Put(PaintCacheDataType::kPath, id, bytes_written); + } *bytes_to_skip = bytes_written; memory_ += bytes_written; remaining_bytes_ -= bytes_written; @@ -422,7 +431,7 @@ void PaintOpWriter::Write(const sk_sp<SkTextBlob>& blob) { sk_sp<PaintShader> PaintOpWriter::TransformShaderIfNecessary( const PaintShader* original, - SkFilterQuality quality, + PaintFlags::FilterQuality quality, const SkM44& current_ctm, uint32_t* paint_image_transfer_cache_entry_id, gfx::SizeF* paint_record_post_scale, @@ -467,7 +476,7 @@ void PaintOpWriter::Write(const SkM44& matrix) { } void PaintOpWriter::Write(const PaintShader* shader, - SkFilterQuality quality, + PaintFlags::FilterQuality quality, const SkM44& current_ctm) { sk_sp<PaintShader> transformed_shader; uint32_t paint_image_transfer_cache_id = kInvalidImageTransferCacheEntryId; @@ -534,8 +543,7 @@ void PaintOpWriter::Write(const PaintShader* shader, const gfx::Rect playback_rect( gfx::ToEnclosingRect(gfx::SkRectToRectF(shader->tile()))); - Write(shader->record_.get(), playback_rect, paint_record_post_scale, - SkMatrix::I()); + Write(shader->record_.get(), playback_rect, paint_record_post_scale); } else { DCHECK_EQ(shader->id_, PaintShader::kInvalidRecordShaderId); Write(false); @@ -585,7 +593,7 @@ void PaintOpWriter::AlignMemory(size_t alignment) { // padding = (alignment - memory % alignment) % alignment; // because alignment is a power of two. This doesn't use modulo operator // however, since it can be slow. - size_t padding = ((memory + alignment - 1) & ~(alignment - 1)) - memory; + size_t padding = base::bits::AlignUp(memory, alignment) - memory; EnsureBytes(padding); if (!valid_) return; @@ -684,6 +692,9 @@ void PaintOpWriter::Write(const PaintFilter* filter, const SkM44& current_ctm) { case PaintFilter::Type::kLightingSpot: Write(static_cast<const LightingSpotPaintFilter&>(*filter), current_ctm); break; + case PaintFilter::Type::kStretch: + Write(static_cast<const StretchPaintFilter&>(*filter), current_ctm); + break; } } @@ -793,18 +804,24 @@ void PaintOpWriter::Write(const ImagePaintFilter& filter, void PaintOpWriter::Write(const RecordPaintFilter& filter, const SkM44& current_ctm) { - WriteSimple(filter.record_bounds()); + // Convert to a fixed scale filter so that any content contained within + // the filter's PaintRecord is rasterized at the scale we use here for + // analysis (e.g. this ensures any contained text blobs will not be missing + // from the cache). + auto scaled_filter = filter.CreateScaledPaintRecord( + current_ctm.asM33(), options_.max_texture_size); + if (!scaled_filter) { + WriteSimple(false); + return; + } - // The logic here to only use the scale component of the matrix during - // analysis is for consistency with the rasterization of the filter later in - // pipeline in skia. For every draw with a filter, SkCanvas creates a layer - // for the draw and modifies the scale for these filters. - // See SkCanvas::internalSaveLayer. - SkMatrix mat = current_ctm.asM33(); - SkSize scale; - if (!mat.isScaleTranslate() && mat.decomposeScale(&scale)) - mat = SkMatrix::Scale(scale.width(), scale.height()); - Write(filter.record().get(), gfx::Rect(), gfx::SizeF(1.f, 1.f), mat); + WriteSimple(true); + WriteSimple(scaled_filter->record_bounds()); + WriteSimple(scaled_filter->raster_scale()); + WriteSimple(scaled_filter->scaling_behavior()); + + Write(scaled_filter->record().get(), gfx::Rect(), + scaled_filter->raster_scale()); } void PaintOpWriter::Write(const MergePaintFilter& filter, @@ -897,10 +914,18 @@ void PaintOpWriter::Write(const LightingSpotPaintFilter& filter, Write(filter.input().get(), current_ctm); } +void PaintOpWriter::Write(const StretchPaintFilter& filter, + const SkM44& current_ctm) { + WriteSimple(filter.stretch_x()); + WriteSimple(filter.stretch_y()); + WriteSimple(filter.width()); + WriteSimple(filter.height()); + Write(filter.input().get(), current_ctm); +} + void PaintOpWriter::Write(const PaintRecord* record, const gfx::Rect& playback_rect, - const gfx::SizeF& post_scale, - const SkMatrix& post_matrix_for_analysis) { + const gfx::SizeF& post_scale) { AlignMemory(PaintOpBuffer::PaintOpAlign); // We need to record how many bytes we will serialize, but we don't know this @@ -920,18 +945,15 @@ void PaintOpWriter::Write(const PaintRecord* record, return; } - // Nested records are used for picture shaders and filters which don't support - // using lcd text. Make sure we disable it here to match this in the text - // analysis canvas. - PaintOp::SerializeOptions lcd_disabled_options( - options_.image_provider, options_.transfer_cache, options_.paint_cache, - options_.strike_server, options_.color_space, - /*can_use_lcd_text=*/false, options_.context_supports_distance_field_text, - options_.max_texture_size); + // Nested records are used for picture shaders and filters. These are always + // converted to a fixed scale mode (hence |post_scale|), which means they are + // first rendered offscreen via SkImage::MakeFromPicture. This inherently does + // not support lcd text, so reflect that in the serialization options. + PaintOp::SerializeOptions lcd_disabled_options = options_; + lcd_disabled_options.can_use_lcd_text = false; SimpleBufferSerializer serializer(memory_, remaining_bytes_, lcd_disabled_options); - serializer.Serialize(record, playback_rect, post_scale, - post_matrix_for_analysis); + serializer.Serialize(record, playback_rect, post_scale); if (!serializer.valid()) { valid_ = false; diff --git a/chromium/cc/paint/paint_op_writer.h b/chromium/cc/paint/paint_op_writer.h index b48378fb224..2e6fc5bfdd1 100644 --- a/chromium/cc/paint/paint_op_writer.h +++ b/chromium/cc/paint/paint_op_writer.h @@ -59,8 +59,7 @@ class CC_PAINT_EXPORT PaintOpWriter { void Write(const SkRect& rect); void Write(const SkIRect& rect); void Write(const SkRRect& rect); - - void Write(const SkPath& path); + void Write(const SkPath& path, UsePaintCache); void Write(const sk_sp<SkData>& data); void Write(const SkColorSpace* data); void Write(const SkSamplingOptions&); @@ -76,7 +75,7 @@ class CC_PAINT_EXPORT PaintOpWriter { // identically when text is involved. void Write(const PaintFlags& flags, const SkM44& current_ctm); void Write(const PaintShader* shader, - SkFilterQuality quality, + PaintFlags::FilterQuality quality, const SkM44& current_ctm); void Write(const PaintFilter* filter, const SkM44& current_ctm); @@ -84,7 +83,9 @@ class CC_PAINT_EXPORT PaintOpWriter { void Write(PaintCanvas::AnnotationType type) { WriteEnum(type); } void Write(SkCanvas::SrcRectConstraint constraint) { WriteEnum(constraint); } void Write(SkColorType color_type) { WriteEnum(color_type); } - void Write(SkFilterQuality filter_quality) { WriteEnum(filter_quality); } + void Write(PaintFlags::FilterQuality filter_quality) { + WriteEnum(filter_quality); + } void Write(SkBlendMode blend_mode) { WriteEnum(blend_mode); } void Write(SkTileMode tile_mode) { WriteEnum(tile_mode); } void Write(SkFilterMode filter_mode) { WriteEnum(filter_mode); } @@ -161,11 +162,11 @@ class CC_PAINT_EXPORT PaintOpWriter { const SkM44& current_ctm); void Write(const LightingPointPaintFilter& filter, const SkM44& current_ctm); void Write(const LightingSpotPaintFilter& filter, const SkM44& current_ctm); + void Write(const StretchPaintFilter& filter, const SkM44& current_ctm); void Write(const PaintRecord* record, const gfx::Rect& playback_rect, - const gfx::SizeF& post_scale, - const SkMatrix& post_matrix_for_analysis); + const gfx::SizeF& post_scale); void Write(const SkRegion& region); void WriteImage(const DecodedDrawImage& decoded_draw_image); void WriteImage(uint32_t transfer_cache_entry_id, bool needs_mips); @@ -174,7 +175,7 @@ class CC_PAINT_EXPORT PaintOpWriter { void EnsureBytes(size_t required_bytes); sk_sp<PaintShader> TransformShaderIfNecessary( const PaintShader* original, - SkFilterQuality quality, + PaintFlags::FilterQuality quality, const SkM44& current_ctm, uint32_t* paint_image_transfer_cache_entry_id, gfx::SizeF* paint_record_post_scale, diff --git a/chromium/cc/paint/paint_recorder.h b/chromium/cc/paint/paint_recorder.h index 57a62507c84..e9de25abdef 100644 --- a/chromium/cc/paint/paint_recorder.h +++ b/chromium/cc/paint/paint_recorder.h @@ -8,7 +8,6 @@ #include "base/compiler_specific.h" #include "cc/paint/paint_record.h" #include "cc/paint/record_paint_canvas.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace cc { diff --git a/chromium/cc/paint/paint_shader.cc b/chromium/cc/paint/paint_shader.cc index 1d392729ec5..a0002f9fde1 100644 --- a/chromium/cc/paint/paint_shader.cc +++ b/chromium/cc/paint/paint_shader.cc @@ -4,7 +4,10 @@ #include "cc/paint/paint_shader.h" +#include <utility> + #include "base/atomic_sequence_num.h" +#include "base/stl_util.h" #include "cc/paint/paint_image_builder.h" #include "cc/paint/paint_op_writer.h" #include "cc/paint/paint_record.h" @@ -48,20 +51,6 @@ bool CompareMatrices(const SkMatrix& a, return PaintOp::AreSkMatricesEqual(a_without_scale, b_without_scale); } -SkRect AdjustForMaxTextureSize(SkRect tile, int max_texture_size) { - if (max_texture_size == 0) - return tile; - - if (tile.width() < max_texture_size && tile.height() < max_texture_size) - return tile; - - float down_scale = max_texture_size / std::max(tile.width(), tile.height()); - tile = SkRect::MakeXYWH(tile.x(), tile.y(), - SkScalarFloorToScalar(tile.width() * down_scale), - SkScalarFloorToScalar(tile.height() * down_scale)); - return tile; -} - } // namespace const PaintShader::RecordShaderId PaintShader::kInvalidRecordShaderId = -1; @@ -244,8 +233,9 @@ bool PaintShader::has_discardable_images() const { (record_ && record_->HasDiscardableImages()); } -bool PaintShader::GetRasterizationTileRect(const SkMatrix& ctm, - SkRect* tile_rect) const { +bool PaintShader::GetClampedRasterizationTileRect(const SkMatrix& ctm, + int max_texture_size, + SkRect* tile_rect) const { DCHECK_EQ(shader_type_, Type::kPaintRecord); // If we are using a fixed scale, the record is rasterized with the original @@ -259,34 +249,10 @@ bool PaintShader::GetRasterizationTileRect(const SkMatrix& ctm, if (local_matrix_.has_value()) matrix.preConcat(local_matrix_.value()); - SkSize scale; - if (!matrix.decomposeScale(&scale)) { - // Decomposition failed, use an approximation. - scale.set(SkScalarSqrt(matrix.getScaleX() * matrix.getScaleX() + - matrix.getSkewX() * matrix.getSkewX()), - SkScalarSqrt(matrix.getScaleY() * matrix.getScaleY() + - matrix.getSkewY() * matrix.getSkewY())); - } - - SkScalar tile_area = - tile_.width() * tile_.height() * scale.width() * scale.height(); - - // Clamp the tile size to about 4M pixels. - // TODO(khushalsagar): We need to consider the max texture size as well. - static const SkScalar kMaxTileArea = 2048 * 2048; - if (tile_area > kMaxTileArea) { - SkScalar clamp_scale = SkScalarSqrt(kMaxTileArea / tile_area); - scale.set(clamp_scale, clamp_scale); - } - - *tile_rect = SkRect::MakeXYWH( - tile_.fLeft * scale.width(), tile_.fTop * scale.height(), - SkScalarCeilToInt(SkScalarAbs(scale.width() * tile_.width())), - SkScalarCeilToInt(SkScalarAbs(scale.height() * tile_.height()))); - + *tile_rect = + PaintRecord::GetFixedScaleBounds(matrix, tile_, max_texture_size); if (tile_rect->isEmpty()) return false; - return true; } @@ -315,9 +281,8 @@ sk_sp<PaintShader> PaintShader::CreateScaledPaintRecord( // Note that the scaling logic here is replicated from // SkPictureShader::refBitmapShader. SkRect tile_rect; - if (!GetRasterizationTileRect(ctm, &tile_rect)) + if (!GetClampedRasterizationTileRect(ctm, max_texture_size, &tile_rect)) return nullptr; - tile_rect = AdjustForMaxTextureSize(tile_rect, max_texture_size); sk_sp<PaintShader> shader(new PaintShader(Type::kPaintRecord)); shader->record_ = record_; @@ -356,10 +321,10 @@ sk_sp<PaintShader> PaintShader::CreatePaintWorkletRecord( sk_sp<PaintShader> PaintShader::CreateDecodedImage( const SkMatrix& ctm, - SkFilterQuality quality, + PaintFlags::FilterQuality quality, ImageProvider* image_provider, uint32_t* transfer_cache_entry_id, - SkFilterQuality* raster_quality, + PaintFlags::FilterQuality* raster_quality, bool* needs_mips, gpu::Mailbox* mailbox) const { DCHECK_EQ(shader_type_, Type::kImage); @@ -413,7 +378,8 @@ sk_sp<PaintShader> PaintShader::CreateDecodedImage( return PaintShader::MakeImage(decoded_paint_image, tx_, ty_, &final_matrix); } -sk_sp<SkShader> PaintShader::GetSkShader(SkFilterQuality quality) const { +sk_sp<SkShader> PaintShader::GetSkShader( + PaintFlags::FilterQuality quality) const { SkSamplingOptions sampling( PaintFlags::FilterQualityToSkSamplingOptions(quality)); @@ -430,7 +396,6 @@ sk_sp<SkShader> PaintShader::GetSkShader(SkFilterQuality quality) const { positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, base::OptionalOrNullptr(local_matrix_)); - break; } case Type::kRadialGradient: return SkGradientShader::MakeRadial( @@ -438,21 +403,18 @@ sk_sp<SkShader> PaintShader::GetSkShader(SkFilterQuality quality) const { positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, base::OptionalOrNullptr(local_matrix_)); - break; case Type::kTwoPointConicalGradient: return SkGradientShader::MakeTwoPointConical( start_point_, start_radius_, end_point_, end_radius_, colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, flags_, base::OptionalOrNullptr(local_matrix_)); - break; case Type::kSweepGradient: return SkGradientShader::MakeSweep( center_.x(), center_.y(), colors_.data(), positions_.empty() ? nullptr : positions_.data(), static_cast<int>(colors_.size()), tx_, start_degrees_, end_degrees_, flags_, base::OptionalOrNullptr(local_matrix_)); - break; case Type::kImage: if (sk_cached_image_) { return sk_cached_image_->makeShader( @@ -467,7 +429,6 @@ sk_sp<SkShader> PaintShader::GetSkShader(SkFilterQuality quality) const { return sk_cached_picture_->makeShader( tx_, ty_, sampling.filter, base::OptionalOrNullptr(local_matrix_), nullptr); - break; // For fixed scale, we create an image shader with an image backed by // the picture. case ScalingBehavior::kFixedScale: { @@ -477,7 +438,6 @@ sk_sp<SkShader> PaintShader::GetSkShader(SkFilterQuality quality) const { SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()); return image->makeShader(tx_, ty_, sampling, base::OptionalOrNullptr(local_matrix_)); - break; } } break; diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h index 54529f65cd8..dab47c4ad04 100644 --- a/chromium/cc/paint/paint_shader.h +++ b/chromium/cc/paint/paint_shader.h @@ -11,6 +11,7 @@ #include "base/stl_util.h" #include "cc/paint/image_analysis_state.h" #include "cc/paint/paint_export.h" +#include "cc/paint/paint_flags.h" #include "cc/paint/paint_image.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkImage.h" @@ -148,7 +149,10 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { return base::OptionalOrNullptr(tile_scale_); } const sk_sp<PaintRecord>& paint_record() const { return record_; } - bool GetRasterizationTileRect(const SkMatrix& ctm, SkRect* tile_rect) const; + bool GetRasterizationTileRect(const SkMatrix& ctm, SkRect* tile_rect) const { + return GetClampedRasterizationTileRect(ctm, /*max_texture_size=*/0, + tile_rect); + } SkTileMode tx() const { return tx_; } SkTileMode ty() const { return ty_; } @@ -184,7 +188,11 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { explicit PaintShader(Type type); - sk_sp<SkShader> GetSkShader(SkFilterQuality quality) const; + bool GetClampedRasterizationTileRect(const SkMatrix& ctm, + int max_texture_size, + SkRect* tile_rect) const; + + sk_sp<SkShader> GetSkShader(PaintFlags::FilterQuality quality) const; // If the type needs a resolve skia object (e.g. SkImage or SkPicture), this // will create and cache it internally. Most types do not need this, but it @@ -208,13 +216,14 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { // |raster_quality| is set to the filter quality the shader should be // rasterized with. // Valid only for PaintImage backed shaders. - sk_sp<PaintShader> CreateDecodedImage(const SkMatrix& ctm, - SkFilterQuality requested_quality, - ImageProvider* image_provider, - uint32_t* transfer_cache_entry_id, - SkFilterQuality* raster_quality, - bool* needs_mips, - gpu::Mailbox* mailbox) const; + sk_sp<PaintShader> CreateDecodedImage( + const SkMatrix& ctm, + PaintFlags::FilterQuality requested_quality, + ImageProvider* image_provider, + uint32_t* transfer_cache_entry_id, + PaintFlags::FilterQuality* raster_quality, + bool* needs_mips, + gpu::Mailbox* mailbox) const; // Creates a paint record shader for worklet-backed images. sk_sp<PaintShader> CreatePaintWorkletRecord( diff --git a/chromium/cc/paint/paint_worklet_input.h b/chromium/cc/paint/paint_worklet_input.h index efbccdfeb69..0af104f2a2f 100644 --- a/chromium/cc/paint/paint_worklet_input.h +++ b/chromium/cc/paint/paint_worklet_input.h @@ -29,6 +29,7 @@ class CC_PAINT_EXPORT PaintWorkletInput public: enum class NativePropertyType { kBackgroundColor, + kClipPath, kInvalid, }; // Uniquely identifies a property from the animation system, so that a @@ -88,6 +89,8 @@ class CC_PAINT_EXPORT PaintWorkletInput using PropertyKeys = std::vector<PropertyKey>; virtual const PropertyKeys& GetPropertyKeys() const = 0; + virtual bool IsCSSPaintWorkletInput() const = 0; + protected: friend class base::RefCountedThreadSafe<PaintWorkletInput>; virtual ~PaintWorkletInput() = default; diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc index bdb735eede9..8349dd9e4a3 100644 --- a/chromium/cc/paint/record_paint_canvas.cc +++ b/chromium/cc/paint/record_paint_canvas.cc @@ -12,6 +12,7 @@ #include "cc/paint/paint_recorder.h" #include "cc/paint/skottie_wrapper.h" #include "third_party/skia/include/core/SkAnnotation.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/utils/SkNWayCanvas.h" namespace cc { @@ -150,7 +151,8 @@ void RecordPaintCanvas::clipRRect(const SkRRect& rrect, void RecordPaintCanvas::clipPath(const SkPath& path, SkClipOp op, - bool antialias) { + bool antialias, + UsePaintCache use_paint_cache) { if (!path.isInverseFillType() && GetCanvas()->getTotalMatrix().rectStaysRect()) { // TODO(enne): do these cases happen? should the caller know that this isn't @@ -172,7 +174,7 @@ void RecordPaintCanvas::clipPath(const SkPath& path, } } - list_->push<ClipPathOp>(path, op, antialias); + list_->push<ClipPathOp>(path, op, antialias, use_paint_cache); GetCanvas()->clipPath(path, op, antialias); return; } @@ -257,8 +259,10 @@ void RecordPaintCanvas::drawRoundRect(const SkRect& rect, } } -void RecordPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) { - list_->push<DrawPathOp>(path, flags); +void RecordPaintCanvas::drawPath(const SkPath& path, + const PaintFlags& flags, + UsePaintCache use_paint_cache) { + list_->push<DrawPathOp>(path, flags, use_paint_cache); } void RecordPaintCanvas::drawImage(const PaintImage& image, diff --git a/chromium/cc/paint/record_paint_canvas.h b/chromium/cc/paint/record_paint_canvas.h index 3f35f5d15c3..c0aa89129fb 100644 --- a/chromium/cc/paint/record_paint_canvas.h +++ b/chromium/cc/paint/record_paint_canvas.h @@ -53,7 +53,10 @@ class CC_PAINT_EXPORT RecordPaintCanvas : public PaintCanvas { void clipRect(const SkRect& rect, SkClipOp op, bool antialias) override; void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) override; - void clipPath(const SkPath& path, SkClipOp op, bool antialias) override; + void clipPath(const SkPath& path, + SkClipOp op, + bool antialias, + UsePaintCache use_paint_cache) override; SkRect getLocalClipBounds() const override; bool getLocalClipBounds(SkRect* bounds) const override; SkIRect getDeviceClipBounds() const override; @@ -77,7 +80,9 @@ class CC_PAINT_EXPORT RecordPaintCanvas : public PaintCanvas { SkScalar rx, SkScalar ry, const PaintFlags& flags) override; - void drawPath(const SkPath& path, const PaintFlags& flags) override; + void drawPath(const SkPath& path, + const PaintFlags& flags, + UsePaintCache use_paint_cache) override; void drawImage(const PaintImage& image, SkScalar left, SkScalar top, diff --git a/chromium/cc/paint/render_surface_filters.cc b/chromium/cc/paint/render_surface_filters.cc index d8b243ee1a1..698af3ca0af 100644 --- a/chromium/cc/paint/render_surface_filters.cc +++ b/chromium/cc/paint/render_surface_filters.cc @@ -4,6 +4,7 @@ #include <stddef.h> #include <algorithm> +#include <utility> #include "cc/paint/render_surface_filters.h" @@ -294,6 +295,12 @@ sk_sp<PaintFilter> RenderSurfaceFilters::BuildImageFilter( } break; } + case FilterOperation::STRETCH: { + image_filter = sk_make_sp<StretchPaintFilter>( + op.amount(), op.outer_threshold(), size.width(), size.height(), + std::move(image_filter)); + break; + } } } return image_filter; diff --git a/chromium/cc/paint/scoped_raster_flags.cc b/chromium/cc/paint/scoped_raster_flags.cc index 8bd3ce0640c..171b5514ae3 100644 --- a/chromium/cc/paint/scoped_raster_flags.cc +++ b/chromium/cc/paint/scoped_raster_flags.cc @@ -64,7 +64,7 @@ void ScopedRasterFlags::DecodeImageShader(const SkMatrix& ctm) { } uint32_t transfer_cache_entry_id = kInvalidImageTransferCacheEntryId; - SkFilterQuality raster_quality = flags()->getFilterQuality(); + PaintFlags::FilterQuality raster_quality = flags()->getFilterQuality(); bool transfer_cache_entry_needs_mips = false; gpu::Mailbox mailbox; auto decoded_shader = flags()->getShader()->CreateDecodedImage( diff --git a/chromium/cc/paint/scoped_raster_flags.h b/chromium/cc/paint/scoped_raster_flags.h index 3550c1181a5..792cbc19fdd 100644 --- a/chromium/cc/paint/scoped_raster_flags.h +++ b/chromium/cc/paint/scoped_raster_flags.h @@ -9,6 +9,7 @@ #include "cc/paint/decode_stashing_image_provider.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_flags.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace cc { diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc index 0f1d38da3fe..34e9cf4f0f5 100644 --- a/chromium/cc/paint/skia_paint_canvas.cc +++ b/chromium/cc/paint/skia_paint_canvas.cc @@ -12,6 +12,7 @@ #include "cc/paint/skottie_wrapper.h" #include "skia/ext/legacy_display_globals.h" #include "third_party/skia/include/core/SkAnnotation.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/docs/SkPDFDocument.h" namespace cc { @@ -125,7 +126,8 @@ void SkiaPaintCanvas::clipRRect(const SkRRect& rrect, void SkiaPaintCanvas::clipPath(const SkPath& path, SkClipOp op, - bool do_anti_alias) { + bool do_anti_alias, + UsePaintCache) { canvas_->clipPath(path, op, do_anti_alias); } @@ -250,7 +252,9 @@ void SkiaPaintCanvas::drawRoundRect(const SkRect& rect, FlushAfterDrawIfNeeded(); } -void SkiaPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) { +void SkiaPaintCanvas::drawPath(const SkPath& path, + const PaintFlags& flags, + UsePaintCache) { ScopedRasterFlags raster_flags(&flags, image_provider_, canvas_->getTotalMatrix(), max_texture_size(), 255u); diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h index bc7057a7248..d83020ced20 100644 --- a/chromium/cc/paint/skia_paint_canvas.h +++ b/chromium/cc/paint/skia_paint_canvas.h @@ -77,7 +77,10 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { void clipRRect(const SkRRect& rrect, SkClipOp op, bool do_anti_alias) override; - void clipPath(const SkPath& path, SkClipOp op, bool do_anti_alias) override; + void clipPath(const SkPath& path, + SkClipOp op, + bool do_anti_alias, + UsePaintCache) override; SkRect getLocalClipBounds() const override; bool getLocalClipBounds(SkRect* bounds) const override; SkIRect getDeviceClipBounds() const override; @@ -101,7 +104,9 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { SkScalar rx, SkScalar ry, const PaintFlags& flags) override; - void drawPath(const SkPath& path, const PaintFlags& flags) override; + void drawPath(const SkPath& path, + const PaintFlags& flags, + UsePaintCache) override; void drawImage(const PaintImage& image, SkScalar left, SkScalar top, diff --git a/chromium/cc/paint/solid_color_analyzer.h b/chromium/cc/paint/solid_color_analyzer.h index e86ef17031e..71a0908a3f9 100644 --- a/chromium/cc/paint/solid_color_analyzer.h +++ b/chromium/cc/paint/solid_color_analyzer.h @@ -14,6 +14,7 @@ #include "ui/gfx/skia_util.h" namespace cc { +class PaintOpBuffer; class CC_PAINT_EXPORT SolidColorAnalyzer { public: diff --git a/chromium/cc/paint/solid_color_analyzer_unittest.cc b/chromium/cc/paint/solid_color_analyzer_unittest.cc index 49b3ce81f13..f05461af0ea 100644 --- a/chromium/cc/paint/solid_color_analyzer_unittest.cc +++ b/chromium/cc/paint/solid_color_analyzer_unittest.cc @@ -4,8 +4,8 @@ #include "cc/paint/solid_color_analyzer.h" +#include "base/cxx17_backports.h" #include "base/memory/ref_counted.h" -#include "base/stl_util.h" #include "build/build_config.h" #include "cc/paint/display_item_list.h" #include "cc/paint/paint_filter.h" diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc index ded0950475b..2af2773414f 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.cc +++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc @@ -78,10 +78,15 @@ static void RasterizeSourceOOP( ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); } + // Assume legacy MSAA if sample count is positive. + gpu::raster::MsaaMode msaa_mode = playback_settings.msaa_sample_count > 0 + ? gpu::raster::kMSAA + : gpu::raster::kNoMSAA; + ri->BeginRasterCHROMIUM( raster_source->background_color(), mailbox_needs_clear, - playback_settings.msaa_sample_count, playback_settings.use_lcd_text, - color_space, mailbox->name); + playback_settings.msaa_sample_count, msaa_mode, + playback_settings.use_lcd_text, color_space, mailbox->name); gfx::Vector2dF recording_to_raster_scale = transform.scale(); recording_to_raster_scale.Scale(1 / raster_source->recording_scale_factor()); gfx::Size content_size = raster_source->GetContentSize(transform.scale()); diff --git a/chromium/cc/raster/paint_worklet_image_provider.cc b/chromium/cc/raster/paint_worklet_image_provider.cc index 040e6d18df7..059c842bd83 100644 --- a/chromium/cc/raster/paint_worklet_image_provider.cc +++ b/chromium/cc/raster/paint_worklet_image_provider.cc @@ -23,10 +23,13 @@ PaintWorkletImageProvider& PaintWorkletImageProvider::operator=( ImageProvider::ScopedResult PaintWorkletImageProvider::GetPaintRecordResult( scoped_refptr<PaintWorkletInput> input) { - // The |records_| contains all known PaintWorkletInputs, whether they are - // painted or not, so |input| should always exist in it. auto it = records_.find(input); - DCHECK(it != records_.end()); + // In the DiscardableImageMap::GatherDiscardableImages(), a DrawImageRect can + // early exit the for loop if its paint rect is empty. In that case, the + // |records_| will not contain that PaintWorkletInput, and we should return + // an empty result. + if (it == records_.end()) + return ImageProvider::ScopedResult(); return ImageProvider::ScopedResult(it->second.second); } diff --git a/chromium/cc/raster/playback_image_provider_unittest.cc b/chromium/cc/raster/playback_image_provider_unittest.cc index fdb5f43c321..2d8d319db65 100644 --- a/chromium/cc/raster/playback_image_provider_unittest.cc +++ b/chromium/cc/raster/playback_image_provider_unittest.cc @@ -25,8 +25,8 @@ sk_sp<SkImage> CreateRasterImage() { DecodedDrawImage CreateDecode() { return DecodedDrawImage(CreateRasterImage(), nullptr, SkSize::MakeEmpty(), - SkSize::Make(1.0f, 1.0f), kMedium_SkFilterQuality, - true); + SkSize::Make(1.0f, 1.0f), + PaintFlags::FilterQuality::kMedium, true); } class MockDecodeCache : public StubDecodeCache { @@ -79,12 +79,12 @@ TEST(PlaybackImageProviderTest, SkipsAllImages) { .set_id(PaintImage::GetNextId()) .set_image(CreateRasterImage(), PaintImage::GetNextContentId()) .TakePaintImage(), - false, rect, kMedium_SkFilterQuality, matrix))); + false, rect, PaintFlags::FilterQuality::kMedium, matrix))); EXPECT_EQ(cache.images_decoded(), 0); EXPECT_FALSE(provider.GetRasterContent( CreateDiscardableDrawImage(gfx::Size(10, 10), nullptr, SkRect::Make(rect), - kMedium_SkFilterQuality, matrix))); + PaintFlags::FilterQuality::kMedium, matrix))); EXPECT_EQ(cache.images_decoded(), 0); } @@ -101,8 +101,8 @@ TEST(PlaybackImageProviderTest, SkipsSomeImages) { SkIRect rect = SkIRect::MakeWH(10, 10); SkM44 matrix = SkM44(); - EXPECT_FALSE(provider.GetRasterContent( - DrawImage(skip_image, false, rect, kMedium_SkFilterQuality, matrix))); + EXPECT_FALSE(provider.GetRasterContent(DrawImage( + skip_image, false, rect, PaintFlags::FilterQuality::kMedium, matrix))); EXPECT_EQ(cache.images_decoded(), 0); } @@ -117,8 +117,9 @@ TEST(PlaybackImageProviderTest, RefAndUnrefDecode) { { SkRect rect = SkRect::MakeWH(10, 10); SkM44 matrix = SkM44(); - auto decode = provider.GetRasterContent(CreateDiscardableDrawImage( - gfx::Size(10, 10), nullptr, rect, kMedium_SkFilterQuality, matrix)); + auto decode = provider.GetRasterContent( + CreateDiscardableDrawImage(gfx::Size(10, 10), nullptr, rect, + PaintFlags::FilterQuality::kMedium, matrix)); EXPECT_TRUE(decode); EXPECT_EQ(cache.refed_image_count(), 1); } @@ -145,7 +146,8 @@ TEST(PlaybackImageProviderTest, SwapsGivenFrames) { SkIRect rect = SkIRect::MakeWH(10, 10); SkM44 matrix = SkM44(); - DrawImage draw_image(image, false, rect, kMedium_SkFilterQuality, matrix); + DrawImage draw_image(image, false, rect, PaintFlags::FilterQuality::kMedium, + matrix); provider.GetRasterContent(draw_image); ASSERT_TRUE(cache.last_image().paint_image()); ASSERT_EQ(cache.last_image().paint_image(), image); @@ -163,8 +165,9 @@ TEST(PlaybackImageProviderTest, BitmapImages) { { SkIRect rect = SkIRect::MakeWH(10, 10); SkM44 matrix = SkM44(); - auto draw_image = DrawImage(CreateBitmapImage(gfx::Size(10, 10)), false, - rect, kMedium_SkFilterQuality, matrix); + auto draw_image = + DrawImage(CreateBitmapImage(gfx::Size(10, 10)), false, rect, + PaintFlags::FilterQuality::kMedium, matrix); auto decode = provider.GetRasterContent(draw_image); EXPECT_TRUE(decode); EXPECT_EQ(cache.refed_image_count(), 1); @@ -184,8 +187,9 @@ TEST(PlaybackImageProviderTest, IgnoresImagesNotSupportedByCache) { { SkIRect rect = SkIRect::MakeWH(10, 10); SkM44 matrix = SkM44(); - auto draw_image = DrawImage(CreateBitmapImage(gfx::Size(10, 10)), false, - rect, kMedium_SkFilterQuality, matrix); + auto draw_image = + DrawImage(CreateBitmapImage(gfx::Size(10, 10)), false, rect, + PaintFlags::FilterQuality::kMedium, matrix); auto decode = provider.GetRasterContent(draw_image); EXPECT_TRUE(decode); EXPECT_EQ(cache.refed_image_count(), 0); diff --git a/chromium/cc/raster/raster_buffer_provider_unittest.cc b/chromium/cc/raster/raster_buffer_provider_unittest.cc index 0c371132a4b..9ba0950e799 100644 --- a/chromium/cc/raster/raster_buffer_provider_unittest.cc +++ b/chromium/cc/raster/raster_buffer_provider_unittest.cc @@ -176,6 +176,7 @@ class RasterImplementationForOOPR void BeginRasterCHROMIUM(GLuint sk_color, GLboolean needs_clear, GLuint msaa_sample_count, + gpu::raster::MsaaMode msaa_mode, GLboolean can_use_lcd_text, const gfx::ColorSpace& color_space, const GLbyte* mailbox) override {} diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h index 98b126da676..ca2aa3c9294 100644 --- a/chromium/cc/raster/raster_source.h +++ b/chromium/cc/raster/raster_source.h @@ -8,6 +8,7 @@ #include <stddef.h> #include <memory> +#include <string> #include <vector> #include "base/containers/flat_map.h" @@ -125,6 +126,9 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { size_t* max_op_size_hint() { return &max_op_size_hint_; } + void set_debug_name(const std::string& name) { debug_name_ = name; } + const std::string& debug_name() const { return debug_name_; } + protected: // RecordingSource is the only class that can create a raster source. friend class RecordingSource; @@ -163,6 +167,8 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { const gfx::Size size_; const int slow_down_raster_scale_factor_for_debug_; const float recording_scale_factor_; + // Used for debugging and tracing. + std::string debug_name_; }; } // namespace cc diff --git a/chromium/cc/raster/task.cc b/chromium/cc/raster/task.cc index 5a34ca86ad2..9fc91ffb6a6 100644 --- a/chromium/cc/raster/task.cc +++ b/chromium/cc/raster/task.cc @@ -4,6 +4,9 @@ #include "cc/raster/task.h" +#include <ostream> +#include <utility> + #include "base/check.h" #include "base/notreached.h" diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc index 638db6c4e0c..805bf859f23 100644 --- a/chromium/cc/resources/resource_pool.cc +++ b/chromium/cc/resources/resource_pool.cc @@ -181,10 +181,12 @@ ResourcePool::PoolResource* ResourcePool::CreateResource( ResourcePool::InUsePoolResource ResourcePool::AcquireResource( const gfx::Size& size, viz::ResourceFormat format, - const gfx::ColorSpace& color_space) { + const gfx::ColorSpace& color_space, + const std::string& debug_name) { PoolResource* resource = ReuseResource(size, format, color_space); if (!resource) resource = CreateResource(size, format, color_space); + resource->set_debug_name(debug_name); return InUsePoolResource(resource, !!context_provider_); } @@ -204,7 +206,8 @@ ResourcePool::TryAcquireResourceForPartialRaster( const gfx::Rect& new_invalidated_rect, uint64_t previous_content_id, gfx::Rect* total_invalidated_rect, - const gfx::ColorSpace& raster_color_space) { + const gfx::ColorSpace& raster_color_space, + const std::string& debug_name) { DCHECK(new_content_id); DCHECK(previous_content_id); *total_invalidated_rect = gfx::Rect(); @@ -266,10 +269,11 @@ ResourcePool::TryAcquireResourceForPartialRaster( resource->format()); *total_invalidated_rect = resource->invalidated_rect(); - // Clear the invalidated rect and content ID on the resource being retunred. + // Clear the invalidated rect and content ID on the resource being returned. // These will be updated when raster completes successfully. resource->set_invalidated_rect(gfx::Rect()); resource->set_content_id(0); + resource->set_debug_name(debug_name); return InUsePoolResource(resource, !!context_provider_); } @@ -624,8 +628,10 @@ void ResourcePool::PoolResource::OnMemoryDump( bool is_free) const { // Resource IDs are not process-unique, so log with the ResourcePool's unique // tracing id. - std::string dump_name = base::StringPrintf( - "cc/tile_memory/provider_%d/resource_%zd", tracing_id, unique_id_); + const std::string dump_name = base::StringPrintf( + "cc/tile_memory/provider_%d/%s%sresource_%zd", tracing_id, + debug_name_.empty() ? "" : debug_name_.c_str(), + debug_name_.empty() ? "" : "/", unique_id_); MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); // The importance value used here needs to be greater than the importance diff --git a/chromium/cc/resources/resource_pool.h b/chromium/cc/resources/resource_pool.h index 248cd33d88b..134fb058b1b 100644 --- a/chromium/cc/resources/resource_pool.h +++ b/chromium/cc/resources/resource_pool.h @@ -10,6 +10,7 @@ #include <map> #include <memory> +#include <string> #include <utility> #include "base/containers/circular_deque.h" @@ -24,7 +25,6 @@ #include "components/viz/common/resources/resource_id.h" #include "components/viz/common/resources/shared_bitmap.h" #include "gpu/command_buffer/common/sync_token.h" -#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/khronos/GLES2/gl2.h" #include "ui/gfx/color_space.h" #include "ui/gfx/geometry/rect.h" @@ -205,9 +205,11 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider { ResourcePool& operator=(const ResourcePool&) = delete; // Tries to reuse a resource. If none are available, makes a new one. - InUsePoolResource AcquireResource(const gfx::Size& size, - viz::ResourceFormat format, - const gfx::ColorSpace& color_space); + InUsePoolResource AcquireResource( + const gfx::Size& size, + viz::ResourceFormat format, + const gfx::ColorSpace& color_space, + const std::string& debug_name = std::string()); // Tries to acquire the resource with |previous_content_id| for us in partial // raster. If successful, this function will retun the invalidated rect which @@ -217,7 +219,8 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider { const gfx::Rect& new_invalidated_rect, uint64_t previous_content_id, gfx::Rect* total_invalidated_rect, - const gfx::ColorSpace& raster_color_space); + const gfx::ColorSpace& raster_color_space, + const std::string& debug_name = std::string()); // Gives the InUsePoolResource a |resource_id_for_export()| in order to allow // exporting of the resource to the display compositor. This must be called @@ -322,6 +325,9 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider { const viz::ClientResourceProvider* resource_provider, bool is_free) const; + void set_debug_name(const std::string& name) { debug_name_ = name; } + const std::string& debug_name() const { return debug_name_; } + private: const size_t unique_id_; const gfx::Size size_; @@ -348,6 +354,9 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider { // out by ResourcePool, to be filled in by the client. Is destroyed on the // compositor thread. std::unique_ptr<SoftwareBacking> software_backing_; + + // Used for debugging and tracing. + std::string debug_name_; }; // Callback from the ResourceProvider to notify when an exported PoolResource diff --git a/chromium/cc/resources/ui_resource_bitmap.cc b/chromium/cc/resources/ui_resource_bitmap.cc index f63f5e38d7f..b4982dfdd0c 100644 --- a/chromium/cc/resources/ui_resource_bitmap.cc +++ b/chromium/cc/resources/ui_resource_bitmap.cc @@ -7,10 +7,13 @@ #include <stdint.h> #include <memory> +#include <utility> #include "base/check_op.h" #include "base/notreached.h" #include "base/numerics/checked_math.h" +#include "build/build_config.h" +#include "gpu/config/gpu_finch_features.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkMallocPixelRef.h" @@ -71,9 +74,28 @@ size_t UIResourceBitmap::SizeInBytes() const { UIResourceBitmap::UIResourceBitmap(const SkBitmap& skbitmap) { DCHECK(skbitmap.isImmutable()); - sk_sp<SkPixelRef> pixel_ref = sk_ref_sp(skbitmap.pixelRef()); - Create(std::move(pixel_ref), skbitmap.info(), - SkColorTypeToUIResourceFormat(skbitmap.colorType())); + const SkBitmap* target = &skbitmap; +#if defined(OS_ANDROID) + SkBitmap copy; + if (features::IsDrDcEnabled()) { + // TODO(vikassoni): Forcing everything to N32 while android backing cannot + // support some other formats. + if (skbitmap.colorType() != kN32_SkColorType) { + SkImageInfo new_info = skbitmap.info().makeColorType(kN32_SkColorType); + copy.allocPixels(new_info, new_info.minRowBytes()); + SkCanvas copy_canvas(copy); + copy_canvas.drawImage(skbitmap.asImage(), 0, 0, SkSamplingOptions(), + nullptr); + copy.setImmutable(); + target = © + } + DCHECK_EQ(target->width(), target->rowBytesAsPixels()); + DCHECK(target->isImmutable()); + } +#endif + sk_sp<SkPixelRef> pixel_ref = sk_ref_sp(target->pixelRef()); + Create(std::move(pixel_ref), target->info(), + SkColorTypeToUIResourceFormat(target->colorType())); } UIResourceBitmap::UIResourceBitmap(const gfx::Size& size, bool is_opaque) { diff --git a/chromium/cc/scheduler/begin_frame_tracker.cc b/chromium/cc/scheduler/begin_frame_tracker.cc index ac085cb128b..ffeda4ce387 100644 --- a/chromium/cc/scheduler/begin_frame_tracker.cc +++ b/chromium/cc/scheduler/begin_frame_tracker.cc @@ -26,11 +26,12 @@ void BeginFrameTracker::Start(const viz::BeginFrameArgs& new_args) { "location", location_string_); // Trace this specific begin frame tracker Start/Finish times. - TRACE_EVENT_COPY_ASYNC_BEGIN2( + TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN2( TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), location_string_.c_str(), - new_args.frame_time.since_origin().InMicroseconds(), "new args", - new_args.AsValue(), "current args", current_args_.AsValue()); + TRACE_ID_WITH_SCOPE(location_string_.c_str(), + new_args.frame_time.since_origin().InMicroseconds()), + "new args", new_args.AsValue(), "current args", current_args_.AsValue()); // Check the new viz::BeginFrameArgs are valid and monotonically increasing. DCHECK(new_args.IsValid()); @@ -58,10 +59,12 @@ const viz::BeginFrameArgs& BeginFrameTracker::Current() const { void BeginFrameTracker::Finish() { DCHECK(!HasFinished()) << "Tried to finish an already finished frame"; current_finished_at_ = base::TimeTicks::Now(); - TRACE_EVENT_COPY_ASYNC_END0( + TRACE_EVENT_COPY_NESTABLE_ASYNC_END0( TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), location_string_.c_str(), - current_args_.frame_time.since_origin().InMicroseconds()); + TRACE_ID_WITH_SCOPE( + location_string_.c_str(), + current_args_.frame_time.since_origin().InMicroseconds())); } const viz::BeginFrameArgs& BeginFrameTracker::Last() const { @@ -83,6 +86,7 @@ base::TimeDelta BeginFrameTracker::Interval() const { } void BeginFrameTracker::AsProtozeroInto( + perfetto::EventContext& ctx, base::TimeTicks now, perfetto::protos::pbzero::BeginImplFrameArgs* state) const { state->set_updated_at_us(current_updated_at_.since_origin().InMicroseconds()); @@ -91,11 +95,11 @@ void BeginFrameTracker::AsProtozeroInto( if (HasFinished()) { state->set_state( perfetto::protos::pbzero::BeginImplFrameArgs::BEGIN_FRAME_FINISHED); - current_args_.AsProtozeroInto(state->set_current_args()); + current_args_.AsProtozeroInto(ctx, state->set_current_args()); } else { state->set_state( perfetto::protos::pbzero::BeginImplFrameArgs::BEGIN_FRAME_USING); - current_args_.AsProtozeroInto(state->set_last_args()); + current_args_.AsProtozeroInto(ctx, state->set_last_args()); } base::TimeTicks frame_time = current_args_.frame_time; diff --git a/chromium/cc/scheduler/begin_frame_tracker.h b/chromium/cc/scheduler/begin_frame_tracker.h index c718ca39559..1165a0ca352 100644 --- a/chromium/cc/scheduler/begin_frame_tracker.h +++ b/chromium/cc/scheduler/begin_frame_tracker.h @@ -12,6 +12,7 @@ #include "components/viz/common/frame_sinks/begin_frame_args.h" namespace perfetto { +class EventContext; namespace protos { namespace pbzero { class BeginImplFrameArgs; @@ -71,6 +72,7 @@ class CC_EXPORT BeginFrameTracker { base::TimeDelta Interval() const; void AsProtozeroInto( + perfetto::EventContext& ctx, base::TimeTicks now, perfetto::protos::pbzero::BeginImplFrameArgs* dict) const; diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index ccb984ddcff..a8ba4e2793f 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -554,31 +554,6 @@ void Scheduler::BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args) { state_machine_.set_should_defer_invalidation_for_fast_main_frame( main_thread_response_expected_before_deadline); - base::TimeDelta bmf_to_activate_estimate = bmf_to_activate_estimate_critical; - if (!begin_main_frame_args_.on_critical_path) { - bmf_to_activate_estimate = - compositor_timing_history_ - ->BeginMainFrameQueueToActivateNotCriticalEstimate(); - } - bool can_activate_before_deadline = - CanBeginMainFrameAndActivateBeforeDeadline(adjusted_args, - bmf_to_activate_estimate, now); - - if (ShouldRecoverMainLatency(adjusted_args, can_activate_before_deadline)) { - TRACE_EVENT_INSTANT0("cc", "SkipBeginMainFrameToReduceLatency", - TRACE_EVENT_SCOPE_THREAD); - state_machine_.SetSkipNextBeginMainFrameToReduceLatency(true); - } else if (ShouldRecoverImplLatency(adjusted_args, - can_activate_before_deadline)) { - TRACE_EVENT_INSTANT0("cc", "SkipBeginImplFrameToReduceLatency", - TRACE_EVENT_SCOPE_THREAD); - skipped_last_frame_to_reduce_latency_ = true; - SendDidNotProduceFrame(args, FrameSkippedReason::kRecoverLatency); - return; - } - - skipped_last_frame_to_reduce_latency_ = false; - // A pipeline is activated if it's subscribed to BeginFrame callbacks. For the // compositor this implies BeginImplFrames while for the main thread it would // be BeginMainFrames. @@ -788,6 +763,7 @@ void Scheduler::OnBeginImplFrameDeadline() { // order to wait for more user-input before starting the next commit. // * Creating a new OuputSurface will not occur during the deadline in // order to allow the state machine to "settle" first. + compositor_timing_history_->RecordDeadlineMode(deadline_mode_); if (!settings_.using_synchronous_renderer_compositor) { compositor_timing_history_->WillFinishImplFrame( state_machine_.needs_redraw()); @@ -869,7 +845,8 @@ void Scheduler::ProcessScheduledActions() { action = state_machine_.NextAction(); TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), "SchedulerStateMachine", [this](perfetto::EventContext ctx) { - this->AsProtozeroInto(ctx.event()->set_cc_scheduler_state()); + this->AsProtozeroInto(ctx, + ctx.event()->set_cc_scheduler_state()); }); base::AutoReset<SchedulerStateMachine::Action> mark_inside_action( &inside_action_, action); @@ -956,6 +933,7 @@ void Scheduler::ProcessScheduledActions() { } void Scheduler::AsProtozeroInto( + perfetto::EventContext& ctx, perfetto::protos::pbzero::ChromeCompositorSchedulerState* state) const { base::TimeTicks now = Now(); @@ -967,8 +945,6 @@ void Scheduler::AsProtozeroInto( state->set_pending_begin_frame_task(!pending_begin_frame_task_.IsCancelled()); state->set_skipped_last_frame_missed_exceeded_deadline( skipped_last_frame_missed_exceeded_deadline_); - state->set_skipped_last_frame_to_reduce_latency( - skipped_last_frame_to_reduce_latency_); state->set_inside_action( SchedulerStateMachine::ActionToProtozeroEnum(inside_action_)); state->set_deadline_mode( @@ -984,14 +960,15 @@ void Scheduler::AsProtozeroInto( state->set_now_to_deadline_scheduled_at_delta_us( (deadline_scheduled_at_ - Now()).InMicroseconds()); - begin_impl_frame_tracker_.AsProtozeroInto(now, + begin_impl_frame_tracker_.AsProtozeroInto(ctx, now, state->set_begin_impl_frame_args()); BeginFrameObserverBase::AsProtozeroInto( - state->set_begin_frame_observer_state()); + ctx, state->set_begin_frame_observer_state()); if (begin_frame_source_) { - begin_frame_source_->AsProtozeroInto(state->set_begin_frame_source_state()); + begin_frame_source_->AsProtozeroInto(ctx, + state->set_begin_frame_source_state()); } } @@ -1001,89 +978,6 @@ void Scheduler::UpdateCompositorTimingHistoryRecordingEnabled() { state_machine_.visible()); } -bool Scheduler::ShouldRecoverMainLatency( - const viz::BeginFrameArgs& args, - bool can_activate_before_deadline) const { - DCHECK(!settings_.using_synchronous_renderer_compositor); - - if (!settings_.enable_main_latency_recovery) - return false; - - // The main thread is in a low latency mode and there's no need to recover. - if (!state_machine_.main_thread_missed_last_deadline()) - return false; - - // When prioritizing impl thread latency, we currently put the - // main thread in a high latency mode. Don't try to fight it. - if (state_machine_.ImplLatencyTakesPriority()) - return false; - - // Ensure that we have data from at least one frame before attempting latency - // recovery. This prevents skipping of frames during loading where the main - // thread is likely slow but we assume it to be fast since we have no history. - static const int kMinNumberOfSamplesBeforeLatencyRecovery = 1; - if (compositor_timing_history_ - ->begin_main_frame_start_to_ready_to_commit_sample_count() < - kMinNumberOfSamplesBeforeLatencyRecovery || - compositor_timing_history_->commit_to_ready_to_activate_sample_count() < - kMinNumberOfSamplesBeforeLatencyRecovery) { - return false; - } - - return can_activate_before_deadline; -} - -bool Scheduler::ShouldRecoverImplLatency( - const viz::BeginFrameArgs& args, - bool can_activate_before_deadline) const { - DCHECK(!settings_.using_synchronous_renderer_compositor); - - if (!settings_.enable_impl_latency_recovery) - return false; - - // Disable impl thread latency recovery when using the unthrottled - // begin frame source since we will always get a BeginFrame before - // the swap ack and our heuristics below will not work. - if (begin_frame_source_ && !begin_frame_source_->IsThrottled()) - return false; - - // If we are swap throttled at the BeginFrame, that means the impl thread is - // very likely in a high latency mode. - bool impl_thread_is_likely_high_latency = state_machine_.IsDrawThrottled(); - if (!impl_thread_is_likely_high_latency) - return false; - - // The deadline may be in the past if our draw time is too long. - bool can_draw_before_deadline = args.frame_time < args.deadline; - - // When prioritizing impl thread latency, the deadline doesn't wait - // for the main thread. - if (state_machine_.ImplLatencyTakesPriority()) - return can_draw_before_deadline; - - // If we only have impl-side updates, the deadline doesn't wait for - // the main thread. - if (state_machine_.OnlyImplSideUpdatesExpected()) - return can_draw_before_deadline; - - // If we get here, we know the main thread is in a low-latency mode relative - // to the impl thread. In this case, only try to also recover impl thread - // latency if both the main and impl threads can run serially before the - // deadline. - return can_activate_before_deadline; -} - -bool Scheduler::CanBeginMainFrameAndActivateBeforeDeadline( - const viz::BeginFrameArgs& args, - base::TimeDelta bmf_to_activate_estimate, - base::TimeTicks now) const { - // Check if the main thread computation and commit can be finished before the - // impl thread's deadline. - base::TimeTicks estimated_draw_time = now + bmf_to_activate_estimate; - - return estimated_draw_time < args.deadline; -} - bool Scheduler::IsBeginMainFrameSent() const { return state_machine_.begin_main_frame_state() == SchedulerStateMachine::BeginMainFrameState::SENT; @@ -1095,7 +989,6 @@ viz::BeginFrameAck Scheduler::CurrentBeginFrameAckForActiveTree() const { void Scheduler::ClearHistory() { // Ensure we reset decisions based on history from the previous navigation. - state_machine_.SetSkipNextBeginMainFrameToReduceLatency(false); compositor_timing_history_->ClearHistory(); ProcessScheduledActions(); } diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h index 86f0c7bede6..082fcb53d21 100644 --- a/chromium/cc/scheduler/scheduler.h +++ b/chromium/cc/scheduler/scheduler.h @@ -250,6 +250,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { void SetMainThreadWantsBeginMainFrameNotExpected(bool new_state); void AsProtozeroInto( + perfetto::EventContext& ctx, perfetto::protos::pbzero::ChromeCompositorSchedulerState* state) const; void SetVideoNeedsBeginFrames(bool video_needs_begin_frames); @@ -284,7 +285,6 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { bool observing_begin_frame_source_ = false; bool skipped_last_frame_missed_exceeded_deadline_ = false; - bool skipped_last_frame_to_reduce_latency_ = false; std::unique_ptr<CompositorTimingHistory> compositor_timing_history_; @@ -384,14 +384,6 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { void DrawForced(); void ProcessScheduledActions(); void UpdateCompositorTimingHistoryRecordingEnabled(); - bool ShouldRecoverMainLatency(const viz::BeginFrameArgs& args, - bool can_activate_before_deadline) const; - bool ShouldRecoverImplLatency(const viz::BeginFrameArgs& args, - bool can_activate_before_deadline) const; - bool CanBeginMainFrameAndActivateBeforeDeadline( - const viz::BeginFrameArgs& args, - base::TimeDelta bmf_to_activate_estimate, - base::TimeTicks now) const; void AdvanceCommitStateIfPossible(); void BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args); diff --git a/chromium/cc/scheduler/scheduler_settings.cc b/chromium/cc/scheduler/scheduler_settings.cc index d63db2a646f..5e6338c0a0d 100644 --- a/chromium/cc/scheduler/scheduler_settings.cc +++ b/chromium/cc/scheduler/scheduler_settings.cc @@ -4,6 +4,8 @@ #include "cc/scheduler/scheduler_settings.h" +#include <utility> + #include "base/trace_event/traced_value.h" namespace cc { @@ -25,10 +27,6 @@ SchedulerSettings::AsValue() const { maximum_number_of_failed_draws_before_draw_is_forced); state->SetBoolean("using_synchronous_renderer_compositor", using_synchronous_renderer_compositor); - state->SetBoolean("enable_impl_latency_recovery", - enable_impl_latency_recovery); - state->SetBoolean("enable_main_latency_recovery", - enable_main_latency_recovery); state->SetBoolean("wait_for_all_pipeline_stages_before_draw", wait_for_all_pipeline_stages_before_draw); return std::move(state); diff --git a/chromium/cc/scheduler/scheduler_settings.h b/chromium/cc/scheduler/scheduler_settings.h index 55ace0031ae..f852ca2a6e3 100644 --- a/chromium/cc/scheduler/scheduler_settings.h +++ b/chromium/cc/scheduler/scheduler_settings.h @@ -37,16 +37,10 @@ class CC_EXPORT SchedulerSettings { // This is enabled for android-webview. bool using_synchronous_renderer_compositor = false; - // This is used to determine whether some begin-frames should be skipped - // (either in the main-thread or the compositor-thread) if previous frames - // have had high latency. It is disabled by default. - bool enable_impl_latency_recovery = false; - bool enable_main_latency_recovery = false; - // Turning this on effectively disables pipelining of compositor frame // production stages by waiting for each stage to complete before producing - // the frame. Turning this on also disables latency-recovery. This is enabled - // for headless-mode and some tests, and disabled elsewhere by default. + // the frame. This is enabled for headless-mode and some tests, and disabled + // elsewhere by default. bool wait_for_all_pipeline_stages_before_draw = false; int maximum_number_of_failed_draws_before_draw_is_forced = 3; diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc index 8fc7ba939d7..0e49dca023e 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.cc +++ b/chromium/cc/scheduler/scheduler_state_machine.cc @@ -262,8 +262,6 @@ void SchedulerStateMachine::AsProtozeroInto( critical_begin_main_frame_to_activate_is_fast_); minor_state->set_main_thread_missed_last_deadline( main_thread_missed_last_deadline_); - minor_state->set_skip_next_begin_main_frame_to_reduce_latency( - skip_next_begin_main_frame_to_reduce_latency_); minor_state->set_video_needs_begin_frames(video_needs_begin_frames_); minor_state->set_defer_begin_main_frame(defer_begin_main_frame_); minor_state->set_last_commit_had_no_updates(last_commit_had_no_updates_); @@ -599,9 +597,6 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { if (IsDrawThrottled() && !just_submitted_in_deadline) return false; - if (skip_next_begin_main_frame_to_reduce_latency_) - return false; - return true; } @@ -1041,13 +1036,6 @@ void SchedulerStateMachine::WillInvalidateLayerTreeFrameSink() { current_frame_number_; } -void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency( - bool skip) { - TRACE_EVENT_INSTANT0("cc", "Scheduler: SkipNextBeginMainFrameToReduceLatency", - TRACE_EVENT_SCOPE_THREAD); - skip_next_begin_main_frame_to_reduce_latency_ = skip; -} - bool SchedulerStateMachine::BeginFrameNeededForVideo() const { return video_needs_begin_frames_; } @@ -1193,8 +1181,6 @@ void SchedulerStateMachine::OnBeginImplFrameIdle() { // This is same as the old prepare tiles funnel behavior. did_prepare_tiles_ = false; - skip_next_begin_main_frame_to_reduce_latency_ = false; - // If a new or undrawn active tree is pending after the deadline, // then the main thread is in a high latency mode. main_thread_missed_last_deadline_ = @@ -1359,14 +1345,6 @@ void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; } -bool SchedulerStateMachine::OnlyImplSideUpdatesExpected() const { - bool has_impl_updates = needs_redraw_ || needs_one_begin_impl_frame_; - bool main_updates_expected = - needs_begin_main_frame_ || - begin_main_frame_state_ != BeginMainFrameState::IDLE || has_pending_tree_; - return has_impl_updates && !main_updates_expected; -} - void SchedulerStateMachine::SetNeedsPrepareTiles() { if (!needs_prepare_tiles_) { TRACE_EVENT0("cc", "SchedulerStateMachine::SetNeedsPrepareTiles"); diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h index 0f0705272da..70f8b8b2f1d 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.h +++ b/chromium/cc/scheduler/scheduler_state_machine.h @@ -64,18 +64,25 @@ class CC_EXPORT SchedulerStateMachine { BeginImplFrameState BeginImplFrameStateToProtozeroEnum(BeginImplFrameState state); + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + // TODO(weiliangc): The histogram is used to understanding what type of + // deadline mode do we encounter in real world and is set to expire after + // 2022. The Enum can be changed after the histogram is removed. // The scheduler uses a deadline to wait for main thread updates before // submitting a compositor frame. BeginImplFrameDeadlineMode specifies when // the deadline should run. enum class BeginImplFrameDeadlineMode { - NONE, // No deadline should be scheduled e.g. for synchronous compositor. - IMMEDIATE, // Deadline should be scheduled to run immediately. - REGULAR, // Deadline should be scheduled to run at the deadline provided by - // in the BeginFrameArgs. - LATE, // Deadline should be scheduled run when the next frame is expected - // to arrive. - BLOCKED, // Deadline should be blocked indefinitely until the next frame - // arrives. + NONE = 0, // No deadline should be scheduled e.g. for synchronous + // compositor. + IMMEDIATE = 1, // Deadline should be scheduled to run immediately. + REGULAR = 2, // Deadline should be scheduled to run at the deadline + // provided by in the BeginFrameArgs. + LATE = 3, // Deadline should be scheduled run when the next frame is + // expected to arrive. + BLOCKED = 4, // Deadline should be blocked indefinitely until the next + // frame arrives. + kMaxValue = BLOCKED, }; // TODO(nuskos): Update Scheduler::ScheduleBeginImplFrameDeadline event to // used typed macros so we can remove this ToString function. @@ -212,8 +219,6 @@ class CC_EXPORT SchedulerStateMachine { return did_invalidate_layer_tree_frame_sink_; } - bool OnlyImplSideUpdatesExpected() const; - // Indicates that prepare-tiles is required. This guarantees another // PrepareTiles will occur shortly (even if no redraw is required). void SetNeedsPrepareTiles(); @@ -265,9 +270,6 @@ class CC_EXPORT SchedulerStateMachine { // from NextAction if the client rejects the BeginMainFrame message. void BeginMainFrameAborted(CommitEarlyOutReason reason); - // Indicates production should be skipped to recover latency. - void SetSkipNextBeginMainFrameToReduceLatency(bool skip); - // For Android WebView, resourceless software draws are allowed even when // invisible. void SetResourcelessSoftwareDraw(bool resourceless_draw); @@ -453,7 +455,6 @@ class CC_EXPORT SchedulerStateMachine { ScrollHandlerState::SCROLL_DOES_NOT_AFFECT_SCROLL_HANDLER; bool critical_begin_main_frame_to_activate_is_fast_ = true; bool main_thread_missed_last_deadline_ = false; - bool skip_next_begin_main_frame_to_reduce_latency_ = false; bool defer_begin_main_frame_ = false; bool video_needs_begin_frames_ = false; bool last_commit_had_no_updates_ = false; diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index c3ae6947720..57bc2db1b0d 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -414,9 +414,6 @@ class SchedulerTest : public testing::Test { client_->set_scheduler(scheduler_.get()); scheduler_->SetBeginFrameSource(frame_source); - // Use large estimates by default to avoid latency recovery in most tests. - fake_compositor_timing_history_->SetAllEstimatesTo(kSlowDuration); - return scheduler_.get(); } @@ -581,8 +578,6 @@ class SchedulerTest : public testing::Test { void AdvanceAndMissOneFrame(); void CheckMainFrameNotSkippedAfterLateCommit(); - void ImplFrameSkippedAfterLateAck(bool is_already_receiving_begin_frames, - bool receive_ack_before_deadline); void ImplFrameNotSkippedAfterLateAck(); void BeginFramesNotFromClient(BeginFrameSourceType bfs_type); void BeginFramesNotFromClient_IsDrawThrottled(BeginFrameSourceType bfs_type); @@ -1774,211 +1769,6 @@ TEST_F(SchedulerTest, MainFrameNotSkippedWhenNoTimingHistory) { EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); } -void SchedulerTest::ImplFrameSkippedAfterLateAck( - bool is_already_receiving_begin_frames, - bool receive_ack_before_deadline) { - // To get into a high latency state, this test disables automatic swap acks. - client_->SetAutomaticSubmitCompositorFrameAck(false); - - // Draw and swap for first BeginFrame - client_->Reset(); - scheduler_->SetNeedsBeginMainFrame(); - scheduler_->SetNeedsRedraw(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - SendNextBeginFrame(); - if (is_already_receiving_begin_frames) { - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); - } else { - EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", - "ScheduledActionSendBeginMainFrame"); - } - - client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(nullptr); - scheduler_->NotifyReadyToActivate(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_ACTIONS("ScheduledActionCommit", "ScheduledActionActivateSyncTree", - "ScheduledActionDrawIfPossible"); - - // Verify we skip every other frame if the swap ack consistently - // comes back late. - for (int i = 0; i < 10; i++) { - // Not calling scheduler_->DidReceiveCompositorFrameAck() until after next - // BeginImplFrame puts the impl thread in high latency mode. - client_->Reset(); - scheduler_->SetNeedsBeginMainFrame(); - scheduler_->SetNeedsRedraw(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - SendNextBeginFrame(); - // Verify that we skip the BeginImplFrame - EXPECT_NO_ACTION(); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - EXPECT_EQ(FrameSkippedReason::kRecoverLatency, - client_->last_frame_skipped_reason()); - - // Verify that we do not perform any actions after we are no longer - // swap throttled. - client_->Reset(); - if (receive_ack_before_deadline) { - // It shouldn't matter if the swap ack comes back before the deadline... - scheduler_->DidReceiveCompositorFrameAck(); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - } else { - // ... or after the deadline. - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - scheduler_->DidReceiveCompositorFrameAck(); - } - EXPECT_NO_ACTION(); - - // Verify that we start the next BeginImplFrame and continue normally - // after having just skipped a BeginImplFrame. - client_->Reset(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - SendNextBeginFrame(); - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); - - client_->Reset(); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(nullptr); - scheduler_->NotifyReadyToActivate(); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_ACTIONS("ScheduledActionCommit", "ScheduledActionActivateSyncTree", - "ScheduledActionDrawIfPossible"); - } -} - -TEST_F(SchedulerTest, - ImplFrameSkippedAfterLateAck_FastEstimates_SubmitAckThenDeadline) { - // Compositor thread latency recovery should be enabled for this test. - scheduler_settings_.enable_impl_latency_recovery = true; - SetUpScheduler(EXTERNAL_BFS); - fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); - - bool is_already_receiving_begin_frames = false; - bool receive_ack_before_deadline = true; - EXPECT_SCOPED(ImplFrameSkippedAfterLateAck(is_already_receiving_begin_frames, - receive_ack_before_deadline)); -} - -TEST_F(SchedulerTest, - ImplFrameSkippedAfterLateAck_FastEstimates_DeadlineThenSubmitAck) { - // Compositor thread latency recovery should be enabled for this test. - scheduler_settings_.enable_impl_latency_recovery = true; - SetUpScheduler(EXTERNAL_BFS); - fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); - - bool is_already_receiving_begin_frames = false; - bool receive_ack_before_deadline = false; - EXPECT_SCOPED(ImplFrameSkippedAfterLateAck(is_already_receiving_begin_frames, - receive_ack_before_deadline)); -} - -TEST_F(SchedulerTest, - ImplFrameSkippedAfterLateAck_LongMainFrameQueueDurationNotCritical) { - // Compositor thread latency recovery should be enabled for this test. - scheduler_settings_.enable_impl_latency_recovery = true; - SetUpScheduler(EXTERNAL_BFS); - fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); - fake_compositor_timing_history_ - ->SetBeginMainFrameQueueDurationNotCriticalEstimate(kSlowDuration); - - bool is_already_receiving_begin_frames = false; - bool receive_ack_before_deadline = false; - EXPECT_SCOPED(ImplFrameSkippedAfterLateAck(is_already_receiving_begin_frames, - receive_ack_before_deadline)); -} - -TEST_F(SchedulerTest, ImplFrameSkippedAfterLateAck_ImplLatencyTakesPriority) { - // Compositor thread latency recovery should be enabled for this test. - scheduler_settings_.enable_impl_latency_recovery = true; - SetUpScheduler(EXTERNAL_BFS); - - // Even if every estimate related to the main thread is slow, we should - // still expect to recover impl thread latency if the draw is fast and we - // are in impl latency takes priority. - client_->Reset(); - scheduler_->SetTreePrioritiesAndScrollState( - SMOOTHNESS_TAKES_PRIORITY, - ScrollHandlerState::SCROLL_DOES_NOT_AFFECT_SCROLL_HANDLER); - fake_compositor_timing_history_->SetAllEstimatesTo(kSlowDuration); - fake_compositor_timing_history_->SetDrawDurationEstimate(kFastDuration); - EXPECT_ACTIONS("AddObserver(this)"); - - bool is_already_receiving_begin_frames = true; - bool receive_ack_before_deadline = false; - EXPECT_SCOPED(ImplFrameSkippedAfterLateAck(is_already_receiving_begin_frames, - receive_ack_before_deadline)); -} - -TEST_F(SchedulerTest, - ImplFrameSkippedAfterLateAck_OnlyImplSideUpdatesExpected) { - // Compositor thread latency recovery should be enabled for this test. - scheduler_settings_.enable_impl_latency_recovery = true; - // This tests that we recover impl thread latency when there are no commits. - SetUpScheduler(EXTERNAL_BFS); - - // To get into a high latency state, this test disables automatic swap acks. - client_->SetAutomaticSubmitCompositorFrameAck(false); - - // Even if every estimate related to the main thread is slow, we should - // still expect to recover impl thread latency if there are no commits from - // the main thread. - fake_compositor_timing_history_->SetAllEstimatesTo(kSlowDuration); - fake_compositor_timing_history_->SetDrawDurationEstimate(kFastDuration); - - // Draw and swap for first BeginFrame - client_->Reset(); - scheduler_->SetNeedsRedraw(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - SendNextBeginFrame(); - EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame"); - - client_->Reset(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); - - // Verify we skip every other frame if the swap ack consistently - // comes back late. - for (int i = 0; i < 10; i++) { - // Not calling scheduler_->DidReceiveCompositorFrameAck() until after next - // BeginImplFrame puts the impl thread in high latency mode. - client_->Reset(); - scheduler_->SetNeedsRedraw(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - SendNextBeginFrame(); - // Verify that we skip the BeginImplFrame - EXPECT_NO_ACTION(); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - EXPECT_EQ(FrameSkippedReason::kRecoverLatency, - client_->last_frame_skipped_reason()); - - // Verify that we do not perform any actions after we are no longer - // swap throttled. - client_->Reset(); - scheduler_->DidReceiveCompositorFrameAck(); - EXPECT_NO_ACTION(); - - // Verify that we start the next BeginImplFrame and continue normally - // after having just skipped a BeginImplFrame. - client_->Reset(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - SendNextBeginFrame(); - EXPECT_ACTIONS("WillBeginImplFrame"); - - client_->Reset(); - // Deadline should be immediate. - EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner_->RunUntilTime(task_runner_->NowTicks()); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); - } -} - void SchedulerTest::ImplFrameNotSkippedAfterLateAck() { // To get into a high latency state, this test disables automatic swap acks. client_->SetAutomaticSubmitCompositorFrameAck(false); @@ -2070,123 +1860,6 @@ TEST_F(SchedulerTest, ImplFrameNotSkippedAfterLateAck_DrawEstimateTooLong) { EXPECT_SCOPED(ImplFrameNotSkippedAfterLateAck()); } -TEST_F(SchedulerTest, MainFrameThenImplFrameSkippedAfterLateCommitAndLateAck) { - // Latency recovery should be enabled for this test. - scheduler_settings_.enable_impl_latency_recovery = true; - scheduler_settings_.enable_main_latency_recovery = true; - // Set up client with custom estimates. - // This test starts off with expensive estimates to prevent latency recovery - // initially, then lowers the estimates to enable it once both the main - // and impl threads are in a high latency mode. - SetUpScheduler(EXTERNAL_BFS); - fake_compositor_timing_history_->SetAllEstimatesTo(kSlowDuration); - - // To get into a high latency state, this test disables automatic swap acks. - client_->SetAutomaticSubmitCompositorFrameAck(false); - - // Impl thread hits deadline before commit finishes to make - // MainThreadMissedLastDeadline true - client_->Reset(); - scheduler_->SetNeedsBeginMainFrame(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - EXPECT_SCOPED(AdvanceFrame()); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(nullptr); - scheduler_->NotifyReadyToActivate(); - EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - - EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", - "ScheduledActionSendBeginMainFrame", "ScheduledActionCommit", - "ScheduledActionActivateSyncTree"); - - // Draw and swap for first commit, start second commit. - client_->Reset(); - scheduler_->SetNeedsBeginMainFrame(); - EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - EXPECT_SCOPED(AdvanceFrame()); - EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(nullptr); - scheduler_->NotifyReadyToActivate(); - - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", - "ScheduledActionDrawIfPossible", "ScheduledActionCommit", - "ScheduledActionActivateSyncTree"); - - // Don't call scheduler_->DidReceiveCompositorFrameAck() until after next - // frame - // to put the impl thread in a high latency mode. - client_->Reset(); - scheduler_->SetNeedsBeginMainFrame(); - EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - EXPECT_SCOPED(AdvanceFrame()); - EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - - EXPECT_ACTIONS("WillBeginImplFrame"); - // Note: BeginMainFrame and swap are skipped here because of - // swap ack backpressure, not because of latency recovery. - EXPECT_FALSE(client_->HasAction("ScheduledActionSendBeginMainFrame")); - EXPECT_FALSE(client_->HasAction("ScheduledActionDrawIfPossible")); - EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - - // Lower estimates so that the scheduler will attempt latency recovery. - fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); - - // Now that both threads are in a high latency mode, make sure we - // skip the BeginMainFrame, then the BeginImplFrame, but not both - // at the same time. - - // Verify we skip BeginMainFrame first. - client_->Reset(); - // Previous commit request is still outstanding. - EXPECT_TRUE(scheduler_->NeedsBeginMainFrame()); - EXPECT_TRUE(scheduler_->IsDrawThrottled()); - SendNextBeginFrame(); - EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->DidReceiveCompositorFrameAck(); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionDrawIfPossible"); - - // Verify we skip the BeginImplFrame second. - client_->Reset(); - // Previous commit request is still outstanding. - EXPECT_TRUE(scheduler_->NeedsBeginMainFrame()); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - SendNextBeginFrame(); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->DidReceiveCompositorFrameAck(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - - EXPECT_NO_ACTION(); - - // Then verify we operate in a low latency mode. - client_->Reset(); - // Previous commit request is still outstanding. - EXPECT_TRUE(scheduler_->NeedsBeginMainFrame()); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - SendNextBeginFrame(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(nullptr); - scheduler_->NotifyReadyToActivate(); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - scheduler_->DidReceiveCompositorFrameAck(); - EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); - - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", - "ScheduledActionCommit", "ScheduledActionActivateSyncTree", - "ScheduledActionDrawIfPossible"); -} - void SchedulerTest::BeginFramesNotFromClient(BeginFrameSourceType bfs_type) { SetUpScheduler(bfs_type); @@ -3918,55 +3591,6 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) { client_->last_frame_skipped_reason()); } -TEST_F(SchedulerTest, BeginFrameAckForSkippedImplFrame) { - // Compositor thread latency recovery should be enabled for this test. - scheduler_settings_.enable_impl_latency_recovery = true; - - SetUpScheduler(EXTERNAL_BFS); - - // To get into a high latency state, this test disables automatic swap acks. - client_->SetAutomaticSubmitCompositorFrameAck(false); - fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); - - // Run a successful redraw that submits a compositor frame but doesn't receive - // a swap ack. Verify that a viz::BeginFrameAck is sent for it. - scheduler_->SetNeedsRedraw(); - client_->Reset(); - - viz::BeginFrameArgs args = SendNextBeginFrame(); - EXPECT_ACTIONS("WillBeginImplFrame"); - EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - EXPECT_TRUE(scheduler_->begin_frames_expected()); - client_->Reset(); - - task_runner_->RunPendingTasks(); // Run posted deadline. - EXPECT_ACTIONS("ScheduledActionDrawIfPossible"); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - EXPECT_TRUE(scheduler_->begin_frames_expected()); - - // Successful draw caused damage. - bool has_damage = true; - EXPECT_EQ(viz::BeginFrameAck(args, has_damage), - client_->last_begin_frame_ack()); - client_->Reset(); - - // Request another redraw that will be skipped because the swap ack is still - // missing. Verify that a new viz::BeginFrameAck is sent. - scheduler_->SetNeedsRedraw(); - client_->Reset(); - - args = SendNextBeginFrame(); - EXPECT_NO_ACTION(); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - EXPECT_TRUE(scheduler_->begin_frames_expected()); - - // Skipped draw: no damage. - has_damage = false; - EXPECT_EQ(viz::BeginFrameAck(args, has_damage), - client_->last_begin_frame_ack()); - client_->Reset(); -} - TEST_F(SchedulerTest, BeginFrameAckForBeginFrameBeforeLastDeadline) { SetUpScheduler(EXTERNAL_BFS); @@ -4326,37 +3950,6 @@ TEST_F(SchedulerTest, SynchronousCompositorImplSideInvalidation) { EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); } -TEST_F(SchedulerTest, DontSkipMainFrameAfterClearingHistory) { - // Set up a fast estimate for the main frame and make it miss the deadline. - scheduler_settings_.main_frame_before_activation_enabled = true; - // Compositor thread latency recovery should be enabled for this test. - scheduler_settings_.enable_main_latency_recovery = true; - SetUpScheduler(EXTERNAL_BFS); - fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); - client_->Reset(); - scheduler_->SetNeedsBeginMainFrame(); - EXPECT_SCOPED(AdvanceFrame()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", - "ScheduledActionSendBeginMainFrame"); - - // Now commit during the second frame, since the main thread missed the last - // deadline but we have a fast estimate, we would want to skip the next main - // frame. - client_->Reset(); - EXPECT_SCOPED(AdvanceFrame()); - scheduler_->SetNeedsBeginMainFrame(); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(nullptr); - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionCommit"); - - // But during the commit, the history is cleared. So the main frame should not - // be skipped. - client_->Reset(); - scheduler_->ClearHistory(); - EXPECT_ACTIONS("ScheduledActionSendBeginMainFrame"); -} - TEST_F(SchedulerTest, NoInvalidationForAnimateOnlyFrames) { SetUpScheduler(EXTERNAL_BFS); fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); @@ -4403,8 +3996,6 @@ TEST_F(SchedulerTest, SendEarlyDidNotProduceFrameIfIdle) { TEST_F(SchedulerTest, HighImplLatencyModePrioritizesMainFramesOverImplInvalidation) { - scheduler_settings_.enable_main_latency_recovery = false; - scheduler_settings_.enable_impl_latency_recovery = false; SetUpScheduler(EXTERNAL_BFS); fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); @@ -4469,6 +4060,7 @@ class SchedulerTestForPowerMode : public SchedulerTest { ~SchedulerTestForPowerMode() override { DCHECK_EQ(this, current_test_); current_test_ = nullptr; + power_mode_arbiter_.RemoveObserver(&observer_); } void AdvanceToArbiterSnapAfter(base::TimeDelta delay) { diff --git a/chromium/cc/tiles/checker_image_tracker.cc b/chromium/cc/tiles/checker_image_tracker.cc index 432201499c3..53d9df8a904 100644 --- a/chromium/cc/tiles/checker_image_tracker.cc +++ b/chromium/cc/tiles/checker_image_tracker.cc @@ -8,7 +8,6 @@ #include "base/bind.h" #include "base/metrics/histogram_macros.h" -#include "base/stl_util.h" #include "base/trace_event/trace_event.h" namespace cc { diff --git a/chromium/cc/tiles/checker_image_tracker.h b/chromium/cc/tiles/checker_image_tracker.h index 5e336665055..76cdea7cac6 100644 --- a/chromium/cc/tiles/checker_image_tracker.h +++ b/chromium/cc/tiles/checker_image_tracker.h @@ -138,7 +138,7 @@ class CC_EXPORT CheckerImageTracker { struct DecodeState { DecodePolicy policy = DecodePolicy::SYNC; bool use_dark_mode = false; - SkFilterQuality filter_quality = kNone_SkFilterQuality; + PaintFlags::FilterQuality filter_quality = PaintFlags::FilterQuality::kNone; SkSize scale = SkSize::MakeEmpty(); gfx::ColorSpace color_space; size_t frame_index = PaintImage::kDefaultFrameIndex; diff --git a/chromium/cc/tiles/checker_image_tracker_unittest.cc b/chromium/cc/tiles/checker_image_tracker_unittest.cc index 4c8886087b8..feb731583e9 100644 --- a/chromium/cc/tiles/checker_image_tracker_unittest.cc +++ b/chromium/cc/tiles/checker_image_tracker_unittest.cc @@ -124,7 +124,7 @@ class CheckerImageTrackerTest : public testing::Test, .set_decoding_mode(PaintImage::DecodingMode::kAsync) .TakePaintImage(), false, SkIRect::MakeWH(dimension, dimension), - kNone_SkFilterQuality, SkM44(), + PaintFlags::FilterQuality::kNone, SkM44(), PaintImage::kDefaultFrameIndex, gfx::ColorSpace()); } @@ -438,7 +438,7 @@ TEST_F(CheckerImageTrackerTest, CheckersOnlyStaticCompletedImages) { .set_paint_image_generator(CreatePaintImageGenerator(image_size)) .TakePaintImage(), false, SkIRect::MakeWH(image_size.width(), image_size.height()), - kNone_SkFilterQuality, SkM44(), PaintImage::kDefaultFrameIndex, + PaintFlags::FilterQuality::kNone, SkM44(), PaintImage::kDefaultFrameIndex, gfx::ColorSpace()); EXPECT_FALSE( ShouldCheckerImage(completed_paint_image, WhichTree::PENDING_TREE)); @@ -470,7 +470,7 @@ TEST_F(CheckerImageTrackerTest, ChoosesMaxScaleAndQuality) { gfx::ColorSpace()); DrawImage scaled_image2 = DrawImage(image.paint_image(), false, image.src_rect(), - kHigh_SkFilterQuality, SkM44::Scale(1.8f, 1.8f), + PaintFlags::FilterQuality::kHigh, SkM44::Scale(1.8f, 1.8f), PaintImage::kDefaultFrameIndex, gfx::ColorSpace()); std::vector<DrawImage> draw_images = {scaled_image1, scaled_image2}; @@ -481,7 +481,7 @@ TEST_F(CheckerImageTrackerTest, ChoosesMaxScaleAndQuality) { EXPECT_EQ(image_controller_.decoded_images()[0].scale(), SkSize::Make(1.8f, 1.8f)); EXPECT_EQ(image_controller_.decoded_images()[0].filter_quality(), - kHigh_SkFilterQuality); + PaintFlags::FilterQuality::kHigh); } TEST_F(CheckerImageTrackerTest, DontCheckerMultiPartImages) { diff --git a/chromium/cc/tiles/decoded_image_tracker.cc b/chromium/cc/tiles/decoded_image_tracker.cc index 763f55239a3..41ec58bc47b 100644 --- a/chromium/cc/tiles/decoded_image_tracker.cc +++ b/chromium/cc/tiles/decoded_image_tracker.cc @@ -48,8 +48,9 @@ void DecodedImageTracker::QueueImageDecode( // Queue the decode in the image controller, but switch out the callback for // our own. auto image_bounds = SkIRect::MakeWH(image.width(), image.height()); - DrawImage draw_image(image, false, image_bounds, kNone_SkFilterQuality, - SkM44(), frame_index, target_color_space); + DrawImage draw_image(image, false, image_bounds, + PaintFlags::FilterQuality::kNone, SkM44(), frame_index, + target_color_space); image_controller_->QueueImageDecode( draw_image, base::BindOnce(&DecodedImageTracker::ImageDecodeFinished, base::Unretained(this), std::move(callback), diff --git a/chromium/cc/tiles/decoded_image_tracker_unittest.cc b/chromium/cc/tiles/decoded_image_tracker_unittest.cc index 09f8c58b206..991394ca3b1 100644 --- a/chromium/cc/tiles/decoded_image_tracker_unittest.cc +++ b/chromium/cc/tiles/decoded_image_tracker_unittest.cc @@ -109,12 +109,13 @@ TEST_F(DecodedImageTrackerTest, Colorspace) { // space differs then that image is not locked. Note that we use the high // filter quality here, since it shouldn't matter and the checks should // succeed anyway. - DrawImage locked_draw_image( - paint_image, false, SkIRect::MakeWH(1, 1), kHigh_SkFilterQuality, SkM44(), - PaintImage::kDefaultFrameIndex, decoded_color_space); + DrawImage locked_draw_image(paint_image, false, SkIRect::MakeWH(1, 1), + PaintFlags::FilterQuality::kHigh, SkM44(), + PaintImage::kDefaultFrameIndex, + decoded_color_space); EXPECT_TRUE(image_controller()->IsDrawImageLocked(locked_draw_image)); DrawImage srgb_draw_image(paint_image, false, SkIRect::MakeWH(1, 1), - kHigh_SkFilterQuality, SkM44(), + PaintFlags::FilterQuality::kHigh, SkM44(), PaintImage::kDefaultFrameIndex, srgb_color_space); EXPECT_FALSE(image_controller()->IsDrawImageLocked(srgb_draw_image)); } @@ -172,9 +173,11 @@ TEST_F(DecodedImageTrackerTest, ImageUsedInDraw) { // Create dummy draw images for each: DrawImage draw_image_1(paint_image_1, false, SkIRect::MakeWH(1, 1), - kHigh_SkFilterQuality, SkM44(), 0, gfx::ColorSpace()); + PaintFlags::FilterQuality::kHigh, SkM44(), 0, + gfx::ColorSpace()); DrawImage draw_image_2(paint_image_2, false, SkIRect::MakeWH(1, 1), - kHigh_SkFilterQuality, SkM44(), 0, gfx::ColorSpace()); + PaintFlags::FilterQuality::kHigh, SkM44(), 0, + gfx::ColorSpace()); // Both should be in the cache: EXPECT_TRUE(image_controller()->IsDrawImageLocked(draw_image_1)); diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc index e61eefa6f15..08259760367 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache.cc @@ -102,8 +102,10 @@ bool SkipImage(const DrawImage& draw_image) { // Returns the filter quality to use for scaling the image to upload scale as // well as for using when passing the decoded image to skia. Due to parity with // SW and power impliciation, limit the filter quality to medium. -SkFilterQuality CalculateDesiredFilterQuality(const DrawImage& draw_image) { - return std::min(kMedium_SkFilterQuality, draw_image.filter_quality()); +PaintFlags::FilterQuality CalculateDesiredFilterQuality( + const DrawImage& draw_image) { + return std::min(PaintFlags::FilterQuality::kMedium, + draw_image.filter_quality()); } // Calculates the scale factor which can be used to scale an image to a given @@ -128,7 +130,7 @@ gfx::Size CalculateSizeForMipLevel(const DrawImage& draw_image, bool ShouldGenerateMips(const DrawImage& draw_image, int upload_scale_mip_level) { // If filter quality is less than medium, don't generate mips. - if (draw_image.filter_quality() < kMedium_SkFilterQuality) + if (draw_image.filter_quality() < PaintFlags::FilterQuality::kMedium) return false; gfx::Size base_size(draw_image.paint_image().width(), @@ -235,7 +237,7 @@ bool DrawAndScaleImage( SkISize::Make(paint_image.width(), paint_image.height()) == pixmap.bounds().size(); const bool is_nearest_neighbor = - draw_image.filter_quality() == kNone_SkFilterQuality; + draw_image.filter_quality() == PaintFlags::FilterQuality::kNone; SkImageInfo info = pixmap.info(); SkYUVAPixmapInfo yuva_pixmap_info; if (do_yuv_decode) { @@ -303,7 +305,7 @@ bool DrawAndScaleImage( decode_info = info.makeWH(decode_size.width(), decode_size.height()); } - const SkFilterQuality filter_quality = + const PaintFlags::FilterQuality filter_quality = CalculateDesiredFilterQuality(draw_image); const SkSamplingOptions sampling( PaintFlags::FilterQualityToSkSamplingOptions(filter_quality)); @@ -477,9 +479,10 @@ size_t GpuImageDecodeCache::InUseCacheKeyHash::operator()( const InUseCacheKey& cache_key) const { return base::HashInts( cache_key.target_color_space.GetHash(), - base::HashInts(cache_key.frame_key.hash(), - base::HashInts(cache_key.upload_scale_mip_level, - cache_key.filter_quality))); + base::HashInts( + cache_key.frame_key.hash(), + base::HashInts(cache_key.upload_scale_mip_level, + static_cast<int>(cache_key.filter_quality)))); } GpuImageDecodeCache::InUseCacheEntry::InUseCacheEntry( @@ -838,7 +841,7 @@ GpuImageDecodeCache::ImageData::ImageData( DecodedDataMode mode, size_t size, const gfx::ColorSpace& target_color_space, - SkFilterQuality quality, + PaintFlags::FilterQuality quality, int upload_scale_mip_level, bool needs_mips, bool is_bitmap_backed, @@ -2236,8 +2239,8 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, return; } - size_t image_width = uploaded_y_image->width(); - size_t image_height = uploaded_y_image->height(); + int image_width = uploaded_y_image->width(); + int image_height = uploaded_y_image->height(); uploaded_image = CreateImageFromYUVATexturesInternal( uploaded_y_image.get(), uploaded_u_image.get(), uploaded_v_image.get(), image_width, image_height, @@ -2950,8 +2953,8 @@ sk_sp<SkImage> GpuImageDecodeCache::CreateImageFromYUVATexturesInternal( const SkImage* uploaded_y_image, const SkImage* uploaded_u_image, const SkImage* uploaded_v_image, - const size_t image_width, - const size_t image_height, + const int image_width, + const int image_height, const SkYUVAInfo::PlaneConfig yuva_plane_config, const SkYUVAInfo::Subsampling yuva_subsampling, const SkYUVColorSpace yuv_color_space, @@ -3053,8 +3056,8 @@ void GpuImageDecodeCache::UpdateMipsIfNeeded(const DrawImage& draw_image, return; } - size_t width = image_y_with_mips_owned->width(); - size_t height = image_y_with_mips_owned->height(); + int width = image_y_with_mips_owned->width(); + int height = image_y_with_mips_owned->height(); sk_sp<SkColorSpace> color_space = SupportsColorSpaceConversion() && draw_image.target_color_space().IsValid() diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h index 5c48f6f0047..edea739d938 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.h +++ b/chromium/cc/tiles/gpu_image_decode_cache.h @@ -514,7 +514,7 @@ class CC_EXPORT GpuImageDecodeCache DecodedDataMode mode, size_t size, const gfx::ColorSpace& target_color_space, - SkFilterQuality quality, + PaintFlags::FilterQuality quality, int upload_scale_mip_level, bool needs_mips, bool is_bitmap_backed, @@ -530,7 +530,7 @@ class CC_EXPORT GpuImageDecodeCache const DecodedDataMode mode; const size_t size; gfx::ColorSpace target_color_space; - SkFilterQuality quality; + PaintFlags::FilterQuality quality; int upload_scale_mip_level; bool needs_mips = false; bool is_bitmap_backed; @@ -574,7 +574,7 @@ class CC_EXPORT GpuImageDecodeCache PaintImage::FrameKey frame_key; int upload_scale_mip_level; - SkFilterQuality filter_quality; + PaintFlags::FilterQuality filter_quality; gfx::ColorSpace target_color_space; }; struct InUseCacheKeyHash { @@ -641,8 +641,8 @@ class CC_EXPORT GpuImageDecodeCache const SkImage* uploaded_y_image, const SkImage* uploaded_u_image, const SkImage* uploaded_v_image, - const size_t image_width, - const size_t image_height, + const int image_width, + const int image_height, const SkYUVAInfo::PlaneConfig yuva_plane_config, const SkYUVAInfo::Subsampling yuva_subsampling, const SkYUVColorSpace yuva_color_space, diff --git a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc index 37d76df12b9..6b43048a119 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc @@ -113,7 +113,7 @@ TEST_P(GpuImageDecodeCachePerfTest, DecodeWithColorConversion) { .set_id(PaintImage::GetNextId()) .set_image(CreateImage(1024, 2048), PaintImage::GetNextContentId()) .TakePaintImage(), - false, SkIRect::MakeWH(1024, 2048), kMedium_SkFilterQuality, + false, SkIRect::MakeWH(1024, 2048), PaintFlags::FilterQuality::kMedium, CreateMatrix(SkSize::Make(1.0f, 1.0f)), 0u, gfx::ColorSpace::CreateXYZD50()); @@ -146,7 +146,7 @@ TEST_P(GpuImageDecodeCachePerfTestNoSw, DecodeWithMips) { .set_id(PaintImage::GetNextId()) .set_image(CreateImage(1024, 2048), PaintImage::GetNextContentId()) .TakePaintImage(), - false, SkIRect::MakeWH(1024, 2048), kMedium_SkFilterQuality, + false, SkIRect::MakeWH(1024, 2048), PaintFlags::FilterQuality::kMedium, CreateMatrix(SkSize::Make(0.6f, 0.6f)), 0u, gfx::ColorSpace()); DecodedDrawImage decoded_image = cache_->GetDecodedImageForDraw(image); @@ -175,7 +175,7 @@ TEST_P(GpuImageDecodeCachePerfTest, AcquireExistingImages) { .set_id(PaintImage::GetNextId()) .set_image(CreateImage(1024, 2048), PaintImage::GetNextContentId()) .TakePaintImage(), - false, SkIRect::MakeWH(1024, 2048), kMedium_SkFilterQuality, + false, SkIRect::MakeWH(1024, 2048), PaintFlags::FilterQuality::kMedium, CreateMatrix(SkSize::Make(1.0f, 1.0f)), 0u, gfx::ColorSpace::CreateXYZD50()); diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc index 076be3aafa6..6c585a065a5 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc @@ -493,7 +493,8 @@ class GpuImageDecodeCacheTest const PaintImage& paint_image, const SkM44& matrix = SkM44(), gfx::ColorSpace* color_space = nullptr, - SkFilterQuality filter_quality = kMedium_SkFilterQuality, + PaintFlags::FilterQuality filter_quality = + PaintFlags::FilterQuality::kMedium, SkIRect* src_rect = nullptr, size_t frame_index = PaintImage::kDefaultFrameIndex, float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel, @@ -517,7 +518,8 @@ class GpuImageDecodeCacheTest const PaintImage& paint_image, const SkM44& matrix = SkM44(), gfx::ColorSpace* color_space = nullptr, - SkFilterQuality filter_quality = kMedium_SkFilterQuality, + PaintFlags::FilterQuality filter_quality = + PaintFlags::FilterQuality::kMedium, SkIRect* src_rect = nullptr, size_t frame_index = PaintImage::kDefaultFrameIndex, float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel) { @@ -753,8 +755,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLowerQuality) { EXPECT_TRUE(result.need_unref); EXPECT_TRUE(result.task); - DrawImage another_draw_image = CreateDrawImageInternal( - image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); + DrawImage another_draw_image = + CreateDrawImageInternal(image, matrix, nullptr /* color_space */, + PaintFlags::FilterQuality::kLow); ImageDecodeCache::TaskResult another_result = cache->GetTaskForImageAndRef( another_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(another_result.need_unref); @@ -871,8 +874,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageHigherQuality) { auto cache = CreateCache(); SkM44 matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f)); PaintImage first_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage first_draw_image = CreateDrawImageInternal( - first_image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); + DrawImage first_draw_image = + CreateDrawImageInternal(first_image, matrix, nullptr /* color_space */, + PaintFlags::FilterQuality::kLow); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); @@ -883,8 +887,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageHigherQuality) { cache->UnrefImage(first_draw_image); - DrawImage second_draw_image = CreateDrawImageInternal( - first_image, matrix, nullptr /* color_space */, kMedium_SkFilterQuality); + DrawImage second_draw_image = + CreateDrawImageInternal(first_image, matrix, nullptr /* color_space */, + PaintFlags::FilterQuality::kMedium); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); @@ -1173,8 +1178,8 @@ TEST_P(GpuImageDecodeCacheTest, GetHdrDecodedImageForDrawToHdr) { constexpr float kCustomWhiteLevel = 200.f; DrawImage draw_image = CreateDrawImageInternal( image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), &color_space, - kMedium_SkFilterQuality, nullptr, PaintImage::kDefaultFrameIndex, - kCustomWhiteLevel); + PaintFlags::FilterQuality::kMedium, nullptr, + PaintImage::kDefaultFrameIndex, kCustomWhiteLevel); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_EQ(draw_image.target_color_space(), color_space); @@ -1316,9 +1321,9 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawLargerScale) { auto cache = CreateCache(); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image = - CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), - nullptr /* color_space */, kLow_SkFilterQuality); + DrawImage draw_image = CreateDrawImageInternal( + image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), nullptr /* color_space */, + PaintFlags::FilterQuality::kLow); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1366,8 +1371,9 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawHigherQuality) { auto cache = CreateCache(); SkM44 matrix = CreateMatrix(SkSize::Make(0.5f, 0.5f)); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image = CreateDrawImageInternal( - image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); + DrawImage draw_image = + CreateDrawImageInternal(image, matrix, nullptr /* color_space */, + PaintFlags::FilterQuality::kLow); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1446,9 +1452,9 @@ TEST_P(GpuImageDecodeCacheTest, GetLargeScaledDecodedImageForDraw) { auto cache = CreateCache(); PaintImage image = CreatePaintImageForFallbackToRGB( gfx::Size(GetLargeImageSize().width(), GetLargeImageSize().height() * 2)); - DrawImage draw_image = - CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), - nullptr /* color_space */, kHigh_SkFilterQuality); + DrawImage draw_image = CreateDrawImageInternal( + image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), nullptr /* color_space */, + PaintFlags::FilterQuality::kHigh); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1468,7 +1474,8 @@ TEST_P(GpuImageDecodeCacheTest, GetLargeScaledDecodedImageForDraw) { EXPECT_EQ(GetLargeImageSize().width(), decoded_draw_image.image()->width()); EXPECT_EQ(GetLargeImageSize().height(), decoded_draw_image.image()->height()); - EXPECT_EQ(decoded_draw_image.filter_quality(), kMedium_SkFilterQuality); + EXPECT_EQ(decoded_draw_image.filter_quality(), + PaintFlags::FilterQuality::kMedium); EXPECT_FALSE(decoded_draw_image.image()->isTextureBacked()); EXPECT_TRUE_IF_NOT_USING_TRANSFER_CACHE( @@ -1610,7 +1617,7 @@ TEST_P(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { DrawImage draw_image(image, false, SkIRect::MakeXYWH(image.width() + 1, image.height() + 1, image.width(), image.height()), - kMedium_SkFilterQuality, + PaintFlags::FilterQuality::kMedium, CreateMatrix(SkSize::Make(1.f, 1.f)), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); @@ -1635,7 +1642,7 @@ TEST_P(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { SkIRect src_rect = SkIRect::MakeXYWH(0, 0, image.width(), image.height()); DrawImage draw_image = CreateDrawImageInternal( image, CreateMatrix(SkSize::Make(1.f, 1.f)), nullptr /* color_space */, - kMedium_SkFilterQuality, &src_rect); + PaintFlags::FilterQuality::kMedium, &src_rect); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); @@ -1793,15 +1800,16 @@ TEST_P(GpuImageDecodeCacheTest, QualityCappedAtMedium) { SkM44 matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f)); // Create an image with kLow_FilterQuality. - DrawImage low_draw_image = CreateDrawImageInternal( - image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); + DrawImage low_draw_image = + CreateDrawImageInternal(image, matrix, nullptr /* color_space */, + PaintFlags::FilterQuality::kLow); ImageDecodeCache::TaskResult low_result = cache->GetTaskForImageAndRef( low_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(low_result.need_unref); EXPECT_TRUE(low_result.task); - // Get the same image at kMedium_SkFilterQuality. We can't re-use low, so we - // should get a new task/ref. + // Get the same image at PaintFlags::FilterQuality::kMedium. We can't re-use + // low, so we should get a new task/ref. DrawImage medium_draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult medium_result = cache->GetTaskForImageAndRef( medium_draw_image, ImageDecodeCache::TracingInfo()); @@ -1809,9 +1817,11 @@ TEST_P(GpuImageDecodeCacheTest, QualityCappedAtMedium) { EXPECT_TRUE(medium_result.task.get()); EXPECT_FALSE(low_result.task.get() == medium_result.task.get()); - // Get the same image at kHigh_SkFilterQuality. We should re-use medium. - DrawImage high_quality_draw_image = CreateDrawImageInternal( - image, matrix, nullptr /* color_space */, kHigh_SkFilterQuality); + // Get the same image at PaintFlags::FilterQuality::kHigh. We should re-use + // medium. + DrawImage high_quality_draw_image = + CreateDrawImageInternal(image, matrix, nullptr /* color_space */, + PaintFlags::FilterQuality::kHigh); ImageDecodeCache::TaskResult high_quality_result = cache->GetTaskForImageAndRef(high_quality_draw_image, ImageDecodeCache::TracingInfo()); @@ -1864,8 +1874,9 @@ TEST_P(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { auto cache = CreateCache(); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); SkM44 matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f)); - DrawImage draw_image = CreateDrawImageInternal( - image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); + DrawImage draw_image = + CreateDrawImageInternal(image, matrix, nullptr /* color_space */, + PaintFlags::FilterQuality::kLow); ImageDecodeCache::TaskResult result = cache->GetOutOfRasterDecodeTaskForImageAndRef(draw_image); @@ -1935,10 +1946,11 @@ TEST_P(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { DrawImage draw_image = CreateDrawImageInternal(image); PaintImage image2 = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image2( - image2, false, SkIRect::MakeWH(image2.width(), image2.height()), - kMedium_SkFilterQuality, CreateMatrix(SkSize::Make(1.0f, 1.0f)), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image2(image2, false, + SkIRect::MakeWH(image2.width(), image2.height()), + PaintFlags::FilterQuality::kMedium, + CreateMatrix(SkSize::Make(1.0f, 1.0f)), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); // Add an image to the cache and un-ref it. { @@ -2141,7 +2153,7 @@ TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) { viz::ContextProvider::ScopedContextLock context_lock(context_provider()); - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(1.0f, 1.0f)), 1u, DefaultColorSpace()); @@ -2657,9 +2669,9 @@ TEST_P(GpuImageDecodeCacheTest, DecodeToScaleNoneQuality) { .set_paint_image_generator(generator) .TakePaintImage(); - DrawImage draw_image = - CreateDrawImageInternal(paint_image, CreateMatrix(SkSize::Make(0.5, 0.5)), - nullptr /* color_space */, kNone_SkFilterQuality); + DrawImage draw_image = CreateDrawImageInternal( + paint_image, CreateMatrix(SkSize::Make(0.5, 0.5)), + nullptr /* color_space */, PaintFlags::FilterQuality::kNone); DecodedDrawImage decoded_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); ASSERT_TRUE(decoded_image.image()); @@ -2678,7 +2690,7 @@ TEST_P(GpuImageDecodeCacheTest, DecodeToScaleNoneQuality) { } TEST_P(GpuImageDecodeCacheTest, BasicMips) { - auto decode_and_check_mips = [this](SkFilterQuality filter_quality, + auto decode_and_check_mips = [this](PaintFlags::FilterQuality filter_quality, SkSize scale, gfx::ColorSpace color_space, bool should_have_mips) { auto cache = CreateCache(); @@ -2726,22 +2738,23 @@ TEST_P(GpuImageDecodeCacheTest, BasicMips) { }; // No scale == no mips. - decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(1.0f, 1.0f), - DefaultColorSpace(), false); + decode_and_check_mips(PaintFlags::FilterQuality::kMedium, + SkSize::Make(1.0f, 1.0f), DefaultColorSpace(), false); // Full mip level scale == no mips - decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(0.5f, 0.5f), - DefaultColorSpace(), false); + decode_and_check_mips(PaintFlags::FilterQuality::kMedium, + SkSize::Make(0.5f, 0.5f), DefaultColorSpace(), false); // Low filter quality == no mips - decode_and_check_mips(kLow_SkFilterQuality, SkSize::Make(0.6f, 0.6f), - DefaultColorSpace(), false); + decode_and_check_mips(PaintFlags::FilterQuality::kLow, + SkSize::Make(0.6f, 0.6f), DefaultColorSpace(), false); // None filter quality == no mips - decode_and_check_mips(kNone_SkFilterQuality, SkSize::Make(0.6f, 0.6f), - DefaultColorSpace(), false); + decode_and_check_mips(PaintFlags::FilterQuality::kNone, + SkSize::Make(0.6f, 0.6f), DefaultColorSpace(), false); // Medium filter quality == mips - decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(0.6f, 0.6f), - DefaultColorSpace(), true); + decode_and_check_mips(PaintFlags::FilterQuality::kMedium, + SkSize::Make(0.6f, 0.6f), DefaultColorSpace(), true); // Color conversion preserves mips - decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(0.6f, 0.6f), + decode_and_check_mips(PaintFlags::FilterQuality::kMedium, + SkSize::Make(0.6f, 0.6f), gfx::ColorSpace::CreateXYZD50(), true); } @@ -2972,8 +2985,8 @@ TEST_P(GpuImageDecodeCacheTest, // This test creates an image that will be YUV decoded and drawn at 80% scale. // Because the final size is between mip levels, we expect the image to be // decoded and uploaded at original size (mip level 0 for all planes) but to - // have mips attached since kMedium_SkFilterQuality uses bilinear filtering - // between mip levels. + // have mips attached since PaintFlags::FilterQuality::kMedium uses bilinear + // filtering between mip levels. if (!do_yuv_decode_) { // The YUV case may choose different mip levels between chroma and luma // planes. @@ -2981,7 +2994,8 @@ TEST_P(GpuImageDecodeCacheTest, } auto owned_cache = CreateCache(); auto decode_and_check_plane_sizes = [this, cache = owned_cache.get()]() { - SkFilterQuality filter_quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality filter_quality = + PaintFlags::FilterQuality::kMedium; SkSize requires_decode_at_original_scale = SkSize::Make(0.8f, 0.8f); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); @@ -3041,8 +3055,8 @@ TEST_P(GpuImageDecodeCacheTest, HighBitDepthYUVDecoding) { // This test creates a high bit depth image that will be YUV decoded and drawn // at 80% scale. Because the final size is between mip levels, we expect the // image to be decoded and uploaded at original size (mip level 0 for all - // planes) but to have mips attached since kMedium_SkFilterQuality uses - // bilinear filtering between mip levels. + // planes) but to have mips attached since PaintFlags::FilterQuality::kMedium + // uses bilinear filtering between mip levels. if (!do_yuv_decode_) { // The YUV case may choose different mip levels between chroma and luma // planes. @@ -3057,7 +3071,8 @@ TEST_P(GpuImageDecodeCacheTest, HighBitDepthYUVDecoding) { DataType::kUnorm8, gfx::ColorSpace target_cs = gfx::ColorSpace::CreateSRGB()) { - SkFilterQuality filter_quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality filter_quality = + PaintFlags::FilterQuality::kMedium; SkSize requires_decode_at_original_scale = SkSize::Make(0.8f, 0.8f); // When we're targeting HDR output, select a reasonable HDR color space for @@ -3271,7 +3286,8 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) { // Because the final size is between mip levels, we expect the image to be // decoded and uploaded at half its original size (mip level 1 for Y plane but // level 0 for chroma planes) and to have mips attached since - // kMedium_SkFilterQuality uses bilinear filtering between mip levels. + // PaintFlags::FilterQuality::kMedium uses bilinear filtering between mip + // levels. if (!do_yuv_decode_) { // The YUV case may choose different mip levels between chroma and luma // planes. @@ -3282,7 +3298,8 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) { [this, cache = owned_cache.get()]( SkSize scaled_size, const SkISize mipped_plane_sizes[SkYUVAInfo::kMaxPlanes]) { - SkFilterQuality filter_quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality filter_quality = + PaintFlags::FilterQuality::kMedium; gfx::Size image_size = GetNormalImageSize(); PaintImage image = CreatePaintImageInternal(image_size); @@ -3478,7 +3495,7 @@ TEST_P(GpuImageDecodeCacheTest, DarkModeImageCacheSize) { // Another draw image with smaller src rect for image1. SkIRect src = SkIRect::MakeWH(10, 10); DrawImage draw_image12 = CreateDrawImageWithDarkModeInternal( - image1, SkM44(), nullptr, kMedium_SkFilterQuality, &src); + image1, SkM44(), nullptr, PaintFlags::FilterQuality::kMedium, &src); ImageDecodeCache::TaskResult result12 = cache->GetTaskForImageAndRef( draw_image12, ImageDecodeCache::TracingInfo()); GetImageAndDrawFinishedForDarkMode(cache.get(), draw_image12, @@ -3573,9 +3590,9 @@ TEST_P(GpuImageDecodeCacheTest, ClippedAndScaledDrawImageRemovesCacheEntry) { // Get task for clipped and scaled image. auto clipped_rect = SkIRect::MakeWH(image.width() * 0.9f, image.height()); - DrawImage clipped_draw_image = - CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), - nullptr, kMedium_SkFilterQuality, &clipped_rect); + DrawImage clipped_draw_image = CreateDrawImageInternal( + image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), nullptr, + PaintFlags::FilterQuality::kMedium, &clipped_rect); ImageDecodeCache::TaskResult clipped_result = cache->GetTaskForImageAndRef( clipped_draw_image, ImageDecodeCache::TracingInfo()); @@ -3668,7 +3685,7 @@ TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, ASSERT_TRUE(target_color_space.IsValid()); const PaintImage image = CreatePaintImageForDecodeAcceleration( ImageType::kJPEG, subsampling_and_expected_data_size.first); - const SkFilterQuality quality = kHigh_SkFilterQuality; + const PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image(image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), @@ -3708,7 +3725,7 @@ TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateXYZD50(); ASSERT_TRUE(target_color_space.IsValid()); const PaintImage image = CreatePaintImageForDecodeAcceleration(); - const SkFilterQuality quality = kHigh_SkFilterQuality; + const PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image(image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), @@ -3747,7 +3764,7 @@ TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateXYZD50(); ASSERT_TRUE(target_color_space.IsValid()); const PaintImage image = CreatePaintImageForDecodeAcceleration(); - const SkFilterQuality quality = kHigh_SkFilterQuality; + const PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image(image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), @@ -3795,7 +3812,7 @@ TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); ASSERT_TRUE(target_color_space.IsValid()); const PaintImage image = CreatePaintImageForDecodeAcceleration(); - const SkFilterQuality quality = kHigh_SkFilterQuality; + const PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image(image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(1.0f, 1.0f)), @@ -3818,7 +3835,7 @@ TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); ASSERT_TRUE(target_color_space.IsValid()); const PaintImage image = CreatePaintImageForDecodeAcceleration(); - const SkFilterQuality quality = kHigh_SkFilterQuality; + const PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image(image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(0.5f, 0.5f)), @@ -3843,7 +3860,7 @@ TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); ASSERT_TRUE(target_color_space.IsValid()); const PaintImage image = CreatePaintImageForDecodeAcceleration(); - const SkFilterQuality quality = kHigh_SkFilterQuality; + const PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image(image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), @@ -3893,7 +3910,7 @@ TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); ASSERT_TRUE(target_color_space.IsValid()); const PaintImage image = CreatePaintImageForDecodeAcceleration(); - const SkFilterQuality quality = kHigh_SkFilterQuality; + const PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image(image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), @@ -3936,7 +3953,7 @@ class GpuImageDecodeCacheWithAcceleratedDecodesFlagsTest TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesFlagsTest, RequestAcceleratedDecodeSuccessfully) { auto cache = CreateCache(); - const SkFilterQuality quality = kHigh_SkFilterQuality; + const PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); ASSERT_TRUE(target_color_space.IsValid()); diff --git a/chromium/cc/tiles/image_controller_unittest.cc b/chromium/cc/tiles/image_controller_unittest.cc index 6fe0adb5ad6..07c0aa90923 100644 --- a/chromium/cc/tiles/image_controller_unittest.cc +++ b/chromium/cc/tiles/image_controller_unittest.cc @@ -242,14 +242,14 @@ int kDefaultTimeoutSeconds = 10; DrawImage CreateDiscardableDrawImage(gfx::Size size) { return DrawImage(CreateDiscardablePaintImage(size), false, SkIRect::MakeWH(size.width(), size.height()), - kNone_SkFilterQuality, SkM44(), + PaintFlags::FilterQuality::kNone, SkM44(), PaintImage::kDefaultFrameIndex, gfx::ColorSpace()); } DrawImage CreateBitmapDrawImage(gfx::Size size) { return DrawImage(CreateBitmapImage(size), false, SkIRect::MakeWH(size.width(), size.height()), - kNone_SkFilterQuality, SkM44(), + PaintFlags::FilterQuality::kNone, SkM44(), PaintImage::kDefaultFrameIndex); } diff --git a/chromium/cc/tiles/image_decode_cache_utils.cc b/chromium/cc/tiles/image_decode_cache_utils.cc index 4e16d1d60bb..b7a3b635eb4 100644 --- a/chromium/cc/tiles/image_decode_cache_utils.cc +++ b/chromium/cc/tiles/image_decode_cache_utils.cc @@ -17,13 +17,13 @@ namespace cc { bool ImageDecodeCacheUtils::ScaleToHalfFloatPixmapUsingN32Intermediate( const SkPixmap& source_pixmap, SkPixmap* scaled_pixmap, - SkFilterQuality filter_quality) { + PaintFlags::FilterQuality filter_quality) { // Target pixmap should be half float backed. DCHECK(scaled_pixmap->colorType() == kRGBA_F16_SkColorType); // Filter quality should be medium or high. This is needed if the device // (Android KitKat and lower) does not support mipmaps properly. Mipmaps are // used only for medium and high filter qualities. - DCHECK(filter_quality >= kMedium_SkFilterQuality); + DCHECK(filter_quality >= PaintFlags::FilterQuality::kMedium); // Convert to kN32 color type if necessary SkPixmap n32_pixmap = source_pixmap; diff --git a/chromium/cc/tiles/image_decode_cache_utils.h b/chromium/cc/tiles/image_decode_cache_utils.h index 12b21de039e..c8adfd1e92d 100644 --- a/chromium/cc/tiles/image_decode_cache_utils.h +++ b/chromium/cc/tiles/image_decode_cache_utils.h @@ -6,7 +6,7 @@ #define CC_TILES_IMAGE_DECODE_CACHE_UTILS_H_ #include "build/build_config.h" -#include "third_party/skia/include/core/SkFilterQuality.h" +#include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkPixmap.h" #if defined(OS_ANDROID) @@ -17,7 +17,7 @@ namespace cc { class ImageDecodeCacheUtils { public: - static bool CanResizeF16Image(SkFilterQuality filter_quality) { + static bool CanResizeF16Image(PaintFlags::FilterQuality filter_quality) { #if defined(OS_ANDROID) // Return false on Android KitKat or lower if filter quality is medium or // high (hence, mipmaps are used), return true otherwise. This is because @@ -25,7 +25,7 @@ class ImageDecodeCacheUtils { // these configs. crbug.com/876349 return (base::android::BuildInfo::GetInstance()->sdk_int() >= base::android::SDK_VERSION_LOLLIPOP) || - (filter_quality < kMedium_SkFilterQuality); + (filter_quality < PaintFlags::FilterQuality::kMedium); #else return true; #endif @@ -34,7 +34,7 @@ class ImageDecodeCacheUtils { static bool ScaleToHalfFloatPixmapUsingN32Intermediate( const SkPixmap& source_pixmap, SkPixmap* scaled_pixmap, - SkFilterQuality filter_quality); + PaintFlags::FilterQuality filter_quality); }; } // namespace cc diff --git a/chromium/cc/tiles/picture_layer_tiling.cc b/chromium/cc/tiles/picture_layer_tiling.cc index 11fdab5657c..2c36d79b565 100644 --- a/chromium/cc/tiles/picture_layer_tiling.cc +++ b/chromium/cc/tiles/picture_layer_tiling.cc @@ -13,11 +13,12 @@ #include "base/check_op.h" #include "base/containers/flat_map.h" +#include "base/cxx17_backports.h" #include "base/numerics/safe_conversions.h" -#include "base/stl_util.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" #include "cc/base/math_util.h" +#include "cc/layers/picture_layer_impl.h" #include "cc/raster/raster_source.h" #include "cc/tiles/prioritized_tile.h" #include "cc/tiles/tile.h" @@ -905,7 +906,8 @@ PrioritizedTile PictureLayerTiling::MakePrioritizedTile( // continue to rasterize the tile right now rather than for images only. if (tile_priority.distance_to_visible < max_skewport_extent_in_screen_space_ && - client_->ScrollInteractionInProgress() && client_->DidCheckerboardQuad()) + client_->ScrollInteractionInProgress() && + client_->CurrentScrollDidCheckerboardLargeArea()) process_for_images_only = false; return PrioritizedTile(tile, this, tile_priority, IsTileOccluded(tile), process_for_images_only, @@ -931,9 +933,16 @@ TilePriority PictureLayerTiling::ComputePriorityForTile( DCHECK_EQ(ComputePriorityRectTypeForTile(tile), priority_rect_type); DCHECK_EQ(TileAt(tile->tiling_i_index(), tile->tiling_j_index()), tile); - TilePriority::PriorityBin priority_bin = client_->HasValidTilePriorities() - ? TilePriority::NOW - : TilePriority::EVENTUALLY; + TilePriority::PriorityBin priority_bin; + if (client_->HasValidTilePriorities()) { + // Occluded tiles are given a lower PriorityBin to ensure they are evicted + // before non-occluded tiles. + priority_bin = + IsTileOccluded(tile) ? TilePriority::SOON : TilePriority::NOW; + } else { + priority_bin = TilePriority::EVENTUALLY; + } + switch (priority_rect_type) { case VISIBLE_RECT: case PENDING_VISIBLE_RECT: diff --git a/chromium/cc/tiles/picture_layer_tiling.h b/chromium/cc/tiles/picture_layer_tiling.h index e651805b98a..18f1aeaf796 100644 --- a/chromium/cc/tiles/picture_layer_tiling.h +++ b/chromium/cc/tiles/picture_layer_tiling.h @@ -53,7 +53,7 @@ class CC_EXPORT PictureLayerTilingClient { virtual const PaintWorkletRecordMap& GetPaintWorkletRecords() const = 0; virtual bool IsDirectlyCompositedImage() const = 0; virtual bool ScrollInteractionInProgress() const = 0; - virtual bool DidCheckerboardQuad() const = 0; + virtual bool CurrentScrollDidCheckerboardLargeArea() const = 0; protected: virtual ~PictureLayerTilingClient() {} diff --git a/chromium/cc/tiles/picture_layer_tiling_set.cc b/chromium/cc/tiles/picture_layer_tiling_set.cc index 70a35e1a5bb..ce4b8dd914c 100644 --- a/chromium/cc/tiles/picture_layer_tiling_set.cc +++ b/chromium/cc/tiles/picture_layer_tiling_set.cc @@ -14,8 +14,8 @@ #include <vector> #include "base/containers/contains.h" +#include "base/containers/cxx20_erase.h" #include "base/memory/ptr_util.h" -#include "base/stl_util.h" #include "base/trace_event/trace_event.h" #include "cc/raster/raster_source.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -714,8 +714,6 @@ PictureLayerTilingSet::CoverageIterator::operator++() { tiling_iter_ = PictureLayerTiling::CoverageIterator( set_->tilings_[current_tiling_].get(), coverage_scale_, last_rect); } - - return *this; } PictureLayerTilingSet::CoverageIterator::operator bool() const { diff --git a/chromium/cc/tiles/prioritized_tile.h b/chromium/cc/tiles/prioritized_tile.h index 54538898271..38ca41dc30e 100644 --- a/chromium/cc/tiles/prioritized_tile.h +++ b/chromium/cc/tiles/prioritized_tile.h @@ -47,6 +47,8 @@ class CC_EXPORT PrioritizedTile { void AsValueInto(base::trace_event::TracedValue* value) const; + const PictureLayerTiling* source_tiling() const { return source_tiling_; } + private: Tile* tile_ = nullptr; const PictureLayerTiling* source_tiling_ = nullptr; diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc index 5df593a29cf..45d3346d81e 100644 --- a/chromium/cc/tiles/software_image_decode_cache.cc +++ b/chromium/cc/tiles/software_image_decode_cache.cc @@ -129,10 +129,10 @@ SkSize GetScaleAdjustment(const SoftwareImageDecodeCache::CacheKey& key) { // to do a bilinear interpolation. The exception to this is if the developer // specified a pixelated effect, which results in a None filter quality (nearest // neighbor). -SkFilterQuality GetDecodedFilterQuality( +PaintFlags::FilterQuality GetDecodedFilterQuality( const SoftwareImageDecodeCache::CacheKey& key) { - return key.is_nearest_neighbor() ? kNone_SkFilterQuality - : kLow_SkFilterQuality; + return key.is_nearest_neighbor() ? PaintFlags::FilterQuality::kNone + : PaintFlags::FilterQuality::kLow; } } // namespace @@ -415,8 +415,8 @@ SoftwareImageDecodeCache::DecodeImageIfNecessary(const CacheKey& key, ? SkIRect::MakeWH(paint_image.width(), paint_image.height()) : gfx::RectToSkIRect(key.src_rect()); DrawImage candidate_draw_image( - paint_image, false, src_rect, kNone_SkFilterQuality, SkM44(), - key.frame_key().frame_index(), key.target_color_space()); + paint_image, false, src_rect, PaintFlags::FilterQuality::kNone, + SkM44(), key.frame_key().frame_index(), key.target_color_space()); candidate_key.emplace(CacheKey::FromDrawImage( candidate_draw_image, GetColorTypeForPaintImage(key.target_color_space(), paint_image))); diff --git a/chromium/cc/tiles/software_image_decode_cache_perftest.cc b/chromium/cc/tiles/software_image_decode_cache_perftest.cc index e51ed57f6e7..ce71e96fd5f 100644 --- a/chromium/cc/tiles/software_image_decode_cache_perftest.cc +++ b/chromium/cc/tiles/software_image_decode_cache_perftest.cc @@ -37,9 +37,9 @@ class SoftwareImageDecodeCachePerfTest : public testing::Test { kTimeCheckInterval) {} void RunFromImage() { - SkFilterQuality qualities[] = {kNone_SkFilterQuality, kLow_SkFilterQuality, - kMedium_SkFilterQuality, - kHigh_SkFilterQuality}; + PaintFlags::FilterQuality qualities[] = { + PaintFlags::FilterQuality::kNone, PaintFlags::FilterQuality::kLow, + PaintFlags::FilterQuality::kMedium, PaintFlags::FilterQuality::kHigh}; std::pair<SkIRect, SkIRect> image_rect_subrect[] = { std::make_pair(SkIRect::MakeWH(100, 100), SkIRect::MakeWH(100, 100)), std::make_pair(SkIRect::MakeWH(100, 100), SkIRect::MakeWH(50, 50)), diff --git a/chromium/cc/tiles/software_image_decode_cache_unittest.cc b/chromium/cc/tiles/software_image_decode_cache_unittest.cc index 8815ece97a5..72043edc560 100644 --- a/chromium/cc/tiles/software_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/software_image_decode_cache_unittest.cc @@ -52,7 +52,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyNoneQuality) { DrawImage draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kNone_SkFilterQuality, + PaintFlags::FilterQuality::kNone, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); @@ -75,7 +75,7 @@ TEST(SoftwareImageDecodeCacheTest, DrawImage draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kLow_SkFilterQuality, + PaintFlags::FilterQuality::kLow, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); @@ -94,7 +94,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropsToLowIfMipLevel0) { DrawImage draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kMedium_SkFilterQuality, + PaintFlags::FilterQuality::kMedium, CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); @@ -114,7 +114,7 @@ TEST(SoftwareImageDecodeCacheTest, LowUnscalableFormatStaysLow) { DrawImage draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kLow_SkFilterQuality, + PaintFlags::FilterQuality::kLow, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); @@ -134,7 +134,7 @@ TEST(SoftwareImageDecodeCacheTest, HighUnscalableFormatBecomesLow) { DrawImage draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kHigh_SkFilterQuality, + PaintFlags::FilterQuality::kHigh, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); @@ -154,7 +154,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyLowQualityKeptLowIfUpscale) { DrawImage draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kLow_SkFilterQuality, + PaintFlags::FilterQuality::kLow, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); @@ -171,7 +171,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyLowQualityKeptLowIfUpscale) { TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQuality) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -191,7 +191,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQuality) { TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfEnlarging) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -212,7 +212,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfEnlarging) { TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfIdentity) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -234,7 +234,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfNearlyIdentity) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -256,7 +256,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfNearlyIdentity2) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -278,7 +278,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfNotDecomposable) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = false; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -300,7 +300,7 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt1_5Scale) { PaintImage paint_image = CreatePaintImage(500, 200); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -321,7 +321,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt1_5Scale) { TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt1_0cale) { PaintImage paint_image = CreatePaintImage(500, 200); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -342,7 +342,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt1_0cale) { TEST(SoftwareImageDecodeCacheTest, ImageKeyLowQualityAt0_75Scale) { PaintImage paint_image = CreatePaintImage(500, 200); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -363,7 +363,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyLowQualityAt0_75Scale) { TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_5Scale) { PaintImage paint_image = CreatePaintImage(500, 200); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -383,7 +383,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_5Scale) { TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_49Scale) { PaintImage paint_image = CreatePaintImage(500, 200); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -403,7 +403,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_49Scale) { TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_1Scale) { PaintImage paint_image = CreatePaintImage(500, 200); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -423,7 +423,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_1Scale) { TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_01Scale) { PaintImage paint_image = CreatePaintImage(500, 200); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; DrawImage draw_image( paint_image, false, @@ -444,7 +444,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyFullDowscalesDropsHighQualityToMedium) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -464,7 +464,7 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, ImageKeyUpscaleIsLowQuality) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -486,7 +486,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToMediumIfTooLarge) { // Just over 64MB when scaled. PaintImage paint_image = CreatePaintImage(4555, 2048); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; // At least one dimension should scale down, so that medium quality doesn't // become low. @@ -509,7 +509,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToLowIfNotDecomposable) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = false; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -530,7 +530,7 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToLowIfIdentity) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -552,7 +552,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToLowIfNearlyIdentity) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -574,7 +574,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToLowIfNearlyIdentity2) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -614,7 +614,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyDownscaleMipLevelWithRounding) { DrawImage draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kMedium_SkFilterQuality, + PaintFlags::FilterQuality::kMedium, CreateMatrix(SkSize::Make(0.2f, 0.2f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); @@ -631,7 +631,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyDownscaleMipLevelWithRounding) { TEST(SoftwareImageDecodeCacheTest, OriginalDecodesAreEqual) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kNone_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kNone; DrawImage draw_image( paint_image, false, @@ -669,7 +669,7 @@ TEST(SoftwareImageDecodeCacheTest, OriginalDecodesAreEqual) { TEST(SoftwareImageDecodeCacheTest, ImageRectDoesNotContainSrcRect) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -690,7 +690,7 @@ TEST(SoftwareImageDecodeCacheTest, ImageRectDoesNotContainSrcRect) { TEST(SoftwareImageDecodeCacheTest, ImageRectDoesNotContainSrcRectWithScale) { PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -712,7 +712,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImage) { TestSoftwareImageDecodeCache cache; PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -744,7 +744,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageProcessUnrefCancel) { TestSoftwareImageDecodeCache cache; PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image( paint_image, false, @@ -779,7 +779,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentQuality) { DrawImage high_quality_draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kHigh_SkFilterQuality, + PaintFlags::FilterQuality::kHigh, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); ImageDecodeCache::TaskResult high_quality_result = @@ -791,7 +791,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentQuality) { DrawImage none_quality_draw_image( paint_image, false, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kNone_SkFilterQuality, + PaintFlags::FilterQuality::kNone, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); ImageDecodeCache::TaskResult none_quality_result = @@ -812,7 +812,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentSize) { TestSoftwareImageDecodeCache cache; PaintImage paint_image = CreatePaintImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage half_size_draw_image( paint_image, false, @@ -846,7 +846,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentSize) { TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentImage) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage first_paint_image = CreatePaintImage(100, 100); DrawImage first_draw_image( @@ -889,7 +889,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentImage) { TEST(SoftwareImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentColorSpace) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; gfx::ColorSpace color_space_a(gfx::ColorSpace::PrimaryID::XYZ_D50, gfx::ColorSpace::TransferID::IEC61966_2_1); @@ -941,7 +941,7 @@ TEST(SoftwareImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentColorSpace) { TEST(SoftwareImageDecodeCacheTest, GetTaskForImageAlreadyDecoded) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -971,7 +971,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageAlreadyDecoded) { TEST(SoftwareImageDecodeCacheTest, GetTaskForImageAlreadyPrerolled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kLow_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kLow; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1007,7 +1007,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageAlreadyPrerolled) { TEST(SoftwareImageDecodeCacheTest, GetTaskForImageCanceledGetsNewTask) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1049,7 +1049,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageCanceledWhileReffedGetsNewTask) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1090,7 +1090,7 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDraw) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1112,7 +1112,8 @@ TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDraw) { EXPECT_EQ(50, decoded_draw_image.image()->height()); EXPECT_FLOAT_EQ(0.5f, decoded_draw_image.scale_adjustment().width()); EXPECT_FLOAT_EQ(0.5f, decoded_draw_image.scale_adjustment().height()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_FALSE(decoded_draw_image.is_scale_adjustment_identity()); cache.DrawWithImageFinished(draw_image, decoded_draw_image); @@ -1123,7 +1124,7 @@ TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDrawWithNonContainedSrcRect) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1145,7 +1146,8 @@ TEST(SoftwareImageDecodeCacheTest, EXPECT_EQ(35, decoded_draw_image.image()->height()); EXPECT_FLOAT_EQ(0.5f, decoded_draw_image.scale_adjustment().width()); EXPECT_FLOAT_EQ(0.5f, decoded_draw_image.scale_adjustment().height()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_FALSE(decoded_draw_image.is_scale_adjustment_identity()); cache.DrawWithImageFinished(draw_image, decoded_draw_image); @@ -1155,7 +1157,7 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1171,7 +1173,8 @@ TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { EXPECT_EQ(50, decoded_draw_image.image()->height()); EXPECT_FLOAT_EQ(0.5f, decoded_draw_image.scale_adjustment().width()); EXPECT_FLOAT_EQ(0.5f, decoded_draw_image.scale_adjustment().height()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_FALSE(decoded_draw_image.is_scale_adjustment_identity()); cache.DrawWithImageFinished(draw_image, decoded_draw_image); @@ -1181,7 +1184,7 @@ TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecodeMultipleTimes) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1197,7 +1200,8 @@ TEST(SoftwareImageDecodeCacheTest, EXPECT_EQ(50, decoded_draw_image.image()->height()); EXPECT_FLOAT_EQ(0.5f, decoded_draw_image.scale_adjustment().width()); EXPECT_FLOAT_EQ(0.5f, decoded_draw_image.scale_adjustment().height()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_FALSE(decoded_draw_image.is_scale_adjustment_identity()); DecodedDrawImage another_decoded_draw_image = @@ -1212,7 +1216,7 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1236,7 +1240,7 @@ TEST(SoftwareImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { TEST(SoftwareImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1260,7 +1264,7 @@ TEST(SoftwareImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { TEST(SoftwareImageDecodeCacheTest, LowQualityFilterIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kLow_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kLow; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image( @@ -1290,7 +1294,7 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityFilterIsHandled) { TEST(SoftwareImageDecodeCacheTest, LowQualityScaledSubrectIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kLow_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kLow; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image(paint_image, false, SkIRect::MakeXYWH(10, 10, 80, 80), @@ -1311,7 +1315,8 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityScaledSubrectIsHandled) { // If we decoded the image and cached it, it would be stored in a different // SkImage object. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); // Low quality will be upgraded to medium and mip-mapped. EXPECT_FALSE(decoded_draw_image.is_scale_adjustment_identity()); EXPECT_EQ(0.5f, decoded_draw_image.scale_adjustment().width()); @@ -1324,7 +1329,7 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityScaledSubrectIsHandled) { TEST(SoftwareImageDecodeCacheTest, NoneQualityScaledSubrectIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kNone_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kNone; PaintImage paint_image = CreatePaintImage(100, 100); DrawImage draw_image(paint_image, false, SkIRect::MakeXYWH(10, 10, 80, 80), @@ -1345,7 +1350,8 @@ TEST(SoftwareImageDecodeCacheTest, NoneQualityScaledSubrectIsHandled) { // If we decoded the image and cached it, it would be stored in a different // SkImage object. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kNone_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kNone, + decoded_draw_image.filter_quality()); EXPECT_TRUE(decoded_draw_image.is_scale_adjustment_identity()); cache.DrawWithImageFinished(draw_image, decoded_draw_image); @@ -1355,7 +1361,7 @@ TEST(SoftwareImageDecodeCacheTest, NoneQualityScaledSubrectIsHandled) { TEST(SoftwareImageDecodeCacheTest, MediumQualityAt01_5ScaleIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image( @@ -1376,7 +1382,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt01_5ScaleIsHandled) { EXPECT_TRUE(decoded_draw_image.image()); // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_EQ(500, decoded_draw_image.image()->width()); EXPECT_EQ(200, decoded_draw_image.image()->height()); @@ -1387,7 +1394,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt01_5ScaleIsHandled) { TEST(SoftwareImageDecodeCacheTest, MediumQualityAt1_0ScaleIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image( @@ -1408,7 +1415,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt1_0ScaleIsHandled) { EXPECT_TRUE(decoded_draw_image.image()); // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_EQ(500, decoded_draw_image.image()->width()); EXPECT_EQ(200, decoded_draw_image.image()->height()); @@ -1419,7 +1427,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt1_0ScaleIsHandled) { TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_75ScaleIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image( @@ -1440,7 +1448,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_75ScaleIsHandled) { EXPECT_TRUE(decoded_draw_image.image()); // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_EQ(500, decoded_draw_image.image()->width()); EXPECT_EQ(200, decoded_draw_image.image()->height()); @@ -1451,7 +1460,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_75ScaleIsHandled) { TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_5ScaleIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image( @@ -1472,7 +1481,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_5ScaleIsHandled) { EXPECT_TRUE(decoded_draw_image.image()); // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_EQ(250, decoded_draw_image.image()->width()); EXPECT_EQ(100, decoded_draw_image.image()->height()); @@ -1483,7 +1493,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_5ScaleIsHandled) { TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_49ScaleIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image( @@ -1504,7 +1514,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_49ScaleIsHandled) { EXPECT_TRUE(decoded_draw_image.image()); // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_EQ(250, decoded_draw_image.image()->width()); EXPECT_EQ(100, decoded_draw_image.image()->height()); @@ -1515,7 +1526,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_49ScaleIsHandled) { TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_1ScaleIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image( @@ -1536,7 +1547,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_1ScaleIsHandled) { EXPECT_TRUE(decoded_draw_image.image()); // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_EQ(63, decoded_draw_image.image()->width()); EXPECT_EQ(25, decoded_draw_image.image()->height()); @@ -1547,7 +1559,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_1ScaleIsHandled) { TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_01ScaleIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image( @@ -1568,7 +1580,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_01ScaleIsHandled) { EXPECT_TRUE(decoded_draw_image.image()); // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image.filter_quality()); EXPECT_EQ(8, decoded_draw_image.image()->width()); EXPECT_EQ(4, decoded_draw_image.image()->height()); @@ -1579,7 +1592,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_01ScaleIsHandled) { TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_001ScaleIsHandled) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image( @@ -1604,7 +1617,7 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityImagesAreTheSameAt0_5And0_49Scale) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; PaintImage paint_image = CreatePaintImage(500, 200); DrawImage draw_image_50( @@ -1638,8 +1651,10 @@ TEST(SoftwareImageDecodeCacheTest, // Decoded image should not be lazy generated. EXPECT_FALSE(decoded_draw_image_50.image()->isLazyGenerated()); EXPECT_FALSE(decoded_draw_image_49.image()->isLazyGenerated()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image_50.filter_quality()); - EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image_49.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image_50.filter_quality()); + EXPECT_EQ(PaintFlags::FilterQuality::kLow, + decoded_draw_image_49.filter_quality()); EXPECT_EQ(250, decoded_draw_image_50.image()->width()); EXPECT_EQ(250, decoded_draw_image_49.image()->width()); EXPECT_EQ(100, decoded_draw_image_50.image()->height()); @@ -1656,7 +1671,7 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, ClearCache) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; for (int i = 0; i < 10; ++i) { PaintImage paint_image = CreatePaintImage(100, 100); @@ -1698,7 +1713,7 @@ TEST(SoftwareImageDecodeCacheTest, CacheDecodesExpectedFrames) { .TakePaintImage(); bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; DrawImage draw_image(image, false, SkIRect::MakeWH(image.width(), image.height()), quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), @@ -1737,7 +1752,7 @@ TEST(SoftwareImageDecodeCacheTest, SizeSubrectingIsHandled) { const int min_dimension = 4 * 1024 + 2; TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kLow_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kLow; auto paint_image = CreateDiscardablePaintImage(gfx::Size(min_dimension, min_dimension), @@ -1771,7 +1786,7 @@ TEST(SoftwareImageDecodeCacheTest, EmptyTargetSizeDecode) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kLow_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kLow; // Populate the cache with an original sized decode. auto paint_image = CreateDiscardablePaintImage( @@ -1798,7 +1813,7 @@ TEST(SoftwareImageDecodeCacheTest, EmptyTargetSizeDecode) { TEST(SoftwareImageDecodeCacheTest, BitmapImageColorConverted) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateDisplayP3D65(); PaintImage paint_image = CreateBitmapImage(gfx::Size(100, 100)); @@ -1824,7 +1839,7 @@ TEST(SoftwareImageDecodeCacheTest, BitmapImageColorConverted) { TEST(SoftwareImageDecodeCacheTest, BitmapImageNotColorConverted) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage paint_image = CreateBitmapImage(gfx::Size(100, 100)); DrawImage draw_image( @@ -1842,7 +1857,7 @@ TEST(SoftwareImageDecodeCacheTest, BitmapImageNotColorConverted) { TEST(SoftwareImageDecodeCacheTest, DISABLED_ContentIdCaching) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kHigh; PaintImage::Id stable_id = 1001; for (int i = 0; i < 10; ++i) { @@ -1875,7 +1890,7 @@ TEST(SoftwareImageDecodeCacheTest, DISABLED_ContentIdCaching) { TEST(SoftwareImageDecodeCacheTest, DecodeToScale) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; SkISize full_size = SkISize::Make(100, 100); std::vector<SkISize> supported_sizes = {SkISize::Make(25, 25), @@ -1935,7 +1950,7 @@ TEST(SoftwareImageDecodeCacheTest, DecodeToScale) { TEST(SoftwareImageDecodeCacheTest, DecodeToScaleSubrect) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kMedium; SkISize full_size = SkISize::Make(100, 100); std::vector<SkISize> supported_sizes = {SkISize::Make(25, 25), @@ -1972,7 +1987,7 @@ TEST(SoftwareImageDecodeCacheTest, DecodeToScaleSubrect) { TEST(SoftwareImageDecodeCacheTest, DecodeToScaleNoneQuality) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; - SkFilterQuality quality = kNone_SkFilterQuality; + PaintFlags::FilterQuality quality = PaintFlags::FilterQuality::kNone; SkISize full_size = SkISize::Make(100, 100); std::vector<SkISize> supported_sizes = {SkISize::Make(25, 25), @@ -2022,10 +2037,11 @@ TEST(SoftwareImageDecodeCacheTest, HdrDecodeToHdr) { PaintImage::GetNextContentId()) .TakePaintImage(); - DrawImage draw_image( - image, false, SkIRect::MakeWH(image.width(), image.height()), - kMedium_SkFilterQuality, CreateMatrix(SkSize::Make(0.5, 0.5), true), - PaintImage::kDefaultFrameIndex, color_space); + DrawImage draw_image(image, false, + SkIRect::MakeWH(image.width(), image.height()), + PaintFlags::FilterQuality::kMedium, + CreateMatrix(SkSize::Make(0.5, 0.5), true), + PaintImage::kDefaultFrameIndex, color_space); DecodedDrawImage decoded_image = cache.GetDecodedImageForDraw(draw_image); EXPECT_EQ(decoded_image.image()->colorType(), kRGBA_F16_SkColorType); @@ -2052,10 +2068,11 @@ TEST(SoftwareImageDecodeCacheTest, HdrDecodeToSdr) { // Note: We use P3 here since software cache shouldn't be used when conversion // to SRGB is needed. auto raster_color_space = gfx::ColorSpace::CreateDisplayP3D65(); - DrawImage draw_image( - image, false, SkIRect::MakeWH(image.width(), image.height()), - kMedium_SkFilterQuality, CreateMatrix(SkSize::Make(0.5, 0.5), true), - PaintImage::kDefaultFrameIndex, raster_color_space); + DrawImage draw_image(image, false, + SkIRect::MakeWH(image.width(), image.height()), + PaintFlags::FilterQuality::kMedium, + CreateMatrix(SkSize::Make(0.5, 0.5), true), + PaintImage::kDefaultFrameIndex, raster_color_space); DecodedDrawImage decoded_image = cache.GetDecodedImageForDraw(draw_image); EXPECT_NE(decoded_image.image()->colorType(), kRGBA_F16_SkColorType); diff --git a/chromium/cc/tiles/software_image_decode_cache_unittest_combinations.cc b/chromium/cc/tiles/software_image_decode_cache_unittest_combinations.cc index 0e34fa5741c..0b1a639d72c 100644 --- a/chromium/cc/tiles/software_image_decode_cache_unittest_combinations.cc +++ b/chromium/cc/tiles/software_image_decode_cache_unittest_combinations.cc @@ -48,7 +48,8 @@ class BaseTest : public testing::Test { src_rect.isEmpty() ? SkIRect::MakeWH(paint_image().width(), paint_image().height()) : src_rect, - kMedium_SkFilterQuality, CreateMatrix(SkSize::Make(scale, scale), true), + PaintFlags::FilterQuality::kMedium, + CreateMatrix(SkSize::Make(scale, scale), true), PaintImage::kDefaultFrameIndex, GetColorSpace()); } diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.cc b/chromium/cc/tiles/software_image_decode_cache_utils.cc index e3198f2fd54..9b7bd63e741 100644 --- a/chromium/cc/tiles/software_image_decode_cache_utils.cc +++ b/chromium/cc/tiles/software_image_decode_cache_utils.cc @@ -149,7 +149,7 @@ SoftwareImageDecodeCacheUtils::GenerateCacheEntryFromCandidate( DCHECK(!key.is_nearest_neighbor()); SkPixmap target_pixmap(target_info, target_pixels->data(), target_info.minRowBytes()); - SkFilterQuality filter_quality = kMedium_SkFilterQuality; + PaintFlags::FilterQuality filter_quality = PaintFlags::FilterQuality::kMedium; if (decoded_pixmap.colorType() == kRGBA_F16_SkColorType && !ImageDecodeCacheUtils::CanResizeF16Image(filter_quality)) { result = ImageDecodeCacheUtils::ScaleToHalfFloatPixmapUsingN32Intermediate( @@ -199,7 +199,8 @@ SoftwareImageDecodeCacheUtils::CacheKey::FromDrawImage(const DrawImage& image, } ProcessingType type = kOriginal; - bool is_nearest_neighbor = image.filter_quality() == kNone_SkFilterQuality; + bool is_nearest_neighbor = + image.filter_quality() == PaintFlags::FilterQuality::kNone; int mip_level = MipMapUtil::GetLevelForSize(src_rect.size(), target_size); // If any of the following conditions hold, then use at most low filter // quality and adjust the target size to match the original image: @@ -289,6 +290,10 @@ SoftwareImageDecodeCacheUtils::CacheKey::CacheKey( SoftwareImageDecodeCacheUtils::CacheKey::CacheKey(const CacheKey& other) = default; +SoftwareImageDecodeCacheUtils::CacheKey& +SoftwareImageDecodeCacheUtils::CacheKey::operator=(const CacheKey& other) = + default; + std::string SoftwareImageDecodeCacheUtils::CacheKey::ToString() const { std::ostringstream str; str << "frame_key[" << frame_key_.ToString() << "]\ntype["; diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.h b/chromium/cc/tiles/software_image_decode_cache_utils.h index 071f7d216a7..964aa832bb6 100644 --- a/chromium/cc/tiles/software_image_decode_cache_utils.h +++ b/chromium/cc/tiles/software_image_decode_cache_utils.h @@ -51,6 +51,7 @@ class SoftwareImageDecodeCacheUtils { SkColorType color_type); CacheKey(const CacheKey& other); + CacheKey& operator=(const CacheKey& other); bool operator==(const CacheKey& other) const { // The frame_key always has to be the same. However, after that all diff --git a/chromium/cc/tiles/tile_manager.cc b/chromium/cc/tiles/tile_manager.cc index e409dd8851c..6f227fe9b7d 100644 --- a/chromium/cc/tiles/tile_manager.cc +++ b/chromium/cc/tiles/tile_manager.cc @@ -1207,9 +1207,11 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask( uint64_t resource_content_id = 0; gfx::Rect invalidated_rect = tile->invalidated_content_rect(); if (UsePartialRaster(msaa_sample_count) && tile->invalidated_id()) { + const std::string& debug_name = + prioritized_tile.source_tiling()->raster_source()->debug_name(); resource = resource_pool_->TryAcquireResourceForPartialRaster( tile->id(), tile->invalidated_content_rect(), tile->invalidated_id(), - &invalidated_rect, raster_color_space); + &invalidated_rect, raster_color_space, debug_name); } bool partial_tile_decode = false; @@ -1218,8 +1220,10 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask( DCHECK_EQ(format, resource.format()); partial_tile_decode = true; } else { - resource = resource_pool_->AcquireResource(tile->desired_texture_size(), - format, raster_color_space); + const std::string& debug_name = + prioritized_tile.source_tiling()->raster_source()->debug_name(); + resource = resource_pool_->AcquireResource( + tile->desired_texture_size(), format, raster_color_space, debug_name); DCHECK(resource); } diff --git a/chromium/cc/tiles/tile_manager_perftest.cc b/chromium/cc/tiles/tile_manager_perftest.cc index 07cd2aa57f2..86b7c6510e2 100644 --- a/chromium/cc/tiles/tile_manager_perftest.cc +++ b/chromium/cc/tiles/tile_manager_perftest.cc @@ -5,9 +5,9 @@ #include <stddef.h> #include <stdint.h> +#include "base/cxx17_backports.h" #include "base/lazy_instance.h" #include "base/location.h" -#include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/timer/lap_timer.h" diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc index b263d53826d..08f465750ba 100644 --- a/chromium/cc/tiles/tile_manager_unittest.cc +++ b/chromium/cc/tiles/tile_manager_unittest.cc @@ -14,6 +14,7 @@ #include "base/run_loop.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/trace_event/process_memory_dump.h" #include "cc/layers/recording_source.h" #include "cc/raster/raster_buffer.h" #include "cc/raster/raster_source.h" @@ -835,6 +836,43 @@ TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueue) { EXPECT_EQ(all_tiles, new_content_tiles); } +// Verifies LayerDebugInfo::name ends up memory dumps. +TEST_F(TileManagerTilePriorityQueueTest, DebugNameAppearsInMemoryDump) { + host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1)); + + gfx::Size layer_bounds(1000, 1000); + + host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds)); + + scoped_refptr<FakeRasterSource> pending_raster_source = + FakeRasterSource::CreateFilledWithText(layer_bounds); + SetupPendingTree(pending_raster_source); + + auto* pending_child_layer = AddLayer<FakePictureLayerImpl>( + host_impl()->pending_tree(), pending_raster_source); + LayerDebugInfo debug_info; + debug_info.name = "debug-name"; + pending_child_layer->UpdateDebugInfo(&debug_info); + pending_child_layer->SetDrawsContent(true); + CopyProperties(pending_layer(), pending_child_layer); + + ActivateTree(); + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + + base::trace_event::MemoryDumpArgs dump_args = { + base::trace_event::MemoryDumpLevelOfDetail::DETAILED}; + base::trace_event::ProcessMemoryDump memory_dump(dump_args); + host_impl()->resource_pool()->OnMemoryDump(dump_args, &memory_dump); + bool found_debug_name = false; + for (const auto& allocator_map_pair : memory_dump.allocator_dumps()) { + if (allocator_map_pair.first.find("debug-name") != std::string::npos) { + found_debug_name = true; + break; + } + } + EXPECT_TRUE(found_debug_name); +} + TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueueWithOcclusion) { host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1)); @@ -1732,6 +1770,106 @@ TEST_F(TileManagerTest, ActivateAndDrawWhenOOM) { } } +class TileManagerOcclusionTest : public TileManagerTest { + public: + LayerTreeSettings CreateSettings() override { + auto settings = TestLayerTreeHostBase::CreateSettings(); + settings.create_low_res_tiling = true; + settings.use_occlusion_for_tile_prioritization = true; + return settings; + } + + void PrepareTilesAndWaitUntilDone( + const GlobalStateThatImpactsTilePriority& state) { + base::RunLoop run_loop; + EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted()) + .WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); })); + tile_manager()->PrepareTiles(state); + run_loop.Run(); + tile_manager()->CheckForCompletedTasks(); + } + + TileManager* tile_manager() { return host_impl()->tile_manager(); } +}; + +TEST_F(TileManagerOcclusionTest, OccludedTileEvictedForVisibleTile) { + gfx::Size layer_bounds(256, 256); + host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds)); + + SetupPendingTree(FakeRasterSource::CreateFilledWithText(layer_bounds)); + const int initial_picture_id = pending_layer()->id(); + ActivateTree(); + + GlobalStateThatImpactsTilePriority global_state = + host_impl()->global_tile_state(); + const LayerTreeSettings settings = CreateSettings(); + global_state.hard_memory_limit_in_bytes = + global_state.soft_memory_limit_in_bytes = + settings.default_tile_size.GetArea() * 4 + 1; + + // Call PrepareTiles and wait for it to complete. It's necessary to wait for + // completion so that the resource is pushed to the tile, which is used + // in calculating eviction. + PrepareTilesAndWaitUntilDone(global_state); + + // At this point the initial PictureLayerImpl should have a Tile with a + // resource. + { + PictureLayerImpl* initial_layer = static_cast<PictureLayerImpl*>( + host_impl()->active_tree()->LayerById(initial_picture_id)); + ASSERT_NE(initial_layer, nullptr); + ASSERT_EQ(1u, initial_layer->picture_layer_tiling_set()->num_tilings()); + std::vector<Tile*> initial_layer_tiles = + initial_layer->picture_layer_tiling_set() + ->tiling_at(0) + ->AllTilesForTesting(); + ASSERT_EQ(1u, initial_layer_tiles.size()); + EXPECT_TRUE(initial_layer_tiles[0]->draw_info().has_resource()); + } + + // Add another layer on top. As there is only enough memory for one tile, + // the top most tile should get a raster task and the bottom layer should + // not. + SetupPendingTree(FakeRasterSource::CreateFilledWithText(layer_bounds)); + + // Advance the frame to ensure PictureLayerTilingSet applies the occlusion to + // the PictureLayerTiling. The amount of time advanced doesn't matter. + host_impl()->AdvanceToNextFrame(base::TimeDelta::FromSeconds(2)); + + auto* picture_layer = AddLayer<FakePictureLayerImpl>( + host_impl()->pending_tree(), + FakeRasterSource::CreateFilledWithText(layer_bounds)); + const int top_most_layer_id = picture_layer->id(); + picture_layer->SetDrawsContent(true); + picture_layer->SetContentsOpaque(true); + CopyProperties(pending_layer(), picture_layer); + ActivateTree(); + PrepareTilesAndWaitUntilDone(global_state); + { + PictureLayerImpl* initial_layer = static_cast<PictureLayerImpl*>( + host_impl()->active_tree()->LayerById(initial_picture_id)); + ASSERT_NE(initial_layer, nullptr); + ASSERT_EQ(1u, initial_layer->picture_layer_tiling_set()->num_tilings()); + std::vector<Tile*> initial_layer_tiles = + initial_layer->picture_layer_tiling_set() + ->tiling_at(0) + ->AllTilesForTesting(); + ASSERT_EQ(1u, initial_layer_tiles.size()); + EXPECT_FALSE(initial_layer_tiles[0]->draw_info().has_resource()); + + PictureLayerImpl* top_most_layer = static_cast<PictureLayerImpl*>( + host_impl()->active_tree()->LayerById(top_most_layer_id)); + ASSERT_NE(top_most_layer, nullptr); + ASSERT_EQ(1u, top_most_layer->picture_layer_tiling_set()->num_tilings()); + std::vector<Tile*> top_most_layer_tiles = + top_most_layer->picture_layer_tiling_set() + ->tiling_at(0) + ->AllTilesForTesting(); + ASSERT_EQ(1u, top_most_layer_tiles.size()); + EXPECT_TRUE(top_most_layer_tiles[0]->draw_info().has_resource()); + } +} + class PixelInspectTileManagerTest : public TileManagerTest { public: ~PixelInspectTileManagerTest() override { diff --git a/chromium/cc/trees/animated_paint_worklet_tracker.cc b/chromium/cc/trees/animated_paint_worklet_tracker.cc index b312c24180d..58d51b838fa 100644 --- a/chromium/cc/trees/animated_paint_worklet_tracker.cc +++ b/chromium/cc/trees/animated_paint_worklet_tracker.cc @@ -8,6 +8,7 @@ #include <utility> #include <vector> +#include "base/containers/cxx20_erase.h" #include "cc/layers/picture_layer_impl.h" namespace cc { diff --git a/chromium/cc/trees/clip_expander.cc b/chromium/cc/trees/clip_expander.cc index 02df3797a41..b4fbe772b47 100644 --- a/chromium/cc/trees/clip_expander.cc +++ b/chromium/cc/trees/clip_expander.cc @@ -14,6 +14,8 @@ ClipExpander::ClipExpander(int filter_effect_id) ClipExpander::ClipExpander(const ClipExpander& other) = default; +ClipExpander& ClipExpander::operator=(const ClipExpander& other) = default; + bool ClipExpander::operator==(const ClipExpander& other) const { return target_effect_id_ == other.target_effect_id_; } diff --git a/chromium/cc/trees/clip_expander.h b/chromium/cc/trees/clip_expander.h index 209477a2553..fef2ad5ecda 100644 --- a/chromium/cc/trees/clip_expander.h +++ b/chromium/cc/trees/clip_expander.h @@ -16,6 +16,7 @@ class CC_EXPORT ClipExpander { public: explicit ClipExpander(int filter_effect_id); ClipExpander(const ClipExpander& other); + ClipExpander& operator=(const ClipExpander& other); bool operator==(const ClipExpander& other) const; diff --git a/chromium/cc/trees/compositor_commit_data.h b/chromium/cc/trees/compositor_commit_data.h index 35a7d8e313a..4fb1482bb96 100644 --- a/chromium/cc/trees/compositor_commit_data.h +++ b/chromium/cc/trees/compositor_commit_data.h @@ -13,6 +13,7 @@ #include "cc/input/scroll_snap_data.h" #include "cc/paint/element_id.h" #include "cc/trees/layer_tree_host_client.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/transform.h" diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc index a97f44689a3..48332d88b20 100644 --- a/chromium/cc/trees/damage_tracker_unittest.cc +++ b/chromium/cc/trees/damage_tracker_unittest.cc @@ -1801,13 +1801,13 @@ TEST_F(DamageTrackerTest, DamageRectTooBig) { SetCopyRequest(root); // Really far left. - child1->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 0)); + child1->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::min() + 100), 0)); child1->SetBounds(gfx::Size(1, 1)); // Really far right. - child2->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::max() - 100, 0)); + child2->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::max() - 100), 0)); child2->SetBounds(gfx::Size(1, 1)); EmulateDrawingOneFrame(root, 1.f); @@ -1838,13 +1838,13 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) { SetBackdropFilter(root, filters); // Really far left. - child1->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 0)); + child1->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::min() + 100), 0)); child1->SetBounds(gfx::Size(1, 1)); // Really far right. - child2->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::max() - 100, 0)); + child2->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::max() - 100), 0)); child2->SetBounds(gfx::Size(1, 1)); float device_scale_factor = 1.f; @@ -1869,14 +1869,14 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) { SetCopyRequest(root); // Really far left. - grand_child1_->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + 500, 0)); + grand_child1_->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::min() + 500), 0)); grand_child1_->SetBounds(gfx::Size(1, 1)); grand_child1_->SetDrawsContent(true); // Really far right. - grand_child2_->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::max() - 500, 0)); + grand_child2_->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::max() - 500), 0)); grand_child2_->SetBounds(gfx::Size(1, 1)); grand_child2_->SetDrawsContent(true); @@ -1959,14 +1959,14 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) { SetBackdropFilter(child1_, filters); // Really far left. - grand_child1_->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + 500, 0)); + grand_child1_->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::min() + 500), 0)); grand_child1_->SetBounds(gfx::Size(1, 1)); grand_child1_->SetDrawsContent(true); // Really far right. - grand_child2_->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::max() - 500, 0)); + grand_child2_->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::max() - 500), 0)); grand_child2_->SetBounds(gfx::Size(1, 1)); grand_child2_->SetDrawsContent(true); @@ -2217,13 +2217,13 @@ TEST_F(DamageTrackerTest, DamageRectOnlyVisibleContentsMoveToOutside) { origin_damage.Union(child2->visible_drawable_content_rect()); // Really far left. - child1->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 0)); + child1->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::min() + 100), 0)); child1->SetBounds(gfx::Size(1, 1)); // Really far right. - child2->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::max() - 100, 0)); + child2->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::max() - 100), 0)); child2->SetBounds(gfx::Size(1, 1)); EmulateDrawingOneFrame(root, 1.f); @@ -2252,8 +2252,8 @@ TEST_F(DamageTrackerTest, DamageRectOnlyVisibleContentsLargeTwoContents) { expected_damage.set_width(GetRenderSurface(root)->content_rect().width()); // Really far left. - child1->SetOffsetToTransformParent( - gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 100)); + child1->SetOffsetToTransformParent(gfx::Vector2dF( + static_cast<float>(std::numeric_limits<int>::min() + 100), 100)); child1->SetBounds( gfx::Size(std::numeric_limits<int>::max(), child1->bounds().height())); @@ -2263,7 +2263,7 @@ TEST_F(DamageTrackerTest, DamageRectOnlyVisibleContentsLargeTwoContents) { gfx::Size(std::numeric_limits<int>::max(), child2->bounds().height())); EmulateDrawingOneFrame(root, 1.f); - // Above damages should be excludebe because they're outside of + // Above damages should be excluded because they're outside of // the root surface. gfx::Rect damage_rect; EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid( diff --git a/chromium/cc/trees/de_jelly_state.h b/chromium/cc/trees/de_jelly_state.h index 2f8be8f2a37..b62d1b0c6e2 100644 --- a/chromium/cc/trees/de_jelly_state.h +++ b/chromium/cc/trees/de_jelly_state.h @@ -7,8 +7,6 @@ #include <map> -#include "base/containers/flat_map.h" -#include "base/no_destructor.h" #include "cc/cc_export.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/transform.h" diff --git a/chromium/cc/trees/draw_properties_unittest.cc b/chromium/cc/trees/draw_properties_unittest.cc index 4f1c15cf86c..f982efe46a1 100644 --- a/chromium/cc/trees/draw_properties_unittest.cc +++ b/chromium/cc/trees/draw_properties_unittest.cc @@ -3575,21 +3575,22 @@ TEST_F(DrawPropertiesTest, OpacityAnimatingOnPendingTree) { active_child->effect_tree_index())); } -class TransformInteropTest : public DrawPropertiesTestBase, - public testing::Test { +class BackfaceVisibilityInteropTest : public DrawPropertiesTestBase, + public testing::Test { public: - TransformInteropTest() : DrawPropertiesTestBase(TransformInteropSettings()) {} + BackfaceVisibilityInteropTest() + : DrawPropertiesTestBase(BackfaceVisibilityInteropSettings()) {} protected: - LayerTreeSettings TransformInteropSettings() { + LayerTreeSettings BackfaceVisibilityInteropSettings() { LayerListSettings settings; - settings.enable_transform_interop = true; + settings.enable_backface_visibility_interop = true; return settings; } }; -TEST_F(TransformInteropTest, BackfaceInvisibleTransform) { +TEST_F(BackfaceVisibilityInteropTest, BackfaceInvisibleTransform) { LayerImpl* root = root_layer(); root->SetDrawsContent(true); LayerImpl* back_facing = AddLayer<LayerImpl>(); diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index b6c84a81c1d..5196b0189dd 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -12,11 +12,13 @@ #include "base/containers/adapters.h" #include "base/containers/stack.h" #include "base/logging.h" +#include "build/build_config.h" #include "cc/base/math_util.h" #include "cc/layers/draw_properties.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" #include "cc/layers/picture_layer.h" +#include "cc/paint/filter_operation.h" #include "cc/trees/clip_node.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" @@ -1121,20 +1123,86 @@ void RecordRenderSurfaceReasonsForTracing( void UpdateElasticOverscroll( PropertyTrees* property_trees, TransformNode* overscroll_elasticity_transform_node, - const gfx::Vector2dF& elastic_overscroll) { + ElementId overscroll_elasticity_effect_element_id, + const gfx::Vector2dF& elastic_overscroll, + const ScrollNode* inner_viewport) { +#if defined(OS_ANDROID) + // On android, elastic overscroll is implemented by stretching the content + // from the overscrolled edge. + if (!overscroll_elasticity_effect_element_id && + !overscroll_elasticity_transform_node) { + DCHECK(elastic_overscroll.IsZero()); + return; + } + if (overscroll_elasticity_effect_element_id) { + if (elastic_overscroll.IsZero() || !inner_viewport) { + property_trees->effect_tree.OnFilterAnimated( + overscroll_elasticity_effect_element_id, FilterOperations()); + return; + } + // The inner viewport container size takes into account the size change as a + // result of the top controls, see ScrollTree::container_bounds. + gfx::Size scroller_size = + property_trees->scroll_tree.container_bounds(inner_viewport->id); + + property_trees->effect_tree.OnFilterAnimated( + overscroll_elasticity_effect_element_id, + FilterOperations( + std::vector<FilterOperation>({FilterOperation::CreateStretchFilter( + -elastic_overscroll.x() / scroller_size.width(), + -elastic_overscroll.y() / scroller_size.height())}))); + return; + } + + // If there is no overscroll elasticity effect node, we apply a stretch + // transform. + overscroll_elasticity_transform_node->local.MakeIdentity(); + overscroll_elasticity_transform_node->origin.SetPoint(0.f, 0.f, 0.f); + overscroll_elasticity_transform_node->to_screen_is_potentially_animated = + !elastic_overscroll.IsZero(); + + if (!elastic_overscroll.IsZero() && inner_viewport) { + // The inner viewport container size takes into account the size change as a + // result of the top controls, see ScrollTree::container_bounds. + gfx::Size scroller_size = + property_trees->scroll_tree.container_bounds(inner_viewport->id); + + overscroll_elasticity_transform_node->local.Scale( + 1.f + std::abs(elastic_overscroll.x()) / scroller_size.width(), + 1.f + std::abs(elastic_overscroll.y()) / scroller_size.height()); + + // If overscrolling to the right, stretch from right. + if (elastic_overscroll.x() > 0.f) { + overscroll_elasticity_transform_node->origin.set_x(scroller_size.width()); + } + + // If overscrolling off the bottom, stretch from bottom. + if (elastic_overscroll.y() > 0.f) { + overscroll_elasticity_transform_node->origin.set_y( + scroller_size.height()); + } + } + overscroll_elasticity_transform_node->needs_local_transform_update = true; + property_trees->transform_tree.set_needs_update(true); +#else // defined(OS_ANDROID) if (!overscroll_elasticity_transform_node) { DCHECK(elastic_overscroll.IsZero()); return; } + // On other platforms, we modify the translation offset to match the + // overscroll amount. if (overscroll_elasticity_transform_node->scroll_offset == gfx::ScrollOffset(elastic_overscroll)) return; overscroll_elasticity_transform_node->scroll_offset = gfx::ScrollOffset(elastic_overscroll); + overscroll_elasticity_transform_node->needs_local_transform_update = true; property_trees->transform_tree.set_needs_update(true); + +#endif // defined(OS_ANDROID) } void ComputeDrawPropertiesOfVisibleLayers(const LayerImplList* layer_list, @@ -1215,7 +1283,6 @@ bool NodeMayContainBackdropBlurFilter(const EffectNode& node) { default: return false; } - return false; } #endif @@ -1245,7 +1312,7 @@ bool CC_EXPORT LayerShouldBeSkippedForDrawPropertiesComputation( if (!transform_node->node_and_ancestors_are_animated_or_invertible || !effect_node->is_drawn) return true; - if (layer->layer_tree_impl()->settings().enable_transform_interop) { + if (layer->layer_tree_impl()->settings().enable_backface_visibility_interop) { return layer->should_check_backface_visibility() && IsLayerBackFaceVisible(layer, layer->transform_tree_index(), property_trees); @@ -1257,7 +1324,7 @@ bool CC_EXPORT LayerShouldBeSkippedForDrawPropertiesComputation( bool CC_EXPORT IsLayerBackFaceVisible(LayerImpl* layer, int transform_tree_index, const PropertyTrees* property_trees) { - if (layer->layer_tree_impl()->settings().enable_transform_interop) { + if (layer->layer_tree_impl()->settings().enable_backface_visibility_interop) { return IsTransformToRootOf3DRenderingContextBackFaceVisible( layer, transform_tree_index, property_trees); } else { @@ -1269,7 +1336,9 @@ bool CC_EXPORT IsLayerBackFaceVisible(LayerImpl* layer, bool CC_EXPORT IsLayerBackFaceVisible(Layer* layer, int transform_tree_index, const PropertyTrees* property_trees) { - if (layer->layer_tree_host()->GetSettings().enable_transform_interop) { + if (layer->layer_tree_host() + ->GetSettings() + .enable_backface_visibility_interop) { return IsTransformToRootOf3DRenderingContextBackFaceVisible( layer, transform_tree_index, property_trees); } else { @@ -1446,9 +1515,11 @@ void CalculateDrawProperties( UpdatePageScaleFactor(property_trees, layer_tree_impl->PageScaleTransformNode(), layer_tree_impl->current_page_scale_factor()); - UpdateElasticOverscroll(property_trees, - layer_tree_impl->OverscrollElasticityTransformNode(), - layer_tree_impl->current_elastic_overscroll()); + UpdateElasticOverscroll( + property_trees, layer_tree_impl->OverscrollElasticityTransformNode(), + layer_tree_impl->OverscrollElasticityEffectElementId(), + layer_tree_impl->current_elastic_overscroll(), + layer_tree_impl->InnerViewportScrollNode()); // Similarly, the device viewport and device transform are shared // by both trees. property_trees->clip_tree.SetViewportClip( diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc index bf2aa003a08..704f3ffa0d3 100644 --- a/chromium/cc/trees/effect_node.cc +++ b/chromium/cc/trees/effect_node.cc @@ -56,6 +56,7 @@ bool EffectNode::operator==(const EffectNode& other) const { screen_space_opacity == other.screen_space_opacity && backdrop_filter_quality == other.backdrop_filter_quality && subtree_capture_id == other.subtree_capture_id && + subtree_size == other.subtree_size && cache_render_surface == other.cache_render_surface && has_copy_request == other.has_copy_request && filters == other.filters && @@ -183,14 +184,16 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { } value->SetString("blend_mode", SkBlendMode_Name(blend_mode)); value->SetString("subtree_capture_id", subtree_capture_id.ToString()); + value->SetString("subtree_size", subtree_size.ToString()); value->SetBoolean("cache_render_surface", cache_render_surface); value->SetBoolean("has_copy_request", has_copy_request); - value->SetBoolean("double_sided", double_sided); value->SetBoolean("hidden_by_backface_visibility", hidden_by_backface_visibility); + value->SetBoolean("double_sided", double_sided); value->SetBoolean("trilinear_filtering", trilinear_filtering); value->SetBoolean("is_drawn", is_drawn); value->SetBoolean("only_draws_visible_content", only_draws_visible_content); + value->SetBoolean("subtree_hidden", subtree_hidden); value->SetBoolean("has_potential_filter_animation", has_potential_filter_animation); value->SetBoolean("has_potential_backdrop_filter_animation", @@ -200,6 +203,7 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { value->SetBoolean("has_masking_child", has_masking_child); value->SetBoolean("effect_changed", effect_changed); value->SetBoolean("subtree_has_copy_request", subtree_has_copy_request); + value->SetBoolean("affected_by_backdrop_filter", affected_by_backdrop_filter); value->SetString("render_surface_reason", RenderSurfaceReasonToString(render_surface_reason)); value->SetInteger("transform_id", transform_id); @@ -211,7 +215,6 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { closest_ancestor_with_copy_request_id); value->SetInteger("closest_ancestor_being_captured_id", closest_ancestor_being_captured_id); - value->SetBoolean("affected_by_backdrop_filter", affected_by_backdrop_filter); } } // namespace cc diff --git a/chromium/cc/trees/effect_node.h b/chromium/cc/trees/effect_node.h index 682befae955..36c0cfb9ad7 100644 --- a/chromium/cc/trees/effect_node.h +++ b/chromium/cc/trees/effect_node.h @@ -10,8 +10,10 @@ #include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" #include "components/viz/common/surfaces/subtree_capture_id.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size_f.h" #include "ui/gfx/mask_filter_info.h" #include "ui/gfx/rrect_f.h" @@ -95,6 +97,7 @@ struct CC_EXPORT EffectNode { gfx::Vector2dF surface_contents_scale; viz::SubtreeCaptureId subtree_capture_id; + gfx::Size subtree_size; bool cache_render_surface : 1; bool has_copy_request : 1; diff --git a/chromium/cc/trees/frame_rate_estimator_unittest.cc b/chromium/cc/trees/frame_rate_estimator_unittest.cc index 03df3952a9b..9aa75eb03d1 100644 --- a/chromium/cc/trees/frame_rate_estimator_unittest.cc +++ b/chromium/cc/trees/frame_rate_estimator_unittest.cc @@ -4,7 +4,7 @@ #include "cc/trees/frame_rate_estimator.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/test/test_simple_task_runner.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/cc/trees/image_animation_controller.h b/chromium/cc/trees/image_animation_controller.h index 22776f886f9..361f1fbc512 100644 --- a/chromium/cc/trees/image_animation_controller.h +++ b/chromium/cc/trees/image_animation_controller.h @@ -20,7 +20,6 @@ #include "cc/paint/paint_image_generator.h" #include "cc/tiles/tile_priority.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace cc { class PaintImage; diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index 9a241b6ae4d..cf54a3665fe 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -25,7 +25,6 @@ #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_math.h" #include "base/single_thread_task_runner.h" -#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" @@ -57,7 +56,9 @@ #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" +#include "cc/trees/mobile_optimized_viewport_util.h" #include "cc/trees/mutator_host.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/property_tree_builder.h" #include "cc/trees/proxy_main.h" #include "cc/trees/render_frame_metadata_observer.h" @@ -67,6 +68,7 @@ #include "cc/trees/transform_node.h" #include "cc/trees/tree_synchronizer.h" #include "cc/trees/ukm_manager.h" +#include "components/viz/common/features.h" #include "services/metrics/public/cpp/ukm_recorder.h" #include "services/tracing/public/cpp/perfetto/flow_event_utils.h" #include "services/tracing/public/cpp/perfetto/macros.h" @@ -158,6 +160,35 @@ LayerTreeHost::LayerTreeHost(InitParams params, CompositorMode mode) debug_state_.RecordRenderingStats()); } +bool LayerTreeHost::IsMobileOptimized() const { + gfx::SizeF scrollable_viewport_size; + auto* inner_node = + property_trees()->scroll_tree.Node(viewport_property_ids_.inner_scroll); + if (!inner_node) + scrollable_viewport_size = gfx::SizeF(); + else + scrollable_viewport_size = gfx::ScaleSize( + gfx::SizeF(inner_node->container_bounds), + 1.0f / (external_page_scale_factor_ * page_scale_factor())); + + gfx::SizeF scrollable_size; + auto* scroll_node = + property_trees()->scroll_tree.Node(viewport_property_ids_.outer_scroll); + if (!scroll_node) { + DCHECK(!inner_node); + scrollable_size = gfx::SizeF(); + } else { + const auto& scroll_tree = property_trees()->scroll_tree; + auto size = scroll_tree.scroll_bounds(scroll_node->id); + size.SetToMax(gfx::SizeF(scroll_tree.container_bounds(scroll_node->id))); + scrollable_size = size; + } + + return util::IsMobileOptimized( + min_page_scale_factor(), max_page_scale_factor(), page_scale_factor(), + scrollable_viewport_size, scrollable_size, is_viewport_mobile_optimized_); +} + void LayerTreeHost::InitializeThreaded( scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) { @@ -604,16 +635,22 @@ void LayerTreeHost::OnDeferMainFrameUpdatesChanged(bool defer_status) { client_->OnDeferMainFrameUpdatesChanged(defer_status); } -void LayerTreeHost::StartDeferringCommits(base::TimeDelta timeout) { - proxy_->StartDeferringCommits(timeout); +bool LayerTreeHost::StartDeferringCommits(base::TimeDelta timeout, + PaintHoldingReason reason) { + return proxy_->StartDeferringCommits(timeout, reason); } void LayerTreeHost::StopDeferringCommits(PaintHoldingCommitTrigger trigger) { proxy_->StopDeferringCommits(trigger); } -void LayerTreeHost::OnDeferCommitsChanged(bool defer_status) { - client_->OnDeferCommitsChanged(defer_status); +bool LayerTreeHost::IsDeferringCommits() const { + return proxy_->IsDeferringCommits(); +} + +void LayerTreeHost::OnDeferCommitsChanged(bool defer_status, + PaintHoldingReason reason) { + client_->OnDeferCommitsChanged(defer_status, reason); } DISABLE_CFI_PERF @@ -641,6 +678,11 @@ void LayerTreeHost::SetNeedsCommit() { events_metrics_manager_.SaveActiveEventMetrics(); } +void LayerTreeHost::SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id) { + proxy_->SetTargetLocalSurfaceId(target_local_surface_id); +} + bool LayerTreeHost::RequestedMainFramePendingForTesting() const { return proxy_->RequestedAnimatePending(); } @@ -749,7 +791,7 @@ bool LayerTreeHost::UpdateLayers() { void LayerTreeHost::DidPresentCompositorFrame( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + std::vector<PresentationTimeCallbackBuffer::MainCallback> callbacks, const gfx::PresentationFeedback& feedback) { for (auto& callback : callbacks) std::move(callback).Run(feedback); @@ -974,7 +1016,7 @@ void LayerTreeHost::UpdateScrollOffsetFromImpl( SetNeedsUpdateLayers(); } - scroll_tree.NotifyDidScroll(id, new_offset, snap_target_ids); + scroll_tree.NotifyDidCompositorScroll(id, new_offset, snap_target_ids); } else if (Layer* layer = LayerByElementId(id)) { layer->SetScrollOffsetFromImplSide(layer->scroll_offset() + delta); SetNeedsUpdateLayers(); @@ -1122,7 +1164,7 @@ bool LayerTreeHost::IsThreaded() const { } void LayerTreeHost::RequestPresentationTimeForNextFrame( - PresentationTimeCallback callback) { + PresentationTimeCallbackBuffer::MainCallback callback) { pending_presentation_time_callbacks_.push_back(std::move(callback)); } @@ -1269,6 +1311,13 @@ void LayerTreeHost::SetViewportRectAndScale( } } + // If a new viz::LocalSurfaceId has been provided, and the viewport has + // changed, we need not begin new frames until it has activated. + if (previous_local_surface_id != local_surface_id_from_parent && + device_viewport_rect_changed && features::IsSurfaceSyncThrottling()) { + SetTargetLocalSurfaceId(local_surface_id_from_parent); + } + if (device_viewport_rect_changed || painted_device_scale_factor_changed || device_scale_factor_changed) { SetPropertyTreesNeedRebuild(); @@ -1381,6 +1430,13 @@ void LayerTreeHost::UpdateViewportIsMobileOptimized( SetNeedsCommit(); } +void LayerTreeHost::SetPrefersReducedMotion(bool prefers_reduced_motion) { + if (prefers_reduced_motion_ == prefers_reduced_motion) + return; + prefers_reduced_motion_ = prefers_reduced_motion; + SetNeedsCommit(); +} + void LayerTreeHost::SetExternalPageScaleFactor( float page_scale_factor, bool is_external_pinch_gesture_active) { @@ -1692,6 +1748,7 @@ void LayerTreeHost::PushLayerTreeHostPropertiesTo( host_impl->SetDebugState(debug_state_); host_impl->SetVisualDeviceViewportSize(visual_device_viewport_size_); host_impl->set_viewport_mobile_optimized(is_viewport_mobile_optimized_); + host_impl->SetPrefersReducedMotion(prefers_reduced_motion_); } Layer* LayerTreeHost::LayerByElementId(ElementId element_id) const { diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index cefbb821f75..48b503c5ecf 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -41,12 +41,14 @@ #include "cc/metrics/frame_sequence_tracker.h" #include "cc/metrics/web_vital_metrics.h" #include "cc/paint/node_id.h" +#include "cc/trees//presentation_time_callback_buffer.h" #include "cc/trees/browser_controls_params.h" #include "cc/trees/compositor_mode.h" #include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_settings.h" #include "cc/trees/mutator_host.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/proxy.h" #include "cc/trees/swap_promise.h" #include "cc/trees/swap_promise_manager.h" @@ -243,6 +245,12 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // synchronization. virtual void SetNeedsCommit(); + // Notifies that a new viz::LocalSurfaceId has been set, ahead of it becoming + // activated. Requests that the compositor thread does not produce new frames + // until it has activated. + virtual void SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id); + // Returns true after SetNeedsAnimate(), SetNeedsUpdateLayers() or // SetNeedsCommit(), until it is satisfied. bool RequestedMainFramePendingForTesting() const; @@ -267,13 +275,17 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // is the interval after which commits will restart if nothing stops // deferring sooner. If multiple calls are made to StartDeferringCommits // while deferal is active, the first timeout continues to apply. - void StartDeferringCommits(base::TimeDelta timeout); + bool StartDeferringCommits(base::TimeDelta timeout, + PaintHoldingReason reason); // Stop deferring commits immediately. void StopDeferringCommits(PaintHoldingCommitTrigger); + // Returns true if commits are currently deferred. + bool IsDeferringCommits() const; + // Notification that the proxy started or stopped deferring commits. - void OnDeferCommitsChanged(bool); + void OnDeferCommitsChanged(bool defer_status, PaintHoldingReason reason); // Returns whether there are any outstanding ScopedDeferMainFrameUpdate, // though commits may be deferred also when the local_surface_id_from_parent() @@ -332,9 +344,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // Registers a callback that is run when the next frame successfully makes it // to the screen (it's entirely possible some frames may be dropped between // the time this is called and the callback is run). - using PresentationTimeCallback = - base::OnceCallback<void(const gfx::PresentationFeedback&)>; - void RequestPresentationTimeForNextFrame(PresentationTimeCallback callback); + void RequestPresentationTimeForNextFrame( + PresentationTimeCallbackBuffer::MainCallback callback); // Registers a callback that is run when any ongoing scroll-animation ends. If // there are no ongoing animations, then the callback is run immediately. @@ -351,6 +362,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { struct ViewportPropertyIds { int overscroll_elasticity_transform = TransformTree::kInvalidNodeId; + ElementId overscroll_elasticity_effect; int page_scale_transform = TransformTree::kInvalidNodeId; int inner_scroll = ScrollTree::kInvalidNodeId; int outer_clip = ClipTree::kInvalidNodeId; @@ -429,6 +441,11 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { void UpdateViewportIsMobileOptimized(bool is_viewport_mobile_optimized); + // Returns if the viewport is considered to be mobile optimized. + bool IsMobileOptimized() const; + + void SetPrefersReducedMotion(bool prefers_reduced_motion); + void SetBrowserControlsParams(const BrowserControlsParams& params); void SetBrowserControlsShownRatio(float top_ratio, float bottom_ratio); @@ -621,7 +638,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { bool UpdateLayers(); void DidPresentCompositorFrame( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + std::vector<PresentationTimeCallbackBuffer::MainCallback> callbacks, const gfx::PresentationFeedback& feedback); // Called when the compositor completed page scale animation. void DidCompletePageScaleAnimation(); @@ -900,6 +917,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // <meta name="viewport" content="initial-scale=1.0"> bool is_viewport_mobile_optimized_ = false; + bool prefers_reduced_motion_ = false; bool have_scroll_event_handlers_ = false; EventListenerProperties event_listener_properties_ [static_cast<size_t>(EventListenerClass::kLast) + 1]; @@ -951,7 +969,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // Presentation time callbacks requested for the next frame are initially // added here. - std::vector<PresentationTimeCallback> pending_presentation_time_callbacks_; + std::vector<PresentationTimeCallbackBuffer::MainCallback> + pending_presentation_time_callbacks_; struct ScrollAnimationState { ScrollAnimationState(); diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h index f411eefcfa5..00f3e63fd42 100644 --- a/chromium/cc/trees/layer_tree_host_client.h +++ b/chromium/cc/trees/layer_tree_host_client.h @@ -11,6 +11,7 @@ #include "base/time/time.h" #include "cc/input/browser_controls_state.h" #include "cc/metrics/frame_sequence_tracker_collection.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/property_tree.h" #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/geometry/vector2d_f.h" @@ -119,8 +120,10 @@ class LayerTreeHostClient { // Notification that the proxy started or stopped deferring main frame updates virtual void OnDeferMainFrameUpdatesChanged(bool) = 0; - // Notification that the proxy started or stopped deferring commits. - virtual void OnDeferCommitsChanged(bool) = 0; + // Notification that the proxy started or stopped deferring commits. |reason| + // indicates why commits are/were deferred. + virtual void OnDeferCommitsChanged(bool defer_status, + PaintHoldingReason reason) = 0; // Visual frame-based updates to the state of the LayerTreeHost are expected // to happen only in calls to LayerTreeHostClient::UpdateLayerTreeHost, which diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index 78addb27c8e..63f61838cbf 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -93,6 +93,7 @@ #include "cc/trees/latency_info_swap_promise_monitor.h" #include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_impl.h" +#include "cc/trees/mobile_optimized_viewport_util.h" #include "cc/trees/mutator_host.h" #include "cc/trees/presentation_time_callback_buffer.h" #include "cc/trees/render_frame_metadata.h" @@ -142,10 +143,11 @@ using ScrollThread = cc::InputHandler::ScrollThread; namespace cc { namespace { -// Used to accommodate finite precision when comparing scaled viewport and -// content widths. While this value may seem large, width=device-width on an N7 -// V1 saw errors of ~0.065 between computed window and content widths. -const float kMobileViewportWidthEpsilon = 0.15f; +// The threshold which determines at what point during a scroll, should the +// tree priority change from SMOOTHNESS_TAKES_PRIORITY to +// NEW_CONTENT_TAKES_PRIORITY. The threshold represents visible checkerboarded +// area. +const float kVisibleCheckerboardedThresholdForPreferNewContent = 0.3f; // In BuildHitTestData we iterate all layers to find all layers that overlap // OOPIFs, but when the number of layers is greater than @@ -161,25 +163,13 @@ static_assert(kContainsSrgbCacheSize == gfx::DisplayColorSpaces::kConfigCount / 2, "sRGB cache must match the size of DisplayColorSpaces"); -bool HasFixedPageScale(LayerTreeImpl* active_tree) { - return active_tree->min_page_scale_factor() == - active_tree->max_page_scale_factor(); -} - -bool HasMobileViewport(LayerTreeImpl* active_tree) { - float window_width_dip = active_tree->current_page_scale_factor() * - active_tree->ScrollableViewportSize().width(); - float content_width_css = active_tree->ScrollableSize().width(); - return content_width_css <= window_width_dip + kMobileViewportWidthEpsilon; -} - bool IsMobileOptimized(LayerTreeImpl* active_tree) { - bool has_mobile_viewport = HasMobileViewport(active_tree); - bool has_fixed_page_scale = HasFixedPageScale(active_tree); - return has_fixed_page_scale || has_mobile_viewport || - (base::FeatureList::IsEnabled( - ::features::kRemoveMobileViewportDoubleTap) && - active_tree->viewport_mobile_optimized()); + return util::IsMobileOptimized(active_tree->min_page_scale_factor(), + active_tree->max_page_scale_factor(), + active_tree->current_page_scale_factor(), + active_tree->ScrollableViewportSize(), + active_tree->ScrollableSize(), + active_tree->viewport_mobile_optimized()); } viz::ResourceFormat TileRasterBufferFormat( @@ -244,16 +234,16 @@ void ApplyFirstScrollTracking(const ui::LatencyInfo* latency, return; } - // Construct a callback that, given presentation feedback, will report the - // time span between the scroll input-event creation and the + // Construct a callback that, given a successful presentation timestamp, will + // report the time span between the scroll input-event creation and the // presentation timestamp. - LayerTreeHost::PresentationTimeCallback presentation_callback = + PresentationTimeCallbackBuffer::CompositorCallback presentation_callback = base::BindOnce( [](base::TimeTicks event_creation, LayerTreeHostImpl* layer_tree_host_impl, - const gfx::PresentationFeedback& feedback) { + base::TimeTicks presentation_timestamp) { layer_tree_host_impl->DidObserveScrollDelay( - feedback.timestamp - event_creation, event_creation); + presentation_timestamp - event_creation, event_creation); }, creation_timestamp, impl); @@ -334,6 +324,7 @@ void LayerTreeHostImpl::DidStartScroll() { void LayerTreeHostImpl::DidEndScroll() { scroll_affects_scroll_handler_ = false; + current_scroll_did_checkerboard_large_area_ = false; } void LayerTreeHostImpl::DidMouseLeave() { @@ -1026,9 +1017,9 @@ void LayerTreeHostImpl::FrameData::AsValueInto( } if (quads_enabled) { value->BeginArray("render_passes"); - for (size_t i = 0; i < render_passes.size(); ++i) { + for (const auto& render_pass : render_passes) { value->BeginDictionary(); - render_passes[i]->AsValueInto(value); + render_pass->AsValueInto(value); value->EndDictionary(); } value->EndArray(); @@ -1290,6 +1281,9 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { layer->transform_tree_index(), target_render_pass); } + } else { + if (settings_.enable_compositing_based_throttling) + throttle_decider_.ProcessLayerNotToDraw(layer); } rendering_stats_instrumentation_->AddVisibleContentArea( @@ -1331,6 +1325,18 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { append_quads_data.use_default_lower_bound_deadline; } + if (total_visible_area > 0 && + GetActivelyScrollingType() != ActivelyScrollingType::kNone) { + float visible_area_checkerboarded_ratio = + (checkerboarded_no_recording_content_area + + checkerboarded_needs_raster_content_area) / + total_visible_area; + if (visible_area_checkerboarded_ratio > + kVisibleCheckerboardedThresholdForPreferNewContent) { + SetCurrentScrollDidCheckerboardLargeArea(); + } + } + // If CommitToActiveTree() is true, then we wait to draw until // NotifyReadyToDraw. That means we're in as good shape as is possible now, // so there's no reason to stop the draw now (and this is not supported by @@ -1509,13 +1515,13 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { UMA_HISTOGRAM_CUSTOM_COUNTS( base::StringPrintf("Compositing.%s.NumActiveLayers", client_name), - base::saturated_cast<int>(active_tree_->NumLayers()), 1, 400, 20); + base::saturated_cast<int>(active_tree_->NumLayers()), 1, 1000, 20); UMA_HISTOGRAM_CUSTOM_COUNTS( base::StringPrintf("Compositing.%s.NumActivePictureLayers", client_name), base::saturated_cast<int>(active_tree_->picture_layers().size()), 1, - 400, 20); + 1000, 20); // TODO(pdr): Instead of skipping empty picture layers, maybe we should // accumulate layer->GetRasterSource()->GetMemoryUsage() above and skip @@ -1752,6 +1758,11 @@ void LayerTreeHostImpl::DidModifyTilePriorities() { client_->SetNeedsPrepareTilesOnImplThread(); } +void LayerTreeHostImpl::SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id) { + target_local_surface_id_ = target_local_surface_id; +} + std::unique_ptr<RasterTilePriorityQueue> LayerTreeHostImpl::BuildRasterQueue( TreePriority tree_priority, RasterTilePriorityQueue::Type type) { @@ -2041,8 +2052,14 @@ void LayerTreeHostImpl::DidReceiveCompositorFrameAck() { void LayerTreeHostImpl::DidPresentCompositorFrame( uint32_t frame_token, const viz::FrameTimingDetails& details) { + // Presentation callbacks registered on the compositor thread are expected to + // be called on the first successful presentation. So, if the presentation is + // failed, we only pop main thread callbacks at this point and leave + // compositor thread callbacks alone until a successful presentation. + const bool main_callbacks_only = details.presentation_feedback.failed(); PresentationTimeCallbackBuffer::PendingCallbacks activated_callbacks = - presentation_time_callbacks_.PopPendingCallbacks(frame_token); + presentation_time_callbacks_.PopPendingCallbacks(frame_token, + main_callbacks_only); // Send all tasks to the client so that it can decide which tasks // should run on which thread. @@ -2426,7 +2443,8 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { active_tree_->ResetAllChangeTracking(); active_tree_->set_has_ever_been_drawn(true); - devtools_instrumentation::DidDrawFrame(id_); + devtools_instrumentation::DidDrawFrame( + id_, frame->begin_frame_ack.frame_id.sequence_number); benchmark_instrumentation::IssueImplThreadRenderingStatsEvent( rendering_stats_instrumentation_->TakeImplThreadRenderingStats()); @@ -2811,20 +2829,20 @@ void LayerTreeHostImpl::UpdateTreeResourcesForGpuRasterizationIfNeeded() { SetRequiresHighResToDraw(); } -void LayerTreeHostImpl::RegisterMainThreadPresentationTimeCallback( +void LayerTreeHostImpl::RegisterMainThreadPresentationTimeCallbackForTesting( uint32_t frame_token, - LayerTreeHost::PresentationTimeCallback callback) { - std::vector<LayerTreeHost::PresentationTimeCallback> as_vector; - as_vector.emplace_back(std::move(callback)); + PresentationTimeCallbackBuffer::MainCallback callback) { + std::vector<PresentationTimeCallbackBuffer::MainCallback> as_vector; + as_vector.push_back(std::move(callback)); presentation_time_callbacks_.RegisterMainThreadPresentationCallbacks( frame_token, std::move(as_vector)); } void LayerTreeHostImpl::RegisterCompositorPresentationTimeCallback( uint32_t frame_token, - LayerTreeHost::PresentationTimeCallback callback) { - std::vector<LayerTreeHost::PresentationTimeCallback> as_vector; - as_vector.emplace_back(std::move(callback)); + PresentationTimeCallbackBuffer::CompositorCallback callback) { + std::vector<PresentationTimeCallbackBuffer::CompositorCallback> as_vector; + as_vector.push_back(std::move(callback)); presentation_time_callbacks_.RegisterCompositorPresentationCallbacks( frame_token, std::move(as_vector)); } @@ -2834,11 +2852,27 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { current_begin_frame_tracker_.Start(args); frame_trackers_.NotifyBeginImplFrame(args); total_frame_counter_.OnBeginFrame(args); - devtools_instrumentation::DidBeginFrame(id_, args.frame_time); + devtools_instrumentation::DidBeginFrame(id_, args.frame_time, + args.frame_id.sequence_number); UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.AcceleratedSurfaceRefreshRate", 1 / args.interval.InSecondsF(), 0, 121, 122); + // When there is a |target_local_surface_id_|, we do not wish to begin + // producing Impl Frames for an older viz::LocalSurfaceId, as it will never + // be displayed. + // + // Once the Main thread has finished adjusting to the new visual properties, + // it will push the updated viz::LocalSurfaceId. Begin Impl Frame production + // if it has already become activated, or is on the |pending_tree| to be + // activated during this frame's production. + const viz::LocalSurfaceId& upcoming_lsid = + pending_tree() ? pending_tree()->local_surface_id_from_parent() + : active_tree()->local_surface_id_from_parent(); + if (target_local_surface_id_.IsNewerThan(upcoming_lsid)) { + return false; + } + if (is_likely_to_require_a_draw_) { // Optimistically schedule a draw. This will let us expect the tile manager // to complete its work so that we can draw new tiles within the impl frame @@ -3139,8 +3173,9 @@ void LayerTreeHostImpl::CreatePendingTree() { pending_tree_fully_painted_ = false; client_->OnCanDrawStateChanged(CanDraw()); - TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("cc", "PendingTree:waiting", - TRACE_ID_LOCAL(pending_tree_.get())); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + "cc", "PendingTree:waiting", TRACE_ID_LOCAL(pending_tree_.get()), + "active_lsid", active_tree()->local_surface_id_from_parent().ToString()); } void LayerTreeHostImpl::PushScrollbarOpacitiesFromActiveToPending() { @@ -3174,8 +3209,10 @@ void LayerTreeHostImpl::PushScrollbarOpacitiesFromActiveToPending() { void LayerTreeHostImpl::ActivateSyncTree() { TRACE_EVENT0("cc,benchmark", "LayerTreeHostImpl::ActivateSyncTree()"); if (pending_tree_) { - TRACE_EVENT_NESTABLE_ASYNC_END0("cc", "PendingTree:waiting", - TRACE_ID_LOCAL(pending_tree_.get())); + TRACE_EVENT_NESTABLE_ASYNC_END1( + "cc", "PendingTree:waiting", TRACE_ID_LOCAL(pending_tree_.get()), + "pending_lsid", + pending_tree_->local_surface_id_from_parent().ToString()); active_tree_->lifecycle().AdvanceTo(LayerTreeLifecycle::kBeginningSync); // In most cases, this will be reset in NotifyReadyToActivate, since we @@ -3965,6 +4002,7 @@ float LayerTreeHostImpl::PageScaleFactor() const { void LayerTreeHostImpl::BindToInputHandler( std::unique_ptr<InputDelegateForCompositor> delegate) { input_delegate_ = std::move(delegate); + input_delegate_->SetPrefersReducedMotion(prefers_reduced_motion_); } void LayerTreeHostImpl::SetVisualDeviceViewportSize( @@ -3976,6 +4014,15 @@ gfx::Size LayerTreeHostImpl::VisualDeviceViewportSize() const { return visual_device_viewport_size_; } +void LayerTreeHostImpl::SetPrefersReducedMotion(bool prefers_reduced_motion) { + if (prefers_reduced_motion_ == prefers_reduced_motion) + return; + + prefers_reduced_motion_ = prefers_reduced_motion; + if (input_delegate_) + input_delegate_->SetPrefersReducedMotion(prefers_reduced_motion_); +} + ScrollTree& LayerTreeHostImpl::GetScrollTree() const { return active_tree_->property_trees()->scroll_tree; } @@ -4944,13 +4991,12 @@ void LayerTreeHostImpl::SetUkmSmoothnessDestination( void LayerTreeHostImpl::NotifyDidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + std::vector<PresentationTimeCallbackBuffer::CompositorCallback> callbacks, const viz::FrameTimingDetails& details) { frame_trackers_.NotifyFramePresented(frame_token, details.presentation_feedback); - for (LayerTreeHost::PresentationTimeCallback& callback : callbacks) { - std::move(callback).Run(details.presentation_feedback); - } + for (auto& callback : callbacks) + std::move(callback).Run(details.presentation_feedback.timestamp); } void LayerTreeHostImpl::AllocateLocalSurfaceId() { diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index bf8b7a8a7bd..1d79f4bfaf2 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -403,6 +403,8 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, return is_viewport_mobile_optimized_; } + void SetPrefersReducedMotion(bool prefers_reduced_motion); + // Updates registered ElementIds present in |changed_list|. Call this after // changing the property trees for the |changed_list| trees. void UpdateElements(ElementListType changed_list); @@ -601,27 +603,37 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, uint32_t next_frame_token() const { return *next_frame_token_; } - // Buffers |callback| until a relevant frame swap ocurrs, at which point the - // callback will be posted to run on the main thread. A frame swap is - // considered relevant if the swapped frame's token is greater than or equal - // to |frame_token|. - void RegisterMainThreadPresentationTimeCallback( + // Buffers `callback` until a relevant presentation feedback arrives, at which + // point the callback will be posted to run on the main thread. A presentation + // feedback is considered relevant if the frame's token is greater than or + // equal to `frame_token`. + void RegisterMainThreadPresentationTimeCallbackForTesting( uint32_t frame_token, - LayerTreeHost::PresentationTimeCallback callback); + PresentationTimeCallbackBuffer::MainCallback callback); - // Buffers |callback| until a relevant frame swap ocurrs, at which point the - // callback will be run on the compositor thread. A frame swap is considered - // relevant if the swapped frame's token is greater than or equal to - // |frame_token|. + // Buffers `callback` until a relevant successful presentation occurs, at + // which point the callback will be run on the compositor thread. A successful + // presentation is considered relevant if the presented frame's token is + // greater than or equal to `frame_token`. void RegisterCompositorPresentationTimeCallback( uint32_t frame_token, - LayerTreeHost::PresentationTimeCallback callback); + PresentationTimeCallbackBuffer::CompositorCallback callback); virtual bool WillBeginImplFrame(const viz::BeginFrameArgs& args); virtual void DidFinishImplFrame(const viz::BeginFrameArgs& args); void DidNotProduceFrame(const viz::BeginFrameAck& ack, FrameSkippedReason reason); void DidModifyTilePriorities(); + // Requests that we do not produce frames until the new viz::LocalSurfaceId + // has been activated. + void SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id); + const viz::LocalSurfaceId& target_local_surface_id() const { + return target_local_surface_id_; + } + const viz::LocalSurfaceId& last_draw_local_surface_id() const { + return last_draw_local_surface_id_; + } LayerTreeImpl* active_tree() { return active_tree_.get(); } const LayerTreeImpl* active_tree() const { return active_tree_.get(); } @@ -651,6 +663,12 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // See comment in equivalent ThreadedInputHandler method for what this means. ActivelyScrollingType GetActivelyScrollingType() const; bool ScrollAffectsScrollHandler() const; + bool CurrentScrollDidCheckerboardLargeArea() const { + return current_scroll_did_checkerboard_large_area_; + } + void SetCurrentScrollDidCheckerboardLargeArea() { + current_scroll_did_checkerboard_large_area_ = true; + } void SetExternalPinchGestureActive(bool active); void set_force_smooth_wheel_scrolling_for_testing(bool enabled) { GetInputHandler().set_force_smooth_wheel_scrolling_for_testing(enabled); @@ -820,7 +838,7 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // was presented. void NotifyDidPresentCompositorFrameOnImplThread( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + std::vector<PresentationTimeCallbackBuffer::CompositorCallback> callbacks, const viz::FrameTimingDetails& details); CompositorFrameReportingController* compositor_frame_reporting_controller() @@ -855,6 +873,10 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, return pending_raster_queries_.get(); } + base::flat_set<viz::FrameSinkId> GetFrameSinksToThrottleForTesting() const { + return throttle_decider_.ids(); + } + protected: LayerTreeHostImpl( const LayerTreeSettings& settings, @@ -1156,6 +1178,8 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // <meta name="viewport" content="initial-scale=1.0"> bool is_viewport_mobile_optimized_ = false; + bool prefers_reduced_motion_ = false; + std::unique_ptr<PendingTreeRasterDurationHistogramTimer> pending_tree_raster_duration_timer_; @@ -1183,6 +1207,8 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, viz::LocalSurfaceId last_draw_local_surface_id_; base::flat_set<viz::SurfaceRange> last_draw_referenced_surfaces_; absl::optional<RenderFrameMetadata> last_draw_render_frame_metadata_; + // The viz::LocalSurfaceId to unthrottle drawing for. + viz::LocalSurfaceId target_local_surface_id_; viz::ChildLocalSurfaceIdAllocator child_local_surface_id_allocator_; // Indicates the direction of the last vertical scroll of the root layer. @@ -1220,6 +1246,11 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient, // sophisticated since so it's not clear how much value it's still providing. bool scroll_affects_scroll_handler_ = false; + // Whether at least 30% of the viewport at the time of draw was + // checkerboarded during a scroll. This bit can get set during a scroll and + // is sticky for the duration of the scroll. + bool current_scroll_did_checkerboard_large_area_ = false; + // Provides support for PaintWorklets which depend on input properties that // are being animated by the compositor (aka 'animated' PaintWorklets). // Responsible for storing animated custom property values and for diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc index e937bb490aa..19a4006dd4a 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -264,7 +264,9 @@ class LayerTreeHostImplTest : public testing::Test, uint32_t frame_token, PresentationTimeCallbackBuffer::PendingCallbacks activated, const viz::FrameTimingDetails& details) override { - std::move(activated.main_thread_callbacks); + // We don't call main thread callbacks in this test. + activated.main_thread_callbacks.clear(); + host_impl_->NotifyDidPresentCompositorFrameOnImplThread( frame_token, std::move(activated.compositor_thread_callbacks), details); } @@ -891,6 +893,16 @@ class CommitToPendingTreeLayerTreeHostImplTest : public LayerTreeHostImplTest { } }; +class OccludedSurfaceThrottlingLayerTreeHostImplTest + : public LayerTreeHostImplTest { + public: + void SetUp() override { + LayerTreeSettings settings = DefaultSettings(); + settings.enable_compositing_based_throttling = true; + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + } +}; + // A test fixture for new animation timelines tests. class LayerTreeHostImplTimelinesTest : public LayerTreeHostImplTest { public: @@ -916,6 +928,7 @@ class TestInputHandlerClient : public InputHandlerClient { void WillShutdown() override {} void Animate(base::TimeTicks time) override {} void ReconcileElasticOverscrollAndRootScroll() override {} + void SetPrefersReducedMotion(bool prefers_reduced_motion) override {} void UpdateRootLayerStateForSynchronousInputHandler( const gfx::ScrollOffset& total_scroll_offset, const gfx::ScrollOffset& max_scroll_offset, @@ -1679,10 +1692,8 @@ class LayerTreeHostImplTestInvokeMainThreadCallbacks auto main_thread_callbacks = std::move(activated.main_thread_callbacks); host_impl_->NotifyDidPresentCompositorFrameOnImplThread( frame_token, std::move(activated.compositor_thread_callbacks), details); - for (LayerTreeHost::PresentationTimeCallback& callback : - main_thread_callbacks) { + for (auto& callback : main_thread_callbacks) std::move(callback).Run(details.presentation_feedback); - } } }; @@ -1692,25 +1703,27 @@ TEST_F(LayerTreeHostImplTestInvokeMainThreadCallbacks, PresentationFeedbackCallbacksFire) { bool compositor_thread_callback_fired = false; bool main_thread_callback_fired = false; - gfx::PresentationFeedback feedback_seen_by_compositor_thread_callback; + base::TimeTicks presentation_time_seen_by_compositor_thread_callback; gfx::PresentationFeedback feedback_seen_by_main_thread_callback; // Register a compositor-thread callback to run when the frame for // |frame_token_1| gets presented. constexpr uint32_t frame_token_1 = 1; host_impl_->RegisterCompositorPresentationTimeCallback( - frame_token_1, base::BindLambdaForTesting( - [&](const gfx::PresentationFeedback& feedback) { - compositor_thread_callback_fired = true; - feedback_seen_by_compositor_thread_callback = - feedback; - })); + frame_token_1, + base::BindLambdaForTesting([&](base::TimeTicks presentation_timestamp) { + DCHECK(presentation_time_seen_by_compositor_thread_callback.is_null()); + DCHECK(!presentation_timestamp.is_null()); + compositor_thread_callback_fired = true; + presentation_time_seen_by_compositor_thread_callback = + presentation_timestamp; + })); // Register a main-thread callback to run when the frame for |frame_token_2| // gets presented. constexpr uint32_t frame_token_2 = 2; ASSERT_GT(frame_token_2, frame_token_1); - host_impl_->RegisterMainThreadPresentationTimeCallback( + host_impl_->RegisterMainThreadPresentationTimeCallbackForTesting( frame_token_2, base::BindLambdaForTesting( [&](const gfx::PresentationFeedback& feedback) { main_thread_callback_fired = true; @@ -1723,8 +1736,8 @@ TEST_F(LayerTreeHostImplTestInvokeMainThreadCallbacks, host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details); EXPECT_TRUE(compositor_thread_callback_fired); - EXPECT_EQ(feedback_seen_by_compositor_thread_callback, - mock_details.presentation_feedback); + EXPECT_EQ(presentation_time_seen_by_compositor_thread_callback, + mock_details.presentation_feedback.timestamp); // Since |frame_token_2| is strictly greater than |frame_token_1|, the // main-thread callback must remain queued for now. @@ -3242,7 +3255,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNodeWithoutScrollLayer) { EXPECT_FALSE(status.needs_main_thread_hit_test); } else { EXPECT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread); - EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, + EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer, status.main_thread_scrolling_reasons); } } @@ -3461,22 +3474,88 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, class MissingTilesLayer : public LayerImpl { public: - MissingTilesLayer(LayerTreeImpl* layer_tree_impl, int id) - : LayerImpl(layer_tree_impl, id), has_missing_tiles_(true) {} - - void set_has_missing_tiles(bool has_missing_tiles) { - has_missing_tiles_ = has_missing_tiles; + static std::unique_ptr<MissingTilesLayer> Create(LayerTreeImpl* tree_impl, + int id) { + return base::WrapUnique(new MissingTilesLayer(tree_impl, id)); } + MissingTilesLayer(LayerTreeImpl* layer_tree_impl, int id) + : LayerImpl(layer_tree_impl, id) {} void AppendQuads(viz::CompositorRenderPass* render_pass, AppendQuadsData* append_quads_data) override { - append_quads_data->num_missing_tiles += has_missing_tiles_; + append_quads_data->num_missing_tiles += 10; + append_quads_data->checkerboarded_no_recording_content_area += 200; + append_quads_data->checkerboarded_needs_raster_content_area += 200; + append_quads_data->visible_layer_area += 200; } - - private: - bool has_missing_tiles_; }; +TEST_P(ScrollUnifiedLayerTreeHostImplTest, + CurrentScrollDidCheckerboardLargeArea) { + LayerTreeSettings settings = DefaultSettings(); + CreateHostImpl(settings, CreateLayerTreeFrameSink()); + host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.25f, 4); + + const gfx::Size content_size(1000, 1000); + const gfx::Size viewport_size(500, 500); + SetupViewportLayersOuterScrolls(viewport_size, content_size); + + LayerImpl* outer_scroll_layer = OuterViewportScrollLayer(); + outer_scroll_layer->SetDrawsContent(true); + LayerImpl* inner_scroll_layer = InnerViewportScrollLayer(); + inner_scroll_layer->SetDrawsContent(true); + + // Add layer that draws content and has checkerboarded areas. + auto* scroll_layer = AddLayer<MissingTilesLayer>(host_impl_->active_tree()); + CopyProperties(inner_scroll_layer, scroll_layer); + scroll_layer->SetBounds(gfx::Size(500, 500)); + scroll_layer->SetDrawsContent(true); + scroll_layer->SetHitTestable(false); + host_impl_->active_tree()->SetElementIdsForTesting(); + + UpdateDrawProperties(host_impl_->active_tree()); + + DrawFrame(); + + // No scroll has taken place so this should be false. + EXPECT_FALSE(host_impl_->CurrentScrollDidCheckerboardLargeArea()); + + // Send scroll begin. + GetInputHandler().ScrollBegin( + BeginState(gfx::Point(250, 250), gfx::Vector2dF(), + ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen); + + DrawFrame(); + + // Even though a ScrollBegin has been processed, we still don't consider the + // interaction to be "actively scrolling". Expect this to be false. + EXPECT_FALSE(host_impl_->CurrentScrollDidCheckerboardLargeArea()); + + gfx::ScrollOffset scroll_delta(0, 10); + + // Send scroll update. + GetInputHandler().ScrollUpdate( + UpdateState(gfx::Point(10, 10), + gfx::ScrollOffsetToVector2dF(scroll_delta), + ui::ScrollInputType::kWheel) + .get()); + + host_impl_->SetFullViewportDamage(); + DrawFrame(); + + // Now that a scroll update has been processed and the latest + // CalculateRenderPasses run has computed significant visible checkerboarding, + // expect this flag to be true. + EXPECT_TRUE(host_impl_->CurrentScrollDidCheckerboardLargeArea()); + + GetInputHandler().ScrollEnd(); + + // Expect state to be reset after a scroll end. + EXPECT_FALSE(host_impl_->CurrentScrollDidCheckerboardLargeArea()); +} + TEST_P(ScrollUnifiedLayerTreeHostImplTest, ImplPinchZoom) { SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100)); DrawFrame(); @@ -11668,7 +11747,8 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) { GetPropertyTrees(root)->effect_tree.AddCopyRequest( root->effect_tree_index(), std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kNativeTextures, base::BindOnce(&Helper::OnResult, base::Unretained(&helper), copy_request_run_loop.QuitClosure()))); DrawFrame(); @@ -12017,7 +12097,7 @@ class LayerTreeHostImplWithBrowserControlsTest : public LayerTreeHostImplTest { settings.commit_to_active_tree = false; CreateHostImpl(settings, CreateLayerTreeFrameSink()); host_impl_->active_tree()->SetBrowserControlsParams( - {top_controls_height_, 0, 0, 0, false, false}); + {static_cast<float>(top_controls_height_), 0, 0, 0, false, false}); host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(1.f, 1.f); } @@ -18083,6 +18163,33 @@ TEST_F(UnifiedScrollingTest, CompositedWithSquashedLayerMutatesTransform) { ScrollEnd(); } +// Verifies that when a surface layer is occluded, its frame sink id will be +// marked as qualified for throttling. +TEST_F(OccludedSurfaceThrottlingLayerTreeHostImplTest, + ThrottleOccludedSurface) { + LayerTreeImpl* tree = host_impl_->active_tree(); + gfx::Rect viewport_rect(0, 0, 800, 600); + auto* root = SetupRootLayer<LayerImpl>(tree, viewport_rect.size()); + + auto* occluded = AddLayer<SurfaceLayerImpl>(tree); + occluded->SetBounds(gfx::Size(400, 300)); + occluded->SetDrawsContent(true); + viz::SurfaceId start = MakeSurfaceId(viz::FrameSinkId(1, 2), 1); + viz::SurfaceId end = MakeSurfaceId(viz::FrameSinkId(3, 4), 1); + occluded->SetRange(viz::SurfaceRange(start, end), 2u); + CopyProperties(root, occluded); + + auto* occluder = AddLayer<SolidColorLayerImpl>(tree); + occluder->SetBounds(gfx::Size(400, 400)); + occluder->SetDrawsContent(true); + occluder->SetContentsOpaque(true); + CopyProperties(root, occluder); + + DrawFrame(); + EXPECT_EQ(host_impl_->GetFrameSinksToThrottleForTesting(), + base::flat_set<viz::FrameSinkId>{end.frame_sink_id()}); +} + TEST_F(LayerTreeHostImplTest, FrameElementIdHitTestSimple) { SetupDefaultRootLayer(gfx::Size(100, 100)); diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc index 98bb4f49eb5..5289f95ac8f 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc @@ -4,7 +4,7 @@ #include <stdint.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "build/build_config.h" #include "cc/layers/solid_color_layer.h" #include "cc/paint/paint_image.h" diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc index d858c5bad32..19f076021e8 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc @@ -4,7 +4,7 @@ #include <stddef.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "build/build_config.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/picture_layer.h" @@ -764,7 +764,7 @@ class LayerTreeHostMaskAsBlendingPixelTest // ARM Windows, macOS, and Fuchsia has some pixels difference // Affected tests: RotatedClippedCircle, RotatedClippedCircleUnderflow // crbug.com/1030244, crbug.com/1048249, crbug.com/1128443 - percentage_pixels_error = 6.1f; + percentage_pixels_error = 6.2f; average_error_allowed_in_bad_pixels = 5.f; large_error_allowed = 20; #else diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc index f52003c1912..b734b02d11f 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc @@ -66,14 +66,16 @@ class LayerTreeHostReadbackPixelTest if (readback_type() == TestReadBackType::kBitmap) { request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap, base::Unretained(this))); } else { DCHECK_NE(renderer_type_, viz::RendererType::kSoftware); request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kNativeTextures, base::BindOnce( &LayerTreeHostReadbackPixelTest::ReadbackResultAsTexture, base::Unretained(this))); @@ -114,17 +116,24 @@ class LayerTreeHostReadbackPixelTest void ReadbackResultAsTexture(std::unique_ptr<viz::CopyOutputResult> result) { EXPECT_TRUE(task_runner_provider()->IsMainThread()); - ASSERT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE); - - gpu::Mailbox mailbox = result->GetTextureResult()->mailbox; - gpu::SyncToken sync_token = result->GetTextureResult()->sync_token; + ASSERT_FALSE(result->IsEmpty()); + ASSERT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA); + ASSERT_EQ(result->destination(), + viz::CopyOutputResult::Destination::kNativeTextures); + + gpu::Mailbox mailbox = result->GetTextureResult()->planes[0].mailbox; + gpu::SyncToken sync_token = + result->GetTextureResult()->planes[0].sync_token; gfx::ColorSpace color_space = result->GetTextureResult()->color_space; EXPECT_EQ(result->GetTextureResult()->color_space, output_color_space_); - viz::ReleaseCallback release_callback = result->TakeTextureOwnership(); + + viz::CopyOutputResult::ReleaseCallbacks release_callbacks = + result->TakeTextureOwnership(); + EXPECT_EQ(1u, release_callbacks.size()); SkBitmap bitmap = CopyMailboxToBitmap(result->size(), mailbox, sync_token, color_space); - std::move(release_callback).Run(gpu::SyncToken(), false); + std::move(release_callbacks[0]).Run(gpu::SyncToken(), false); ReadbackResultAsBitmap(std::make_unique<viz::CopyOutputSkBitmapResult>( result->rect(), std::move(bitmap))); @@ -439,6 +448,7 @@ ReadbackTestConfig const kTestConfigs[] = { #endif // BUILDFLAG(ENABLE_GL_BACKEND_TESTS) #if BUILDFLAG(ENABLE_VULKAN_BACKEND_TESTS) ReadbackTestConfig{viz::RendererType::kSkiaVk, TestReadBackType::kBitmap}, + ReadbackTestConfig{viz::RendererType::kSkiaVk, TestReadBackType::kTexture}, #endif // BUILDFLAG(ENABLE_VULKAN_BACKEND_TESTS) #if BUILDFLAG(ENABLE_DAWN_BACKEND_TESTS) ReadbackTestConfig{viz::RendererType::kSkiaDawn, TestReadBackType::kBitmap}, diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index ff324688b0d..ee9668d3820 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -18,6 +18,7 @@ #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "base/test/bind.h" +#include "base/test/scoped_feature_list.h" #include "base/test/simple_test_tick_clock.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -59,12 +60,14 @@ #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/scroll_node.h" #include "cc/trees/single_thread_proxy.h" #include "cc/trees/swap_promise.h" #include "cc/trees/swap_promise_manager.h" #include "cc/trees/transform_node.h" #include "components/ukm/test_ukm_recorder.h" +#include "components/viz/common/features.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" @@ -651,7 +654,8 @@ class LayerTreeHostFreeContextResourcesOnDestroy : public LayerTreeHostContextCacheTest { public: void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { if (!first_will_begin_impl_frame_) return; @@ -681,7 +685,8 @@ class LayerTreeHostCacheBehaviorOnLayerTreeFrameSinkRecreated : public LayerTreeHostContextCacheTest { public: void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { // This code is run once, to trigger recreation of our LayerTreeFrameSink. if (test_state_ != TestState::INIT) return; @@ -2680,6 +2685,225 @@ class LayerTreeHostTestDeviceScaleFactorChange : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorChange); +// Tests that when the LayerTreeHost has received an updated Viewport Rect and +// viz::LocalSurfaceId that the Impl Frame does not begin until the new tree has +// been either activated or pushed as the new pending tree. +class LayerTreeHostTestViewportRectChangeBlockedMainThread + : public LayerTreeHostTest { + public: + LayerTreeHostTestViewportRectChangeBlockedMainThread() { + scoped_feature_list_.InitAndEnableFeature(features::kSurfaceSyncThrottling); + } + + void SetupTree() override { + root_layer_ = Layer::Create(); + root_layer_->SetBounds(initial_size_); + + child_layer_ = FakePictureLayer::Create(&client_); + child_layer_->SetBounds(gfx::Size(10, 10)); + root_layer_->AddChild(child_layer_); + + layer_tree_host()->SetRootLayer(root_layer_); + LayerTreeHostTest::SetupTree(); + client_.set_bounds(root_layer_->bounds()); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void StopDeferringCommits() { scoped_defer_main_frame_update_.reset(); } + + void ChangeViewportRect() { + gfx::Rect rect = layer_tree_host()->device_viewport_rect(); + rect.set_size(target_size_); + GenerateNewLocalSurfaceId(); + target_local_surface_id_ = GetCurrentLocalSurfaceId(); + layer_tree_host()->SetViewportRectAndScale(rect, 1.f, + GetCurrentLocalSurfaceId()); + // Block Main to simulate it being busy with a long layout. + PostGetDeferMainFrameUpdateToMainThread(&scoped_defer_main_frame_update_); + } + + void WillCommitCompleteOnThread(LayerTreeHostImpl* host_impl) override { + switch (host_impl->sync_tree()->source_frame_number()) { + case 0: + initial_local_surface_id_ = GetCurrentLocalSurfaceId(); + + // After we have committed the initial tree, enqueue the change to the + // viewport rect for the next stage of the test. + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce( + &LayerTreeHostTestViewportRectChangeBlockedMainThread:: + ChangeViewportRect, + base::Unretained(this))); + break; + } + } + + void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, + const viz::BeginFrameArgs& args, + bool has_damage) override { + switch (host_impl->active_tree()->source_frame_number()) { + case -1: + EXPECT_FALSE(host_impl->active_tree() + ->local_surface_id_from_parent() + .is_valid()); + break; + case 0: + EXPECT_EQ(initial_local_surface_id_, + host_impl->active_tree()->local_surface_id_from_parent()); + // Main creates a new |target_local_surface_id_| and posts it back to + // the Compositor thread in ChangeViewportRect. However if it is + // possible for a new Impl frame to start before the queued setting of + // |target_local_surface_id_| has been processed. So ignore those. + // + // The |source_frame_number| will not advance until a new tree has + // been committed. Which will not occur until we've passed here and + // called StopDeferringCommits. If the test times out there there is a + // bug in syncing the id. + if (!host_impl->target_local_surface_id().is_valid()) + return; + EXPECT_EQ(target_local_surface_id_, + host_impl->target_local_surface_id()); + // On slower configurations more than one frame at the original + // |source_frame_number| can be triggered between when we begin allowing + // commits again, and before the commit occurs. + // + // If so do not attempt to re-unblock. Once we have stopped the blocking + // the first time, all subsequent Commit/Activate/BeginMainFramme will + // be allowed to continue as normal. + // + // When this occurs there will be a new pending tree. We also expect + // there to be damage now, to unblock impl frame production ahead of the + // upcoming activation. CC is already build around Activations that can + // arrive mid-frame. It does this by delaying non-immediate mode + // painting until either Activation arrives, or an internal deadline is + // hit. + // + // The normal flow is: + // Main hasn't committed yet, due to explicitly being blocked. + // BeginImplFrame - we don't have damage + // Main is unblocked by StopDeferringCommits + // Main commits + // Main activates + // BeginImplFrame has new active_tree and starts + // + // The slower flow is: + // Main hasn't committed yet, due to explicitly being blocked. + // BeginImplFrame - we don't want have damage + // Main is unblocked by StopDeferringCommits + // Main commits + // BeginImplFrame has new pending_tree and starts + // Main activates + // Impl receives activation + // Painting. + if (host_impl->pending_tree()) { + EXPECT_TRUE(has_damage); + return; + } + // When the |active_tree| in the LayerTreeHostImpl is behind the new + // |target_local_surface_id| that the LayerTreeHost is processing, + // there should be no damage. + EXPECT_FALSE(has_damage) + << "target " << target_local_surface_id_.ToString(); + // Unblock the main thread now to allow for activation of the new + // surface. + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce( + &LayerTreeHostTestViewportRectChangeBlockedMainThread:: + StopDeferringCommits, + base::Unretained(this))); + break; + case 1: + // When the main thread has become unblocked and pushed the new tree, + // frame production should continue. + EXPECT_EQ(target_local_surface_id_, + host_impl->active_tree()->local_surface_id_from_parent()); + EXPECT_EQ(target_local_surface_id_, + host_impl->target_local_surface_id()); + EXPECT_TRUE(has_damage); + break; + } + } + + void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { + switch (host_impl->active_tree()->source_frame_number()) { + case -1: + EXPECT_EQ(initial_local_surface_id_, + host_impl->pending_tree()->local_surface_id_from_parent()); + break; + case 0: + EXPECT_EQ(initial_local_surface_id_, + host_impl->active_tree()->local_surface_id_from_parent()); + // For single threaded compositing we will not have built a + // |pending_tree| yet. + if (host_impl->pending_tree()) { + EXPECT_EQ(target_local_surface_id_, + host_impl->pending_tree()->local_surface_id_from_parent()); + } + break; + case 1: + EXPECT_EQ(target_local_surface_id_, + host_impl->active_tree()->local_surface_id_from_parent()); + break; + } + } + + DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + DrawResult draw_result) override { + // The damage rect will be set before drawing. + gfx::Rect root_damage_rect = frame_data->render_passes.back()->damage_rect; + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + EXPECT_EQ(initial_size_, root_damage_rect.size()); + break; + case 1: + EXPECT_EQ(target_size_, root_damage_rect.size()); + PostSetNeedsRedrawToMainThread(); + break; + } + return draw_result; + } + + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { + // The viz::LocalSurfaceId is not associated with a frame until it is drawn. + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + EXPECT_EQ(initial_local_surface_id_, + host_impl->last_draw_local_surface_id()); + break; + case 1: + EXPECT_EQ(target_local_surface_id_, + host_impl->last_draw_local_surface_id()); + EndTest(); + break; + } + } + + private: + std::unique_ptr<ScopedDeferMainFrameUpdate> scoped_defer_main_frame_update_; + + viz::LocalSurfaceId initial_local_surface_id_; + viz::LocalSurfaceId target_local_surface_id_; + + const gfx::Size initial_size_ = {10, 20}; + const gfx::Size target_size_ = {20, 30}; + + FakeContentLayerClient client_; + scoped_refptr<Layer> root_layer_; + scoped_refptr<Layer> child_layer_; + + base::test::ScopedFeatureList scoped_feature_list_; +}; + +// TODO(crbug.com/1223226): Disabled on Chrome OS due to flakiness. +#if !defined(OS_CHROMEOS) +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostTestViewportRectChangeBlockedMainThread); +#endif + class LayerTreeHostTestRasterColorSpaceChange : public LayerTreeHostTest { public: void SetupTree() override { @@ -3154,7 +3378,8 @@ class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails } void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { if (impl->pending_tree()) frame_count_with_pending_tree_++; @@ -3354,10 +3579,10 @@ class ViewportDeltasAppliedDuringPinch : public LayerTreeHostTest, void AfterTest() override { EXPECT_TRUE(sent_gesture_); } // ScrollCallbacks - void DidScroll(ElementId element_id, - const gfx::ScrollOffset& scroll_offset, - const absl::optional<TargetSnapAreaElementIds>& - snap_target_ids) override { + void DidCompositorScroll(ElementId element_id, + const gfx::ScrollOffset& scroll_offset, + const absl::optional<TargetSnapAreaElementIds>& + snap_target_ids) override { last_scrolled_element_id_ = element_id; last_scrolled_offset_ = scroll_offset; } @@ -3561,7 +3786,8 @@ class LayerTreeHostTestDeferMainFrameUpdate : public LayerTreeHostTest { } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { // Impl frames happen while commits are deferred. num_will_begin_impl_frame_++; switch (num_will_begin_impl_frame_) { @@ -3819,7 +4045,8 @@ class LayerTreeHostTestAnimateOnlyBeginFrames } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { EXPECT_EQ(args.animate_only, (begin_frame_count_ >= 2 && begin_frame_count_ <= 4)); } @@ -3932,7 +4159,8 @@ class LayerTreeHostTestCompositeImmediatelyStateTransitions } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { EXPECT_EQ(current_state_, kStartedTest); current_state_ = kStartedImplFrame; @@ -5543,34 +5771,57 @@ class LayerTreeHostTestElasticOverscroll : public LayerTreeHostTest { } } + void VerifyOverscroll(const gfx::Vector2dF& stretch_amount, + const gfx::Transform& transform) { +#if defined(OS_ANDROID) + gfx::Vector2dF scale = transform.Scale2d(); + // On android, overscroll stretches the content. We don't assert the amount + // of stretch but there should be some stretch for overscroll and no stretch + // without it. + if (stretch_amount.x() == 0.f) + EXPECT_EQ(1.f, scale.x()); + else + EXPECT_GT(scale.x(), 1.f); + if (stretch_amount.y() == 0.f) + EXPECT_EQ(1.f, scale.y()); + else + EXPECT_GT(scale.y(), 1.f); +#else // defined(OS_ANDROID) + gfx::Transform expected_draw_transform; + expected_draw_transform.Translate(-stretch_amount); + EXPECT_EQ(expected_draw_transform, transform); +#endif // defined(OS_ANDROID) + } + void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { num_draws_++; LayerImpl* content_layer_impl = host_impl->active_tree()->LayerById(content_layer_id_); - gfx::Transform expected_draw_transform; switch (num_draws_) { case 1: // Initially, there's no overscroll. - EXPECT_EQ(expected_draw_transform, content_layer_impl->DrawTransform()); + VerifyOverscroll(gfx::Vector2dF(), content_layer_impl->DrawTransform()); // Begin overscrolling. This should be reflected in the draw transform // the next time we draw. scroll_elasticity_helper_->SetStretchAmount(gfx::Vector2dF(5.f, 6.f)); break; case 2: - expected_draw_transform.Translate(-5.0, -6.0); - EXPECT_EQ(expected_draw_transform, content_layer_impl->DrawTransform()); + // We should have some overscroll. + VerifyOverscroll(gfx::Vector2dF(5.f, 6.f), + content_layer_impl->DrawTransform()); scroll_elasticity_helper_->SetStretchAmount(gfx::Vector2dF(3.f, 2.f)); break; case 3: - expected_draw_transform.Translate(-3.0, -2.0); - EXPECT_EQ(expected_draw_transform, content_layer_impl->DrawTransform()); + VerifyOverscroll(gfx::Vector2dF(3.f, 2.f), + content_layer_impl->DrawTransform()); scroll_elasticity_helper_->SetStretchAmount(gfx::Vector2dF()); break; case 4: - EXPECT_EQ(expected_draw_transform, content_layer_impl->DrawTransform()); + // In the final frame there is no more overscroll. + VerifyOverscroll(gfx::Vector2dF(), content_layer_impl->DrawTransform()); EndTest(); break; default: @@ -5746,8 +5997,8 @@ class LayerTreeHostTestBreakSwapPromise : public LayerTreeHostTest { } void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override { - int frame = host_impl->active_tree()->source_frame_number(); - if (frame == 2) { + int frame_num = host_impl->active_tree()->source_frame_number(); + if (frame_num == 2) { EndTest(); } } @@ -6016,7 +6267,8 @@ class LayerTreeHostTestDeferSwapPromiseForVisibility } void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { if (!sent_queue_request_) { sent_queue_request_ = true; MainThreadTaskRunner()->PostTask( @@ -6472,7 +6724,8 @@ class LayerTreeHostTestWillBeginImplFrameHasDidFinishImplFrame } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { EXPECT_EQ(will_begin_impl_frame_count_, did_finish_impl_frame_count_); EXPECT_FALSE(TestEnded()); will_begin_impl_frame_count_++; @@ -6545,7 +6798,8 @@ class LayerTreeHostTestBeginMainFrameTimeIsAlsoImplTime } void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { impl_frame_args_.push_back(args); will_begin_impl_frame_count_++; @@ -7938,7 +8192,8 @@ class LayerTreeHostTestBeginFrameAcks : public LayerTreeHostTest { void BeginTest() override { PostSetNeedsCommitToMainThread(); } void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { EXPECT_TRUE(args.IsValid()); current_begin_frame_args_ = args; } @@ -7999,8 +8254,8 @@ class LayerTreeHostTestQueueImageDecode : public LayerTreeHostTest { image_ = DrawImage(CreateDiscardablePaintImage(gfx::Size(400, 400)), false, - SkIRect::MakeWH(400, 400), kNone_SkFilterQuality, SkM44(), - PaintImage::kDefaultFrameIndex, gfx::ColorSpace()); + SkIRect::MakeWH(400, 400), PaintFlags::FilterQuality::kNone, + SkM44(), PaintImage::kDefaultFrameIndex, gfx::ColorSpace()); auto callback = base::BindRepeating( &LayerTreeHostTestQueueImageDecode::ImageDecodeFinished, base::Unretained(this)); @@ -8350,8 +8605,9 @@ class LayerTreeHostTestImageAnimationSynchronousSchedulingSoftwareDraw } }; -MULTI_THREAD_TEST_F( - LayerTreeHostTestImageAnimationSynchronousSchedulingSoftwareDraw); +// TODO(crbug.com/1092110): Flaky on TSan bot. +// MULTI_THREAD_TEST_F( +// LayerTreeHostTestImageAnimationSynchronousSchedulingSoftwareDraw); class LayerTreeHostTestImageDecodingHints : public LayerTreeHostTest { public: @@ -9012,7 +9268,8 @@ class LayerTreeHostTestEventsMetrics : public LayerTreeHostTest { tick_clock.Advance(base::TimeDelta::FromMicroseconds(10)); std::unique_ptr<EventMetrics> metrics = EventMetrics::CreateForTesting( ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kContinued, ui::ScrollInputType::kWheel, + EventMetrics::ScrollParams(ui::ScrollInputType::kWheel, false, + EventMetrics::ScrollUpdateType::kContinued), event_time, &tick_clock); DCHECK_NE(metrics, nullptr); { @@ -9051,7 +9308,8 @@ class LayerTreeHostTestKeepEventsMetricsForVisibility : public LayerTreeHostTestEventsMetrics { protected: void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { // Skip if we have already received a begin-impl-frame and acted on it. if (received_will_begin_impl_frame_) return; @@ -9117,7 +9375,8 @@ class LayerTreeHostTestKeepEventsMetricsForDeferredMainFrameUpdate : public LayerTreeHostTestEventsMetrics { protected: void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { // Skip if we have already received a begin-impl-frame and acted on it. if (received_will_begin_impl_frame_) return; @@ -9198,7 +9457,8 @@ class LayerTreeHostTestKeepEventsMetricsForDeferredCommit : public LayerTreeHostTestEventsMetrics { protected: void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { // Skip if we have already received a begin-impl-frame and acted on it. if (received_will_begin_impl_frame_) return; @@ -9240,7 +9500,9 @@ class LayerTreeHostTestKeepEventsMetricsForDeferredCommit private: void DeferCommitOnMain() { - layer_tree_host()->StartDeferringCommits(base::TimeDelta::FromDays(1)); + layer_tree_host()->StartDeferringCommits( + base::TimeDelta::FromDays(1), + PaintHoldingReason::kFirstContentfulPaint); } void PostDeferCommit() { @@ -9276,7 +9538,8 @@ class LayerTreeHostTestIgnoreEventsMetricsForNoUpdate : public LayerTreeHostTestEventsMetrics { protected: void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { // Continue only if we are waiting for the second frame's being-impl-frame. // The first frame will end up in a commit which is not what we want. if (state_ != State::kWaitingForSecondFrameBeginImpl) @@ -9376,7 +9639,8 @@ class LayerTreeHostUkmSmoothnessMetric : public LayerTreeTest { } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { host_impl->dropped_frame_counter()->OnFcpReceived(); } @@ -9421,7 +9685,8 @@ class LayerTreeHostUkmSmoothnessMemoryOwnership : public LayerTreeTest { } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { host_impl->dropped_frame_counter()->OnFcpReceived(); host_impl->SetNeedsCommit(); } diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index 1b528540583..356a8789575 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -1033,7 +1033,8 @@ class LayerTreeHostPresentationDuringAnimation } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { if (host_impl->next_frame_token() >= 5) host_impl->BlockNotifyReadyToActivateForTesting(false); } @@ -1125,7 +1126,8 @@ class LayerTreeHostAnimationTestScrollOffsetAnimationRemoval } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { host_impl->BlockNotifyReadyToActivateForTesting( ShouldBlockActivation(host_impl)); } @@ -1348,7 +1350,8 @@ class LayerTreeHostAnimationTestAnimationsAddedToNewAndExistingLayers } void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, - const viz::BeginFrameArgs& args) override { + const viz::BeginFrameArgs& args, + bool has_damage) override { if (!host_impl->pending_tree() || host_impl->pending_tree()->source_frame_number() != 2) return; diff --git a/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc b/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc index 2423cef7446..747063b9b35 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc @@ -8,6 +8,7 @@ #include "cc/test/fake_picture_layer.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/transform_node.h" +#include "third_party/skia/include/core/SkTextBlob.h" namespace cc { namespace { @@ -38,7 +39,9 @@ class FakeCaptureContentLayerClient : public FakeContentLayerClient { display_list->StartPaint(); display_list->push<DrawTextBlobOp>( SkTextBlob::MakeFromString(holder.text().data(), SkFont()), - holder.rect().x(), holder.rect().y(), holder.node_id(), PaintFlags()); + static_cast<float>(holder.rect().x()), + static_cast<float>(holder.rect().y()), holder.node_id(), + PaintFlags()); display_list->EndPaintOfUnpaired(holder.rect()); } display_list->Finalize(); diff --git a/chromium/cc/trees/layer_tree_host_unittest_context.cc b/chromium/cc/trees/layer_tree_host_unittest_context.cc index 6358f0afb4b..cb7610f60f5 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_context.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_context.cc @@ -6,7 +6,7 @@ #include <stdint.h> #include "base/bind.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.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 5640d141a98..02c23624f96 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc @@ -95,7 +95,8 @@ class LayerTreeHostCopyRequestTestMultipleRequests switch (frame) { case 1: child->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&LayerTreeHostCopyRequestTestMultipleRequests:: CopyOutputCallback, base::Unretained(this), 0))); @@ -113,18 +114,21 @@ class LayerTreeHostCopyRequestTestMultipleRequests EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[0].ToString()); child->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&LayerTreeHostCopyRequestTestMultipleRequests:: CopyOutputCallback, base::Unretained(this), 1))); root->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&LayerTreeHostCopyRequestTestMultipleRequests:: CopyOutputCallback, base::Unretained(this), 2))); grand_child->RequestCopyOfOutput( std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&LayerTreeHostCopyRequestTestMultipleRequests:: CopyOutputCallback, base::Unretained(this), 3))); @@ -260,7 +264,8 @@ class LayerTreeHostCopyRequestCompletionCausesCommit switch (frame) { case 1: layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce(&LayerTreeHostCopyRequestCompletionCausesCommit:: CopyOutputCallback))); break; @@ -326,13 +331,15 @@ class LayerTreeHostCopyRequestTestLayerDestroyed case 1: main_destroyed_->RequestCopyOfOutput(std::make_unique< viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback, base::Unretained(this), &main_destroyed_event_))); impl_destroyed_->RequestCopyOfOutput(std::make_unique< viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback, base::Unretained(this), &impl_destroyed_event_))); @@ -437,7 +444,8 @@ class LayerTreeHostCopyRequestTestInHiddenSubtree void AddCopyRequest(Layer* layer) { layer->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostCopyRequestTestInHiddenSubtree::CopyOutputCallback, base::Unretained(this)))); @@ -563,7 +571,8 @@ class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest PostSetNeedsCommitToMainThread(); copy_layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest:: CopyOutputCallback, @@ -670,7 +679,8 @@ class LayerTreeHostCopyRequestTestClippedOut PostSetNeedsCommitToMainThread(); copy_layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostCopyRequestTestClippedOut::CopyOutputCallback, base::Unretained(this)))); @@ -733,7 +743,8 @@ class LayerTreeHostCopyRequestTestScaledLayer std::unique_ptr<viz::CopyOutputRequest> request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostCopyRequestTestScaledLayer::CopyOutputCallback, base::Unretained(this))); @@ -786,7 +797,8 @@ class LayerTreeHostTestAsyncTwoReadbacksWithoutDraw void AddCopyRequest(Layer* layer) { layer->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostTestAsyncTwoReadbacksWithoutDraw::CopyOutputCallback, base::Unretained(this)))); @@ -913,7 +925,9 @@ class LayerTreeHostCopyRequestTestDeleteSharedImage std::unique_ptr<viz::CopyOutputResult> result) { EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread()); EXPECT_EQ(gfx::Size(10, 10).ToString(), result->size().ToString()); - EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE); + EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA); + EXPECT_EQ(result->destination(), + viz::CopyOutputResult::Destination::kNativeTextures); EXPECT_NE(result->GetTextureResult(), nullptr); // Save the result for later. @@ -926,7 +940,8 @@ class LayerTreeHostCopyRequestTestDeleteSharedImage void InsertCopyRequest() { copy_layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputResult::Destination::kNativeTextures, base::BindOnce(&LayerTreeHostCopyRequestTestDeleteSharedImage:: ReceiveCopyRequestOutputAndCommit, base::Unretained(this)))); @@ -1088,6 +1103,22 @@ class LayerTreeHostCopyRequestTestCountSharedImages } } + std::unique_ptr<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 { + // Since this test counts shared images and SkiaRenderer uses shared images + // for render passes, we need render pass allocation to be stable. + auto settings = renderer_settings; + settings.disable_render_pass_bypassing = true; + auto frame_sink = LayerTreeHostCopyRequestTest::CreateLayerTreeFrameSink( + settings, refresh_rate, std::move(compositor_context_provider), + std::move(worker_context_provider)); + return frame_sink; + } + void DisplayDidDrawAndSwapOnThread() override { auto* sii = display_context_provider_->SharedImageInterface(); switch (num_swaps_++) { @@ -1135,7 +1166,8 @@ class LayerTreeHostCopyRequestTestCreatesSharedImage void RequestCopy(Layer* layer) override { // Request a normal texture copy. This should create a new shared image. copy_layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputResult::Destination::kNativeTextures, base::BindOnce( &LayerTreeHostCopyRequestTestCreatesSharedImage::CopyOutputCallback, base::Unretained(this)))); @@ -1143,21 +1175,25 @@ class LayerTreeHostCopyRequestTestCreatesSharedImage void CopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) { EXPECT_FALSE(result->IsEmpty()); - EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE); + EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA); + EXPECT_EQ(result->destination(), + viz::CopyOutputResult::Destination::kNativeTextures); ASSERT_NE(nullptr, result->GetTextureResult()); release_ = result->TakeTextureOwnership(); - EXPECT_TRUE(release_); + EXPECT_EQ(1u, release_.size()); } void AfterTest() override { - std::move(release_).Run(gpu::SyncToken(), false); + for (auto& release : release_) { + std::move(release).Run(gpu::SyncToken(), false); + } // Except the copy to have made a new shared image. EXPECT_EQ(num_shared_images_without_readback_ + 1, num_shared_images_with_readback_); } - viz::ReleaseCallback release_; + viz::CopyOutputResult::ReleaseCallbacks release_; }; INSTANTIATE_TEST_SUITE_P( @@ -1216,7 +1252,8 @@ class LayerTreeHostCopyRequestTestDestroyBeforeCopy // drawing to take place. std::unique_ptr<viz::CopyOutputRequest> request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputResult::Destination::kNativeTextures, base::BindOnce(&LayerTreeHostCopyRequestTestDestroyBeforeCopy:: CopyOutputCallback, base::Unretained(this))); @@ -1308,7 +1345,8 @@ class LayerTreeHostCopyRequestTestShutdownBeforeCopy // drawing to take place. std::unique_ptr<viz::CopyOutputRequest> request = std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputResult::Destination::kNativeTextures, base::BindOnce(&LayerTreeHostCopyRequestTestShutdownBeforeCopy:: CopyOutputCallback, base::Unretained(this))); @@ -1381,7 +1419,8 @@ class LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest // Send a copy request after the first commit. if (layer_tree_host()->SourceFrameNumber() == 1) { child_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>( - viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, + viz::CopyOutputRequest::ResultFormat::RGBA, + viz::CopyOutputRequest::ResultDestination::kSystemMemory, base::BindOnce( &LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest:: CopyOutputCallback, diff --git a/chromium/cc/trees/layer_tree_host_unittest_damage.cc b/chromium/cc/trees/layer_tree_host_unittest_damage.cc index 2d2993d1413..479801c7cb0 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_damage.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_damage.cc @@ -337,7 +337,10 @@ class LayerTreeHostScrollbarDamageTest : public LayerTreeHostDamageTest { content_layer_ = FakePictureLayer::Create(&client_); content_layer_->SetElementId( LayerIdToElementIdForTesting(content_layer_->id())); - content_layer_->SetScrollable(root_layer->bounds()); + + // The size of the container in which scrolling contents are visible need + // to be smaller than the bounds of the layer itself. + content_layer_->SetScrollable(gfx::Size(80, 180)); content_layer_->SetScrollOffset(gfx::ScrollOffset(10, 20)); content_layer_->SetBounds(gfx::Size(100, 200)); content_layer_->SetIsDrawable(true); diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index 5767d3784b2..3a4ecfab2da 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -87,10 +87,10 @@ class LayerTreeHostScrollTest : public LayerTreeTest, public ScrollCallbacks { } // ScrollCallbacks - void DidScroll(ElementId element_id, - const gfx::ScrollOffset& scroll_offset, - const absl::optional<TargetSnapAreaElementIds>& - snap_target_ids) override { + void DidCompositorScroll(ElementId element_id, + const gfx::ScrollOffset& scroll_offset, + const absl::optional<TargetSnapAreaElementIds>& + snap_target_ids) override { // Simulates cc client (e.g Blink) behavior when handling impl-side scrolls. SetScrollOffsetFromImplSide(layer_tree_host()->LayerByElementId(element_id), scroll_offset); @@ -621,11 +621,12 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { } } - void DidScroll(ElementId element_id, - const gfx::ScrollOffset& offset, - const absl::optional<TargetSnapAreaElementIds>& - snap_target_ids) override { - LayerTreeHostScrollTest::DidScroll(element_id, offset, snap_target_ids); + void DidCompositorScroll(ElementId element_id, + const gfx::ScrollOffset& offset, + const absl::optional<TargetSnapAreaElementIds>& + snap_target_ids) override { + LayerTreeHostScrollTest::DidCompositorScroll(element_id, offset, + snap_target_ids); if (element_id == expected_scroll_layer_->element_id()) { final_scroll_offset_ = CurrentScrollOffset(expected_scroll_layer_); EXPECT_EQ(offset, final_scroll_offset_); @@ -1094,7 +1095,6 @@ class SmoothScrollAnimationEndNotification : public LayerTreeHostScrollTest { child_layer_ = Layer::Create(); child_layer_->SetElementId( LayerIdToElementIdForTesting(child_layer_->id())); - child_layer_->SetBounds(gfx::Size(110, 110)); child_layer_->SetIsDrawable(true); child_layer_->SetHitTestable(true); @@ -1111,11 +1111,11 @@ class SmoothScrollAnimationEndNotification : public LayerTreeHostScrollTest { void BeginTest() override { PostSetNeedsCommitToMainThread(); } void WillCommit() override { + if (TestEnded()) + return; // Keep the test committing (otherwise the early out for no update // will stall the test). - if (layer_tree_host()->SourceFrameNumber() < 2) { - layer_tree_host()->SetNeedsCommit(); - } + layer_tree_host()->SetNeedsCommit(); } void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { @@ -1767,6 +1767,8 @@ class ThreadCheckingInputHandlerClient : public InputHandlerClient { } } + void SetPrefersReducedMotion(bool prefers_reduced_motion) override {} + void UpdateRootLayerStateForSynchronousInputHandler( const gfx::ScrollOffset& total_scroll_offset, const gfx::ScrollOffset& max_scroll_offset, @@ -1840,9 +1842,10 @@ class LayerTreeHostScrollTestLayerStructureChange } } - void DidScroll(ElementId element_id, - const gfx::ScrollOffset&, - const absl::optional<TargetSnapAreaElementIds>&) override { + void DidCompositorScroll( + ElementId element_id, + const gfx::ScrollOffset&, + const absl::optional<TargetSnapAreaElementIds>&) override { if (scroll_destroy_whole_tree_) { layer_tree_host()->SetRootLayer(nullptr); layer_tree_host()->property_trees()->clear(); @@ -2235,6 +2238,7 @@ class MockInputHandlerClient : public InputHandlerClient { void WillShutdown() override {} void Animate(base::TimeTicks) override {} + void SetPrefersReducedMotion(bool prefers_reduced_motion) override {} void UpdateRootLayerStateForSynchronousInputHandler( const gfx::ScrollOffset& total_scroll_offset, const gfx::ScrollOffset& max_scroll_offset, diff --git a/chromium/cc/trees/layer_tree_host_unittest_video.cc b/chromium/cc/trees/layer_tree_host_unittest_video.cc index f69dc76d788..c7b664cb484 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_video.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_video.cc @@ -16,6 +16,9 @@ namespace cc { namespace { +constexpr auto kTestTransform = + media::VideoTransformation(media::VIDEO_ROTATION_90, /*mirrored=*/true); + // These tests deal with compositing video. class LayerTreeHostVideoTest : public LayerTreeTest {}; @@ -28,7 +31,7 @@ class LayerTreeHostVideoTestSetNeedsDisplay root->SetIsDrawable(true); scoped_refptr<VideoLayer> video = - VideoLayer::Create(&video_frame_provider_, media::VIDEO_ROTATION_90); + VideoLayer::Create(&video_frame_provider_, kTestTransform); video->SetPosition(gfx::PointF(3.f, 3.f)); video->SetBounds(gfx::Size(4, 5)); video->SetIsDrawable(true); @@ -74,7 +77,7 @@ class LayerTreeHostVideoTestSetNeedsDisplay VideoLayerImpl* video = static_cast<VideoLayerImpl*>( host_impl->active_tree()->LayerById(video_layer_id_)); - EXPECT_EQ(media::VIDEO_ROTATION_90, video->video_rotation()); + EXPECT_EQ(kTestTransform, video->video_transform_for_testing()); if (num_draws_ == 0) video->SetNeedsRedraw(); diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index 8d78780435b..e923a3e0551 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -17,11 +17,11 @@ #include "base/containers/adapters.h" #include "base/containers/contains.h" +#include "base/cxx17_backports.h" #include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" #include "base/json/json_writer.h" #include "base/metrics/histogram_macros.h" -#include "base/numerics/ranges.h" #include "base/strings/stringprintf.h" #include "base/timer/elapsed_timer.h" #include "base/trace_event/trace_event.h" @@ -108,8 +108,8 @@ std::pair<gfx::PointF, gfx::PointF> GetVisibleSelectionEndPoints( const gfx::RectF& rect, const gfx::PointF& top, const gfx::PointF& bottom) { - gfx::PointF start(base::ClampToRange(top.x(), rect.x(), rect.right()), - base::ClampToRange(top.y(), rect.y(), rect.bottom())); + gfx::PointF start(base::clamp(top.x(), rect.x(), rect.right()), + base::clamp(top.y(), rect.y(), rect.bottom())); gfx::PointF end = start + gfx::Vector2dF(bottom.x() - top.x(), bottom.y() - top.y()); return {start, end}; @@ -805,15 +805,15 @@ void LayerTreeImpl::SetBackdropFilterMutated( } void LayerTreeImpl::AddPresentationCallbacks( - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks) { + std::vector<PresentationTimeCallbackBuffer::MainCallback> callbacks) { std::copy(std::make_move_iterator(callbacks.begin()), std::make_move_iterator(callbacks.end()), std::back_inserter(presentation_callbacks_)); } -std::vector<LayerTreeHost::PresentationTimeCallback> +std::vector<PresentationTimeCallbackBuffer::MainCallback> LayerTreeImpl::TakePresentationCallbacks() { - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks; + std::vector<PresentationTimeCallbackBuffer::MainCallback> callbacks; callbacks.swap(presentation_callbacks_); return callbacks; } @@ -1070,7 +1070,7 @@ bool LayerTreeImpl::ClampTopControlsShownRatio() { host_impl_->browser_controls_manager()->TopControlsShownRatioRange(); } return top_controls_shown_ratio_->SetCurrent( - base::ClampToRange(ratio, range.first, range.second)); + base::clamp(ratio, range.first, range.second)); } bool LayerTreeImpl::ClampBottomControlsShownRatio() { @@ -1083,7 +1083,7 @@ bool LayerTreeImpl::ClampBottomControlsShownRatio() { host_impl_->browser_controls_manager()->BottomControlsShownRatioRange(); } return bottom_controls_shown_ratio_->SetCurrent( - base::ClampToRange(ratio, range.first, range.second)); + base::clamp(ratio, range.first, range.second)); } bool LayerTreeImpl::SetCurrentBrowserControlsShownRatio(float top_ratio, @@ -1292,6 +1292,10 @@ const TransformNode* LayerTreeImpl::OverscrollElasticityTransformNode() const { viewport_property_ids_.overscroll_elasticity_transform); } +ElementId LayerTreeImpl::OverscrollElasticityEffectElementId() const { + return viewport_property_ids_.overscroll_elasticity_effect; +} + const TransformNode* LayerTreeImpl::PageScaleTransformNode() const { return property_trees()->transform_tree.Node( viewport_property_ids_.page_scale_transform); diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index 708b0702807..6971fd0d614 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -284,8 +284,8 @@ class CC_EXPORT LayerTreeImpl { gfx::ScrollOffset TotalMaxScrollOffset() const; void AddPresentationCallbacks( - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks); - std::vector<LayerTreeHost::PresentationTimeCallback> + std::vector<PresentationTimeCallbackBuffer::MainCallback> callbacks); + std::vector<PresentationTimeCallbackBuffer::MainCallback> TakePresentationCallbacks(); bool has_presentation_callbacks() const { return !presentation_callbacks_.empty(); @@ -303,6 +303,7 @@ class CC_EXPORT LayerTreeImpl { const_cast<const LayerTreeImpl*>(this) ->OverscrollElasticityTransformNode()); } + ElementId OverscrollElasticityEffectElementId() const; const TransformNode* PageScaleTransformNode() const; TransformNode* PageScaleTransformNode() { return const_cast<TransformNode*>( @@ -734,6 +735,10 @@ class CC_EXPORT LayerTreeImpl { return host_impl_->GetActivelyScrollingType(); } + bool CurrentScrollDidCheckerboardLargeArea() { + return host_impl_->CurrentScrollDidCheckerboardLargeArea(); + } + // These functions are used for plumbing DelegatedInkMetadata from blink // through the compositor and into viz via a compositor frame. They should // only be called after the JS API |updateInkTrailStartPoint| has been @@ -917,7 +922,8 @@ class CC_EXPORT LayerTreeImpl { // Display transform hint to tag frames generated from this tree. gfx::OverlayTransform display_transform_hint_ = gfx::OVERLAY_TRANSFORM_NONE; - std::vector<LayerTreeHost::PresentationTimeCallback> presentation_callbacks_; + std::vector<PresentationTimeCallbackBuffer::MainCallback> + presentation_callbacks_; // Event metrics that are reported back from the main thread. EventMetrics::List events_metrics_from_main_thread_; diff --git a/chromium/cc/trees/layer_tree_impl_unittest.cc b/chromium/cc/trees/layer_tree_impl_unittest.cc index a132530bfaa..ff133afcada 100644 --- a/chromium/cc/trees/layer_tree_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_impl_unittest.cc @@ -4,7 +4,7 @@ #include "cc/trees/layer_tree_impl.h" -#include "base/numerics/ranges.h" +#include "base/cxx17_backports.h" #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/fake_raster_source.h" @@ -23,8 +23,8 @@ std::pair<gfx::PointF, gfx::PointF> GetVisibleSelectionEndPoints( const gfx::RectF& rect, const gfx::PointF& top, const gfx::PointF& bottom) { - gfx::PointF start(base::ClampToRange(top.x(), rect.x(), rect.right()), - base::ClampToRange(top.y(), rect.y(), rect.bottom())); + gfx::PointF start(base::clamp(top.x(), rect.x(), rect.right()), + base::clamp(top.y(), rect.y(), rect.bottom())); gfx::PointF end = start + gfx::Vector2dF(bottom.x() - top.x(), bottom.y() - top.y()); return {start, end}; diff --git a/chromium/cc/trees/layer_tree_settings.cc b/chromium/cc/trees/layer_tree_settings.cc index 2a13e654421..b8168bd5b9e 100644 --- a/chromium/cc/trees/layer_tree_settings.cc +++ b/chromium/cc/trees/layer_tree_settings.cc @@ -26,10 +26,6 @@ SchedulerSettings LayerTreeSettings::ToSchedulerSettings() const { main_frame_before_activation_enabled; scheduler_settings.using_synchronous_renderer_compositor = using_synchronous_renderer_compositor; - scheduler_settings.enable_impl_latency_recovery = - enable_impl_latency_recovery; - scheduler_settings.enable_main_latency_recovery = - enable_main_latency_recovery; scheduler_settings.wait_for_all_pipeline_stages_before_draw = wait_for_all_pipeline_stages_before_draw; return scheduler_settings; diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h index 7fa2b4da8c8..e38646f8834 100644 --- a/chromium/cc/trees/layer_tree_settings.h +++ b/chromium/cc/trees/layer_tree_settings.h @@ -38,8 +38,6 @@ class CC_EXPORT LayerTreeSettings { // When |enable_early_damage_check| is true, the early damage check is // performed if one of the last |damaged_frame_limit| frames had no damage. int damaged_frame_limit = 3; - bool enable_impl_latency_recovery = false; - bool enable_main_latency_recovery = false; bool can_use_lcd_text = true; bool gpu_rasterization_disabled = false; int gpu_rasterization_msaa_sample_count = -1; @@ -193,7 +191,7 @@ class CC_EXPORT LayerTreeSettings { // When enabled, enforces new interoperable semantics for 3D transforms. // See crbug.com/1008483. - bool enable_transform_interop = false; + bool enable_backface_visibility_interop = false; // Enables ThrottleDecider which produces a list of FrameSinkIds that are // candidates for throttling. diff --git a/chromium/cc/trees/mobile_optimized_viewport_util.cc b/chromium/cc/trees/mobile_optimized_viewport_util.cc new file mode 100644 index 00000000000..2f70f27a70d --- /dev/null +++ b/chromium/cc/trees/mobile_optimized_viewport_util.cc @@ -0,0 +1,41 @@ +// Copyright 2021 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/mobile_optimized_viewport_util.h" + +#include "base/feature_list.h" +#include "cc/base/features.h" +#include "ui/gfx/geometry/size_f.h" + +namespace cc { +namespace util { +namespace { +// Used to accommodate finite precision when comparing scaled viewport and +// content widths. While this value may seem large, width=device-width on an N7 +// V1 saw errors of ~0.065 between computed window and content widths. +const float kMobileViewportWidthEpsilon = 0.15f; +} // namespace + +bool IsMobileOptimized(float min_page_scale_factor, + float max_page_scale_factor, + float current_page_scale_factor, + gfx::SizeF scrollable_viewport_size, + gfx::SizeF scrollable_size, + bool viewport_meta_mobile_optimized) { + bool has_fixed_page_scale = min_page_scale_factor == max_page_scale_factor; + + float window_width_dip = + current_page_scale_factor * scrollable_viewport_size.width(); + float content_width_css = scrollable_size.width(); + bool has_mobile_viewport = + content_width_css <= window_width_dip + kMobileViewportWidthEpsilon; + + return has_mobile_viewport || has_fixed_page_scale || + (base::FeatureList::IsEnabled( + ::features::kRemoveMobileViewportDoubleTap) && + viewport_meta_mobile_optimized); +} + +} // namespace util +} // namespace cc diff --git a/chromium/cc/trees/mobile_optimized_viewport_util.h b/chromium/cc/trees/mobile_optimized_viewport_util.h new file mode 100644 index 00000000000..9bd59891aba --- /dev/null +++ b/chromium/cc/trees/mobile_optimized_viewport_util.h @@ -0,0 +1,34 @@ +// Copyright 2021 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_MOBILE_OPTIMIZED_VIEWPORT_UTIL_H_ +#define CC_TREES_MOBILE_OPTIMIZED_VIEWPORT_UTIL_H_ + +#include "ui/gfx/geometry/size_f.h" + +namespace cc { +namespace util { + +// Returns whether the viewport should be considered mobile optimized, +// not needing the double tap to zoom gesture. +// Arguments: +// min_page_scale_factor - the minimum page scale +// max_page_scale_factor - the maximum page scale +// current_page_scale_factor - current page scale +// scrollable_viewport_size - the size of the user-visible scrolling viewport +// in CSS layout coordinates +// scrollable_size - the size of the root scrollable area in CSS layout +// coordinates +// viewport_meta_mobile_optimized - if the viewport meta tag is mobile +// optimized +bool IsMobileOptimized(float min_page_scale_factor, + float max_page_scale_factor, + float current_page_scale_factor, + gfx::SizeF scrollable_viewport_size, + gfx::SizeF scrollable_size, + bool viewport_meta_mobile_optimized); +} // namespace util +} // namespace cc + +#endif // CC_TREES_MOBILE_OPTIMIZED_VIEWPORT_UTIL_H_ diff --git a/chromium/cc/trees/occlusion_tracker.cc b/chromium/cc/trees/occlusion_tracker.cc index 91f8fefa627..0f9c92265c8 100644 --- a/chromium/cc/trees/occlusion_tracker.cc +++ b/chromium/cc/trees/occlusion_tracker.cc @@ -104,7 +104,7 @@ static SimpleEnclosedRegion TransformSurfaceOpaqueRegion( // to each rect within |region| in order to transform the entire Region. // TODO(danakj): Find a rect interior to each transformed quad. - if (!transform.Preserves2dAxisAlignment()) + if (!transform.NonDegeneratePreserves2dAxisAlignment()) return SimpleEnclosedRegion(); SimpleEnclosedRegion transformed_region; @@ -371,7 +371,7 @@ void OcclusionTracker::MarkOccludedBehindLayer(const LayerImpl* layer) { gfx::Transform draw_transform = layer->DrawTransform(); // TODO(danakj): Find a rect interior to each transformed quad. - if (!draw_transform.Preserves2dAxisAlignment()) + if (!draw_transform.NonDegeneratePreserves2dAxisAlignment()) return; gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface( diff --git a/chromium/cc/trees/occlusion_unittest.cc b/chromium/cc/trees/occlusion_unittest.cc index e5b31e41193..a0c17bd881c 100644 --- a/chromium/cc/trees/occlusion_unittest.cc +++ b/chromium/cc/trees/occlusion_unittest.cc @@ -6,7 +6,7 @@ #include <stddef.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { diff --git a/chromium/cc/trees/paint_holding_commit_trigger.cc b/chromium/cc/trees/paint_holding_commit_trigger.cc new file mode 100644 index 00000000000..daba1843c87 --- /dev/null +++ b/chromium/cc/trees/paint_holding_commit_trigger.cc @@ -0,0 +1,22 @@ +// Copyright 2021 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/paint_holding_commit_trigger.h" + +#include "base/notreached.h" + +namespace cc { + +PaintHoldingCommitTrigger ReasonToTimeoutTrigger(PaintHoldingReason reason) { + switch (reason) { + case PaintHoldingReason::kFirstContentfulPaint: + return PaintHoldingCommitTrigger::kTimeoutFCP; + case PaintHoldingReason::kDocumentTransition: + return PaintHoldingCommitTrigger::kTimeoutDocumentTransition; + } + NOTREACHED(); + return PaintHoldingCommitTrigger::kTimeoutFCP; +} + +} // namespace cc diff --git a/chromium/cc/trees/paint_holding_commit_trigger.h b/chromium/cc/trees/paint_holding_commit_trigger.h index b8828028019..5c9439d7f78 100644 --- a/chromium/cc/trees/paint_holding_commit_trigger.h +++ b/chromium/cc/trees/paint_holding_commit_trigger.h @@ -5,6 +5,8 @@ #ifndef CC_TREES_PAINT_HOLDING_COMMIT_TRIGGER_H_ #define CC_TREES_PAINT_HOLDING_COMMIT_TRIGGER_H_ +#include "cc/trees/paint_holding_reason.h" + namespace cc { enum class PaintHoldingCommitTrigger { @@ -18,13 +20,19 @@ enum class PaintHoldingCommitTrigger { // The commit was triggered by first contentful paint (FCP) kFirstContentfulPaint = 2, // The commit was triggered by a timeout waiting for FCP - kTimeout = 3, + kTimeoutFCP = 3, // The timeout was never set, probably due to non-main frame kNotDeferred = 4, + // The commit was triggered by a document transition start + kDocumentTransition = 5, + // The commit was triggered by a timeout waiting for document transition start + kTimeoutDocumentTransition = 6, // Required for UMA enum - kMaxValue = kNotDeferred + kMaxValue = kTimeoutDocumentTransition }; +PaintHoldingCommitTrigger ReasonToTimeoutTrigger(PaintHoldingReason reason); + } // namespace cc #endif // CC_TREES_PAINT_HOLDING_COMMIT_TRIGGER_H_ diff --git a/chromium/cc/trees/paint_holding_reason.h b/chromium/cc/trees/paint_holding_reason.h new file mode 100644 index 00000000000..3e063246b4b --- /dev/null +++ b/chromium/cc/trees/paint_holding_reason.h @@ -0,0 +1,21 @@ +// Copyright 2021 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_PAINT_HOLDING_REASON_H_ +#define CC_TREES_PAINT_HOLDING_REASON_H_ + +namespace cc { + +enum class PaintHoldingReason { + // Deferred to allow a frame with contentful paint. + kFirstContentfulPaint, + + // Deferred to allow the document to be updated asynchronously for a + // transition. + kDocumentTransition, +}; + +} // namespace cc + +#endif // CC_TREES_PAINT_HOLDING_REASON_H_ diff --git a/chromium/cc/trees/presentation_time_callback_buffer.cc b/chromium/cc/trees/presentation_time_callback_buffer.cc index c6de964c244..7ae602bf330 100644 --- a/chromium/cc/trees/presentation_time_callback_buffer.cc +++ b/chromium/cc/trees/presentation_time_callback_buffer.cc @@ -7,6 +7,8 @@ #include <utility> #include <vector> +#include "components/viz/common/quads/compositor_frame_metadata.h" + namespace cc { PresentationTimeCallbackBuffer::PresentationTimeCallbackBuffer() = default; @@ -37,7 +39,7 @@ PresentationTimeCallbackBuffer::FrameTokenInfo::~FrameTokenInfo() = default; void PresentationTimeCallbackBuffer::RegisterMainThreadPresentationCallbacks( uint32_t frame_token, - std::vector<CallbackType> callbacks) { + std::vector<MainCallback> callbacks) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); FrameTokenInfo& frame_info = GetOrMakeRegistration(frame_token); @@ -45,20 +47,16 @@ void PresentationTimeCallbackBuffer::RegisterMainThreadPresentationCallbacks( auto& sink = frame_info.main_thread_callbacks; sink.reserve(sink.size() + callbacks.size()); std::move(callbacks.begin(), callbacks.end(), std::back_inserter(sink)); - - DCHECK_LE(frame_token_infos_.size(), 25u); } void PresentationTimeCallbackBuffer::RegisterCompositorPresentationCallbacks( uint32_t frame_token, - std::vector<CallbackType> callbacks) { + std::vector<CompositorCallback> callbacks) { // Splice the given |callbacks| onto the vector of existing callbacks. - std::vector<LayerTreeHost::PresentationTimeCallback>& sink = + std::vector<CompositorCallback>& sink = GetOrMakeRegistration(frame_token).compositor_thread_callbacks; sink.reserve(sink.size() + callbacks.size()); std::move(callbacks.begin(), callbacks.end(), std::back_inserter(sink)); - - DCHECK_LE(frame_token_infos_.size(), 25u); } PresentationTimeCallbackBuffer::PendingCallbacks::PendingCallbacks() = default; @@ -70,29 +68,33 @@ PresentationTimeCallbackBuffer::PendingCallbacks::operator=( PresentationTimeCallbackBuffer::PendingCallbacks::~PendingCallbacks() = default; PresentationTimeCallbackBuffer::PendingCallbacks -PresentationTimeCallbackBuffer::PopPendingCallbacks(uint32_t frame_token) { +PresentationTimeCallbackBuffer::PopPendingCallbacks(uint32_t frame_token, + bool main_only) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); PendingCallbacks result; - while (!frame_token_infos_.empty()) { - auto info = frame_token_infos_.begin(); + for (auto info = frame_token_infos_.begin(); + info != frame_token_infos_.end();) { if (viz::FrameTokenGT(info->token, frame_token)) break; - // Collect the main-thread callbacks. It's the caller's job to post them to - // the main thread. std::move(info->main_thread_callbacks.begin(), info->main_thread_callbacks.end(), std::back_inserter(result.main_thread_callbacks)); - - // Collect the compositor-thread callbacks. It's the caller's job to run - // them on the compositor thread. - std::move(info->compositor_thread_callbacks.begin(), - info->compositor_thread_callbacks.end(), - std::back_inserter(result.compositor_thread_callbacks)); - - frame_token_infos_.erase(info); + info->main_thread_callbacks.clear(); + + const bool should_keep_callbacks = + main_only && !info->compositor_thread_callbacks.empty(); + + if (should_keep_callbacks) { + ++info; + } else { + std::move(info->compositor_thread_callbacks.begin(), + info->compositor_thread_callbacks.end(), + std::back_inserter(result.compositor_thread_callbacks)); + info = frame_token_infos_.erase(info); + } } return result; @@ -105,6 +107,7 @@ PresentationTimeCallbackBuffer::GetOrMakeRegistration(uint32_t frame_token) { if (frame_token_infos_.empty() || viz::FrameTokenGT(frame_token, frame_token_infos_.back().token)) { frame_token_infos_.emplace_back(frame_token); + DCHECK_LE(frame_token_infos_.size(), 25u); } // Registrations should use monotonically increasing frame tokens. diff --git a/chromium/cc/trees/presentation_time_callback_buffer.h b/chromium/cc/trees/presentation_time_callback_buffer.h index 820b8fc3767..ab36aa5bd22 100644 --- a/chromium/cc/trees/presentation_time_callback_buffer.h +++ b/chromium/cc/trees/presentation_time_callback_buffer.h @@ -6,9 +6,11 @@ #include <vector> +#include "base/callback_forward.h" #include "base/containers/circular_deque.h" #include "base/sequence_checker.h" -#include "cc/trees/layer_tree_host.h" +#include "cc/cc_export.h" +#include "ui/gfx/presentation_feedback.h" namespace cc { @@ -32,7 +34,15 @@ namespace cc { // CC_EXPORT is only needed for testing. class CC_EXPORT PresentationTimeCallbackBuffer { public: - using CallbackType = LayerTreeHost::PresentationTimeCallback; + // TODO(crbug.com/1199373): Compositor thread callbacks are only run for + // successful presentations and only need the presentation timestamp. On the + // other hand, main thread callbacks can be run on both successful and failed + // presentations and need a full `gfx::PresentationFeedback`. Conceptually, + // main thread callbacks should only be run for successful presentations, too, + // in which case the two callback signatures can be unified. + using MainCallback = + base::OnceCallback<void(const gfx::PresentationFeedback&)>; + using CompositorCallback = base::OnceCallback<void(base::TimeTicks)>; PresentationTimeCallbackBuffer(); @@ -51,14 +61,14 @@ class CC_EXPORT PresentationTimeCallbackBuffer { // main thread once they're popped. void RegisterMainThreadPresentationCallbacks( uint32_t frame_token, - std::vector<CallbackType> callbacks); + std::vector<MainCallback> callbacks); // Buffers the given |callbacks| in preparation for a GPU frame swap at or // after the given |frame_token|. Calling code invokes these callbacks on the // compositor thread once they're popped. void RegisterCompositorPresentationCallbacks( uint32_t frame_token, - std::vector<CallbackType> callbacks); + std::vector<CompositorCallback> callbacks); // Structured return value for |PopPendingCallbacks|. CC_EXPORT is only // needed for testing. @@ -75,18 +85,19 @@ class CC_EXPORT PresentationTimeCallbackBuffer { // Holds callbacks registered through // |RegisterMainThreadPresentationCallbacks|. - std::vector<CallbackType> main_thread_callbacks; + std::vector<MainCallback> main_thread_callbacks; // Holds callbacks registered through // |RegisterCompositorPresentationCallbacks|. - std::vector<CallbackType> compositor_thread_callbacks; + std::vector<CompositorCallback> compositor_thread_callbacks; }; - // Call this once the presentation for the given |frame_token| has completed. + // Call this once the presentation for the given `frame_token` has completed. // Yields any pending callbacks that were registered against a frame token - // that was less than or equal to the given |frame_token|. It is the caller's + // that was less than or equal to the given `frame_token`. If `main_only` is + // true, only callbacks for the main thread are returned. It is the caller's // responsibility to run the callbacks on the right threads/sequences. - PendingCallbacks PopPendingCallbacks(uint32_t frame_token); + PendingCallbacks PopPendingCallbacks(uint32_t frame_token, bool main_only); private: // Stores information needed once we get a response for a particular @@ -104,10 +115,10 @@ class CC_EXPORT PresentationTimeCallbackBuffer { uint32_t token; // The callbacks to send back to the main thread. - std::vector<CallbackType> main_thread_callbacks; + std::vector<MainCallback> main_thread_callbacks; // The callbacks to invoke on the compositor thread. - std::vector<CallbackType> compositor_thread_callbacks; + std::vector<CompositorCallback> compositor_thread_callbacks; }; // Returns a reference to a |FrameTokenInfo| with the given |frame_token|. diff --git a/chromium/cc/trees/presentation_time_callback_buffer_unittest.cc b/chromium/cc/trees/presentation_time_callback_buffer_unittest.cc index 6b8aa1e0186..ee68a898f3c 100644 --- a/chromium/cc/trees/presentation_time_callback_buffer_unittest.cc +++ b/chromium/cc/trees/presentation_time_callback_buffer_unittest.cc @@ -4,16 +4,18 @@ #include "cc/trees/presentation_time_callback_buffer.h" +#include "base/bind.h" +#include "base/callback.h" #include "testing/gtest/include/gtest/gtest.h" namespace { -std::vector<cc::PresentationTimeCallbackBuffer::CallbackType> GenerateCallbacks( - int num_callbacks) { - std::vector<cc::PresentationTimeCallbackBuffer::CallbackType> result; +std::vector<cc::PresentationTimeCallbackBuffer::MainCallback> +GenerateMainCallbacks(int num_callbacks) { + std::vector<cc::PresentationTimeCallbackBuffer::MainCallback> result; while (num_callbacks-- > 0) { - // PresentationTimeCallbackBuffer isn't supposed to invoke any callbacks. + // `PresentationTimeCallbackBuffer` isn't supposed to invoke any callbacks. // We can check for that by passing callbacks which cause test failure. result.push_back(base::BindOnce([](const gfx::PresentationFeedback&) { FAIL() << "Callbacks should not be directly invoked by " @@ -24,6 +26,22 @@ std::vector<cc::PresentationTimeCallbackBuffer::CallbackType> GenerateCallbacks( return result; } +std::vector<cc::PresentationTimeCallbackBuffer::CompositorCallback> +GenerateCompositorCallbacks(int num_callbacks) { + std::vector<cc::PresentationTimeCallbackBuffer::CompositorCallback> result; + + while (num_callbacks-- > 0) { + // `PresentationTimeCallbackBuffer` isn't supposed to invoke any callbacks. + // We can check for that by passing callbacks which cause test failure. + result.push_back(base::BindOnce([](base::TimeTicks presentation_timestamp) { + FAIL() << "Callbacks should not be directly invoked by " + "PresentationTimeCallbackBuffer"; + })); + } + + return result; +} + constexpr uint32_t kFrameToken1 = 234; constexpr uint32_t kFrameToken2 = 345; constexpr uint32_t kFrameToken3 = 456; @@ -36,7 +54,7 @@ namespace cc { TEST(PresentationTimeCallbackBufferTest, TestNoCallbacks) { PresentationTimeCallbackBuffer buffer; - auto result = buffer.PopPendingCallbacks(kFrameToken1); + auto result = buffer.PopPendingCallbacks(kFrameToken1, false); EXPECT_TRUE(result.main_thread_callbacks.empty()); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); @@ -46,25 +64,25 @@ TEST(PresentationTimeCallbackBufferTest, TestOneMainThreadCallback) { PresentationTimeCallbackBuffer buffer; buffer.RegisterMainThreadPresentationCallbacks(kFrameToken2, - GenerateCallbacks(1)); + GenerateMainCallbacks(1)); // Make sure that popping early frame tokens doesn't return irrelevant // entries. { - auto result = buffer.PopPendingCallbacks(kFrameToken1); + auto result = buffer.PopPendingCallbacks(kFrameToken1, false); EXPECT_TRUE(result.main_thread_callbacks.empty()); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); } { - auto result = buffer.PopPendingCallbacks(kFrameToken2); + auto result = buffer.PopPendingCallbacks(kFrameToken2, false); EXPECT_EQ(result.main_thread_callbacks.size(), 1ull); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); } // Make sure that the buffer has removed the registration since the "pop". { - auto result = buffer.PopPendingCallbacks(kFrameToken2); + auto result = buffer.PopPendingCallbacks(kFrameToken2, false); EXPECT_TRUE(result.main_thread_callbacks.empty()); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); } @@ -73,26 +91,26 @@ TEST(PresentationTimeCallbackBufferTest, TestOneMainThreadCallback) { TEST(PresentationTimeCallbackBufferTest, TestOneCompositorThreadCallback) { PresentationTimeCallbackBuffer buffer; - buffer.RegisterCompositorPresentationCallbacks(kFrameToken2, - GenerateCallbacks(1)); + buffer.RegisterCompositorPresentationCallbacks( + kFrameToken2, GenerateCompositorCallbacks(1)); // Make sure that popping early frame tokens doesn't return irrelevant // entries. { - auto result = buffer.PopPendingCallbacks(kFrameToken1); + auto result = buffer.PopPendingCallbacks(kFrameToken1, false); EXPECT_TRUE(result.main_thread_callbacks.empty()); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); } { - auto result = buffer.PopPendingCallbacks(kFrameToken2); + auto result = buffer.PopPendingCallbacks(kFrameToken2, false); EXPECT_TRUE(result.main_thread_callbacks.empty()); EXPECT_EQ(result.compositor_thread_callbacks.size(), 1ull); } // Make sure that the buffer has removed the registration since the "pop". { - auto result = buffer.PopPendingCallbacks(kFrameToken2); + auto result = buffer.PopPendingCallbacks(kFrameToken2, false); EXPECT_TRUE(result.main_thread_callbacks.empty()); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); } @@ -102,27 +120,27 @@ TEST(PresentationTimeCallbackBufferTest, TestMixedCallbacks) { PresentationTimeCallbackBuffer buffer; buffer.RegisterMainThreadPresentationCallbacks(kFrameToken2, - GenerateCallbacks(1)); - buffer.RegisterCompositorPresentationCallbacks(kFrameToken2, - GenerateCallbacks(1)); + GenerateMainCallbacks(1)); + buffer.RegisterCompositorPresentationCallbacks( + kFrameToken2, GenerateCompositorCallbacks(1)); // Make sure that popping early frame tokens doesn't return irrelevant // entries. { - auto result = buffer.PopPendingCallbacks(kFrameToken1); + auto result = buffer.PopPendingCallbacks(kFrameToken1, false); EXPECT_TRUE(result.main_thread_callbacks.empty()); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); } { - auto result = buffer.PopPendingCallbacks(kFrameToken2); + auto result = buffer.PopPendingCallbacks(kFrameToken2, false); EXPECT_EQ(result.main_thread_callbacks.size(), 1ull); EXPECT_EQ(result.compositor_thread_callbacks.size(), 1ull); } // Make sure that the buffer has removed the registrations since the "pop". { - auto result = buffer.PopPendingCallbacks(kFrameToken2); + auto result = buffer.PopPendingCallbacks(kFrameToken2, false); EXPECT_TRUE(result.main_thread_callbacks.empty()); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); } @@ -133,19 +151,66 @@ TEST(PresentationTimeCallbackBufferTest, TestCallbackBatching) { // Register one callback for frame1, two for frame2 and two for frame4. buffer.RegisterMainThreadPresentationCallbacks(kFrameToken1, - GenerateCallbacks(1)); + GenerateMainCallbacks(1)); buffer.RegisterMainThreadPresentationCallbacks(kFrameToken2, - GenerateCallbacks(2)); + GenerateMainCallbacks(2)); buffer.RegisterMainThreadPresentationCallbacks(kFrameToken4, - GenerateCallbacks(2)); + GenerateMainCallbacks(2)); // Pop callbacks up to and including frame3. Should be three in total; one // from frame1 and two from frame2. { - auto result = buffer.PopPendingCallbacks(kFrameToken3); + auto result = buffer.PopPendingCallbacks(kFrameToken3, false); EXPECT_EQ(result.main_thread_callbacks.size(), 3ull); EXPECT_TRUE(result.compositor_thread_callbacks.empty()); } } +// Tests that popping callbacks for main thread only vs. for both main and +// compositor threads works properly. +TEST(PresentationTimeCallbackBufferTest, PopMainCallbacksOnly) { + PresentationTimeCallbackBuffer buffer; + + // Register callbacks for main and compositor threads of 3 frames. + buffer.RegisterMainThreadPresentationCallbacks(kFrameToken1, + GenerateMainCallbacks(1)); + buffer.RegisterCompositorPresentationCallbacks( + kFrameToken1, GenerateCompositorCallbacks(1)); + buffer.RegisterMainThreadPresentationCallbacks(kFrameToken2, + GenerateMainCallbacks(1)); + buffer.RegisterCompositorPresentationCallbacks( + kFrameToken2, GenerateCompositorCallbacks(1)); + buffer.RegisterMainThreadPresentationCallbacks(kFrameToken3, + GenerateMainCallbacks(1)); + buffer.RegisterCompositorPresentationCallbacks( + kFrameToken3, GenerateCompositorCallbacks(1)); + + // Pop only main thread callbacks up to and including frame1. The result + // should only contain 1 main thread callback of frame1 and no compositor + // thread callback. + { + auto result = buffer.PopPendingCallbacks(kFrameToken1, true); + EXPECT_EQ(result.main_thread_callbacks.size(), 1ull); + EXPECT_TRUE(result.compositor_thread_callbacks.empty()); + } + + // Pop only main thread callbacks up to and including frame2. The result + // should only contain 1 main thread callback of frame2 and no compositor + // thread callback. + { + auto result = buffer.PopPendingCallbacks(kFrameToken2, true); + EXPECT_EQ(result.main_thread_callbacks.size(), 1ull); + EXPECT_TRUE(result.compositor_thread_callbacks.empty()); + } + + // Pop both main and compositor thread callbacks up to and including frame3. + // The result should contain 1 main thread callback of frame3 and all 3 + // compositor thread callbacks of the 3 frames. + { + auto result = buffer.PopPendingCallbacks(kFrameToken3, false); + EXPECT_EQ(result.main_thread_callbacks.size(), 1ull); + EXPECT_EQ(result.compositor_thread_callbacks.size(), 3ull); + } +} + } // namespace cc diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc index 211e67ce70d..076797230cf 100644 --- a/chromium/cc/trees/property_tree.cc +++ b/chromium/cc/trees/property_tree.cc @@ -1495,7 +1495,8 @@ void ScrollTree::CollectScrollDeltas( CompositorCommitData* commit_data, ElementId inner_viewport_scroll_element_id, bool use_fractional_deltas, - const base::flat_set<ElementId>& snapped_elements) { + const base::flat_map<ElementId, TargetSnapAreaElementIds>& + snapped_elements) { DCHECK(!property_trees()->is_main_thread); TRACE_EVENT0("cc", "ScrollTree::CollectScrollDeltas"); for (auto map_entry : synced_scroll_offset_map_) { @@ -1505,13 +1506,8 @@ void ScrollTree::CollectScrollDeltas( ElementId id = map_entry.first; absl::optional<TargetSnapAreaElementIds> snap_target_ids; - if (snapped_elements.find(id) != snapped_elements.end()) { - ScrollNode* scroll_node = FindNodeFromElementId(id); - if (scroll_node && scroll_node->snap_container_data) { - snap_target_ids = scroll_node->snap_container_data.value() - .GetTargetSnapAreaElementIds(); - } - } + if (snapped_elements.contains(id)) + snap_target_ids = snapped_elements.at(id); // Snap targets are set at the end of scroll offset animations (i.e when the // animation state is updated to FINISHED). The state can be updated after @@ -1724,13 +1720,15 @@ void ScrollTree::SetScrollCallbacks(base::WeakPtr<ScrollCallbacks> callbacks) { callbacks_ = std::move(callbacks); } -void ScrollTree::NotifyDidScroll( +void ScrollTree::NotifyDidCompositorScroll( ElementId scroll_element_id, const gfx::ScrollOffset& scroll_offset, const absl::optional<TargetSnapAreaElementIds>& snap_target_ids) { DCHECK(property_trees()->is_main_thread); - if (callbacks_) - callbacks_->DidScroll(scroll_element_id, scroll_offset, snap_target_ids); + if (callbacks_) { + callbacks_->DidCompositorScroll(scroll_element_id, scroll_offset, + snap_target_ids); + } } void ScrollTree::NotifyDidChangeScrollbarsHidden(ElementId scroll_element_id, diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h index 13a73759d45..3ac68f86ee4 100644 --- a/chromium/cc/trees/property_tree.h +++ b/chromium/cc/trees/property_tree.h @@ -23,6 +23,7 @@ #include "cc/paint/filter_operations.h" #include "cc/trees/mutator_host.h" #include "cc/trees/sticky_position_constraint.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/transform.h" @@ -57,12 +58,7 @@ class CC_EXPORT PropertyTree { public: PropertyTree(); PropertyTree(const PropertyTree& other) = delete; - - // These C++ special member functions cannot be implicit inline because - // they are exported by CC_EXPORT. They will be instantiated in every - // compilation units that included this header, and compilation can fail - // because T may be incomplete. - virtual ~PropertyTree(); + ~PropertyTree(); PropertyTree<T>& operator=(const PropertyTree<T>&); // Property tree node starts from index 0. See equivalent constants in @@ -78,11 +74,11 @@ class CC_EXPORT PropertyTree { int Insert(const T& tree_node, int parent_id); T* Node(int i) { - DCHECK(i < static_cast<int>(nodes_.size())); + CHECK_LT(i, static_cast<int>(nodes_.size())); return i > kInvalidNodeId ? &nodes_[i] : nullptr; } const T* Node(int i) const { - DCHECK(i < static_cast<int>(nodes_.size())); + CHECK_LT(i, static_cast<int>(nodes_.size())); return i > kInvalidNodeId ? &nodes_[i] : nullptr; } @@ -95,7 +91,7 @@ class CC_EXPORT PropertyTree { void clear(); size_t size() const { return nodes_.size(); } - virtual void set_needs_update(bool needs_update) { + void set_needs_update(bool needs_update) { needs_update_ = needs_update; } bool needs_update() const { return needs_update_; } @@ -129,7 +125,7 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { // compilation units that included this header, and compilation can fail // because TransformCachedNodeData may be incomplete. TransformTree(const TransformTree&) = delete; - ~TransformTree() final; + ~TransformTree(); TransformTree& operator=(const TransformTree&); #if DCHECK_IS_ON() @@ -155,7 +151,7 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { void UpdateNodeOrAncestorsWillChangeTransform(TransformNode* node, TransformNode* parent_node); - void set_needs_update(bool needs_update) final; + void set_needs_update(bool needs_update); // We store the page scale factor on the transform tree so that it can be // easily be retrieved and updated in UpdatePageScale. @@ -288,7 +284,7 @@ class CC_EXPORT ClipTree final : public PropertyTree<ClipNode> { class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> { public: EffectTree(); - ~EffectTree() final; + ~EffectTree(); EffectTree& operator=(const EffectTree& from); @@ -385,9 +381,10 @@ class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> { class ScrollCallbacks { public: // Called after the composited scroll offset changed. - virtual void DidScroll(ElementId scroll_element_id, - const gfx::ScrollOffset&, - const absl::optional<TargetSnapAreaElementIds>&) = 0; + virtual void DidCompositorScroll( + ElementId scroll_element_id, + const gfx::ScrollOffset&, + const absl::optional<TargetSnapAreaElementIds>&) = 0; // Called after the hidden status of composited scrollbars changed. Note that // |scroll_element_id| is the element id of the scroll not of the scrollbars. virtual void DidChangeScrollbarsHidden(ElementId scroll_element_id, @@ -400,7 +397,7 @@ class ScrollCallbacks { class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { public: ScrollTree(); - ~ScrollTree() final; + ~ScrollTree(); ScrollTree& operator=(const ScrollTree& from); @@ -449,10 +446,12 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { // Collects deltas for scroll changes on the impl thread that need to be // reported to the main thread during the main frame. As such, should only be // called on the impl thread side PropertyTrees. - void CollectScrollDeltas(CompositorCommitData* commit_data, - ElementId inner_viewport_scroll_element_id, - bool use_fractional_deltas, - const base::flat_set<ElementId>& snapped_elements); + void CollectScrollDeltas( + CompositorCommitData* commit_data, + ElementId inner_viewport_scroll_element_id, + bool use_fractional_deltas, + const base::flat_map<ElementId, TargetSnapAreaElementIds>& + snapped_elements); // Applies deltas sent in the previous main frame onto the impl thread state. // Should only be called on the impl thread side PropertyTrees. @@ -502,7 +501,7 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> { void SetScrollCallbacks(base::WeakPtr<ScrollCallbacks> callbacks); - void NotifyDidScroll( + void NotifyDidCompositorScroll( ElementId scroll_element_id, const gfx::ScrollOffset& scroll_offset, const absl::optional<TargetSnapAreaElementIds>& snap_target_ids); diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc index 5bd4eeeae24..99083b30235 100644 --- a/chromium/cc/trees/property_tree_builder.cc +++ b/chromium/cc/trees/property_tree_builder.cc @@ -10,6 +10,7 @@ #include <memory> #include <set> #include <utility> +#include <vector> #include "base/auto_reset.h" #include "cc/base/math_util.h" @@ -26,6 +27,7 @@ #include "cc/trees/transform_node.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/vector2d_conversions.h" namespace cc { @@ -455,6 +457,23 @@ bool PropertyTreeBuilderContext::AddEffectNodeIfNeeded( node->opacity = layer->opacity(); node->blend_mode = layer->blend_mode(); node->subtree_capture_id = layer->subtree_capture_id(); + + // Layers marked with a valid |subtree_capture_id| represent a subsection + // of the tree that should be rendered and copied as a separate render pass. + // Using the layer bounds as the subtree size here allows us to crop out + // undesired sections of the render pass, such as the shadow added by the + // shadow layer. + // + // If it becomes desirable to capture a different sub-rectangle of the render + // pass, a new custom size (or potentially rect) can be plumbed through the + // layer to here. + if (node->subtree_capture_id.is_valid()) { + // Layer bounds are specified in layer space, which excludes device and + // page scale factors. While the page scale can be ignored for subtree + // capture purposes, the device scale must be accounted for here. + node->subtree_size = gfx::ScaleToFlooredSize( + layer->bounds(), layer_tree_host_->device_scale_factor()); + } node->cache_render_surface = layer->cache_render_surface(); node->has_copy_request = layer->HasCopyRequest(); node->filters = layer->filters(); diff --git a/chromium/cc/trees/property_tree_builder_unittest.cc b/chromium/cc/trees/property_tree_builder_unittest.cc index 2576916ed38..0b11f149d9f 100644 --- a/chromium/cc/trees/property_tree_builder_unittest.cc +++ b/chromium/cc/trees/property_tree_builder_unittest.cc @@ -356,8 +356,9 @@ TEST_F(PropertyTreeBuilderTest, VisibleRectWithClippingAndFilters) { gfx::Transform vertical_flip; vertical_flip.Scale(1, -1); - sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>( - SkMatrix(vertical_flip.matrix()), kLow_SkFilterQuality, nullptr); + sk_sp<PaintFilter> flip_filter = + sk_make_sp<MatrixPaintFilter>(SkMatrix(vertical_flip.matrix()), + PaintFlags::FilterQuality::kLow, nullptr); FilterOperations reflection_filter; reflection_filter.Append( FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>( @@ -416,8 +417,9 @@ TEST_F(PropertyTreeBuilderTest, VisibleRectWithScalingClippingAndFilters) { gfx::Transform vertical_flip; vertical_flip.Scale(1, -1); - sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>( - SkMatrix(vertical_flip.matrix()), kLow_SkFilterQuality, nullptr); + sk_sp<PaintFilter> flip_filter = + sk_make_sp<MatrixPaintFilter>(SkMatrix(vertical_flip.matrix()), + PaintFlags::FilterQuality::kLow, nullptr); FilterOperations reflection_filter; reflection_filter.Append( FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>( @@ -1827,5 +1829,32 @@ TEST_F(PropertyTreeBuilderTest, kRoundedCorner4Radius * kDeviceScale); } +TEST_F(PropertyTreeBuilderTest, SubtreeSize) { + constexpr viz::SubtreeCaptureId kCaptureId{42}; + + auto parent = Layer::Create(); + host()->SetRootLayer(parent); + auto child = Layer::Create(); + parent->AddChild(child); + child->SetSubtreeCaptureId(kCaptureId); + + // Layer has empty bounds. + Commit(1.1f); + EffectNode* node = GetEffectNode(child.get()); + EXPECT_EQ((gfx::Size{}), node->subtree_size); + EXPECT_EQ(kCaptureId, node->subtree_capture_id); + + // Layer has bounds, scaling is 1. + child->SetBounds(gfx::Size{1280, 720}); + Commit(1.0f); + node = GetEffectNode(child.get()); + EXPECT_EQ((gfx::Size{1280, 720}), node->subtree_size); + + // Layer has bounds, scaling is 2. + Commit(2.0f); + node = GetEffectNode(child.get()); + EXPECT_EQ((gfx::Size{2560, 1440}), node->subtree_size); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h index c927579ed7b..316fbd1a25e 100644 --- a/chromium/cc/trees/proxy.h +++ b/chromium/cc/trees/proxy.h @@ -14,8 +14,10 @@ #include "cc/cc_export.h" #include "cc/input/browser_controls_state.h" #include "cc/trees/paint_holding_commit_trigger.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/task_runner_provider.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" +#include "components/viz/common/surfaces/local_surface_id.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "url/gurl.h" @@ -48,6 +50,8 @@ class CC_EXPORT Proxy { virtual void SetNeedsCommit() = 0; virtual void SetNeedsRedraw(const gfx::Rect& damage_rect) = 0; virtual void SetNextCommitWaitsForActivation() = 0; + virtual void SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id) = 0; // Returns true if an animate or commit has been requested, and hasn't // completed yet. @@ -61,11 +65,14 @@ class CC_EXPORT Proxy { // but continues to update the document lifecycle in // LayerTreeHost::BeginMainFrameUpdate. If multiple calls are made when // deferal is active the first |timeout| continues to apply. - virtual void StartDeferringCommits(base::TimeDelta timeout) = 0; + virtual bool StartDeferringCommits(base::TimeDelta timeout, + PaintHoldingReason reason) = 0; // Immediately stop deferring commits. virtual void StopDeferringCommits(PaintHoldingCommitTrigger) = 0; + virtual bool IsDeferringCommits() const = 0; + virtual bool CommitRequested() const = 0; // Must be called before using the proxy. diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index 726d6ff1233..2c8babc0eb0 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -187,6 +187,12 @@ void ProxyImpl::SetNeedsCommitOnImpl() { SetNeedsCommitOnImplThread(); } +void ProxyImpl::SetTargetLocalSurfaceIdOnImpl( + const viz::LocalSurfaceId& target_local_surface_id) { + DCHECK(IsImplThread()); + host_impl_->SetTargetLocalSurfaceId(target_local_surface_id); +} + void ProxyImpl::BeginMainFrameAbortedOnImpl( CommitEarlyOutReason reason, base::TimeTicks main_thread_start_time, @@ -404,6 +410,7 @@ void ProxyImpl::RenewTreePriority() { DCHECK(IsImplThread()); bool scroll_type_considered_interaction = false; + bool prefer_new_content = false; bool non_scroll_interaction_in_progress = host_impl_->IsPinchGestureActive() || host_impl_->page_scale_animation_active(); @@ -430,8 +437,14 @@ void ProxyImpl::RenewTreePriority() { user_interaction_in_progress); } + if (host_impl_->CurrentScrollDidCheckerboardLargeArea() && + base::FeatureList::IsEnabled( + features::kPreferNewContentForCheckerboardedScrolls)) { + prefer_new_content = true; + } + // Schedule expiration if smoothness currently takes priority. - if (user_interaction_in_progress) + if (user_interaction_in_progress && !prefer_new_content) smoothness_priority_expiration_notifier_.Schedule(); // We use the same priority for both trees by default. diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h index 05eb84ed3eb..15f13db26b1 100644 --- a/chromium/cc/trees/proxy_impl.h +++ b/chromium/cc/trees/proxy_impl.h @@ -54,6 +54,8 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, void SetDeferBeginMainFrameOnImpl(bool defer_begin_main_frame) const; void SetNeedsRedrawOnImpl(const gfx::Rect& damage_rect); void SetNeedsCommitOnImpl(); + void SetTargetLocalSurfaceIdOnImpl( + const viz::LocalSurfaceId& target_local_surface_id); void BeginMainFrameAbortedOnImpl( CommitEarlyOutReason reason, base::TimeTicks main_thread_start_time, diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 6f1e341a730..735a814e1f5 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -22,6 +22,7 @@ #include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/mutator_host.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/proxy_impl.h" #include "cc/trees/render_frame_metadata_observer.h" #include "cc/trees/scoped_abort_remaining_swap_promises.h" @@ -41,8 +42,7 @@ ProxyMain::ProxyMain(LayerTreeHost* layer_tree_host, deferred_final_pipeline_stage_(NO_PIPELINE_STAGE), commit_waits_for_activation_(false), started_(false), - defer_main_frame_update_(false), - defer_commits_(false) { + defer_main_frame_update_(false) { TRACE_EVENT0("cc", "ProxyMain::ProxyMain"); DCHECK(task_runner_provider_); DCHECK(IsMainThread()); @@ -170,24 +170,10 @@ void ProxyMain::BeginMainFrame( final_pipeline_stage_ = max_requested_pipeline_stage_; max_requested_pipeline_stage_ = NO_PIPELINE_STAGE; - // When we don't need to produce a CompositorFrame, there's also no need to - // commit our updates. We still need to run layout and paint though, as it can - // have side effects on page loading behavior. - bool skip_commit = begin_main_frame_state->begin_frame_args.animate_only; - // If main frame updates and commits are deferred, skip the entire pipeline. - bool skip_full_pipeline = defer_main_frame_update_; - - // We may have previously skipped paint and commit. If we should still skip it - // now, and there was no intermediate request for a commit since the last - // BeginMainFrame, we can skip the full pipeline. - skip_full_pipeline |= - skip_commit && final_pipeline_stage_ == NO_PIPELINE_STAGE; - - if (skip_full_pipeline) { + if (defer_main_frame_update_) { TRACE_EVENT_INSTANT0("cc", "EarlyOut_DeferCommit", TRACE_EVENT_SCOPE_THREAD); - // In this case, since the commit is deferred to a later time, gathered // events metrics are not discarded so that they can be reported if the // commit happens in the future. @@ -215,9 +201,9 @@ void ProxyMain::BeginMainFrame( // Check now if we should stop deferring commits due to a timeout. We // may also stop deferring in layer_tree_host_->BeginMainFrame, but update // the status at this point to keep scroll in sync. - if (defer_commits_ && base::TimeTicks::Now() > commits_restart_time_) - StopDeferringCommits(PaintHoldingCommitTrigger::kTimeout); - skip_commit |= defer_commits_; + if (IsDeferringCommits() && base::TimeTicks::Now() > commits_restart_time_) + StopDeferringCommits(ReasonToTimeoutTrigger(*paint_holding_reason_)); + bool skip_commit = IsDeferringCommits(); if (!skip_commit) { // Synchronizes scroll offsets and page scale deltas (for pinch zoom) from @@ -264,7 +250,12 @@ void ProxyMain::BeginMainFrame( // avoid committing right now, or we may be deferring commits but not // deferring main frame updates. Either may have changed the status // of the defer... flags, so re-evaluate skip_commit. - skip_commit |= defer_main_frame_update_ || defer_commits_; + skip_commit |= defer_main_frame_update_ || IsDeferringCommits(); + + // When we don't need to produce a CompositorFrame, there's also no need to + // commit our updates. We still need to run layout and paint though, as it can + // have side effects on page loading behavior. + skip_commit |= begin_main_frame_state->begin_frame_args.animate_only; if (skip_commit) { current_pipeline_stage_ = NO_PIPELINE_STAGE; @@ -317,7 +308,8 @@ void ProxyMain::BeginMainFrame( layer_tree_host_->WillCommit(); devtools_instrumentation::ScopedCommitTrace commit_task( - layer_tree_host_->GetId()); + layer_tree_host_->GetId(), + begin_main_frame_state->begin_frame_args.frame_id.sequence_number); current_pipeline_stage_ = COMMIT_PIPELINE_STAGE; if (final_pipeline_stage_ < COMMIT_PIPELINE_STAGE) { @@ -387,7 +379,7 @@ void ProxyMain::BeginMainFrame( void ProxyMain::DidPresentCompositorFrame( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + std::vector<PresentationTimeCallbackBuffer::MainCallback> callbacks, const gfx::PresentationFeedback& feedback) { layer_tree_host_->DidPresentCompositorFrame(frame_token, std::move(callbacks), feedback); @@ -477,6 +469,15 @@ void ProxyMain::SetNextCommitWaitsForActivation() { commit_waits_for_activation_ = true; } +void ProxyMain::SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id) { + DCHECK(IsMainThread()); + ImplThreadTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(&ProxyImpl::SetTargetLocalSurfaceIdOnImpl, + base::Unretained(proxy_impl_.get()), + target_local_surface_id)); +} + bool ProxyMain::RequestedAnimatePending() { return max_requested_pipeline_stage_ >= ANIMATE_PIPELINE_STAGE; } @@ -505,35 +506,43 @@ void ProxyMain::SetDeferMainFrameUpdate(bool defer_main_frame_update) { defer_main_frame_update)); } -void ProxyMain::StartDeferringCommits(base::TimeDelta timeout) { +bool ProxyMain::StartDeferringCommits(base::TimeDelta timeout, + PaintHoldingReason reason) { DCHECK(task_runner_provider_->IsMainThread()); // Do nothing if already deferring. The timeout remains as it was from when // we most recently began deferring. - if (defer_commits_) - return; + if (IsDeferringCommits()) + return false; TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("cc", "ProxyMain::SetDeferCommits", TRACE_ID_LOCAL(this)); - defer_commits_ = true; + paint_holding_reason_ = reason; commits_restart_time_ = base::TimeTicks::Now() + timeout; // Notify dependent systems that the deferral status has changed. - layer_tree_host_->OnDeferCommitsChanged(defer_commits_); + layer_tree_host_->OnDeferCommitsChanged(true, reason); + return true; } void ProxyMain::StopDeferringCommits(PaintHoldingCommitTrigger trigger) { - if (!defer_commits_) + if (!IsDeferringCommits()) return; - defer_commits_ = false; + auto reason = *paint_holding_reason_; + paint_holding_reason_.reset(); UMA_HISTOGRAM_ENUMERATION("PaintHolding.CommitTrigger2", trigger); commits_restart_time_ = base::TimeTicks(); TRACE_EVENT_NESTABLE_ASYNC_END0("cc", "ProxyMain::SetDeferCommits", TRACE_ID_LOCAL(this)); // Notify depended systems that the deferral status has changed. - layer_tree_host_->OnDeferCommitsChanged(defer_commits_); + layer_tree_host_->OnDeferCommitsChanged(false, reason); +} + +bool ProxyMain::IsDeferringCommits() const { + DCHECK(IsMainThread()); + return paint_holding_reason_.has_value(); } bool ProxyMain::CommitRequested() const { diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h index 186de9d07b6..1157b1adcfb 100644 --- a/chromium/cc/trees/proxy_main.h +++ b/chromium/cc/trees/proxy_main.h @@ -11,6 +11,7 @@ #include "cc/cc_export.h" #include "cc/input/browser_controls_state.h" #include "cc/trees/layer_tree_host.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/proxy.h" #include "cc/trees/proxy_common.h" @@ -58,7 +59,7 @@ class CC_EXPORT ProxyMain : public Proxy { std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state); void DidPresentCompositorFrame( uint32_t frame_token, - std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, + std::vector<PresentationTimeCallbackBuffer::MainCallback> callbacks, const gfx::PresentationFeedback& feedback); void NotifyThroughputTrackerResults(CustomTrackerResults results); void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay, @@ -85,10 +86,14 @@ class CC_EXPORT ProxyMain : public Proxy { void SetNeedsCommit() override; void SetNeedsRedraw(const gfx::Rect& damage_rect) override; void SetNextCommitWaitsForActivation() override; + void SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id) override; bool RequestedAnimatePending() override; void SetDeferMainFrameUpdate(bool defer_main_frame_update) override; - void StartDeferringCommits(base::TimeDelta timeout) override; + bool StartDeferringCommits(base::TimeDelta timeout, + PaintHoldingReason reason) override; void StopDeferringCommits(PaintHoldingCommitTrigger) override; + bool IsDeferringCommits() const override; bool CommitRequested() const override; void Start() override; void Stop() override; @@ -146,9 +151,9 @@ class CC_EXPORT ProxyMain : public Proxy { bool started_; // defer_main_frame_update_ will also cause commits to be deferred, regardless - // of the setting for defer_commits_. + // of the setting for paint_holding_reason_. bool defer_main_frame_update_; - bool defer_commits_; + absl::optional<PaintHoldingReason> paint_holding_reason_; // Only used when defer_commits_ is active and must be set in such cases. base::TimeTicks commits_restart_time_; diff --git a/chromium/cc/trees/scroll_node.h b/chromium/cc/trees/scroll_node.h index b178e47f83d..33c764c370a 100644 --- a/chromium/cc/trees/scroll_node.h +++ b/chromium/cc/trees/scroll_node.h @@ -11,6 +11,7 @@ #include "cc/input/scroll_snap_data.h" #include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/geometry/size.h" namespace base { diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index a24ce56473e..3dfb23d7532 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -27,6 +27,7 @@ #include "cc/trees/layer_tree_host_single_thread_client.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/mutator_host.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/render_frame_metadata_observer.h" #include "cc/trees/scoped_abort_remaining_swap_promises.h" #include "components/power_scheduler/power_mode_arbiter.h" @@ -56,7 +57,6 @@ SingleThreadProxy::SingleThreadProxy(LayerTreeHost* layer_tree_host, #endif inside_draw_(false), defer_main_frame_update_(false), - defer_commits_(false), animate_requested_(false), update_layers_requested_(false), commit_requested_(false), @@ -194,7 +194,7 @@ void SingleThreadProxy::DoCommit(const viz::BeginFrameArgs& commit_args) { layer_tree_host_->WillCommit(); devtools_instrumentation::ScopedCommitTrace commit_task( - layer_tree_host_->GetId()); + layer_tree_host_->GetId(), commit_args.frame_id.sequence_number); // Commit immediately. { @@ -271,6 +271,13 @@ void SingleThreadProxy::SetNextCommitWaitsForActivation() { DCHECK(task_runner_provider_->IsMainThread()); } +void SingleThreadProxy::SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id) { + if (!scheduler_on_impl_thread_) + return; + host_impl_->SetTargetLocalSurfaceId(target_local_surface_id); +} + bool SingleThreadProxy::RequestedAnimatePending() { return animate_requested_ || update_layers_requested_ || commit_requested_ || needs_impl_frame_; @@ -303,36 +310,44 @@ void SingleThreadProxy::SetDeferMainFrameUpdate(bool defer_main_frame_update) { scheduler_on_impl_thread_->SetDeferBeginMainFrame(defer_main_frame_update_); } -void SingleThreadProxy::StartDeferringCommits(base::TimeDelta timeout) { +bool SingleThreadProxy::StartDeferringCommits(base::TimeDelta timeout, + PaintHoldingReason reason) { DCHECK(task_runner_provider_->IsMainThread()); // Do nothing if already deferring. The timeout remains as it was from when // we most recently began deferring. - if (defer_commits_) - return; + if (IsDeferringCommits()) + return false; TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("cc", "SingleThreadProxy::SetDeferCommits", TRACE_ID_LOCAL(this)); - defer_commits_ = true; + paint_holding_reason_ = reason; commits_restart_time_ = base::TimeTicks::Now() + timeout; // Notify dependent systems that the deferral status has changed. - layer_tree_host_->OnDeferCommitsChanged(defer_commits_); + layer_tree_host_->OnDeferCommitsChanged(true, reason); + return true; } void SingleThreadProxy::StopDeferringCommits( PaintHoldingCommitTrigger trigger) { - if (!defer_commits_) + if (!IsDeferringCommits()) return; - defer_commits_ = false; + auto reason = *paint_holding_reason_; + paint_holding_reason_.reset(); commits_restart_time_ = base::TimeTicks(); UMA_HISTOGRAM_ENUMERATION("PaintHolding.CommitTrigger2", trigger); TRACE_EVENT_NESTABLE_ASYNC_END0("cc", "SingleThreadProxy::SetDeferCommits", TRACE_ID_LOCAL(this)); // Notify dependent systems that the deferral status has changed. - layer_tree_host_->OnDeferCommitsChanged(defer_commits_); + layer_tree_host_->OnDeferCommitsChanged(false, reason); +} + +bool SingleThreadProxy::IsDeferringCommits() const { + DCHECK(task_runner_provider_->IsMainThread()); + return paint_holding_reason_.has_value(); } bool SingleThreadProxy::CommitRequested() const { @@ -533,15 +548,13 @@ void SingleThreadProxy::DidPresentCompositorFrameOnImplThread( uint32_t frame_token, PresentationTimeCallbackBuffer::PendingCallbacks callbacks, const viz::FrameTimingDetails& details) { - std::vector<LayerTreeHost::PresentationTimeCallback> main_thread_callbacks = - std::move(callbacks.main_thread_callbacks); DebugScopedSetImplThread impl(task_runner_provider_); host_impl_->NotifyDidPresentCompositorFrameOnImplThread( frame_token, std::move(callbacks.compositor_thread_callbacks), details); { DebugScopedSetMainThread main(task_runner_provider_); layer_tree_host_->DidPresentCompositorFrame( - frame_token, std::move(main_thread_callbacks), + frame_token, std::move(callbacks.main_thread_callbacks), details.presentation_feedback); } if (scheduler_on_impl_thread_) { @@ -852,8 +865,8 @@ void SingleThreadProxy::BeginMainFrame( // Check now if we should stop deferring commits. Do this before // DoBeginMainFrame because the latter updates scroll offsets, which // we should avoid if deferring commits. - if (defer_commits_ && base::TimeTicks::Now() > commits_restart_time_) - StopDeferringCommits(PaintHoldingCommitTrigger::kTimeout); + if (IsDeferringCommits() && base::TimeTicks::Now() > commits_restart_time_) + StopDeferringCommits(ReasonToTimeoutTrigger(*paint_holding_reason_)); DoBeginMainFrame(begin_frame_args); @@ -862,7 +875,7 @@ void SingleThreadProxy::BeginMainFrame( // At this point the main frame may have deferred commits to avoid committing // right now. - if (defer_main_frame_update_ || defer_commits_ || + if (defer_main_frame_update_ || IsDeferringCommits() || begin_frame_args.animate_only) { TRACE_EVENT_INSTANT0("cc", "EarlyOut_DeferCommit_InsideBeginMainFrame", TRACE_EVENT_SCOPE_THREAD); @@ -879,7 +892,7 @@ void SingleThreadProxy::DoBeginMainFrame( const viz::BeginFrameArgs& begin_frame_args) { // Only update scroll deltas if we are going to commit the frame, otherwise // scroll offsets get confused. - if (!defer_commits_) { + if (!IsDeferringCommits()) { // The impl-side scroll deltas may be manipulated directly via the // InputHandler on the UI thread and the scale deltas may change when they // are clamped on the impl thread. diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h index c5d23a90a38..9d3c9f1864b 100644 --- a/chromium/cc/trees/single_thread_proxy.h +++ b/chromium/cc/trees/single_thread_proxy.h @@ -14,9 +14,11 @@ #include "base/time/time.h" #include "cc/scheduler/scheduler.h" #include "cc/trees/layer_tree_host_impl.h" +#include "cc/trees/paint_holding_reason.h" #include "cc/trees/proxy.h" #include "cc/trees/task_runner_provider.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/common/surfaces/local_surface_id.h" namespace viz { class BeginFrameSource; @@ -53,10 +55,14 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void SetNeedsCommit() override; void SetNeedsRedraw(const gfx::Rect& damage_rect) override; void SetNextCommitWaitsForActivation() override; + void SetTargetLocalSurfaceId( + const viz::LocalSurfaceId& target_local_surface_id) override; bool RequestedAnimatePending() override; void SetDeferMainFrameUpdate(bool defer_main_frame_update) override; - void StartDeferringCommits(base::TimeDelta timeout) override; + bool StartDeferringCommits(base::TimeDelta timeout, + PaintHoldingReason reason) override; void StopDeferringCommits(PaintHoldingCommitTrigger) override; + bool IsDeferringCommits() const override; bool CommitRequested() const override; void Start() override; void Stop() override; @@ -204,7 +210,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, #endif bool inside_draw_; bool defer_main_frame_update_; - bool defer_commits_; + absl::optional<PaintHoldingReason> paint_holding_reason_; bool animate_requested_; bool update_layers_requested_; bool commit_requested_; diff --git a/chromium/cc/trees/sticky_position_constraint.cc b/chromium/cc/trees/sticky_position_constraint.cc index 19f4cabe0fe..bc163937dae 100644 --- a/chromium/cc/trees/sticky_position_constraint.cc +++ b/chromium/cc/trees/sticky_position_constraint.cc @@ -19,6 +19,9 @@ StickyPositionConstraint::StickyPositionConstraint() StickyPositionConstraint::StickyPositionConstraint( const StickyPositionConstraint& other) = default; +StickyPositionConstraint& StickyPositionConstraint::operator=( + const StickyPositionConstraint& other) = default; + bool StickyPositionConstraint::operator==( const StickyPositionConstraint& other) const { return is_anchored_left == other.is_anchored_left && diff --git a/chromium/cc/trees/sticky_position_constraint.h b/chromium/cc/trees/sticky_position_constraint.h index 3a6ac2408dc..94b016753b8 100644 --- a/chromium/cc/trees/sticky_position_constraint.h +++ b/chromium/cc/trees/sticky_position_constraint.h @@ -17,6 +17,7 @@ namespace cc { struct CC_EXPORT StickyPositionConstraint { StickyPositionConstraint(); StickyPositionConstraint(const StickyPositionConstraint& other); + StickyPositionConstraint& operator=(const StickyPositionConstraint& other); bool is_anchored_left : 1; bool is_anchored_right : 1; diff --git a/chromium/cc/trees/throttle_decider.cc b/chromium/cc/trees/throttle_decider.cc index 3ce286bc80b..f2f31cfa327 100644 --- a/chromium/cc/trees/throttle_decider.cc +++ b/chromium/cc/trees/throttle_decider.cc @@ -6,6 +6,7 @@ #include <vector> +#include "cc/layers/surface_layer_impl.h" #include "components/viz/common/quads/compositor_render_pass_draw_quad.h" #include "components/viz/common/quads/surface_draw_quad.h" #include "components/viz/common/surfaces/surface_range.h" @@ -86,6 +87,14 @@ void ThrottleDecider::ProcessRenderPass( id_to_pass_map_.emplace(render_pass.id, &render_pass); } +void ThrottleDecider::ProcessLayerNotToDraw(const LayerImpl* layer) { + if (layer->is_surface_layer()) { + const auto* surface_layer = static_cast<const SurfaceLayerImpl*>(layer); + if (surface_layer->range().IsValid()) + ids_.insert(surface_layer->range().end().frame_sink_id()); + } +} + bool ThrottleDecider::HasThrottlingChanged() const { return ids_ != last_ids_; } diff --git a/chromium/cc/trees/throttle_decider.h b/chromium/cc/trees/throttle_decider.h index d41479a423c..eb1c3418f26 100644 --- a/chromium/cc/trees/throttle_decider.h +++ b/chromium/cc/trees/throttle_decider.h @@ -12,6 +12,7 @@ #include "components/viz/common/surfaces/frame_sink_id.h" namespace cc { +class LayerImpl; // This class is used to decide if any frame sinks in a render pass list // satisfies the compositing-based criteria to be throttled. @@ -30,6 +31,9 @@ class CC_EXPORT ThrottleDecider { // intersection calculation of surface/quad rects are confined to the render // pass's constituent quads. void ProcessRenderPass(const viz::CompositorRenderPass& render_pass); + // Process a layer that will not draw. This is only relevant for surface + // layers and checks if the embedded frame sink is qualified for throttling. + void ProcessLayerNotToDraw(const LayerImpl* layer); bool HasThrottlingChanged() const; const base::flat_set<viz::FrameSinkId>& ids() const { return ids_; } diff --git a/chromium/cc/trees/transform_node.cc b/chromium/cc/trees/transform_node.cc index 1c91fcae2fc..765b6fbf836 100644 --- a/chromium/cc/trees/transform_node.cc +++ b/chromium/cc/trees/transform_node.cc @@ -39,6 +39,8 @@ TransformNode::TransformNode() TransformNode::TransformNode(const TransformNode&) = default; +TransformNode& TransformNode::operator=(const TransformNode&) = default; + #if DCHECK_IS_ON() bool TransformNode::operator==(const TransformNode& other) const { return id == other.id && parent_id == other.parent_id && diff --git a/chromium/cc/trees/transform_node.h b/chromium/cc/trees/transform_node.h index 71d8e98f459..2e62467e4e5 100644 --- a/chromium/cc/trees/transform_node.h +++ b/chromium/cc/trees/transform_node.h @@ -23,6 +23,7 @@ namespace cc { struct CC_EXPORT TransformNode { TransformNode(); TransformNode(const TransformNode&); + TransformNode& operator=(const TransformNode&); // The node index of this node in the transform tree node vector. int id; diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc index 7a1d5b0def0..6b89b13c49f 100644 --- a/chromium/cc/trees/tree_synchronizer_unittest.cc +++ b/chromium/cc/trees/tree_synchronizer_unittest.cc @@ -662,9 +662,9 @@ TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) { // Pull ScrollOffset delta for main thread, and change offset on main thread std::unique_ptr<CompositorCommitData> commit_data(new CompositorCommitData()); - scroll_tree.CollectScrollDeltas(commit_data.get(), ElementId(), - settings.commit_fractional_scroll_deltas, - base::flat_set<ElementId>()); + scroll_tree.CollectScrollDeltas( + commit_data.get(), ElementId(), settings.commit_fractional_scroll_deltas, + base::flat_map<ElementId, TargetSnapAreaElementIds>()); host_->proxy()->SetNeedsCommit(); host_->ApplyCompositorChanges(commit_data.get()); EXPECT_EQ(gfx::ScrollOffset(20, 30), scroll_layer->scroll_offset()); diff --git a/chromium/cc/trees/ukm_manager_unittest.cc b/chromium/cc/trees/ukm_manager_unittest.cc index 4e36a6e2fd3..738de678558 100644 --- a/chromium/cc/trees/ukm_manager_unittest.cc +++ b/chromium/cc/trees/ukm_manager_unittest.cc @@ -142,13 +142,11 @@ class UkmManagerTest : public testing::Test { std::unique_ptr<EventMetrics> CreateEventMetrics( ui::EventType type, - absl::optional<EventMetrics::ScrollUpdateType> scroll_update_type, - absl::optional<ui::ScrollInputType> scroll_input_type) { + absl::optional<EventMetrics::ScrollParams> scroll_params) { base::TimeTicks event_time = AdvanceNowByMs(10); AdvanceNowByMs(10); std::unique_ptr<EventMetrics> metrics = EventMetrics::CreateForTesting( - type, scroll_update_type, scroll_input_type, event_time, - &test_tick_clock_); + type, scroll_params, event_time, &test_tick_clock_); if (metrics) { AdvanceNowByMs(10); metrics->SetDispatchStageTimestamp( @@ -523,15 +521,24 @@ TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) { } TEST_F(UkmManagerTest, EventLatency) { + const bool kIsInertial = true; + const bool kIsNotInertial = false; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { - CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, absl::nullopt, - ui::ScrollInputType::kWheel), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_BEGIN, + EventMetrics::ScrollParams(ui::ScrollInputType::kWheel, + kIsNotInertial)), CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kStarted, - ui::ScrollInputType::kWheel), + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsNotInertial, + EventMetrics::ScrollUpdateType::kStarted)), CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, - EventMetrics::ScrollUpdateType::kContinued, - ui::ScrollInputType::kWheel), + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsNotInertial, + EventMetrics::ScrollUpdateType::kContinued)), + CreateEventMetrics(ui::ET_GESTURE_SCROLL_UPDATE, + EventMetrics::ScrollParams( + ui::ScrollInputType::kWheel, kIsInertial, + EventMetrics::ScrollUpdateType::kContinued)), }; EXPECT_THAT(event_metrics_ptrs, ::testing::Each(::testing::NotNull())); EventMetrics::List events_metrics( @@ -609,7 +616,7 @@ TEST_F(UkmManagerTest, EventLatency) { processed_viz_breakdown); const auto& entries = test_ukm_recorder_->GetEntriesByName(kEventLatency); - EXPECT_EQ(3u, entries.size()); + EXPECT_EQ(4u, entries.size()); for (size_t i = 0; i < entries.size(); i++) { const auto* entry = entries[i]; const auto* event_metrics = events_metrics[i].get(); |