diff options
author | Andras Becsi <andras.becsi@digia.com> | 2013-12-11 21:33:03 +0100 |
---|---|---|
committer | Andras Becsi <andras.becsi@digia.com> | 2013-12-13 12:34:07 +0100 |
commit | f2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch) | |
tree | 0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/cc | |
parent | 5362912cdb5eea702b68ebe23702468d17c3017a (diff) | |
download | qtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz |
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f
Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/cc')
280 files changed, 22157 insertions, 8008 deletions
diff --git a/chromium/cc/DEPS b/chromium/cc/DEPS index ad58ebab32d..91ebb2067c9 100644 --- a/chromium/cc/DEPS +++ b/chromium/cc/DEPS @@ -6,11 +6,10 @@ include_rules = [ "+third_party/skia/include", "+third_party/khronos/GLES2/gl2.h", "+third_party/khronos/GLES2/gl2ext.h", - "+ui/base", + "+ui/events/latency_info.h", "+ui/gfx", "+ui/gl", # DO NOT ADD ANY NEW WEBKIT HEADERS TO THIS LIST. # TODO(danakj): Drop dependencies on WebKit Platform API from cc. "+third_party/WebKit/public/platform/WebGraphicsContext3D.h", - "+third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h", ] diff --git a/chromium/cc/OWNERS b/chromium/cc/OWNERS index e90c2f30dd9..57a0e846d3e 100644 --- a/chromium/cc/OWNERS +++ b/chromium/cc/OWNERS @@ -17,7 +17,7 @@ danakj@chromium.org piman@chromium.org danakj@chromium.org -# resource management +# resource management / mac-specific # unofficial: epenner@chromium.org ccameron@chromium.org @@ -36,9 +36,11 @@ vmpstr@chromium.org # math / geometry / layer_tree_host_common shawnsingh@chromium.org enne@chromium.org +vollick@chromium.org # animation vollick@chromium.org +ajuma@chromium.org per-file *.isolate=csharp@chromium.org per-file *.isolate=maruel@chromium.org diff --git a/chromium/cc/PRESUBMIT.py b/chromium/cc/PRESUBMIT.py index bcf71ef0d52..9e84e3f6609 100644 --- a/chromium/cc/PRESUBMIT.py +++ b/chromium/cc/PRESUBMIT.py @@ -74,11 +74,17 @@ def CheckStdAbs(input_api, output_api, using_std_abs_files.append(f.LocalPath()) if re.search(r"\bfabsf?\(", contents): found_fabs_files.append(f.LocalPath()); - # The following regular expression in words says: - # "if there is no 'std::' behind an 'abs(' or 'absf(', - # or if there is no 'std::' behind a 'fabs(' or 'fabsf(', - # then it's a match." - if re.search(r"((?<!std::)(\babsf?\()|(?<!std::)(\bfabsf?\())", contents): + + no_std_prefix = r"(?<!std::)" + # Matches occurrences of abs/absf/fabs/fabsf without a "std::" prefix. + abs_without_prefix = r"%s(\babsf?\()" % no_std_prefix + fabs_without_prefix = r"%s(\bfabsf?\()" % no_std_prefix + # Skips matching any lines that have "// NOLINT". + no_nolint = r"(?![^\n]*//\s+NOLINT)" + + expression = re.compile("(%s|%s)%s" % + (abs_without_prefix, fabs_without_prefix, no_nolint)) + if expression.search(contents): missing_std_prefix_files.append(f.LocalPath()) result = [] diff --git a/chromium/cc/animation/animation.cc b/chromium/cc/animation/animation.cc index 214b8bfc47f..b509cfd994f 100644 --- a/chromium/cc/animation/animation.cc +++ b/chromium/cc/animation/animation.cc @@ -167,6 +167,12 @@ double Animation::TrimTimeToCurrentIteration(double monotonic_time) const { // subtract all time spent paused. trimmed -= start_time_ + total_paused_time_; + // If we're just starting or we're waiting on receiving a start time, + // time is 'stuck' at the initial state. + if ((run_state_ == Starting && !has_set_start_time()) || + needs_synchronized_start_time()) + trimmed = time_offset_; + // Zero is always the start of the animation. if (trimmed <= 0) return 0; diff --git a/chromium/cc/animation/animation_curve.h b/chromium/cc/animation/animation_curve.h index 3860234ab13..f4cdb10560c 100644 --- a/chromium/cc/animation/animation_curve.h +++ b/chromium/cc/animation/animation_curve.h @@ -10,6 +10,10 @@ #include "cc/output/filter_operations.h" #include "ui/gfx/transform.h" +namespace gfx { +class BoxF; +} + namespace cc { class FilterAnimationCurve; @@ -49,6 +53,12 @@ class CC_EXPORT TransformAnimationCurve : public AnimationCurve { virtual gfx::Transform GetValue(double t) const = 0; + // Sets |bounds| to be the bounding box for the region within which |box| + // will move during this animation. If this region cannot be computed, + // returns false. + virtual bool AnimatedBoundsForBox(const gfx::BoxF& box, + gfx::BoxF* bounds) const = 0; + // Partial Animation implementation. virtual CurveType Type() const OVERRIDE; }; diff --git a/chromium/cc/animation/animation_unittest.cc b/chromium/cc/animation/animation_unittest.cc index 337d3eedb35..23d41697679 100644 --- a/chromium/cc/animation/animation_unittest.cc +++ b/chromium/cc/animation/animation_unittest.cc @@ -110,6 +110,41 @@ TEST(AnimationTest, TrimTimeZeroDuration) { EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(1.0)); } +TEST(AnimationTest, TrimTimeStarting) { + scoped_ptr<Animation> anim(CreateAnimation(1, 5.0)); + anim->SetRunState(Animation::Starting, 0.0); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(-1.0)); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(0.0)); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(1.0)); + anim->set_time_offset(2.0); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(-1.0)); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(0.0)); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(1.0)); + anim->set_start_time(1.0); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(-1.0)); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(0.0)); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(1.0)); + EXPECT_EQ(3.0, anim->TrimTimeToCurrentIteration(2.0)); +} + +TEST(AnimationTest, TrimTimeNeedsSynchronizedStartTime) { + scoped_ptr<Animation> anim(CreateAnimation(1, 5.0)); + anim->SetRunState(Animation::Running, 0.0); + anim->set_needs_synchronized_start_time(true); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(-1.0)); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(0.0)); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(1.0)); + anim->set_time_offset(2.0); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(-1.0)); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(0.0)); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(1.0)); + anim->set_start_time(1.0); + anim->set_needs_synchronized_start_time(false); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(0.0)); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(1.0)); + EXPECT_EQ(3.0, anim->TrimTimeToCurrentIteration(2.0)); +} + TEST(AnimationTest, IsFinishedAtZeroIterations) { scoped_ptr<Animation> anim(CreateAnimation(0)); anim->SetRunState(Animation::Running, 0.0); diff --git a/chromium/cc/animation/keyframed_animation_curve.cc b/chromium/cc/animation/keyframed_animation_curve.cc index c016c91335b..14dfd9c8446 100644 --- a/chromium/cc/animation/keyframed_animation_curve.cc +++ b/chromium/cc/animation/keyframed_animation_curve.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "cc/animation/keyframed_animation_curve.h" +#include "ui/gfx/box_f.h" namespace cc { @@ -231,6 +232,28 @@ gfx::Transform KeyframedTransformAnimationCurve::GetValue(double t) const { return GetCurveValue<gfx::Transform, TransformKeyframe>(&keyframes_, t); } +bool KeyframedTransformAnimationCurve::AnimatedBoundsForBox( + const gfx::BoxF& box, + gfx::BoxF* bounds) const { + DCHECK_GE(keyframes_.size(), 2ul); + *bounds = gfx::BoxF(); + for (size_t i = 0; i < keyframes_.size() - 1; ++i) { + gfx::BoxF bounds_for_step; + float min_progress = 0.0; + float max_progress = 1.0; + if (keyframes_[i]->timing_function()) + keyframes_[i]->timing_function()->Range(&min_progress, &max_progress); + if (!keyframes_[i+1]->Value().BlendedBoundsForBox(box, + keyframes_[i]->Value(), + min_progress, + max_progress, + &bounds_for_step)) + return false; + bounds->Union(bounds_for_step); + } + return true; +} + scoped_ptr<KeyframedFilterAnimationCurve> KeyframedFilterAnimationCurve:: Create() { return make_scoped_ptr(new KeyframedFilterAnimationCurve); diff --git a/chromium/cc/animation/keyframed_animation_curve.h b/chromium/cc/animation/keyframed_animation_curve.h index 9dda773caec..5892dc70e58 100644 --- a/chromium/cc/animation/keyframed_animation_curve.h +++ b/chromium/cc/animation/keyframed_animation_curve.h @@ -135,6 +135,8 @@ class CC_EXPORT KeyframedTransformAnimationCurve // TransformAnimationCurve implementation virtual gfx::Transform GetValue(double t) const OVERRIDE; + virtual bool AnimatedBoundsForBox(const gfx::BoxF& box, + gfx::BoxF* bounds) const OVERRIDE; private: KeyframedTransformAnimationCurve(); diff --git a/chromium/cc/animation/keyframed_animation_curve_unittest.cc b/chromium/cc/animation/keyframed_animation_curve_unittest.cc index 32efc147636..48f511c0317 100644 --- a/chromium/cc/animation/keyframed_animation_curve_unittest.cc +++ b/chromium/cc/animation/keyframed_animation_curve_unittest.cc @@ -7,6 +7,7 @@ #include "cc/animation/transform_operations.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/box_f.h" namespace cc { namespace { @@ -332,5 +333,29 @@ TEST(KeyframedAnimationCurveTest, CubicBezierTimingFunction) { EXPECT_FLOAT_EQ(1.f, curve->GetValue(1.f)); } +// Tests that animated bounds are computed as expected. +TEST(KeyframedAnimationCurveTest, AnimatedBounds) { + scoped_ptr<KeyframedTransformAnimationCurve> curve( + KeyframedTransformAnimationCurve::Create()); + + TransformOperations operations1; + curve->AddKeyframe(TransformKeyframe::Create( + 0.0, operations1, scoped_ptr<TimingFunction>())); + operations1.AppendTranslate(2.0, 3.0, -1.0); + curve->AddKeyframe(TransformKeyframe::Create( + 0.5, operations1, scoped_ptr<TimingFunction>())); + TransformOperations operations2; + operations2.AppendTranslate(4.0, 1.0, 2.0); + curve->AddKeyframe(TransformKeyframe::Create( + 1.0, operations2, EaseTimingFunction::Create())); + + gfx::BoxF box(2.f, 3.f, 4.f, 1.f, 3.f, 2.f); + gfx::BoxF bounds; + + EXPECT_TRUE(curve->AnimatedBoundsForBox(box, &bounds)); + EXPECT_EQ(gfx::BoxF(2.f, 3.f, 3.f, 5.f, 6.f, 5.f).ToString(), + bounds.ToString()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/layer_animation_controller.cc b/chromium/cc/animation/layer_animation_controller.cc index 5823b7919d2..04920acc6ef 100644 --- a/chromium/cc/animation/layer_animation_controller.cc +++ b/chromium/cc/animation/layer_animation_controller.cc @@ -12,6 +12,7 @@ #include "cc/animation/keyframed_animation_curve.h" #include "cc/animation/layer_animation_value_observer.h" #include "cc/base/scoped_ptr_algorithm.h" +#include "ui/gfx/box_f.h" #include "ui/gfx/transform.h" namespace cc { @@ -343,6 +344,31 @@ void LayerAnimationController::RemoveEventObserver( event_observers_.RemoveObserver(observer); } +bool LayerAnimationController::AnimatedBoundsForBox(const gfx::BoxF& box, + gfx::BoxF* bounds) { + // Compute bounds based on animations for which is_finished() is false. + // Do nothing if there are no such animations; in this case, it is assumed + // that callers will take care of computing bounds based on the owning layer's + // actual transform. + *bounds = gfx::BoxF(); + for (size_t i = 0; i < active_animations_.size(); ++i) { + if (active_animations_[i]->is_finished() || + active_animations_[i]->target_property() != Animation::Transform) + continue; + + const TransformAnimationCurve* transform_animation_curve = + active_animations_[i]->curve()->ToTransformAnimationCurve(); + gfx::BoxF animation_bounds; + bool success = + transform_animation_curve->AnimatedBoundsForBox(box, &animation_bounds); + if (!success) + return false; + bounds->Union(animation_bounds); + } + + return true; +} + void LayerAnimationController::PushNewAnimationsToImplThread( LayerAnimationController* controller_impl) const { // Any new animations owned by the main thread's controller are cloned and @@ -645,16 +671,6 @@ void LayerAnimationController::TickAnimations(double monotonic_time) { double trimmed = active_animations_[i]->TrimTimeToCurrentIteration(monotonic_time); - // Animation assumes its initial value until it gets the synchronized - // start time from the impl thread and can start ticking. - if (active_animations_[i]->needs_synchronized_start_time()) - trimmed = 0; - - // A just-started animation assumes its initial value. - if (active_animations_[i]->run_state() == Animation::Starting && - !active_animations_[i]->has_set_start_time()) - trimmed = 0; - switch (active_animations_[i]->target_property()) { case Animation::Transform: { const TransformAnimationCurve* transform_animation_curve = diff --git a/chromium/cc/animation/layer_animation_controller.h b/chromium/cc/animation/layer_animation_controller.h index e450f1534d5..4138704af66 100644 --- a/chromium/cc/animation/layer_animation_controller.h +++ b/chromium/cc/animation/layer_animation_controller.h @@ -17,7 +17,10 @@ #include "cc/base/scoped_ptr_vector.h" #include "ui/gfx/transform.h" -namespace gfx { class Transform; } +namespace gfx { +class BoxF; +class Transform; +} namespace cc { @@ -100,6 +103,8 @@ class CC_EXPORT LayerAnimationController layer_animation_delegate_ = delegate; } + bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds); + protected: friend class base::RefCounted<LayerAnimationController>; diff --git a/chromium/cc/animation/layer_animation_controller_unittest.cc b/chromium/cc/animation/layer_animation_controller_unittest.cc index 2e06f5cc829..e55c53c07f3 100644 --- a/chromium/cc/animation/layer_animation_controller_unittest.cc +++ b/chromium/cc/animation/layer_animation_controller_unittest.cc @@ -12,14 +12,12 @@ #include "cc/test/animation_test_common.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/box_f.h" #include "ui/gfx/transform.h" namespace cc { namespace { -void ExpectTranslateX(double translate_x, const gfx::Transform& matrix) { - EXPECT_FLOAT_EQ(translate_x, matrix.matrix().getDouble(0, 3)); } - scoped_ptr<Animation> CreateAnimation(scoped_ptr<AnimationCurve> curve, int id, Animation::TargetProperty property) { @@ -1117,5 +1115,74 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) { EXPECT_NE(0.5f, dummy.opacity()); } +TEST(LayerAnimationControllerTest, AnimatedBounds) { + scoped_refptr<LayerAnimationController> controller_impl( + LayerAnimationController::Create(0)); + + scoped_ptr<KeyframedTransformAnimationCurve> curve1( + KeyframedTransformAnimationCurve::Create()); + + TransformOperations operations1; + curve1->AddKeyframe(TransformKeyframe::Create( + 0.0, operations1, scoped_ptr<TimingFunction>())); + operations1.AppendTranslate(10.0, 15.0, 0.0); + curve1->AddKeyframe(TransformKeyframe::Create( + 1.0, operations1, scoped_ptr<TimingFunction>())); + + scoped_ptr<Animation> animation(Animation::Create( + curve1.PassAs<AnimationCurve>(), 1, 1, Animation::Transform)); + controller_impl->AddAnimation(animation.Pass()); + + scoped_ptr<KeyframedTransformAnimationCurve> curve2( + KeyframedTransformAnimationCurve::Create()); + + TransformOperations operations2; + curve2->AddKeyframe(TransformKeyframe::Create( + 0.0, operations2, scoped_ptr<TimingFunction>())); + operations2.AppendScale(2.0, 3.0, 4.0); + curve2->AddKeyframe(TransformKeyframe::Create( + 1.0, operations2, scoped_ptr<TimingFunction>())); + + animation = Animation::Create( + curve2.PassAs<AnimationCurve>(), 2, 2, Animation::Transform); + controller_impl->AddAnimation(animation.Pass()); + + gfx::BoxF box(1.f, 2.f, -1.f, 3.f, 4.f, 5.f); + gfx::BoxF bounds; + + EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds)); + EXPECT_EQ(gfx::BoxF(1.f, 2.f, -4.f, 13.f, 19.f, 20.f).ToString(), + bounds.ToString()); + + controller_impl->GetAnimation(1, Animation::Transform)->SetRunState( + cc::Animation::Finished, 0.0); + + // Only the unfinished animation should affect the animated bounds. + EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds)); + EXPECT_EQ(gfx::BoxF(1.f, 2.f, -4.f, 7.f, 16.f, 20.f).ToString(), + bounds.ToString()); + + controller_impl->GetAnimation(2, Animation::Transform)->SetRunState( + cc::Animation::Finished, 0.0); + + // There are no longer any running animations. + EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds)); + EXPECT_EQ(gfx::BoxF().ToString(), bounds.ToString()); + + // Add an animation whose bounds we don't yet support computing. + scoped_ptr<KeyframedTransformAnimationCurve> curve3( + KeyframedTransformAnimationCurve::Create()); + TransformOperations operations3; + curve3->AddKeyframe(TransformKeyframe::Create( + 0.0, operations3, scoped_ptr<TimingFunction>())); + operations3.AppendSkew(1.0, 2.0); + curve3->AddKeyframe(TransformKeyframe::Create( + 1.0, operations3, scoped_ptr<TimingFunction>())); + animation = Animation::Create( + curve3.PassAs<AnimationCurve>(), 3, 3, Animation::Transform); + controller_impl->AddAnimation(animation.Pass()); + EXPECT_FALSE(controller_impl->AnimatedBoundsForBox(box, &bounds)); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/scrollbar_animation_controller.h b/chromium/cc/animation/scrollbar_animation_controller.h index 55d29457689..d3667f10bca 100644 --- a/chromium/cc/animation/scrollbar_animation_controller.h +++ b/chromium/cc/animation/scrollbar_animation_controller.h @@ -18,14 +18,15 @@ class CC_EXPORT ScrollbarAnimationController { public: virtual ~ScrollbarAnimationController() {} - virtual bool IsScrollGestureInProgress() const = 0; virtual bool IsAnimating() const = 0; virtual base::TimeDelta DelayBeforeStart(base::TimeTicks now) const = 0; virtual bool Animate(base::TimeTicks now) = 0; virtual void DidScrollGestureBegin() = 0; virtual void DidScrollGestureEnd(base::TimeTicks now) = 0; - virtual void DidProgrammaticallyUpdateScroll(base::TimeTicks now) = 0; + + // Returns true if we should start an animation. + virtual bool DidScrollUpdate(base::TimeTicks now) = 0; }; } // namespace cc diff --git a/chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc b/chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc index 53827120a5c..eafcca08d51 100644 --- a/chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc +++ b/chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc @@ -6,6 +6,7 @@ #include "base/time/time.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/scrollbar_layer_impl_base.h" namespace cc { @@ -24,16 +25,13 @@ ScrollbarAnimationControllerLinearFade::ScrollbarAnimationControllerLinearFade( : ScrollbarAnimationController(), scroll_layer_(scroll_layer), scroll_gesture_in_progress_(false), + scroll_gesture_has_scrolled_(false), fadeout_delay_(fadeout_delay), fadeout_length_(fadeout_length) {} ScrollbarAnimationControllerLinearFade:: ~ScrollbarAnimationControllerLinearFade() {} -bool ScrollbarAnimationControllerLinearFade::IsScrollGestureInProgress() const { - return scroll_gesture_in_progress_; -} - bool ScrollbarAnimationControllerLinearFade::IsAnimating() const { return !last_awaken_time_.is_null(); } @@ -47,35 +45,44 @@ base::TimeDelta ScrollbarAnimationControllerLinearFade::DelayBeforeStart( bool ScrollbarAnimationControllerLinearFade::Animate(base::TimeTicks now) { float opacity = OpacityAtTime(now); - scroll_layer_->SetScrollbarOpacity(opacity); + ApplyOpacityToScrollbars(opacity); if (!opacity) last_awaken_time_ = base::TimeTicks(); return IsAnimating() && DelayBeforeStart(now) == base::TimeDelta(); } void ScrollbarAnimationControllerLinearFade::DidScrollGestureBegin() { - scroll_layer_->SetScrollbarOpacity(1.0f); scroll_gesture_in_progress_ = true; - last_awaken_time_ = base::TimeTicks(); + scroll_gesture_has_scrolled_ = false; } void ScrollbarAnimationControllerLinearFade::DidScrollGestureEnd( base::TimeTicks now) { + // The animation should not be triggered if no scrolling has occurred. + if (scroll_gesture_has_scrolled_) + last_awaken_time_ = now; + scroll_gesture_has_scrolled_ = false; scroll_gesture_in_progress_ = false; - last_awaken_time_ = now; } -void ScrollbarAnimationControllerLinearFade::DidProgrammaticallyUpdateScroll( +bool ScrollbarAnimationControllerLinearFade::DidScrollUpdate( base::TimeTicks now) { - // Don't set scroll_gesture_in_progress_ as this scroll is not from a gesture - // and we won't receive ScrollEnd. - scroll_layer_->SetScrollbarOpacity(1.0f); + ApplyOpacityToScrollbars(1.0f); + // The animation should only be activated if the scroll updated occurred + // programatically, outside the scope of a scroll gesture. + if (scroll_gesture_in_progress_) { + last_awaken_time_ = base::TimeTicks(); + scroll_gesture_has_scrolled_ = true; + return false; + } + last_awaken_time_ = now; + return true; } float ScrollbarAnimationControllerLinearFade::OpacityAtTime( base::TimeTicks now) { - if (scroll_gesture_in_progress_) + if (scroll_gesture_has_scrolled_) return 1.0f; if (last_awaken_time_.is_null()) @@ -92,4 +99,17 @@ float ScrollbarAnimationControllerLinearFade::OpacityAtTime( return 0.0f; } +void ScrollbarAnimationControllerLinearFade::ApplyOpacityToScrollbars( + float opacity) { + ScrollbarLayerImplBase* horizontal_scrollbar = + scroll_layer_->horizontal_scrollbar_layer(); + if (horizontal_scrollbar) + horizontal_scrollbar->SetOpacity(opacity); + + ScrollbarLayerImplBase* vertical_scrollbar = + scroll_layer_->vertical_scrollbar_layer(); + if (vertical_scrollbar) + vertical_scrollbar->SetOpacity(opacity); +} + } // namespace cc diff --git a/chromium/cc/animation/scrollbar_animation_controller_linear_fade.h b/chromium/cc/animation/scrollbar_animation_controller_linear_fade.h index 9ecb3c1617c..eca4c4f86f0 100644 --- a/chromium/cc/animation/scrollbar_animation_controller_linear_fade.h +++ b/chromium/cc/animation/scrollbar_animation_controller_linear_fade.h @@ -23,14 +23,13 @@ class CC_EXPORT ScrollbarAnimationControllerLinearFade virtual ~ScrollbarAnimationControllerLinearFade(); // ScrollbarAnimationController overrides. - virtual bool IsScrollGestureInProgress() const OVERRIDE; virtual bool IsAnimating() const OVERRIDE; virtual base::TimeDelta DelayBeforeStart(base::TimeTicks now) const OVERRIDE; virtual bool Animate(base::TimeTicks now) OVERRIDE; virtual void DidScrollGestureBegin() OVERRIDE; virtual void DidScrollGestureEnd(base::TimeTicks now) OVERRIDE; - virtual void DidProgrammaticallyUpdateScroll(base::TimeTicks now) OVERRIDE; + virtual bool DidScrollUpdate(base::TimeTicks now) OVERRIDE; protected: ScrollbarAnimationControllerLinearFade(LayerImpl* scroll_layer, @@ -39,11 +38,13 @@ class CC_EXPORT ScrollbarAnimationControllerLinearFade private: float OpacityAtTime(base::TimeTicks now); + void ApplyOpacityToScrollbars(float opacity); LayerImpl* scroll_layer_; base::TimeTicks last_awaken_time_; bool scroll_gesture_in_progress_; + bool scroll_gesture_has_scrolled_; base::TimeDelta fadeout_delay_; base::TimeDelta fadeout_length_; diff --git a/chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc b/chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc index a8365e8b4de..b4c1160b081 100644 --- a/chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc +++ b/chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc @@ -4,7 +4,7 @@ #include "cc/animation/scrollbar_animation_controller_linear_fade.h" -#include "cc/layers/scrollbar_layer_impl.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,7 +19,7 @@ class ScrollbarAnimationControllerLinearFadeTest : public testing::Test { protected: virtual void SetUp() { scroll_layer_ = LayerImpl::Create(host_impl_.active_tree(), 1); - scrollbar_layer_ = ScrollbarLayerImpl::Create( + scrollbar_layer_ = PaintedScrollbarLayerImpl::Create( host_impl_.active_tree(), 2, HORIZONTAL); scroll_layer_->SetMaxScrollOffset(gfx::Vector2d(50, 50)); @@ -36,7 +36,7 @@ class ScrollbarAnimationControllerLinearFadeTest : public testing::Test { FakeLayerTreeHostImpl host_impl_; scoped_ptr<ScrollbarAnimationControllerLinearFade> scrollbar_controller_; scoped_ptr<LayerImpl> scroll_layer_; - scoped_ptr<ScrollbarLayerImpl> scrollbar_layer_; + scoped_ptr<PaintedScrollbarLayerImpl> scrollbar_layer_; }; TEST_F(ScrollbarAnimationControllerLinearFadeTest, HiddenInBegin) { @@ -44,20 +44,43 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, HiddenInBegin) { EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity()); } -TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByScrollGesture) { +TEST_F(ScrollbarAnimationControllerLinearFadeTest, + HiddenAfterNonScrollingGesture) { + scrollbar_controller_->DidScrollGestureBegin(); + EXPECT_FALSE(scrollbar_controller_->IsAnimating()); + EXPECT_FALSE(scrollbar_controller_->Animate(base::TimeTicks())); + EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity()); + + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(100); + EXPECT_FALSE(scrollbar_controller_->Animate(time)); + EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity()); + scrollbar_controller_->DidScrollGestureEnd(time); + + time += base::TimeDelta::FromSeconds(100); + EXPECT_FALSE(scrollbar_controller_->IsAnimating()); + EXPECT_FALSE(scrollbar_controller_->Animate(time)); + EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity()); +} + +TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByScrollingGesture) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); scrollbar_controller_->DidScrollGestureBegin(); - EXPECT_TRUE(scrollbar_controller_->IsScrollGestureInProgress()); + scrollbar_controller_->Animate(time); + EXPECT_FALSE(scrollbar_controller_->IsAnimating()); + EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity()); + + EXPECT_FALSE(scrollbar_controller_->DidScrollUpdate(time)); EXPECT_FALSE(scrollbar_controller_->IsAnimating()); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); time += base::TimeDelta::FromSeconds(100); scrollbar_controller_->Animate(time); + EXPECT_FALSE(scrollbar_controller_->IsAnimating()); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); scrollbar_controller_->DidScrollGestureEnd(time); - EXPECT_FALSE(scrollbar_controller_->IsScrollGestureInProgress()); EXPECT_TRUE(scrollbar_controller_->IsAnimating()); EXPECT_EQ(2, scrollbar_controller_->DelayBeforeStart(time).InSeconds()); @@ -80,6 +103,7 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByScrollGesture) { time += base::TimeDelta::FromSeconds(1); scrollbar_controller_->DidScrollGestureBegin(); + EXPECT_FALSE(scrollbar_controller_->DidScrollUpdate(time)); scrollbar_controller_->DidScrollGestureEnd(time); time += base::TimeDelta::FromSeconds(1); @@ -106,8 +130,7 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByScrollGesture) { TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) { base::TimeTicks time; time += base::TimeDelta::FromSeconds(1); - scrollbar_controller_->DidProgrammaticallyUpdateScroll(time); - EXPECT_FALSE(scrollbar_controller_->IsScrollGestureInProgress()); + EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time)); EXPECT_TRUE(scrollbar_controller_->IsAnimating()); EXPECT_EQ(2, scrollbar_controller_->DelayBeforeStart(time).InSeconds()); scrollbar_controller_->Animate(time); @@ -116,7 +139,7 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) { time += base::TimeDelta::FromSeconds(1); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); - scrollbar_controller_->DidProgrammaticallyUpdateScroll(time); + EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time)); time += base::TimeDelta::FromSeconds(1); scrollbar_controller_->Animate(time); @@ -135,7 +158,7 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) { EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity()); time += base::TimeDelta::FromSeconds(1); - scrollbar_controller_->DidProgrammaticallyUpdateScroll(time); + EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time)); time += base::TimeDelta::FromSeconds(1); scrollbar_controller_->Animate(time); EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); @@ -157,5 +180,67 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) { EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity()); } +TEST_F(ScrollbarAnimationControllerLinearFadeTest, + AnimationPreservedByNonScrollingGesture) { + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(1); + EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time)); + EXPECT_TRUE(scrollbar_controller_->IsAnimating()); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + + time += base::TimeDelta::FromSeconds(3); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity()); + + scrollbar_controller_->DidScrollGestureBegin(); + EXPECT_TRUE(scrollbar_controller_->IsAnimating()); + EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity()); + + scrollbar_controller_->DidScrollGestureEnd(time); + EXPECT_TRUE(scrollbar_controller_->IsAnimating()); + EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity()); + + time += base::TimeDelta::FromSeconds(1); + EXPECT_FALSE(scrollbar_controller_->Animate(time)); + EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity()); +} + +TEST_F(ScrollbarAnimationControllerLinearFadeTest, + AnimationOverriddenByScrollingGesture) { + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(1); + EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time)); + EXPECT_TRUE(scrollbar_controller_->IsAnimating()); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + + time += base::TimeDelta::FromSeconds(3); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity()); + + scrollbar_controller_->DidScrollGestureBegin(); + EXPECT_TRUE(scrollbar_controller_->IsAnimating()); + EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity()); + + time += base::TimeDelta::FromSeconds(1); + EXPECT_FALSE(scrollbar_controller_->DidScrollUpdate(time)); + EXPECT_FALSE(scrollbar_controller_->IsAnimating()); + EXPECT_FLOAT_EQ(1, scrollbar_layer_->opacity()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->DidScrollGestureEnd(time); + EXPECT_TRUE(scrollbar_controller_->IsAnimating()); + EXPECT_FLOAT_EQ(1, scrollbar_layer_->opacity()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/scrollbar_animation_controller_thinning.cc b/chromium/cc/animation/scrollbar_animation_controller_thinning.cc new file mode 100644 index 00000000000..fcacfaab4a3 --- /dev/null +++ b/chromium/cc/animation/scrollbar_animation_controller_thinning.cc @@ -0,0 +1,132 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/animation/scrollbar_animation_controller_thinning.h" + +#include <algorithm> + +#include "base/time/time.h" +#include "cc/layers/layer_impl.h" +#include "cc/layers/scrollbar_layer_impl_base.h" + +namespace cc { + +scoped_ptr<ScrollbarAnimationControllerThinning> +ScrollbarAnimationControllerThinning::Create(LayerImpl* scroll_layer) { + return make_scoped_ptr(new ScrollbarAnimationControllerThinning( + scroll_layer, + base::TimeDelta::FromMilliseconds(500), + base::TimeDelta::FromMilliseconds(300))); +} + +scoped_ptr<ScrollbarAnimationControllerThinning> +ScrollbarAnimationControllerThinning::CreateForTest(LayerImpl* scroll_layer, + base::TimeDelta animation_delay, base::TimeDelta animation_duration) { + return make_scoped_ptr(new ScrollbarAnimationControllerThinning( + scroll_layer, animation_delay, animation_duration)); +} + +ScrollbarAnimationControllerThinning::ScrollbarAnimationControllerThinning( + LayerImpl* scroll_layer, + base::TimeDelta animation_delay, + base::TimeDelta animation_duration) + : ScrollbarAnimationController(), + scroll_layer_(scroll_layer), + scroll_gesture_in_progress_(false), + animation_delay_(animation_delay), + animation_duration_(animation_duration) {} + +ScrollbarAnimationControllerThinning::~ScrollbarAnimationControllerThinning() { +} + +bool ScrollbarAnimationControllerThinning::IsAnimating() const { + return !last_awaken_time_.is_null(); +} + +base::TimeDelta ScrollbarAnimationControllerThinning::DelayBeforeStart( + base::TimeTicks now) const { + if (now > last_awaken_time_ + animation_delay_) + return base::TimeDelta(); + return animation_delay_ - (now - last_awaken_time_); +} + +bool ScrollbarAnimationControllerThinning::Animate(base::TimeTicks now) { + float progress = AnimationProgressAtTime(now); + float opacity = OpacityAtAnimationProgress(progress); + float thumb_thickness_scale = ThumbThicknessScaleAtAnimationProgress( + progress); + ApplyOpacityAndThumbThicknessScale(opacity, thumb_thickness_scale); + if (progress == 1.f) + last_awaken_time_ = base::TimeTicks(); + return IsAnimating() && DelayBeforeStart(now) == base::TimeDelta(); +} + +void ScrollbarAnimationControllerThinning::DidScrollGestureBegin() { + ApplyOpacityAndThumbThicknessScale(1, 1); + last_awaken_time_ = base::TimeTicks(); + scroll_gesture_in_progress_ = true; +} + +void ScrollbarAnimationControllerThinning::DidScrollGestureEnd( + base::TimeTicks now) { + last_awaken_time_ = now; + scroll_gesture_in_progress_ = false; +} + +bool ScrollbarAnimationControllerThinning::DidScrollUpdate( + base::TimeTicks now) { + ApplyOpacityAndThumbThicknessScale(1, 1); + + last_awaken_time_ = now; + return true; +} + +float ScrollbarAnimationControllerThinning::AnimationProgressAtTime( + base::TimeTicks now) { + if (scroll_gesture_in_progress_) + return 0; + + if (last_awaken_time_.is_null()) + return 1; + + base::TimeDelta delta = now - last_awaken_time_; + float progress = (delta - animation_delay_).InSecondsF() / + animation_duration_.InSecondsF(); + return std::max(std::min(progress, 1.f), 0.f); +} + +float ScrollbarAnimationControllerThinning::OpacityAtAnimationProgress( + float progress) { + const float kIdleOpacity = 0.7f; + + return ((1 - kIdleOpacity) * (1.f - progress)) + kIdleOpacity; +} + +float +ScrollbarAnimationControllerThinning::ThumbThicknessScaleAtAnimationProgress( + float progress) { + const float kIdleThicknessScale = 0.4f; + + return ((1 - kIdleThicknessScale) * (1.f - progress)) + kIdleThicknessScale; +} + +void ScrollbarAnimationControllerThinning::ApplyOpacityAndThumbThicknessScale( + float opacity, float thumb_thickness_scale) { + ScrollbarLayerImplBase* horizontal_scrollbar = + scroll_layer_->horizontal_scrollbar_layer(); + if (horizontal_scrollbar) { + horizontal_scrollbar->SetOpacity(opacity); + horizontal_scrollbar->set_thumb_thickness_scale_factor( + thumb_thickness_scale); + } + + ScrollbarLayerImplBase* vertical_scrollbar = + scroll_layer_->vertical_scrollbar_layer(); + if (vertical_scrollbar) { + vertical_scrollbar->SetOpacity(opacity); + vertical_scrollbar->set_thumb_thickness_scale_factor(thumb_thickness_scale); + } +} + +} // namespace cc diff --git a/chromium/cc/animation/scrollbar_animation_controller_thinning.h b/chromium/cc/animation/scrollbar_animation_controller_thinning.h new file mode 100644 index 00000000000..0211479ef9a --- /dev/null +++ b/chromium/cc/animation/scrollbar_animation_controller_thinning.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_ANIMATION_SCROLLBAR_ANIMATION_CONTROLLER_THINNING_H_ +#define CC_ANIMATION_SCROLLBAR_ANIMATION_CONTROLLER_THINNING_H_ + +#include "base/memory/scoped_ptr.h" +#include "cc/animation/scrollbar_animation_controller.h" +#include "cc/base/cc_export.h" + +namespace cc { +class LayerImpl; + +// Scrollbar animation that partially fades and thins after an idle delay. +class CC_EXPORT ScrollbarAnimationControllerThinning + : public ScrollbarAnimationController { + public: + static scoped_ptr<ScrollbarAnimationControllerThinning> Create( + LayerImpl* scroll_layer); + + static scoped_ptr<ScrollbarAnimationControllerThinning> CreateForTest( + LayerImpl* scroll_layer, + base::TimeDelta animation_delay, + base::TimeDelta animation_duration); + + virtual ~ScrollbarAnimationControllerThinning(); + + // ScrollbarAnimationController overrides. + virtual bool IsAnimating() const OVERRIDE; + virtual base::TimeDelta DelayBeforeStart(base::TimeTicks now) const OVERRIDE; + + virtual bool Animate(base::TimeTicks now) OVERRIDE; + virtual void DidScrollGestureBegin() OVERRIDE; + virtual void DidScrollGestureEnd(base::TimeTicks now) OVERRIDE; + virtual bool DidScrollUpdate(base::TimeTicks now) OVERRIDE; + + protected: + ScrollbarAnimationControllerThinning(LayerImpl* scroll_layer, + base::TimeDelta animation_delay, + base::TimeDelta animation_duration); + + private: + // Returns how far through the animation we are as a progress value from + // 0 to 1. + float AnimationProgressAtTime(base::TimeTicks now); + float OpacityAtAnimationProgress(float progress); + float ThumbThicknessScaleAtAnimationProgress(float progress); + void ApplyOpacityAndThumbThicknessScale(float opacity, + float thumb_thickness_scale); + + LayerImpl* scroll_layer_; + + base::TimeTicks last_awaken_time_; + bool scroll_gesture_in_progress_; + + base::TimeDelta animation_delay_; + base::TimeDelta animation_duration_; + + DISALLOW_COPY_AND_ASSIGN(ScrollbarAnimationControllerThinning); +}; + +} // namespace cc + +#endif // CC_ANIMATION_SCROLLBAR_ANIMATION_CONTROLLER_THINNING_H_ diff --git a/chromium/cc/animation/scrollbar_animation_controller_thinning_unittest.cc b/chromium/cc/animation/scrollbar_animation_controller_thinning_unittest.cc new file mode 100644 index 00000000000..c83bdc81b39 --- /dev/null +++ b/chromium/cc/animation/scrollbar_animation_controller_thinning_unittest.cc @@ -0,0 +1,185 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/animation/scrollbar_animation_controller_thinning.h" + +#include "cc/layers/solid_color_scrollbar_layer_impl.h" +#include "cc/test/fake_impl_proxy.h" +#include "cc/test/fake_layer_tree_host_impl.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class ScrollbarAnimationControllerThinningTest : public testing::Test { + public: + ScrollbarAnimationControllerThinningTest() : host_impl_(&proxy_) {} + + protected: + virtual void SetUp() { + scroll_layer_ = LayerImpl::Create(host_impl_.active_tree(), 1); + const int kId = 2; + const int kThumbThickness = 10; + const bool kIsLeftSideVerticalScrollbar = false; + scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create( + host_impl_.active_tree(), kId, HORIZONTAL, kThumbThickness, + kIsLeftSideVerticalScrollbar); + + scroll_layer_->SetMaxScrollOffset(gfx::Vector2d(50, 50)); + scroll_layer_->SetBounds(gfx::Size(50, 50)); + scroll_layer_->SetHorizontalScrollbarLayer(scrollbar_layer_.get()); + + scrollbar_controller_ = ScrollbarAnimationControllerThinning::CreateForTest( + scroll_layer_.get(), + base::TimeDelta::FromSeconds(2), + base::TimeDelta::FromSeconds(3)); + } + + FakeImplProxy proxy_; + FakeLayerTreeHostImpl host_impl_; + scoped_ptr<ScrollbarAnimationControllerThinning> scrollbar_controller_; + scoped_ptr<LayerImpl> scroll_layer_; + scoped_ptr<SolidColorScrollbarLayerImpl> scrollbar_layer_; +}; + +TEST_F(ScrollbarAnimationControllerThinningTest, Idle) { + scrollbar_controller_->Animate(base::TimeTicks()); + EXPECT_FLOAT_EQ(0.7f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.4f, scrollbar_layer_->thumb_thickness_scale_factor()); +} + +TEST_F(ScrollbarAnimationControllerThinningTest, AwakenByScrollGesture) { + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->DidScrollGestureBegin(); + EXPECT_FALSE(scrollbar_controller_->IsAnimating()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(100); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + scrollbar_controller_->DidScrollGestureEnd(time); + + EXPECT_TRUE(scrollbar_controller_->IsAnimating()); + EXPECT_EQ(2, scrollbar_controller_->DelayBeforeStart(time).InSeconds()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.9f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.6f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + + scrollbar_controller_->DidScrollGestureBegin(); + scrollbar_controller_->DidScrollGestureEnd(time); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.9f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.6f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.7f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.4f, scrollbar_layer_->thumb_thickness_scale_factor()); +} + +TEST_F(ScrollbarAnimationControllerThinningTest, AwakenByProgrammaticScroll) { + base::TimeTicks time; + time += base::TimeDelta::FromSeconds(1); + EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time)); + EXPECT_TRUE(scrollbar_controller_->IsAnimating()); + EXPECT_EQ(2, scrollbar_controller_->DelayBeforeStart(time).InSeconds()); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time)); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.9f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.6f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time)); + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.9f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.6f, scrollbar_layer_->thumb_thickness_scale_factor()); + + time += base::TimeDelta::FromSeconds(1); + scrollbar_controller_->Animate(time); + EXPECT_FLOAT_EQ(0.7f, scrollbar_layer_->opacity()); + EXPECT_FLOAT_EQ(0.4f, scrollbar_layer_->thumb_thickness_scale_factor()); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/animation/timing_function.cc b/chromium/cc/animation/timing_function.cc index 65d607fd573..7fdb37fed96 100644 --- a/chromium/cc/animation/timing_function.cc +++ b/chromium/cc/animation/timing_function.cc @@ -12,7 +12,7 @@ namespace cc { namespace { -static const double BEZIER_EPSILON = 1e-7; +static const double kBezierEpsilon = 1e-7; static const int MAX_STEPS = 30; static double eval_bezier(double x1, double x2, double t) { @@ -48,14 +48,14 @@ static double bezier_interp(double x1, double step = 1.0; for (int i = 0; i < MAX_STEPS; ++i, step *= 0.5) { const double error = eval_bezier(x1, x2, t) - x; - if (std::abs(error) < BEZIER_EPSILON) + if (std::abs(error) < kBezierEpsilon) break; t += error > 0.0 ? -step : step; } // We should have terminated the above loop because we got close to x, not // because we exceeded MAX_STEPS. Do a DCHECK here to confirm. - DCHECK_GT(BEZIER_EPSILON, std::abs(eval_bezier(x1, x2, t) - x)); + DCHECK_GT(kBezierEpsilon, std::abs(eval_bezier(x1, x2, t) - x)); // Step 2. Return the interpolated y values at the t we computed above. return eval_bezier(y1, y2, t); @@ -93,6 +93,53 @@ scoped_ptr<AnimationCurve> CubicBezierTimingFunction::Clone() const { new CubicBezierTimingFunction(*this)).PassAs<AnimationCurve>(); } +void CubicBezierTimingFunction::Range(float* min, float* max) const { + *min = 0.f; + *max = 1.f; + if (0.f <= y1_ && y1_ < 1.f && 0.f <= y2_ && y2_ <= 1.f) + return; + + // Represent the function's derivative in the form at^2 + bt + c. + float a = 3.f * (y1_ - y2_) + 1.f; + float b = 2.f * (y2_ - 2.f * y1_); + float c = y1_; + + // Check if the derivative is constant. + if (std::abs(a) < kBezierEpsilon && + std::abs(b) < kBezierEpsilon) + return; + + // Zeros of the function's derivative. + float t_1 = 0.f; + float t_2 = 0.f; + + if (std::abs(a) < kBezierEpsilon) { + // The function's derivative is linear. + t_1 = -c / b; + } else { + // The function's derivative is a quadratic. We find the zeros of this + // quadratic using the quadratic formula. + float discriminant = b * b - 4 * a * c; + if (discriminant < 0.f) + return; + float discriminant_sqrt = sqrt(discriminant); + t_1 = (-b + discriminant_sqrt) / (2.f * a); + t_2 = (-b - discriminant_sqrt) / (2.f * a); + } + + float sol_1 = 0.f; + float sol_2 = 0.f; + + if (0.f < t_1 && t_1 < 1.f) + sol_1 = eval_bezier(y1_, y2_, t_1); + + if (0.f < t_2 && t_2 < 1.f) + sol_2 = eval_bezier(y1_, y2_, t_2); + + *min = std::min(std::min(*min, sol_1), sol_2); + *max = std::max(std::max(*max, sol_1), sol_2); +} + // These numbers come from // http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag. scoped_ptr<TimingFunction> EaseTimingFunction::Create() { diff --git a/chromium/cc/animation/timing_function.h b/chromium/cc/animation/timing_function.h index 3aa2f253d81..056ad414193 100644 --- a/chromium/cc/animation/timing_function.h +++ b/chromium/cc/animation/timing_function.h @@ -19,6 +19,10 @@ class CC_EXPORT TimingFunction : public FloatAnimationCurve { // Partial implementation of FloatAnimationCurve. virtual double Duration() const OVERRIDE; + // The smallest and largest values returned by GetValue for inputs in + // [0, 1]. + virtual void Range(float* min, float* max) const = 0; + protected: TimingFunction(); @@ -36,6 +40,8 @@ class CC_EXPORT CubicBezierTimingFunction : public TimingFunction { virtual float GetValue(double time) const OVERRIDE; virtual scoped_ptr<AnimationCurve> Clone() const OVERRIDE; + virtual void Range(float* min, float* max) const OVERRIDE; + protected: CubicBezierTimingFunction(double x1, double y1, double x2, double y2); diff --git a/chromium/cc/animation/timing_function_unittest.cc b/chromium/cc/animation/timing_function_unittest.cc index 2caa12d0772..4be225fd6b9 100644 --- a/chromium/cc/animation/timing_function_unittest.cc +++ b/chromium/cc/animation/timing_function_unittest.cc @@ -67,5 +67,75 @@ TEST(TimingFunctionTest, CubicBezierTimingFunctionUnclampedYValues) { EXPECT_NEAR(function->GetValue(1.0), 1.0, epsilon); } +TEST(TimingFunctionTest, CubicBezierTimingFunctionRange) { + double epsilon = 0.00015; + float min, max; + + // Derivative is a constant. + scoped_ptr<CubicBezierTimingFunction> function = + CubicBezierTimingFunction::Create(0.25, (1.0 / 3.0), 0.75, (2.0 / 3.0)); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_EQ(1.f, max); + + // Derivative is linear. + function = CubicBezierTimingFunction::Create(0.25, -0.5, 0.75, (-1.0 / 6.0)); + function->Range(&min, &max); + EXPECT_NEAR(min, -0.225, epsilon); + EXPECT_EQ(1.f, max); + + // Derivative has no real roots. + function = CubicBezierTimingFunction::Create(0.25, 0.25, 0.75, 0.5); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_EQ(1.f, max); + + // Derivative has exactly one real root. + function = CubicBezierTimingFunction::Create(0.0, 1.0, 1.0, 0.0); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_EQ(1.f, max); + + // Derivative has one root < 0 and one root > 1. + function = CubicBezierTimingFunction::Create(0.25, 0.1, 0.75, 0.9); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_EQ(1.f, max); + + // Derivative has two roots in [0,1]. + function = CubicBezierTimingFunction::Create(0.25, 2.5, 0.75, 0.5); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_NEAR(max, 1.28818, epsilon); + function = CubicBezierTimingFunction::Create(0.25, 0.5, 0.75, -1.5); + function->Range(&min, &max); + EXPECT_NEAR(min, -0.28818, epsilon); + EXPECT_EQ(1.f, max); + + // Derivative has one root < 0 and one root in [0,1]. + function = CubicBezierTimingFunction::Create(0.25, 0.1, 0.75, 1.5); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_NEAR(max, 1.10755, epsilon); + + // Derivative has one root in [0,1] and one root > 1. + function = CubicBezierTimingFunction::Create(0.25, -0.5, 0.75, 0.9); + function->Range(&min, &max); + EXPECT_NEAR(min, -0.10755, epsilon); + EXPECT_EQ(1.f, max); + + // Derivative has two roots < 0. + function = CubicBezierTimingFunction::Create(0.25, 0.3, 0.75, 0.633); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_EQ(1.f, max); + + // Derivative has two roots > 1. + function = CubicBezierTimingFunction::Create(0.25, 0.367, 0.75, 0.7); + function->Range(&min, &max); + EXPECT_EQ(0.f, min); + EXPECT_EQ(1.f, max); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/transform_operation.cc b/chromium/cc/animation/transform_operation.cc index dacea06d797..93f40f37f1a 100644 --- a/chromium/cc/animation/transform_operation.cc +++ b/chromium/cc/animation/transform_operation.cc @@ -2,14 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <algorithm> #include <cmath> #include <limits> +#include "base/logging.h" #include "cc/animation/transform_operation.h" +#include "ui/gfx/box_f.h" #include "ui/gfx/vector3d_f.h" namespace { -const double kAngleEpsilon = 1e-4; +const SkMScalar kAngleEpsilon = 1e-4; } namespace cc { @@ -24,10 +27,10 @@ static bool IsOperationIdentity(const TransformOperation* operation) { static bool ShareSameAxis(const TransformOperation* from, const TransformOperation* to, - double* axis_x, - double* axis_y, - double* axis_z, - double* angle_from) { + SkMScalar* axis_x, + SkMScalar* axis_y, + SkMScalar* axis_z, + SkMScalar* angle_from) { if (IsOperationIdentity(from) && IsOperationIdentity(to)) return false; @@ -47,20 +50,21 @@ static bool ShareSameAxis(const TransformOperation* from, return true; } - double length_2 = from->rotate.axis.x * from->rotate.axis.x + - from->rotate.axis.y * from->rotate.axis.y + - from->rotate.axis.z * from->rotate.axis.z; - double other_length_2 = to->rotate.axis.x * to->rotate.axis.x + - to->rotate.axis.y * to->rotate.axis.y + - to->rotate.axis.z * to->rotate.axis.z; + SkMScalar length_2 = from->rotate.axis.x * from->rotate.axis.x + + from->rotate.axis.y * from->rotate.axis.y + + from->rotate.axis.z * from->rotate.axis.z; + SkMScalar other_length_2 = to->rotate.axis.x * to->rotate.axis.x + + to->rotate.axis.y * to->rotate.axis.y + + to->rotate.axis.z * to->rotate.axis.z; if (length_2 <= kAngleEpsilon || other_length_2 <= kAngleEpsilon) return false; - double dot = to->rotate.axis.x * from->rotate.axis.x + - to->rotate.axis.y * from->rotate.axis.y + - to->rotate.axis.z * from->rotate.axis.z; - double error = std::abs(1.0 - (dot * dot) / (length_2 * other_length_2)); + SkMScalar dot = to->rotate.axis.x * from->rotate.axis.x + + to->rotate.axis.y * from->rotate.axis.y + + to->rotate.axis.z * from->rotate.axis.z; + SkMScalar error = + std::abs(SK_MScalar1 - (dot * dot) / (length_2 * other_length_2)); bool result = error < kAngleEpsilon; if (result) { *axis_x = to->rotate.axis.x; @@ -73,14 +77,16 @@ static bool ShareSameAxis(const TransformOperation* from, return result; } -static double BlendDoubles(double from, double to, double progress) { +static SkMScalar BlendSkMScalars(SkMScalar from, + SkMScalar to, + SkMScalar progress) { return from * (1 - progress) + to * progress; } bool TransformOperation::BlendTransformOperations( const TransformOperation* from, const TransformOperation* to, - double progress, + SkMScalar progress, gfx::Transform* result) { if (IsOperationIdentity(from) && IsOperationIdentity(to)) return true; @@ -94,26 +100,26 @@ bool TransformOperation::BlendTransformOperations( switch (interpolation_type) { case TransformOperation::TransformOperationTranslate: { - double from_x = IsOperationIdentity(from) ? 0 : from->translate.x; - double from_y = IsOperationIdentity(from) ? 0 : from->translate.y; - double from_z = IsOperationIdentity(from) ? 0 : from->translate.z; - double to_x = IsOperationIdentity(to) ? 0 : to->translate.x; - double to_y = IsOperationIdentity(to) ? 0 : to->translate.y; - double to_z = IsOperationIdentity(to) ? 0 : to->translate.z; - result->Translate3d(BlendDoubles(from_x, to_x, progress), - BlendDoubles(from_y, to_y, progress), - BlendDoubles(from_z, to_z, progress)); + SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->translate.x; + SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->translate.y; + SkMScalar from_z = IsOperationIdentity(from) ? 0 : from->translate.z; + SkMScalar to_x = IsOperationIdentity(to) ? 0 : to->translate.x; + SkMScalar to_y = IsOperationIdentity(to) ? 0 : to->translate.y; + SkMScalar to_z = IsOperationIdentity(to) ? 0 : to->translate.z; + result->Translate3d(BlendSkMScalars(from_x, to_x, progress), + BlendSkMScalars(from_y, to_y, progress), + BlendSkMScalars(from_z, to_z, progress)); break; } case TransformOperation::TransformOperationRotate: { - double axis_x = 0; - double axis_y = 0; - double axis_z = 1; - double from_angle = 0; - double to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle; + SkMScalar axis_x = 0; + SkMScalar axis_y = 0; + SkMScalar axis_z = 1; + SkMScalar from_angle = 0; + SkMScalar to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle; if (ShareSameAxis(from, to, &axis_x, &axis_y, &axis_z, &from_angle)) { result->RotateAbout(gfx::Vector3dF(axis_x, axis_y, axis_z), - BlendDoubles(from_angle, to_angle, progress)); + BlendSkMScalars(from_angle, to_angle, progress)); } else { gfx::Transform to_matrix; if (!IsOperationIdentity(to)) @@ -128,33 +134,35 @@ bool TransformOperation::BlendTransformOperations( break; } case TransformOperation::TransformOperationScale: { - double from_x = IsOperationIdentity(from) ? 1 : from->scale.x; - double from_y = IsOperationIdentity(from) ? 1 : from->scale.y; - double from_z = IsOperationIdentity(from) ? 1 : from->scale.z; - double to_x = IsOperationIdentity(to) ? 1 : to->scale.x; - double to_y = IsOperationIdentity(to) ? 1 : to->scale.y; - double to_z = IsOperationIdentity(to) ? 1 : to->scale.z; - result->Scale3d(BlendDoubles(from_x, to_x, progress), - BlendDoubles(from_y, to_y, progress), - BlendDoubles(from_z, to_z, progress)); + SkMScalar from_x = IsOperationIdentity(from) ? 1 : from->scale.x; + SkMScalar from_y = IsOperationIdentity(from) ? 1 : from->scale.y; + SkMScalar from_z = IsOperationIdentity(from) ? 1 : from->scale.z; + SkMScalar to_x = IsOperationIdentity(to) ? 1 : to->scale.x; + SkMScalar to_y = IsOperationIdentity(to) ? 1 : to->scale.y; + SkMScalar to_z = IsOperationIdentity(to) ? 1 : to->scale.z; + result->Scale3d(BlendSkMScalars(from_x, to_x, progress), + BlendSkMScalars(from_y, to_y, progress), + BlendSkMScalars(from_z, to_z, progress)); break; } case TransformOperation::TransformOperationSkew: { - double from_x = IsOperationIdentity(from) ? 0 : from->skew.x; - double from_y = IsOperationIdentity(from) ? 0 : from->skew.y; - double to_x = IsOperationIdentity(to) ? 0 : to->skew.x; - double to_y = IsOperationIdentity(to) ? 0 : to->skew.y; - result->SkewX(BlendDoubles(from_x, to_x, progress)); - result->SkewY(BlendDoubles(from_y, to_y, progress)); + SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->skew.x; + SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->skew.y; + SkMScalar to_x = IsOperationIdentity(to) ? 0 : to->skew.x; + SkMScalar to_y = IsOperationIdentity(to) ? 0 : to->skew.y; + result->SkewX(BlendSkMScalars(from_x, to_x, progress)); + result->SkewY(BlendSkMScalars(from_y, to_y, progress)); break; } case TransformOperation::TransformOperationPerspective: { - double from_perspective_depth = IsOperationIdentity(from) ? - std::numeric_limits<double>::max() : from->perspective_depth; - double to_perspective_depth = IsOperationIdentity(to) ? - std::numeric_limits<double>::max() : to->perspective_depth; - result->ApplyPerspectiveDepth( - BlendDoubles(from_perspective_depth, to_perspective_depth, progress)); + SkMScalar from_perspective_depth = + IsOperationIdentity(from) ? std::numeric_limits<SkMScalar>::max() + : from->perspective_depth; + SkMScalar to_perspective_depth = IsOperationIdentity(to) + ? std::numeric_limits<SkMScalar>::max() + : to->perspective_depth; + result->ApplyPerspectiveDepth(BlendSkMScalars( + from_perspective_depth, to_perspective_depth, progress)); break; } case TransformOperation::TransformOperationMatrix: { @@ -177,4 +185,130 @@ bool TransformOperation::BlendTransformOperations( return true; } +static void ApplyScaleToBox(float x_scale, + float y_scale, + float z_scale, + gfx::BoxF* box) { + if (x_scale < 0) + box->set_x(-box->right()); + if (y_scale < 0) + box->set_y(-box->bottom()); + if (z_scale < 0) + box->set_z(-box->front()); + box->Scale(std::abs(x_scale), std::abs(y_scale), std::abs(z_scale)); +} + +static void UnionBoxWithZeroScale(gfx::BoxF* box) { + float min_x = std::min(box->x(), 0.f); + float min_y = std::min(box->y(), 0.f); + float min_z = std::min(box->z(), 0.f); + float max_x = std::max(box->right(), 0.f); + float max_y = std::max(box->bottom(), 0.f); + float max_z = std::max(box->front(), 0.f); + *box = gfx::BoxF( + min_x, min_y, min_z, max_x - min_x, max_y - min_y, max_z - min_z); +} + +bool TransformOperation::BlendedBoundsForBox(const gfx::BoxF& box, + const TransformOperation* from, + const TransformOperation* to, + SkMScalar min_progress, + SkMScalar max_progress, + gfx::BoxF* bounds) { + bool is_identity_from = IsOperationIdentity(from); + bool is_identity_to = IsOperationIdentity(to); + if (is_identity_from && is_identity_to) { + *bounds = box; + return true; + } + + TransformOperation::Type interpolation_type = + TransformOperation::TransformOperationIdentity; + if (is_identity_to) + interpolation_type = from->type; + else + interpolation_type = to->type; + + switch (interpolation_type) { + case TransformOperation::TransformOperationTranslate: { + SkMScalar from_x, from_y, from_z; + if (is_identity_from) { + from_x = from_y = from_z = 0.0; + } else { + from_x = from->translate.x; + from_y = from->translate.y; + from_z = from->translate.z; + } + SkMScalar to_x, to_y, to_z; + if (is_identity_to) { + to_x = to_y = to_z = 0.0; + } else { + to_x = to->translate.x; + to_y = to->translate.y; + to_z = to->translate.z; + } + *bounds = box; + *bounds += gfx::Vector3dF(BlendSkMScalars(from_x, to_x, min_progress), + BlendSkMScalars(from_y, to_y, min_progress), + BlendSkMScalars(from_z, to_z, min_progress)); + gfx::BoxF bounds_max = box; + bounds_max += gfx::Vector3dF(BlendSkMScalars(from_x, to_x, max_progress), + BlendSkMScalars(from_y, to_y, max_progress), + BlendSkMScalars(from_z, to_z, max_progress)); + bounds->Union(bounds_max); + return true; + } + case TransformOperation::TransformOperationScale: { + SkMScalar from_x, from_y, from_z; + if (is_identity_from) { + from_x = from_y = from_z = 1.0; + } else { + from_x = from->scale.x; + from_y = from->scale.y; + from_z = from->scale.z; + } + SkMScalar to_x, to_y, to_z; + if (is_identity_to) { + to_x = to_y = to_z = 1.0; + } else { + to_x = to->scale.x; + to_y = to->scale.y; + to_z = to->scale.z; + } + *bounds = box; + ApplyScaleToBox( + SkMScalarToFloat(BlendSkMScalars(from_x, to_x, min_progress)), + SkMScalarToFloat(BlendSkMScalars(from_y, to_y, min_progress)), + SkMScalarToFloat(BlendSkMScalars(from_z, to_z, min_progress)), + bounds); + gfx::BoxF bounds_max = box; + ApplyScaleToBox( + SkMScalarToFloat(BlendSkMScalars(from_x, to_x, max_progress)), + SkMScalarToFloat(BlendSkMScalars(from_y, to_y, max_progress)), + SkMScalarToFloat(BlendSkMScalars(from_z, to_z, max_progress)), + &bounds_max); + if (!bounds->IsEmpty() && !bounds_max.IsEmpty()) { + bounds->Union(bounds_max); + } else if (!bounds->IsEmpty()) { + UnionBoxWithZeroScale(bounds); + } else if (!bounds_max.IsEmpty()) { + UnionBoxWithZeroScale(&bounds_max); + *bounds = bounds_max; + } + + return true; + } + case TransformOperation::TransformOperationIdentity: + *bounds = box; + return true; + case TransformOperation::TransformOperationRotate: + case TransformOperation::TransformOperationSkew: + case TransformOperation::TransformOperationPerspective: + case TransformOperation::TransformOperationMatrix: + return false; + } + NOTREACHED(); + return false; +} + } // namespace cc diff --git a/chromium/cc/animation/transform_operation.h b/chromium/cc/animation/transform_operation.h index 74673ab48cb..345ff295e39 100644 --- a/chromium/cc/animation/transform_operation.h +++ b/chromium/cc/animation/transform_operation.h @@ -7,6 +7,10 @@ #include "ui/gfx/transform.h" +namespace gfx { +class BoxF; +} + namespace cc { struct TransformOperation { @@ -28,34 +32,41 @@ struct TransformOperation { gfx::Transform matrix; union { - double perspective_depth; + SkMScalar perspective_depth; struct { - double x, y; + SkMScalar x, y; } skew; struct { - double x, y, z; + SkMScalar x, y, z; } scale; struct { - double x, y, z; + SkMScalar x, y, z; } translate; struct { struct { - double x, y, z; + SkMScalar x, y, z; } axis; - double angle; + SkMScalar angle; } rotate; }; bool IsIdentity() const; static bool BlendTransformOperations(const TransformOperation* from, const TransformOperation* to, - double progress, + SkMScalar progress, gfx::Transform* result); + + static bool BlendedBoundsForBox(const gfx::BoxF& box, + const TransformOperation* from, + const TransformOperation* to, + SkMScalar min_progress, + SkMScalar max_progress, + gfx::BoxF* bounds); }; } // namespace cc diff --git a/chromium/cc/animation/transform_operations.cc b/chromium/cc/animation/transform_operations.cc index d74f1a5343a..f8abe441fbf 100644 --- a/chromium/cc/animation/transform_operations.cc +++ b/chromium/cc/animation/transform_operations.cc @@ -6,6 +6,7 @@ #include <algorithm> +#include "ui/gfx/box_f.h" #include "ui/gfx/transform_util.h" #include "ui/gfx/vector3d_f.h" @@ -34,13 +35,49 @@ gfx::Transform TransformOperations::Apply() const { return to_return; } -gfx::Transform TransformOperations::Blend( - const TransformOperations& from, double progress) const { +gfx::Transform TransformOperations::Blend(const TransformOperations& from, + SkMScalar progress) const { gfx::Transform to_return; BlendInternal(from, progress, &to_return); return to_return; } +bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box, + const TransformOperations& from, + SkMScalar min_progress, + SkMScalar max_progress, + gfx::BoxF* bounds) const { + *bounds = box; + + bool from_identity = from.IsIdentity(); + bool to_identity = IsIdentity(); + if (from_identity && to_identity) + return true; + + if (!MatchesTypes(from)) + return false; + + size_t num_operations = + std::max(from_identity ? 0 : from.operations_.size(), + to_identity ? 0 : operations_.size()); + for (size_t i = 0; i < num_operations; ++i) { + gfx::BoxF bounds_for_operation; + const TransformOperation* from_op = + from_identity ? NULL : &from.operations_[i]; + const TransformOperation* to_op = to_identity ? NULL : &operations_[i]; + if (!TransformOperation::BlendedBoundsForBox(*bounds, + from_op, + to_op, + min_progress, + max_progress, + &bounds_for_operation)) + return false; + *bounds = bounds_for_operation; + } + + return true; +} + bool TransformOperations::MatchesTypes(const TransformOperations& other) const { if (IsIdentity() || other.IsIdentity()) return true; @@ -64,7 +101,9 @@ bool TransformOperations::CanBlendWith( return BlendInternal(other, 0.5, &dummy); } -void TransformOperations::AppendTranslate(double x, double y, double z) { +void TransformOperations::AppendTranslate(SkMScalar x, + SkMScalar y, + SkMScalar z) { TransformOperation to_add; to_add.matrix.Translate3d(x, y, z); to_add.type = TransformOperation::TransformOperationTranslate; @@ -75,8 +114,10 @@ void TransformOperations::AppendTranslate(double x, double y, double z) { decomposed_transform_dirty_ = true; } -void TransformOperations::AppendRotate(double x, double y, double z, - double degrees) { +void TransformOperations::AppendRotate(SkMScalar x, + SkMScalar y, + SkMScalar z, + SkMScalar degrees) { TransformOperation to_add; to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees); to_add.type = TransformOperation::TransformOperationRotate; @@ -88,7 +129,7 @@ void TransformOperations::AppendRotate(double x, double y, double z, decomposed_transform_dirty_ = true; } -void TransformOperations::AppendScale(double x, double y, double z) { +void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) { TransformOperation to_add; to_add.matrix.Scale3d(x, y, z); to_add.type = TransformOperation::TransformOperationScale; @@ -99,7 +140,7 @@ void TransformOperations::AppendScale(double x, double y, double z) { decomposed_transform_dirty_ = true; } -void TransformOperations::AppendSkew(double x, double y) { +void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) { TransformOperation to_add; to_add.matrix.SkewX(x); to_add.matrix.SkewY(y); @@ -110,7 +151,7 @@ void TransformOperations::AppendSkew(double x, double y) { decomposed_transform_dirty_ = true; } -void TransformOperations::AppendPerspective(double depth) { +void TransformOperations::AppendPerspective(SkMScalar depth) { TransformOperation to_add; to_add.matrix.ApplyPerspectiveDepth(depth); to_add.type = TransformOperation::TransformOperationPerspective; @@ -140,7 +181,7 @@ bool TransformOperations::IsIdentity() const { } bool TransformOperations::BlendInternal(const TransformOperations& from, - double progress, + SkMScalar progress, gfx::Transform* result) const { bool from_identity = from.IsIdentity(); bool to_identity = IsIdentity(); diff --git a/chromium/cc/animation/transform_operations.h b/chromium/cc/animation/transform_operations.h index b5f960fa688..7c509349092 100644 --- a/chromium/cc/animation/transform_operations.h +++ b/chromium/cc/animation/transform_operations.h @@ -13,6 +13,7 @@ #include "ui/gfx/transform.h" namespace gfx { +class BoxF; struct DecomposedTransform; } @@ -43,7 +44,18 @@ class CC_EXPORT TransformOperations { // transforms are baked to matrices (using apply), and the matrices are // then decomposed and interpolated. For more information, see // http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition. - gfx::Transform Blend(const TransformOperations& from, double progress) const; + gfx::Transform Blend(const TransformOperations& from, + SkMScalar progress) const; + + // Sets |bounds| be the bounding box for the region within which |box| will + // exist when it is transformed by the result of calling Blend on |from| and + // with progress in the range [min_progress, max_progress]. If this region + // cannot be computed, returns false. + bool BlendedBoundsForBox(const gfx::BoxF& box, + const TransformOperations& from, + SkMScalar min_progress, + SkMScalar max_progress, + gfx::BoxF* bounds) const; // Returns true if this operation and its descendants have the same types // as other and its descendants. @@ -54,17 +66,18 @@ class CC_EXPORT TransformOperations { // fails (this can happen if either matrix cannot be decomposed). bool CanBlendWith(const TransformOperations& other) const; - void AppendTranslate(double x, double y, double z); - void AppendRotate(double x, double y, double z, double degrees); - void AppendScale(double x, double y, double z); - void AppendSkew(double x, double y); - void AppendPerspective(double depth); + void AppendTranslate(SkMScalar x, SkMScalar y, SkMScalar z); + void AppendRotate(SkMScalar x, SkMScalar y, SkMScalar z, SkMScalar degrees); + void AppendScale(SkMScalar x, SkMScalar y, SkMScalar z); + void AppendSkew(SkMScalar x, SkMScalar y); + void AppendPerspective(SkMScalar depth); void AppendMatrix(const gfx::Transform& matrix); void AppendIdentity(); bool IsIdentity() const; private: - bool BlendInternal(const TransformOperations& from, double progress, + bool BlendInternal(const TransformOperations& from, + SkMScalar progress, gfx::Transform* result) const; std::vector<TransformOperation> operations_; diff --git a/chromium/cc/animation/transform_operations_unittest.cc b/chromium/cc/animation/transform_operations_unittest.cc index 8fc9b949d74..a7e4c510a52 100644 --- a/chromium/cc/animation/transform_operations_unittest.cc +++ b/chromium/cc/animation/transform_operations_unittest.cc @@ -8,6 +8,7 @@ #include "cc/animation/transform_operations.h" #include "cc/test/geometry_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/box_f.h" #include "ui/gfx/vector3d_f.h" namespace cc { @@ -146,9 +147,9 @@ TEST(TransformOperationTest, IdentityAlwaysMatches) { } TEST(TransformOperationTest, ApplyTranslate) { - double x = 1; - double y = 2; - double z = 3; + SkMScalar x = 1; + SkMScalar y = 2; + SkMScalar z = 3; TransformOperations operations; operations.AppendTranslate(x, y, z); gfx::Transform expected; @@ -157,10 +158,10 @@ TEST(TransformOperationTest, ApplyTranslate) { } TEST(TransformOperationTest, ApplyRotate) { - double x = 1; - double y = 2; - double z = 3; - double degrees = 80; + SkMScalar x = 1; + SkMScalar y = 2; + SkMScalar z = 3; + SkMScalar degrees = 80; TransformOperations operations; operations.AppendRotate(x, y, z, degrees); gfx::Transform expected; @@ -169,9 +170,9 @@ TEST(TransformOperationTest, ApplyRotate) { } TEST(TransformOperationTest, ApplyScale) { - double x = 1; - double y = 2; - double z = 3; + SkMScalar x = 1; + SkMScalar y = 2; + SkMScalar z = 3; TransformOperations operations; operations.AppendScale(x, y, z); gfx::Transform expected; @@ -180,8 +181,8 @@ TEST(TransformOperationTest, ApplyScale) { } TEST(TransformOperationTest, ApplySkew) { - double x = 1; - double y = 2; + SkMScalar x = 1; + SkMScalar y = 2; TransformOperations operations; operations.AppendSkew(x, y); gfx::Transform expected; @@ -191,7 +192,7 @@ TEST(TransformOperationTest, ApplySkew) { } TEST(TransformOperationTest, ApplyPerspective) { - double depth = 800; + SkMScalar depth = 800; TransformOperations operations; operations.AppendPerspective(depth); gfx::Transform expected; @@ -200,9 +201,9 @@ TEST(TransformOperationTest, ApplyPerspective) { } TEST(TransformOperationTest, ApplyMatrix) { - double dx = 1; - double dy = 2; - double dz = 3; + SkMScalar dx = 1; + SkMScalar dy = 2; + SkMScalar dz = 3; gfx::Transform expected_matrix; expected_matrix.Translate3d(dx, dy, dz); TransformOperations matrix_transform; @@ -211,13 +212,13 @@ TEST(TransformOperationTest, ApplyMatrix) { } TEST(TransformOperationTest, ApplyOrder) { - double sx = 2; - double sy = 4; - double sz = 8; + SkMScalar sx = 2; + SkMScalar sy = 4; + SkMScalar sz = 8; - double dx = 1; - double dy = 2; - double dz = 3; + SkMScalar dx = 1; + SkMScalar dy = 2; + SkMScalar dz = 3; TransformOperations operations; operations.AppendScale(sx, sy, sz); @@ -236,21 +237,21 @@ TEST(TransformOperationTest, ApplyOrder) { } TEST(TransformOperationTest, BlendOrder) { - double sx1 = 2; - double sy1 = 4; - double sz1 = 8; + SkMScalar sx1 = 2; + SkMScalar sy1 = 4; + SkMScalar sz1 = 8; - double dx1 = 1; - double dy1 = 2; - double dz1 = 3; + SkMScalar dx1 = 1; + SkMScalar dy1 = 2; + SkMScalar dz1 = 3; - double sx2 = 4; - double sy2 = 8; - double sz2 = 16; + SkMScalar sx2 = 4; + SkMScalar sy2 = 8; + SkMScalar sz2 = 16; - double dx2 = 10; - double dy2 = 20; - double dz2 = 30; + SkMScalar dx2 = 10; + SkMScalar dy2 = 20; + SkMScalar dz2 = 30; TransformOperations operations_from; operations_from.AppendScale(sx1, sy1, sz1); @@ -270,7 +271,7 @@ TEST(TransformOperationTest, BlendOrder) { gfx::Transform translate_to; translate_to.Translate3d(dx2, dy2, dz2); - double progress = 0.25; + SkMScalar progress = 0.25f; gfx::Transform blended_scale = scale_to; blended_scale.Blend(scale_from, progress); @@ -285,11 +286,11 @@ TEST(TransformOperationTest, BlendOrder) { expected, operations_to.Blend(operations_from, progress)); } -static void CheckProgress(double progress, - const gfx::Transform& from_matrix, - const gfx::Transform& to_matrix, - const TransformOperations& from_transform, - const TransformOperations& to_transform) { +static void CheckProgress(SkMScalar progress, + const gfx::Transform& from_matrix, + const gfx::Transform& to_matrix, + const TransformOperations& from_transform, + const TransformOperations& to_transform) { gfx::Transform expected_matrix = to_matrix; expected_matrix.Blend(from_matrix, progress); EXPECT_TRANSFORMATION_MATRIX_EQ( @@ -297,9 +298,9 @@ static void CheckProgress(double progress, } TEST(TransformOperationTest, BlendProgress) { - double sx = 2; - double sy = 4; - double sz = 8; + SkMScalar sx = 2; + SkMScalar sy = 4; + SkMScalar sz = 8; TransformOperations operations_from; operations_from.AppendScale(sx, sy, sz); @@ -317,28 +318,28 @@ TEST(TransformOperationTest, BlendProgress) { CheckProgress(-1, matrix_from, matrix_to, operations_from, operations_to); CheckProgress(0, matrix_from, matrix_to, operations_from, operations_to); - CheckProgress(0.25, matrix_from, matrix_to, operations_from, operations_to); - CheckProgress(0.5, matrix_from, matrix_to, operations_from, operations_to); + CheckProgress(0.25f, matrix_from, matrix_to, operations_from, operations_to); + CheckProgress(0.5f, matrix_from, matrix_to, operations_from, operations_to); CheckProgress(1, matrix_from, matrix_to, operations_from, operations_to); CheckProgress(2, matrix_from, matrix_to, operations_from, operations_to); } TEST(TransformOperationTest, BlendWhenTypesDoNotMatch) { - double sx1 = 2; - double sy1 = 4; - double sz1 = 8; + SkMScalar sx1 = 2; + SkMScalar sy1 = 4; + SkMScalar sz1 = 8; - double dx1 = 1; - double dy1 = 2; - double dz1 = 3; + SkMScalar dx1 = 1; + SkMScalar dy1 = 2; + SkMScalar dz1 = 3; - double sx2 = 4; - double sy2 = 8; - double sz2 = 16; + SkMScalar sx2 = 4; + SkMScalar sy2 = 8; + SkMScalar sz2 = 16; - double dx2 = 10; - double dy2 = 20; - double dz2 = 30; + SkMScalar dx2 = 10; + SkMScalar dy2 = 20; + SkMScalar dz2 = 30; TransformOperations operations_from; operations_from.AppendScale(sx1, sy1, sz1); @@ -356,7 +357,7 @@ TEST(TransformOperationTest, BlendWhenTypesDoNotMatch) { to.Translate3d(dx2, dy2, dz2); to.Scale3d(sx2, sy2, sz2); - double progress = 0.25; + SkMScalar progress = 0.25f; gfx::Transform expected = to; expected.Blend(from, progress); @@ -372,7 +373,7 @@ TEST(TransformOperationTest, LargeRotationsWithSameAxis) { TransformOperations operations_to; operations_to.AppendRotate(0, 0, 2, 360); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180); @@ -388,7 +389,7 @@ TEST(TransformOperationTest, LargeRotationsWithSameAxisInDifferentDirection) { TransformOperations operations_to; operations_to.AppendRotate(0, 0, -1, 180); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; @@ -403,7 +404,7 @@ TEST(TransformOperationTest, LargeRotationsWithDifferentAxes) { TransformOperations operations_to; operations_to.AppendRotate(0, 1, 0, 175); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform matrix_from; matrix_from.RotateAbout(gfx::Vector3dF(0, 0, 1), 175); @@ -425,7 +426,7 @@ TEST(TransformOperationTest, BlendRotationFromIdentity) { TransformOperations operations; operations.AppendRotate(0, 0, 1, 360); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180); @@ -433,7 +434,7 @@ TEST(TransformOperationTest, BlendRotationFromIdentity) { EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); - progress = -0.5; + progress = -0.5f; expected.MakeIdentity(); expected.RotateAbout(gfx::Vector3dF(0, 0, 1), -180); @@ -441,7 +442,7 @@ TEST(TransformOperationTest, BlendRotationFromIdentity) { EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); - progress = 1.5; + progress = 1.5f; expected.MakeIdentity(); expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 540); @@ -459,7 +460,7 @@ TEST(TransformOperationTest, BlendTranslationFromIdentity) { TransformOperations operations; operations.AppendTranslate(2, 2, 2); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.Translate3d(1, 1, 1); @@ -467,7 +468,7 @@ TEST(TransformOperationTest, BlendTranslationFromIdentity) { EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); - progress = -0.5; + progress = -0.5f; expected.MakeIdentity(); expected.Translate3d(-1, -1, -1); @@ -475,7 +476,7 @@ TEST(TransformOperationTest, BlendTranslationFromIdentity) { EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); - progress = 1.5; + progress = 1.5f; expected.MakeIdentity(); expected.Translate3d(3, 3, 3); @@ -493,7 +494,7 @@ TEST(TransformOperationTest, BlendScaleFromIdentity) { TransformOperations operations; operations.AppendScale(3, 3, 3); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.Scale3d(2, 2, 2); @@ -501,7 +502,7 @@ TEST(TransformOperationTest, BlendScaleFromIdentity) { EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); - progress = -0.5; + progress = -0.5f; expected.MakeIdentity(); expected.Scale3d(0, 0, 0); @@ -509,7 +510,7 @@ TEST(TransformOperationTest, BlendScaleFromIdentity) { EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); - progress = 1.5; + progress = 1.5f; expected.MakeIdentity(); expected.Scale3d(4, 4, 4); @@ -527,7 +528,7 @@ TEST(TransformOperationTest, BlendSkewFromIdentity) { TransformOperations operations; operations.AppendSkew(2, 2); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.SkewX(1); @@ -536,7 +537,7 @@ TEST(TransformOperationTest, BlendSkewFromIdentity) { EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); - progress = -0.5; + progress = -0.5f; expected.MakeIdentity(); expected.SkewX(-1); @@ -545,7 +546,7 @@ TEST(TransformOperationTest, BlendSkewFromIdentity) { EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); - progress = 1.5; + progress = 1.5f; expected.MakeIdentity(); expected.SkewX(3); @@ -564,11 +565,11 @@ TEST(TransformOperationTest, BlendPerspectiveFromIdentity) { TransformOperations operations; operations.AppendPerspective(1000); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; - expected.ApplyPerspectiveDepth( - 500 + 0.5 * std::numeric_limits<double>::max()); + expected.ApplyPerspectiveDepth(500 + + 0.5 * std::numeric_limits<SkMScalar>::max()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected, operations.Blend(*identity_operations[i], progress)); @@ -583,7 +584,7 @@ TEST(TransformOperationTest, BlendRotationToIdentity) { TransformOperations operations; operations.AppendRotate(0, 0, 1, 360); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180); @@ -601,7 +602,7 @@ TEST(TransformOperationTest, BlendTranslationToIdentity) { TransformOperations operations; operations.AppendTranslate(2, 2, 2); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.Translate3d(1, 1, 1); @@ -619,7 +620,7 @@ TEST(TransformOperationTest, BlendScaleToIdentity) { TransformOperations operations; operations.AppendScale(3, 3, 3); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.Scale3d(2, 2, 2); @@ -637,7 +638,7 @@ TEST(TransformOperationTest, BlendSkewToIdentity) { TransformOperations operations; operations.AppendSkew(2, 2); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; expected.SkewX(1); @@ -656,11 +657,11 @@ TEST(TransformOperationTest, BlendPerspectiveToIdentity) { TransformOperations operations; operations.AppendPerspective(1000); - double progress = 0.5; + SkMScalar progress = 0.5f; gfx::Transform expected; - expected.ApplyPerspectiveDepth( - 500 + 0.5 * std::numeric_limits<double>::max()); + expected.ApplyPerspectiveDepth(500 + + 0.5 * std::numeric_limits<SkMScalar>::max()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected, identity_operations[i]->Blend(operations, progress)); @@ -707,5 +708,176 @@ TEST(TransformOperationTest, ExtrapolateMatrixBlending) { expected, operations1.Blend(operations2, -0.5)); } +TEST(TransformOperationTest, BlendedBoundsWhenTypesDoNotMatch) { + TransformOperations operations_from; + operations_from.AppendScale(2.0, 4.0, 8.0); + operations_from.AppendTranslate(1.0, 2.0, 3.0); + + TransformOperations operations_to; + operations_to.AppendTranslate(10.0, 20.0, 30.0); + operations_to.AppendScale(4.0, 8.0, 16.0); + + gfx::BoxF box(1.f, 1.f, 1.f); + gfx::BoxF bounds; + + SkMScalar min_progress = 0.f; + SkMScalar max_progress = 1.f; + + EXPECT_FALSE(operations_to.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); +} + +TEST(TransformOperationTest, BlendedBoundsForIdentity) { + TransformOperations operations_from; + operations_from.AppendIdentity(); + TransformOperations operations_to; + operations_to.AppendIdentity(); + + gfx::BoxF box(1.f, 2.f, 3.f); + gfx::BoxF bounds; + + SkMScalar min_progress = 0.f; + SkMScalar max_progress = 1.f; + + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(box.ToString(), bounds.ToString()); +} + +TEST(TransformOperationTest, BlendedBoundsForTranslate) { + TransformOperations operations_from; + operations_from.AppendTranslate(3.0, -4.0, 2.0); + TransformOperations operations_to; + operations_to.AppendTranslate(7.0, 4.0, -2.0); + + gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f); + gfx::BoxF bounds; + + SkMScalar min_progress = -0.5f; + SkMScalar max_progress = 1.5f; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(2.f, -6.f, -1.f, 12.f, 20.f, 12.f).ToString(), + bounds.ToString()); + + min_progress = 0.f; + max_progress = 1.f; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(4.f, -2.f, 1.f, 8.f, 12.f, 8.f).ToString(), + bounds.ToString()); + + TransformOperations identity; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, identity, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(1.f, 2.f, 1.f, 11.f, 8.f, 6.f).ToString(), + bounds.ToString()); + + EXPECT_TRUE(identity.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(1.f, -2.f, 3.f, 7.f, 8.f, 6.f).ToString(), + bounds.ToString()); +} + +TEST(TransformOperationTest, BlendedBoundsForScale) { + TransformOperations operations_from; + operations_from.AppendScale(3.0, 0.5, 2.0); + TransformOperations operations_to; + operations_to.AppendScale(7.0, 4.0, -2.0); + + gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f); + gfx::BoxF bounds; + + SkMScalar min_progress = -0.5f; + SkMScalar max_progress = 1.5f; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(1.f, -7.5f, -28.f, 44.f, 42.f, 56.f).ToString(), + bounds.ToString()); + + min_progress = 0.f; + max_progress = 1.f; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(3.f, 1.f, -14.f, 32.f, 23.f, 28.f).ToString(), + bounds.ToString()); + + TransformOperations identity; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, identity, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(1.f, 2.f, -14.f, 34.f, 22.f, 21.f).ToString(), + bounds.ToString()); + + EXPECT_TRUE(identity.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(1.f, 1.f, 3.f, 14.f, 5.f, 11.f).ToString(), + bounds.ToString()); +} + +TEST(TransformOperationTest, BlendedBoundsWithZeroScale) { + TransformOperations zero_scale; + zero_scale.AppendScale(0.0, 0.0, 0.0); + TransformOperations non_zero_scale; + non_zero_scale.AppendScale(2.0, -4.0, 5.0); + + gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f); + gfx::BoxF bounds; + + SkMScalar min_progress = 0.f; + SkMScalar max_progress = 1.f; + EXPECT_TRUE(zero_scale.BlendedBoundsForBox( + box, non_zero_scale, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(), + bounds.ToString()); + + EXPECT_TRUE(non_zero_scale.BlendedBoundsForBox( + box, zero_scale, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(), + bounds.ToString()); + + EXPECT_TRUE(zero_scale.BlendedBoundsForBox( + box, zero_scale, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF().ToString(), bounds.ToString()); +} + +TEST(TransformOperationTest, BlendedBoundsForSequence) { + TransformOperations operations_from; + operations_from.AppendTranslate(2.0, 4.0, -1.0); + operations_from.AppendScale(-1.0, 2.0, 3.0); + operations_from.AppendTranslate(1.0, -5.0, 1.0); + TransformOperations operations_to; + operations_to.AppendTranslate(6.0, -2.0, 3.0); + operations_to.AppendScale(-3.0, -2.0, 5.0); + operations_to.AppendTranslate(13.0, -1.0, 5.0); + + gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f); + gfx::BoxF bounds; + + SkMScalar min_progress = -0.5f; + SkMScalar max_progress = 1.5f; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(-57.f, -59.f, -1.f, 76.f, 112.f, 80.f).ToString(), + bounds.ToString()); + + min_progress = 0.f; + max_progress = 1.f; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(-32.f, -25.f, 7.f, 42.f, 44.f, 48.f).ToString(), + bounds.ToString()); + + TransformOperations identity; + EXPECT_TRUE(operations_to.BlendedBoundsForBox( + box, identity, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(-33.f, -13.f, 3.f, 57.f, 19.f, 52.f).ToString(), + bounds.ToString()); + + EXPECT_TRUE(identity.BlendedBoundsForBox( + box, operations_from, min_progress, max_progress, &bounds)); + EXPECT_EQ(gfx::BoxF(-7.f, -3.f, 2.f, 15.f, 23.f, 20.f).ToString(), + bounds.ToString()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/base/float_quad_unittest.cc b/chromium/cc/base/float_quad_unittest.cc index bb3446c0d69..186624eec34 100644 --- a/chromium/cc/base/float_quad_unittest.cc +++ b/chromium/cc/base/float_quad_unittest.cc @@ -25,15 +25,15 @@ TEST(FloatQuadTest, IsRectilinearTest) { rectilinear_trans[7].Scale(100000, 100000); rectilinear_trans[7].Rotate(180.0); + gfx::QuadF original( + gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)); + for (int i = 0; i < kNumRectilinear; ++i) { bool clipped = false; - gfx::QuadF quad = MathUtil::MapQuad( - rectilinear_trans[i], - gfx::QuadF( - gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)), - &clipped); - ASSERT_TRUE(!clipped); - EXPECT_TRUE(quad.IsRectilinear()); + gfx::QuadF quad = + MathUtil::MapQuad(rectilinear_trans[i], original, &clipped); + ASSERT_TRUE(!clipped) << "case " << i; + EXPECT_TRUE(quad.IsRectilinear()) << "case " << i; } const int kNumNonRectilinear = 10; @@ -51,13 +51,10 @@ TEST(FloatQuadTest, IsRectilinearTest) { for (int i = 0; i < kNumNonRectilinear; ++i) { bool clipped = false; - gfx::QuadF quad = MathUtil::MapQuad( - non_rectilinear_trans[i], - gfx::QuadF( - gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)), - &clipped); - ASSERT_TRUE(!clipped); - EXPECT_FALSE(quad.IsRectilinear()); + gfx::QuadF quad = + MathUtil::MapQuad(non_rectilinear_trans[i], original, &clipped); + ASSERT_TRUE(!clipped) << "case " << i; + EXPECT_FALSE(quad.IsRectilinear()) << "case " << i; } } diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc index fa7b21a8c29..40b8d11c68c 100644 --- a/chromium/cc/base/math_util.cc +++ b/chromium/cc/base/math_util.cc @@ -108,15 +108,15 @@ gfx::Rect MathUtil::MapClippedRect(const gfx::Transform& transform, gfx::RectF MathUtil::MapClippedRect(const gfx::Transform& transform, const gfx::RectF& src_rect) { - if (transform.IsIdentityOrTranslation()) + if (transform.IsIdentityOrTranslation()) { return src_rect + - gfx::Vector2dF( - static_cast<float>(transform.matrix().getDouble(0, 3)), - static_cast<float>(transform.matrix().getDouble(1, 3))); + gfx::Vector2dF(SkMScalarToFloat(transform.matrix().get(0, 3)), + SkMScalarToFloat(transform.matrix().get(1, 3))); + } // Apply the transform, but retain the result in homogeneous coordinates. - double quad[4 * 2]; // input: 4 x 2D points + SkMScalar quad[4 * 2]; // input: 4 x 2D points quad[0] = src_rect.x(); quad[1] = src_rect.y(); quad[2] = src_rect.right(); @@ -126,7 +126,7 @@ gfx::RectF MathUtil::MapClippedRect(const gfx::Transform& transform, quad[6] = src_rect.x(); quad[7] = src_rect.bottom(); - double result[4 * 4]; // output: 4 x 4D homogeneous points + SkMScalar result[4 * 4]; // output: 4 x 4D homogeneous points transform.matrix().map2(quad, 4, result); HomogeneousCoordinate hc0(result[0], result[1], result[2], result[3]); @@ -140,9 +140,8 @@ gfx::RectF MathUtil::ProjectClippedRect(const gfx::Transform& transform, const gfx::RectF& src_rect) { if (transform.IsIdentityOrTranslation()) { return src_rect + - gfx::Vector2dF( - static_cast<float>(transform.matrix().getDouble(0, 3)), - static_cast<float>(transform.matrix().getDouble(1, 3))); + gfx::Vector2dF(SkMScalarToFloat(transform.matrix().get(0, 3)), + SkMScalarToFloat(transform.matrix().get(1, 3))); } // Perform the projection, but retain the result in homogeneous coordinates. @@ -330,8 +329,8 @@ gfx::QuadF MathUtil::MapQuad(const gfx::Transform& transform, if (transform.IsIdentityOrTranslation()) { gfx::QuadF mapped_quad(q); mapped_quad += - gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), - static_cast<float>(transform.matrix().getDouble(1, 3))); + gfx::Vector2dF(SkMScalarToFloat(transform.matrix().get(0, 3)), + SkMScalarToFloat(transform.matrix().get(1, 3))); *clipped = false; return mapped_quad; } @@ -446,8 +445,29 @@ gfx::PointF MathUtil::ProjectPoint(const gfx::Transform& transform, return h.CartesianPoint2d(); } +gfx::RectF MathUtil::ScaleRectProportional(const gfx::RectF& input_outer_rect, + const gfx::RectF& scale_outer_rect, + const gfx::RectF& scale_inner_rect) { + gfx::RectF output_inner_rect = input_outer_rect; + float scale_rect_to_input_scale_x = + scale_outer_rect.width() / input_outer_rect.width(); + float scale_rect_to_input_scale_y = + scale_outer_rect.height() / input_outer_rect.height(); + + gfx::Vector2dF top_left_diff = + scale_inner_rect.origin() - scale_outer_rect.origin(); + gfx::Vector2dF bottom_right_diff = + scale_inner_rect.bottom_right() - scale_outer_rect.bottom_right(); + output_inner_rect.Inset(top_left_diff.x() / scale_rect_to_input_scale_x, + top_left_diff.y() / scale_rect_to_input_scale_y, + -bottom_right_diff.x() / scale_rect_to_input_scale_x, + -bottom_right_diff.y() / scale_rect_to_input_scale_y); + return output_inner_rect; +} + static inline float ScaleOnAxis(double a, double b, double c) { - return std::sqrt(a * a + b * b + c * c); + // Do the sqrt as a double to not lose precision. + return static_cast<float>(std::sqrt(a * a + b * b + c * c)); } gfx::Vector2dF MathUtil::ComputeTransform2dScaleComponents( diff --git a/chromium/cc/base/math_util.h b/chromium/cc/base/math_util.h index 37f0c07602f..7912f8a8a25 100644 --- a/chromium/cc/base/math_util.h +++ b/chromium/cc/base/math_util.h @@ -45,7 +45,7 @@ struct HomogeneousCoordinate { // 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()); - double inv_w = 1.0 / w(); + SkMScalar inv_w = 1.0 / w(); return gfx::PointF(x() * inv_w, y() * inv_w); } @@ -56,7 +56,7 @@ struct HomogeneousCoordinate { // 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()); - double inv_w = 1.0 / w(); + SkMScalar inv_w = 1.0 / w(); return gfx::Point3F(x() * inv_w, y() * inv_w, z() * inv_w); } @@ -144,6 +144,15 @@ class CC_EXPORT MathUtil { static gfx::Vector2dF ComputeTransform2dScaleComponents(const gfx::Transform&, float fallbackValue); + // Makes a rect that has the same relationship to input_outer_rect as + // scale_inner_rect has to scale_outer_rect. scale_inner_rect should be + // contained within scale_outer_rect, and likewise the rectangle that is + // returned will be within input_outer_rect at a similar relative, scaled + // position. + static gfx::RectF ScaleRectProportional(const gfx::RectF& input_outer_rect, + const gfx::RectF& scale_outer_rect, + const gfx::RectF& scale_inner_rect); + // Returns the smallest angle between the given two vectors in degrees. // Neither vector is assumed to be normalized. static float SmallestAngleBetweenVectors(gfx::Vector2dF v1, diff --git a/chromium/cc/base/scoped_ptr_hash_map.h b/chromium/cc/base/scoped_ptr_hash_map.h deleted file mode 100644 index 47135822d2d..00000000000 --- a/chromium/cc/base/scoped_ptr_hash_map.h +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_BASE_SCOPED_PTR_HASH_MAP_H_ -#define CC_BASE_SCOPED_PTR_HASH_MAP_H_ - -#include <algorithm> -#include <utility> - -#include "base/basictypes.h" -#include "base/containers/hash_tables.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/stl_util.h" - -namespace cc { - -// This type acts like a hash_map<K, scoped_ptr<V> >, based on top of -// base::hash_map. The ScopedPtrHashMap has ownership of all values in the data -// structure. -template <typename Key, typename Value> -class ScopedPtrHashMap { - typedef base::hash_map<Key, Value*> Container; - - public: - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; - - ScopedPtrHashMap() {} - - ~ScopedPtrHashMap() { clear(); } - - void swap(ScopedPtrHashMap<Key, Value>& other) { - data_.swap(other.data_); - } - - std::pair<iterator, bool> insert( - std::pair<Key, const scoped_ptr<Value> > pair) { - return data_.insert( - std::pair<Key, Value*>(pair.first, pair.second.release())); - } - - // Replaces value but not key if key is already present. - std::pair<iterator, bool> set(Key key, scoped_ptr<Value> data) { - iterator it = find(key); - if (it != end()) - erase(it); - Value* raw_ptr = data.release(); - return data_.insert(std::pair<Key, Value*>(key, raw_ptr)); - } - - // Does nothing if key is already present - std::pair<iterator, bool> add(Key key, scoped_ptr<Value> data) { - Value* raw_ptr = data.release(); - return data_.insert(std::pair<Key, Value*>(key, raw_ptr)); - } - - void erase(iterator it) { - if (it->second) - delete it->second; - data_.erase(it); - } - - size_t erase(const Key& k) { - iterator it = data_.find(k); - if (it == data_.end()) - return 0; - erase(it); - return 1; - } - - scoped_ptr<Value> take(iterator it) { - DCHECK(it != data_.end()); - if (it == data_.end()) - return scoped_ptr<Value>(); - - Key key = it->first; - scoped_ptr<Value> ret(it->second); - data_.erase(it); - data_.insert(std::pair<Key, Value*>(key, static_cast<Value*>(NULL))); - return ret.Pass(); - } - - scoped_ptr<Value> take(const Key& k) { - iterator it = find(k); - if (it == data_.end()) - return scoped_ptr<Value>(); - - return take(it); - } - - scoped_ptr<Value> take_and_erase(iterator it) { - DCHECK(it != data_.end()); - if (it == data_.end()) - return scoped_ptr<Value>(); - - scoped_ptr<Value> ret(it->second); - data_.erase(it); - return ret.Pass(); - } - - scoped_ptr<Value> take_and_erase(const Key& k) { - iterator it = find(k); - if (it == data_.end()) - return scoped_ptr<Value>(); - - return take_and_erase(it); - } - - // Returns the first element in the hash_map that matches the given key. - // If no such element exists it returns NULL. - Value* get(const Key& k) const { - const_iterator it = find(k); - if (it == end()) - return 0; - return it->second; - } - - inline bool contains(const Key& k) const { return data_.count(k) > 0; } - - inline void clear() { STLDeleteValues(&data_); } - - inline const_iterator find(const Key& k) const { return data_.find(k); } - inline iterator find(const Key& k) { return data_.find(k); } - - inline size_t count(const Key& k) const { return data_.count(k); } - inline std::pair<const_iterator, const_iterator> equal_range( - const Key& k) const { - return data_.equal_range(k); - } - inline std::pair<iterator, iterator> equal_range(const Key& k) { - return data_.equal_range(k); - } - - inline size_t size() const { return data_.size(); } - inline size_t max_size() const { return data_.max_size(); } - - inline bool empty() const { return data_.empty(); } - - inline size_t bucket_count() const { return data_.bucket_count(); } - inline void resize(size_t size) const { return data_.resize(size); } - - inline iterator begin() { return data_.begin(); } - inline const_iterator begin() const { return data_.begin(); } - inline iterator end() { return data_.end(); } - inline const_iterator end() const { return data_.end(); } - - private: - Container data_; - - DISALLOW_COPY_AND_ASSIGN(ScopedPtrHashMap); -}; - -} // namespace cc - -#endif // CC_BASE_SCOPED_PTR_HASH_MAP_H_ diff --git a/chromium/cc/base/switches.cc b/chromium/cc/base/switches.cc index 2dac58b6911..87e10ce0089 100644 --- a/chromium/cc/base/switches.cc +++ b/chromium/cc/base/switches.cc @@ -14,6 +14,9 @@ namespace switches { const char kBackgroundColorInsteadOfCheckerboard[] = "background-color-instead-of-checkerboard"; +// Disables LCD text. +const char kDisableLCDText[] = "disable-lcd-text"; + const char kDisableThreadedAnimation[] = "disable-threaded-animation"; // Disables layer-edge anti-aliasing in the compositor. @@ -24,6 +27,9 @@ const char kDisableCompositedAntialiasing[] = // Overrides the kEnableImplSidePainting flag. const char kDisableImplSidePainting[] = "disable-impl-side-painting"; +// Enables LCD text. +const char kEnableLCDText[] = "enable-lcd-text"; + // Paint content on the compositor thread instead of the main thread. const char kEnableImplSidePainting[] = "enable-impl-side-painting"; @@ -125,12 +131,33 @@ const char kUIShowOccludingRects[] = "ui-show-occluding-rects"; const char kShowNonOccludingRects[] = "show-nonoccluding-rects"; const char kUIShowNonOccludingRects[] = "ui-show-nonoccluding-rects"; -// Enable the codepath that uses images within TileManager. -const char kUseMapImage[] = "use-map-image"; +// Enable rasterizer that writes directly to GPU memory. +const char kEnableMapImage[] = "enable-map-image"; + +// Disable rasterizer that writes directly to GPU memory. +// Overrides the kEnableMapImage flag. +const char kDisableMapImage[] = "disable-map-image"; // Prevents the layer tree unit tests from timing out. const char kCCLayerTreeTestNoTimeout[] = "cc-layer-tree-test-no-timeout"; +// Disable textures using RGBA_4444 layout. +const char kDisable4444Textures[] = "disable-4444-textures"; + +bool IsLCDTextEnabled() { + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(cc::switches::kDisableLCDText)) + return false; + else if (command_line->HasSwitch(cc::switches::kEnableLCDText)) + return true; + +#if defined(OS_ANDROID) + return false; +#else + return true; +#endif +} + bool IsImplSidePaintingEnabled() { const CommandLine& command_line = *CommandLine::ForCurrentProcess(); @@ -146,5 +173,16 @@ bool IsImplSidePaintingEnabled() { #endif } +bool IsMapImageEnabled() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + + if (command_line.HasSwitch(cc::switches::kDisableMapImage)) + return false; + else if (command_line.HasSwitch(cc::switches::kEnableMapImage)) + return true; + + return false; +} + } // namespace switches } // namespace cc diff --git a/chromium/cc/base/switches.h b/chromium/cc/base/switches.h index f87eb53ee16..97fe833390c 100644 --- a/chromium/cc/base/switches.h +++ b/chromium/cc/base/switches.h @@ -17,9 +17,11 @@ namespace switches { // Switches for the renderer compositor only. CC_EXPORT extern const char kBackgroundColorInsteadOfCheckerboard[]; +CC_EXPORT extern const char kDisableLCDText[]; CC_EXPORT extern const char kDisableImplSidePainting[]; CC_EXPORT extern const char kDisableThreadedAnimation[]; CC_EXPORT extern const char kDisableCompositedAntialiasing[]; +CC_EXPORT extern const char kEnableLCDText[]; CC_EXPORT extern const char kEnableImplSidePainting[]; CC_EXPORT extern const char kEnableTopControlsPositionCalculation[]; CC_EXPORT extern const char kForceDirectLayerDrawing[]; @@ -37,7 +39,9 @@ CC_EXPORT extern const char kMaxUnusedResourceMemoryUsagePercentage[]; CC_EXPORT extern const char kEnablePinchVirtualViewport[]; CC_EXPORT extern const char kEnablePartialSwap[]; CC_EXPORT extern const char kStrictLayerPropertyChangeChecking[]; -CC_EXPORT extern const char kUseMapImage[]; +CC_EXPORT extern const char kEnableMapImage[]; +CC_EXPORT extern const char kDisableMapImage[]; +CC_EXPORT extern const char kDisable4444Textures[]; // Switches for both the renderer and ui compositors. CC_EXPORT extern const char kUIDisablePartialSwap[]; @@ -65,7 +69,9 @@ CC_EXPORT extern const char kUIShowNonOccludingRects[]; // Unit test related. CC_EXPORT extern const char kCCLayerTreeTestNoTimeout[]; +CC_EXPORT bool IsLCDTextEnabled(); CC_EXPORT bool IsImplSidePaintingEnabled(); +CC_EXPORT bool IsMapImageEnabled(); } // namespace switches } // namespace cc diff --git a/chromium/cc/cc.gyp b/chromium/cc/cc.gyp index 78060a4175c..ffc86153f29 100644 --- a/chromium/cc/cc.gyp +++ b/chromium/cc/cc.gyp @@ -14,11 +14,11 @@ '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', '<(DEPTH)/gpu/gpu.gyp:gpu', - '<(DEPTH)/skia/skia.gyp:skia', '<(DEPTH)/media/media.gyp:media', + '<(DEPTH)/skia/skia.gyp:skia', + '<(DEPTH)/third_party/WebKit/public/blink.gyp:blink_minimal', '<(DEPTH)/ui/gl/gl.gyp:gl', '<(DEPTH)/ui/ui.gyp:ui', - '<(DEPTH)/third_party/WebKit/public/blink.gyp:blink_minimal', ], 'defines': [ 'CC_IMPLEMENTATION=1', @@ -44,6 +44,8 @@ 'animation/scrollbar_animation_controller.h', 'animation/scrollbar_animation_controller_linear_fade.cc', 'animation/scrollbar_animation_controller_linear_fade.h', + 'animation/scrollbar_animation_controller_thinning.cc', + 'animation/scrollbar_animation_controller_thinning.h', 'animation/timing_function.cc', 'animation/timing_function.h', 'animation/transform_operation.cc', @@ -59,7 +61,6 @@ 'base/region.h', 'base/scoped_ptr_algorithm.h', 'base/scoped_ptr_deque.h', - 'base/scoped_ptr_hash_map.h', 'base/scoped_ptr_vector.h', 'base/switches.cc', 'base/switches.h', @@ -86,6 +87,10 @@ 'debug/rendering_stats_instrumentation.cc', 'debug/rendering_stats_instrumentation.h', 'debug/ring_buffer.h', + 'debug/test_context_provider.cc', + 'debug/test_context_provider.h', + 'debug/test_web_graphics_context_3d.cc', + 'debug/test_web_graphics_context_3d.h', 'debug/traced_picture.cc', 'debug/traced_picture.h', 'debug/traced_value.cc', @@ -121,6 +126,7 @@ 'layers/io_surface_layer_impl.h', 'layers/layer.cc', 'layers/layer.h', + 'layers/layer_client.h', 'layers/layer_impl.cc', 'layers/layer_impl.h', 'layers/layer_iterator.cc', @@ -134,6 +140,10 @@ 'layers/nine_patch_layer_impl.cc', 'layers/nine_patch_layer_impl.h', 'layers/paint_properties.h', + 'layers/painted_scrollbar_layer.cc', + 'layers/painted_scrollbar_layer.h', + 'layers/painted_scrollbar_layer_impl.cc', + 'layers/painted_scrollbar_layer_impl.h', 'layers/picture_image_layer.cc', 'layers/picture_image_layer.h', 'layers/picture_image_layer_impl.cc', @@ -148,14 +158,17 @@ 'layers/render_surface.h', 'layers/render_surface_impl.cc', 'layers/render_surface_impl.h', - 'layers/scrollbar_layer.cc', - 'layers/scrollbar_layer.h', - 'layers/scrollbar_layer_impl.cc', - 'layers/scrollbar_layer_impl.h', + 'layers/scrollbar_layer_impl_base.cc', + 'layers/scrollbar_layer_impl_base.h', + 'layers/scrollbar_layer_interface.h', 'layers/solid_color_layer.cc', 'layers/solid_color_layer.h', 'layers/solid_color_layer_impl.cc', 'layers/solid_color_layer_impl.h', + 'layers/solid_color_scrollbar_layer.cc', + 'layers/solid_color_scrollbar_layer.h', + 'layers/solid_color_scrollbar_layer_impl.cc', + 'layers/solid_color_scrollbar_layer_impl.h', 'layers/texture_layer.cc', 'layers/texture_layer.h', 'layers/texture_layer_client.h', @@ -180,6 +193,7 @@ 'output/compositor_frame_ack.h', 'output/compositor_frame_metadata.cc', 'output/compositor_frame_metadata.h', + 'output/context_provider.cc', 'output/context_provider.h', 'output/copy_output_request.cc', 'output/copy_output_request.h', @@ -300,8 +314,11 @@ 'resources/raster_mode.h', 'resources/raster_worker_pool.cc', 'resources/raster_worker_pool.h', + 'resources/release_callback.h', 'resources/resource.cc', 'resources/resource.h', + 'resources/resource_format.h', + 'resources/resource_format.cc', 'resources/resource_pool.cc', 'resources/resource_pool.h', 'resources/resource_provider.cc', @@ -312,16 +329,21 @@ 'resources/resource_update_controller.h', 'resources/resource_update_queue.cc', 'resources/resource_update_queue.h', + 'resources/returned_resource.h', 'resources/scoped_resource.cc', 'resources/scoped_resource.h', 'resources/scoped_ui_resource.cc', 'resources/scoped_ui_resource.h', + 'resources/single_release_callback.cc', + 'resources/single_release_callback.h', 'resources/skpicture_content_layer_updater.cc', 'resources/skpicture_content_layer_updater.h', 'resources/sync_point_helper.cc', 'resources/sync_point_helper.h', 'resources/texture_mailbox.cc', 'resources/texture_mailbox.h', + 'resources/texture_mailbox_deleter.cc', + 'resources/texture_mailbox_deleter.h', 'resources/tile.cc', 'resources/tile.h', 'resources/tile_manager.cc', @@ -354,6 +376,8 @@ 'scheduler/texture_uploader.cc', 'scheduler/texture_uploader.h', 'scheduler/time_source.h', + 'trees/blocking_task_runner.cc', + 'trees/blocking_task_runner.h', 'trees/damage_tracker.cc', 'trees/damage_tracker.h', 'trees/layer_sorter.cc', diff --git a/chromium/cc/cc_tests.gyp b/chromium/cc/cc_tests.gyp index 7e2ff87f574..f271a7074be 100644 --- a/chromium/cc/cc_tests.gyp +++ b/chromium/cc/cc_tests.gyp @@ -10,6 +10,7 @@ 'animation/keyframed_animation_curve_unittest.cc', 'animation/layer_animation_controller_unittest.cc', 'animation/scrollbar_animation_controller_linear_fade_unittest.cc', + 'animation/scrollbar_animation_controller_thinning_unittest.cc', 'animation/timing_function_unittest.cc', 'animation/transform_operations_unittest.cc', 'base/float_quad_unittest.cc', @@ -31,6 +32,7 @@ 'layers/nine_patch_layer_unittest.cc', 'layers/picture_image_layer_impl_unittest.cc', 'layers/picture_layer_impl_unittest.cc', + 'layers/picture_layer_unittest.cc', 'layers/render_surface_unittest.cc', 'layers/scrollbar_layer_unittest.cc', 'layers/solid_color_layer_impl_unittest.cc', @@ -59,6 +61,7 @@ 'resources/resource_provider_unittest.cc', 'resources/resource_update_controller_unittest.cc', 'resources/scoped_resource_unittest.cc', + 'resources/texture_mailbox_deleter_unittest.cc', 'resources/tile_manager_unittest.cc', 'resources/tile_priority_unittest.cc', 'resources/video_resource_updater_unittest.cc', @@ -100,8 +103,6 @@ 'test/fake_content_layer_client.h', 'test/fake_content_layer_impl.cc', 'test/fake_content_layer_impl.h', - 'test/fake_context_provider.cc', - 'test/fake_context_provider.h', 'test/fake_delegated_renderer_layer.cc', 'test/fake_delegated_renderer_layer.h', 'test/fake_delegated_renderer_layer_impl.cc', @@ -113,6 +114,8 @@ 'test/fake_layer_tree_host_impl_client.cc', 'test/fake_layer_tree_host_impl_client.h', 'test/fake_layer_tree_host_impl.h', + 'test/fake_painted_scrollbar_layer.cc', + 'test/fake_painted_scrollbar_layer.h', 'test/fake_picture_layer.cc', 'test/fake_picture_layer.h', 'test/fake_picture_layer_impl.cc', @@ -126,8 +129,6 @@ 'test/fake_rendering_stats_instrumentation.h', 'test/fake_scrollbar.cc', 'test/fake_scrollbar.h', - 'test/fake_scrollbar_layer.cc', - 'test/fake_scrollbar_layer.h', 'test/fake_tile_manager.cc', 'test/fake_tile_manager.h', 'test/fake_tile_manager_client.h', @@ -174,8 +175,6 @@ 'test/skia_common.h', 'test/test_tile_priorities.cc', 'test/test_tile_priorities.h', - 'test/test_web_graphics_context_3d.cc', - 'test/test_web_graphics_context_3d.h', 'test/tiled_layer_test_common.cc', 'test/tiled_layer_test_common.h', ], @@ -240,15 +239,18 @@ '../skia/skia.gyp:skia', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', + '../testing/perf/perf_test.gyp:*', '../ui/ui.gyp:ui', 'cc.gyp:cc', 'cc_test_support', ], 'sources': [ + 'resources/picture_layer_tiling_perftest.cc', 'resources/raster_worker_pool_perftest.cc', 'resources/tile_manager_perftest.cc', 'resources/worker_pool_perftest.cc', 'test/cc_test_suite.cc', + 'test/lap_timer.cc', 'test/run_all_unittests.cc', 'trees/layer_tree_host_perftest.cc', ], diff --git a/chromium/cc/debug/devtools_instrumentation.h b/chromium/cc/debug/devtools_instrumentation.h index 7a77d404454..eea9e62f02a 100644 --- a/chromium/cc/debug/devtools_instrumentation.h +++ b/chromium/cc/debug/devtools_instrumentation.h @@ -14,11 +14,13 @@ namespace internal { const char kCategory[] = "cc,devtools"; const char kLayerId[] = "layerId"; const char kLayerTreeId[] = "layerTreeId"; +const char kPixelRefId[] = "pixelRefId"; + +const char kImageDecodeTask[] = "ImageDecodeTask"; } const char kPaintLayer[] = "PaintLayer"; const char kRasterTask[] = "RasterTask"; -const char kImageDecodeTask[] = "ImageDecodeTask"; const char kPaintSetup[] = "PaintSetup"; const char kUpdateLayer[] = "UpdateLayer"; @@ -38,6 +40,19 @@ class ScopedLayerTask { DISALLOW_COPY_AND_ASSIGN(ScopedLayerTask); }; +class ScopedImageDecodeTask { + public: + explicit ScopedImageDecodeTask(void* pixelRef) { + TRACE_EVENT_BEGIN1(internal::kCategory, internal::kImageDecodeTask, + internal::kPixelRefId, reinterpret_cast<uint64>(pixelRef)); + } + ~ScopedImageDecodeTask() { + TRACE_EVENT_END0(internal::kCategory, internal::kImageDecodeTask); + } + private: + DISALLOW_COPY_AND_ASSIGN(ScopedImageDecodeTask); +}; + class ScopedLayerTreeTask { public: ScopedLayerTreeTask(const char* event_name, diff --git a/chromium/cc/debug/fake_web_graphics_context_3d.h b/chromium/cc/debug/fake_web_graphics_context_3d.h index 3c8a6f53b76..04c983de006 100644 --- a/chromium/cc/debug/fake_web_graphics_context_3d.h +++ b/chromium/cc/debug/fake_web_graphics_context_3d.h @@ -67,9 +67,6 @@ class CC_EXPORT FakeWebGraphicsContext3D WebKit::WGC3Dsizei num_attachments, const WebKit::WGC3Denum* attachments) {} - virtual void setMemoryAllocationChangedCallbackCHROMIUM( - WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) {} - virtual WebKit::WebString getRequestableExtensionsCHROMIUM(); virtual void requestExtensionCHROMIUM(const char*) {} diff --git a/chromium/cc/debug/frame_rate_counter.cc b/chromium/cc/debug/frame_rate_counter.cc index 8a829ff711a..2f3b291fea9 100644 --- a/chromium/cc/debug/frame_rate_counter.cc +++ b/chromium/cc/debug/frame_rate_counter.cc @@ -44,7 +44,7 @@ base::TimeDelta FrameRateCounter::RecentFrameInterval(size_t n) const { FrameRateCounter::FrameRateCounter(bool has_impl_thread) : has_impl_thread_(has_impl_thread), dropped_frame_count_(0) {} -void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) { +void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp, bool software) { ring_buffer_.SaveToBuffer(timestamp); // Check if frame interval can be computed. @@ -55,16 +55,26 @@ void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) { RecentFrameInterval(ring_buffer_.BufferSize() - 1); if (has_impl_thread_ && ring_buffer_.CurrentIndex() > 0) { - UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay", - frame_interval_seconds.InMilliseconds(), - 1, - 120, - 60); + if (software) { + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Renderer4.SoftwareCompositorThreadImplDrawDelay", + frame_interval_seconds.InMilliseconds(), + 1, + 120, + 60); + } else { + UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay", + frame_interval_seconds.InMilliseconds(), + 1, + 120, + 60); + } } if (!IsBadFrameInterval(frame_interval_seconds) && frame_interval_seconds.InSecondsF() > kDroppedFrameTime) - ++dropped_frame_count_; + dropped_frame_count_ += + frame_interval_seconds.InSecondsF() / kDroppedFrameTime; } bool FrameRateCounter::IsBadFrameInterval( diff --git a/chromium/cc/debug/frame_rate_counter.h b/chromium/cc/debug/frame_rate_counter.h index 32b4c2c24f1..0b69b29aea5 100644 --- a/chromium/cc/debug/frame_rate_counter.h +++ b/chromium/cc/debug/frame_rate_counter.h @@ -22,7 +22,7 @@ class FrameRateCounter { int dropped_frame_count() const { return dropped_frame_count_; } size_t time_stamp_history_size() const { return ring_buffer_.BufferSize(); } - void SaveTimeStamp(base::TimeTicks timestamp); + void SaveTimeStamp(base::TimeTicks timestamp, bool software); // n = 0 returns the oldest frame interval retained in the history, while n = // time_stamp_history_size() - 1 returns the most recent frame interval. diff --git a/chromium/cc/debug/overdraw_metrics.cc b/chromium/cc/debug/overdraw_metrics.cc index 5b19370fe2c..a14284d800d 100644 --- a/chromium/cc/debug/overdraw_metrics.cc +++ b/chromium/cc/debug/overdraw_metrics.cc @@ -147,11 +147,11 @@ void OverdrawMetrics::RecordMetrics( } } -static gfx::Size DeviceViewportSize(const LayerTreeHost* host) { +static gfx::Size DrawViewportSize(const LayerTreeHost* host) { return host->device_viewport_size(); } -static gfx::Size DeviceViewportSize(const LayerTreeHostImpl* host_impl) { - return host_impl->device_viewport_size(); +static gfx::Size DrawViewportSize(const LayerTreeHostImpl* host_impl) { + return host_impl->DrawViewportSize(); } template <typename LayerTreeHostType> @@ -160,13 +160,13 @@ void OverdrawMetrics::RecordMetricsInternal( const LayerTreeHostType* layer_tree_host) const { // This gives approximately 10x the percentage of pixels to fill the viewport // once. - float normalization = 1000.f / (DeviceViewportSize(layer_tree_host).width() * - DeviceViewportSize(layer_tree_host).height()); + float normalization = 1000.f / (DrawViewportSize(layer_tree_host).width() * + DrawViewportSize(layer_tree_host).height()); // This gives approximately 100x the percentage of tiles to fill the viewport // once, if all tiles were 256x256. float tile_normalization = - 10000.f / (DeviceViewportSize(layer_tree_host).width() / 256.f * - DeviceViewportSize(layer_tree_host).height() / 256.f); + 10000.f / (DrawViewportSize(layer_tree_host).width() / 256.f * + DrawViewportSize(layer_tree_host).height() / 256.f); // This gives approximately 10x the percentage of bytes to fill the viewport // once, assuming 4 bytes per pixel. float byte_normalization = normalization / 4; diff --git a/chromium/cc/debug/rendering_stats.cc b/chromium/cc/debug/rendering_stats.cc index 12c54530c71..64cf69e0fe3 100644 --- a/chromium/cc/debug/rendering_stats.cc +++ b/chromium/cc/debug/rendering_stats.cc @@ -2,98 +2,195 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/values.h" #include "cc/debug/rendering_stats.h" namespace cc { -RenderingStats::RenderingStats() - : animation_frame_count(0), - screen_frame_count(0), +MainThreadRenderingStats::MainThreadRenderingStats() + : animation_frame_count(0), + screen_frame_count(0), + commit_count(0), + painted_pixel_count(0), + recorded_pixel_count(0), + image_gathering_count(0) {} + +ImplThreadRenderingStats::ImplThreadRenderingStats() + : screen_frame_count(0), dropped_frame_count(0), - total_commit_count(0), - total_pixels_painted(0), - total_pixels_recorded(0), - total_pixels_rasterized(0), - num_impl_thread_scrolls(0), - num_main_thread_scrolls(0), - num_layers_drawn(0), - num_missing_tiles(0), - total_deferred_image_decode_count(0), - total_deferred_image_cache_hit_count(0), - total_image_gathering_count(0), - total_tiles_analyzed(0), - solid_color_tiles_analyzed(0) {} + rasterized_pixel_count(0), + impl_thread_scroll_count(0), + main_thread_scroll_count(0), + drawn_layer_count(0), + missing_tile_count(0), + deferred_image_decode_count(0), + deferred_image_cache_hit_count(0), + tile_analysis_count(0), + solid_color_tile_analysis_count(0) {} void RenderingStats::EnumerateFields(Enumerator* enumerator) const { - enumerator->AddInt64("numAnimationFrames", animation_frame_count); - enumerator->AddInt64("numFramesSentToScreen", screen_frame_count); - enumerator->AddInt64("droppedFrameCount", dropped_frame_count); + enumerator->AddInt64("numAnimationFrames", + main_stats.animation_frame_count); + enumerator->AddInt64("numFramesSentToScreen", main_stats.screen_frame_count + + impl_stats.screen_frame_count); enumerator->AddDouble("totalPaintTimeInSeconds", - total_paint_time.InSecondsF()); + main_stats.paint_time.InSecondsF()); enumerator->AddDouble("totalRecordTimeInSeconds", - total_record_time.InSecondsF()); + main_stats.record_time.InSecondsF()); + enumerator->AddDouble("totalCommitTimeInSeconds", + main_stats.commit_time.InSecondsF()); + enumerator->AddInt64("totalCommitCount", main_stats.commit_count); + enumerator->AddInt64("totalPixelsPainted", main_stats.painted_pixel_count); + enumerator->AddInt64("totalPixelsRecorded", main_stats.recorded_pixel_count); + enumerator->AddInt64("totalImageGatheringCount", + main_stats.image_gathering_count); + enumerator->AddDouble("totalImageGatheringTimeInSeconds", + main_stats.image_gathering_time.InSecondsF()); + enumerator->AddInt64("droppedFrameCount", impl_stats.dropped_frame_count); enumerator->AddDouble("totalRasterizeTimeInSeconds", - total_rasterize_time.InSecondsF()); + impl_stats.rasterize_time.InSecondsF()); enumerator->AddDouble( "totalRasterizeTimeForNowBinsOnPendingTree", - total_rasterize_time_for_now_bins_on_pending_tree.InSecondsF()); - enumerator->AddDouble("totalCommitTimeInSeconds", - total_commit_time.InSecondsF()); + impl_stats.rasterize_time_for_now_bins_on_pending_tree.InSecondsF()); enumerator->AddDouble("bestRasterizeTimeInSeconds", - best_rasterize_time.InSecondsF()); - enumerator->AddInt64("totalCommitCount", total_commit_count); - enumerator->AddInt64("totalPixelsPainted", total_pixels_painted); - enumerator->AddInt64("totalPixelsRecorded", total_pixels_recorded); - enumerator->AddInt64("totalPixelsRasterized", total_pixels_rasterized); - enumerator->AddInt64("numImplThreadScrolls", num_impl_thread_scrolls); - enumerator->AddInt64("numMainThreadScrolls", num_main_thread_scrolls); - enumerator->AddInt64("numLayersDrawn", num_layers_drawn); - enumerator->AddInt64("numMissingTiles", num_missing_tiles); + impl_stats.best_rasterize_time.InSecondsF()); + enumerator->AddInt64("totalPixelsRasterized", + impl_stats.rasterized_pixel_count); + enumerator->AddInt64("numImplThreadScrolls", + impl_stats.impl_thread_scroll_count); + enumerator->AddInt64("numMainThreadScrolls", + impl_stats.main_thread_scroll_count); + enumerator->AddInt64("numLayersDrawn", impl_stats.drawn_layer_count); + enumerator->AddInt64("numMissingTiles", impl_stats.missing_tile_count); enumerator->AddInt64("totalDeferredImageDecodeCount", - total_deferred_image_decode_count); - enumerator->AddInt64("totalTilesAnalyzed", total_tiles_analyzed); + impl_stats.deferred_image_decode_count); + enumerator->AddInt64("totalTilesAnalyzed", impl_stats.tile_analysis_count); enumerator->AddInt64("solidColorTilesAnalyzed", - solid_color_tiles_analyzed); + impl_stats.solid_color_tile_analysis_count); enumerator->AddInt64("totalDeferredImageCacheHitCount", - total_deferred_image_cache_hit_count); - enumerator->AddInt64("totalImageGatheringCount", - total_image_gathering_count); + impl_stats.deferred_image_cache_hit_count); enumerator->AddDouble("totalDeferredImageDecodeTimeInSeconds", - total_deferred_image_decode_time.InSecondsF()); - enumerator->AddDouble("totalImageGatheringTimeInSeconds", - total_image_gathering_time.InSecondsF()); + impl_stats.deferred_image_decode_time.InSecondsF()); enumerator->AddDouble("totalTileAnalysisTimeInSeconds", - total_tile_analysis_time.InSecondsF()); + impl_stats.tile_analysis_time.InSecondsF()); } -void RenderingStats::Add(const RenderingStats& other) { +void MainThreadRenderingStats::IssueTraceEvent() const { + TRACE_EVENT_INSTANT1("benchmark", + "MainThreadRenderingStats::IssueTraceEvent", + TRACE_EVENT_SCOPE_THREAD, + "data", AsTraceableData()); +} + +scoped_ptr<base::debug::ConvertableToTraceFormat> +MainThreadRenderingStats::AsTraceableData() const { + scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue()); + record_data->SetInteger("animation_frame_count", + animation_frame_count); + record_data->SetInteger("screen_frame_count", + screen_frame_count); + record_data->SetDouble("paint_time", + paint_time.InSecondsF()); + record_data->SetDouble("record_time", + record_time.InSecondsF()); + record_data->SetDouble("commit_time", + commit_time.InSecondsF()); + record_data->SetInteger("commit_count", + commit_count); + record_data->SetInteger("painted_pixel_count", + painted_pixel_count); + record_data->SetInteger("recorded_pixel_count", + recorded_pixel_count); + record_data->SetInteger("image_gathering_count", + image_gathering_count); + record_data->SetDouble("image_gathering_time", + image_gathering_time.InSecondsF()); + return TracedValue::FromValue(record_data.release()); +} + +void ImplThreadRenderingStats::IssueTraceEvent() const { + TRACE_EVENT_INSTANT1("benchmark", + "ImplThreadRenderingStats::IssueTraceEvent", + TRACE_EVENT_SCOPE_THREAD, + "data", AsTraceableData()); +} + +scoped_ptr<base::debug::ConvertableToTraceFormat> +ImplThreadRenderingStats::AsTraceableData() const { + scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue()); + record_data->SetInteger("screen_frame_count", + screen_frame_count); + record_data->SetInteger("dropped_frame_count", + dropped_frame_count); + record_data->SetDouble("rasterize_time", + rasterize_time.InSecondsF()); + record_data->SetDouble( + "rasterize_time_for_now_bins_on_pending_tree", + rasterize_time_for_now_bins_on_pending_tree.InSecondsF()); + record_data->SetDouble("best_rasterize_time", + best_rasterize_time.InSecondsF()); + record_data->SetInteger("rasterized_pixel_count", + rasterized_pixel_count); + record_data->SetInteger("impl_thread_scroll_count", + impl_thread_scroll_count); + record_data->SetInteger("main_thread_scroll_count", + main_thread_scroll_count); + record_data->SetInteger("drawn_layer_count", + drawn_layer_count); + record_data->SetInteger("missing_tile_count", + missing_tile_count); + record_data->SetInteger("deferred_image_decode_count", + deferred_image_decode_count); + record_data->SetInteger("deferred_image_cache_hit_count", + deferred_image_cache_hit_count); + record_data->SetInteger("tile_analysis_count", + tile_analysis_count); + record_data->SetInteger("solid_color_tile_analysis_count", + solid_color_tile_analysis_count); + record_data->SetDouble("deferred_image_decode_time", + deferred_image_decode_time.InSecondsF()); + record_data->SetDouble("tile_analysis_time", + tile_analysis_time.InSecondsF()); + return TracedValue::FromValue(record_data.release()); +} + + +void MainThreadRenderingStats::Add(const MainThreadRenderingStats& other) { animation_frame_count += other.animation_frame_count; screen_frame_count += other.screen_frame_count; + paint_time += other.paint_time; + record_time += other.record_time; + commit_time += other.commit_time; + commit_count += other.commit_count; + painted_pixel_count += other.painted_pixel_count; + recorded_pixel_count += other.recorded_pixel_count; + image_gathering_count += other.image_gathering_count; + image_gathering_time += other.image_gathering_time; +} + +void ImplThreadRenderingStats::Add(const ImplThreadRenderingStats& other) { + screen_frame_count += other.screen_frame_count; dropped_frame_count += other.dropped_frame_count; - total_paint_time += other.total_paint_time; - total_record_time += other.total_record_time; - total_rasterize_time += other.total_rasterize_time; - total_rasterize_time_for_now_bins_on_pending_tree += - other.total_rasterize_time_for_now_bins_on_pending_tree; - total_commit_time += other.total_commit_time; + rasterize_time += other.rasterize_time; + rasterize_time_for_now_bins_on_pending_tree += + other.rasterize_time_for_now_bins_on_pending_tree; best_rasterize_time += other.best_rasterize_time; - total_commit_count += other.total_commit_count; - total_pixels_painted += other.total_pixels_painted; - total_pixels_recorded += other.total_pixels_recorded; - total_pixels_rasterized += other.total_pixels_rasterized; - num_impl_thread_scrolls += other.num_impl_thread_scrolls; - num_main_thread_scrolls += other.num_main_thread_scrolls; - num_layers_drawn += other.num_layers_drawn; - num_missing_tiles += other.num_missing_tiles; - total_deferred_image_decode_count += other.total_deferred_image_decode_count; - total_deferred_image_cache_hit_count += - other.total_deferred_image_cache_hit_count; - total_image_gathering_count += other.total_image_gathering_count; - total_deferred_image_decode_time += other.total_deferred_image_decode_time; - total_image_gathering_time += other.total_image_gathering_time; - total_tiles_analyzed += other.total_tiles_analyzed; - solid_color_tiles_analyzed += other.solid_color_tiles_analyzed; - total_tile_analysis_time += other.total_tile_analysis_time; + rasterized_pixel_count += other.rasterized_pixel_count; + impl_thread_scroll_count += other.impl_thread_scroll_count; + main_thread_scroll_count += other.main_thread_scroll_count; + drawn_layer_count += other.drawn_layer_count; + missing_tile_count += other.missing_tile_count; + deferred_image_decode_count += other.deferred_image_decode_count; + deferred_image_cache_hit_count += other.deferred_image_cache_hit_count; + deferred_image_decode_time += other.deferred_image_decode_time; + tile_analysis_count += other.tile_analysis_count; + solid_color_tile_analysis_count += other.solid_color_tile_analysis_count; + tile_analysis_time += other.tile_analysis_time; +} + +void RenderingStats::Add(const RenderingStats& other) { + main_stats.Add(other.main_stats); + impl_stats.Add(other.impl_stats); } } // namespace cc diff --git a/chromium/cc/debug/rendering_stats.h b/chromium/cc/debug/rendering_stats.h index eb01a571b11..ee13706034a 100644 --- a/chromium/cc/debug/rendering_stats.h +++ b/chromium/cc/debug/rendering_stats.h @@ -8,55 +8,79 @@ #include "base/basictypes.h" #include "base/time/time.h" #include "cc/base/cc_export.h" +#include "cc/debug/traced_value.h" namespace cc { -struct CC_EXPORT RenderingStats { +// In conjunction with EnumerateFields, this allows the embedder to +// enumerate the values in this structure without +// having to embed references to its specific member variables. This +// simplifies the addition of new fields to this type. +class RenderingStatsEnumerator { + public: + virtual void AddInt64(const char* name, int64 value) = 0; + virtual void AddDouble(const char* name, double value) = 0; + virtual void AddInt(const char* name, int value) = 0; + virtual void AddTimeDeltaInSecondsF(const char* name, + const base::TimeDelta& value) = 0; + + protected: + virtual ~RenderingStatsEnumerator() {} +}; + +struct CC_EXPORT MainThreadRenderingStats { + // Note: when adding new members, please remember to update EnumerateFields + // and Add in rendering_stats.cc. + int64 animation_frame_count; int64 screen_frame_count; - int64 dropped_frame_count; - base::TimeDelta total_paint_time; - base::TimeDelta total_record_time; - base::TimeDelta total_rasterize_time; - base::TimeDelta total_rasterize_time_for_now_bins_on_pending_tree; - base::TimeDelta total_commit_time; - base::TimeDelta best_rasterize_time; - int64 total_commit_count; - int64 total_pixels_painted; - int64 total_pixels_recorded; - int64 total_pixels_rasterized; - int64 num_impl_thread_scrolls; - int64 num_main_thread_scrolls; - int64 num_layers_drawn; - int64 num_missing_tiles; - int64 total_deferred_image_decode_count; - int64 total_deferred_image_cache_hit_count; - int64 total_image_gathering_count; - int64 total_tiles_analyzed; - int64 solid_color_tiles_analyzed; - base::TimeDelta total_deferred_image_decode_time; - base::TimeDelta total_image_gathering_time; - base::TimeDelta total_tile_analysis_time; + base::TimeDelta paint_time; + base::TimeDelta record_time; + base::TimeDelta commit_time; + int64 commit_count; + int64 painted_pixel_count; + int64 recorded_pixel_count; + int64 image_gathering_count; + base::TimeDelta image_gathering_time; + + MainThreadRenderingStats(); + void IssueTraceEvent() const; + scoped_ptr<base::debug::ConvertableToTraceFormat> AsTraceableData() const; + void Add(const MainThreadRenderingStats& other); +}; + +struct CC_EXPORT ImplThreadRenderingStats { // Note: when adding new members, please remember to update EnumerateFields // and Add in rendering_stats.cc. - RenderingStats(); - - // In conjunction with EnumerateFields, this allows the embedder to - // enumerate the values in this structure without - // having to embed references to its specific member variables. This - // simplifies the addition of new fields to this type. - class Enumerator { - public: - virtual void AddInt64(const char* name, int64 value) = 0; - virtual void AddDouble(const char* name, double value) = 0; - virtual void AddInt(const char* name, int value) = 0; - virtual void AddTimeDeltaInSecondsF(const char* name, - const base::TimeDelta& value) = 0; - - protected: - virtual ~Enumerator() {} - }; + int64 screen_frame_count; + int64 dropped_frame_count; + base::TimeDelta rasterize_time; + base::TimeDelta rasterize_time_for_now_bins_on_pending_tree; + base::TimeDelta best_rasterize_time; + int64 rasterized_pixel_count; + int64 impl_thread_scroll_count; + int64 main_thread_scroll_count; + int64 drawn_layer_count; + int64 missing_tile_count; + int64 deferred_image_decode_count; + int64 deferred_image_cache_hit_count; + int64 tile_analysis_count; + int64 solid_color_tile_analysis_count; + base::TimeDelta deferred_image_decode_time; + base::TimeDelta tile_analysis_time; + + ImplThreadRenderingStats(); + void IssueTraceEvent() const; + scoped_ptr<base::debug::ConvertableToTraceFormat> AsTraceableData() const; + void Add(const ImplThreadRenderingStats& other); +}; + +struct CC_EXPORT RenderingStats { + typedef RenderingStatsEnumerator Enumerator; + + MainThreadRenderingStats main_stats; + ImplThreadRenderingStats impl_stats; // Outputs the fields in this structure to the provided enumerator. void EnumerateFields(Enumerator* enumerator) const; diff --git a/chromium/cc/debug/rendering_stats_instrumentation.cc b/chromium/cc/debug/rendering_stats_instrumentation.cc index b3886af51c0..3c2c96b8b00 100644 --- a/chromium/cc/debug/rendering_stats_instrumentation.cc +++ b/chromium/cc/debug/rendering_stats_instrumentation.cc @@ -20,19 +20,40 @@ RenderingStatsInstrumentation::~RenderingStatsInstrumentation() {} RenderingStats RenderingStatsInstrumentation::GetRenderingStats() { base::AutoLock scoped_lock(lock_); - return rendering_stats_; + RenderingStats rendering_stats; + rendering_stats.main_stats = main_stats_accu_; + rendering_stats.main_stats.Add(main_stats_); + rendering_stats.impl_stats = impl_stats_accu_; + rendering_stats.impl_stats.Add(impl_stats_); + return rendering_stats; +} + +void RenderingStatsInstrumentation::AccumulateAndClearMainThreadStats() { + main_stats_accu_.Add(main_stats_); + main_stats_ = MainThreadRenderingStats(); +} + +void RenderingStatsInstrumentation::AccumulateAndClearImplThreadStats() { + impl_stats_accu_.Add(impl_stats_); + impl_stats_ = ImplThreadRenderingStats(); } base::TimeTicks RenderingStatsInstrumentation::StartRecording() const { - if (record_rendering_stats_) + if (record_rendering_stats_) { + if (base::TimeTicks::IsThreadNowSupported()) + return base::TimeTicks::ThreadNow(); return base::TimeTicks::HighResNow(); + } return base::TimeTicks(); } base::TimeDelta RenderingStatsInstrumentation::EndRecording( base::TimeTicks start_time) const { - if (!start_time.is_null()) + if (!start_time.is_null()) { + if (base::TimeTicks::IsThreadNowSupported()) + return base::TimeTicks::ThreadNow() - start_time; return base::TimeTicks::HighResNow() - start_time; + } return base::TimeDelta(); } @@ -41,23 +62,27 @@ void RenderingStatsInstrumentation::IncrementAnimationFrameCount() { return; base::AutoLock scoped_lock(lock_); - rendering_stats_.animation_frame_count++; + main_stats_.animation_frame_count++; } -void RenderingStatsInstrumentation::SetScreenFrameCount(int64 count) { +void RenderingStatsInstrumentation::IncrementScreenFrameCount( + int64 count, bool main_thread) { if (!record_rendering_stats_) return; base::AutoLock scoped_lock(lock_); - rendering_stats_.screen_frame_count = count; + if (main_thread) + main_stats_.screen_frame_count += count; + else + impl_stats_.screen_frame_count += count; } -void RenderingStatsInstrumentation::SetDroppedFrameCount(int64 count) { +void RenderingStatsInstrumentation::IncrementDroppedFrameCount(int64 count) { if (!record_rendering_stats_) return; base::AutoLock scoped_lock(lock_); - rendering_stats_.dropped_frame_count = count; + impl_stats_.dropped_frame_count += count; } void RenderingStatsInstrumentation::AddCommit(base::TimeDelta duration) { @@ -65,8 +90,8 @@ void RenderingStatsInstrumentation::AddCommit(base::TimeDelta duration) { return; base::AutoLock scoped_lock(lock_); - rendering_stats_.total_commit_time += duration; - rendering_stats_.total_commit_count++; + main_stats_.commit_time += duration; + main_stats_.commit_count++; } void RenderingStatsInstrumentation::AddPaint(base::TimeDelta duration, @@ -75,8 +100,8 @@ void RenderingStatsInstrumentation::AddPaint(base::TimeDelta duration, return; base::AutoLock scoped_lock(lock_); - rendering_stats_.total_paint_time += duration; - rendering_stats_.total_pixels_painted += pixels; + main_stats_.paint_time += duration; + main_stats_.painted_pixel_count += pixels; } void RenderingStatsInstrumentation::AddRecord(base::TimeDelta duration, @@ -85,8 +110,8 @@ void RenderingStatsInstrumentation::AddRecord(base::TimeDelta duration, return; base::AutoLock scoped_lock(lock_); - rendering_stats_.total_record_time += duration; - rendering_stats_.total_pixels_recorded += pixels; + main_stats_.record_time += duration; + main_stats_.recorded_pixel_count += pixels; } void RenderingStatsInstrumentation::AddRaster(base::TimeDelta total_duration, @@ -97,12 +122,12 @@ void RenderingStatsInstrumentation::AddRaster(base::TimeDelta total_duration, return; base::AutoLock scoped_lock(lock_); - rendering_stats_.total_rasterize_time += total_duration; - rendering_stats_.best_rasterize_time += best_duration; - rendering_stats_.total_pixels_rasterized += pixels; + impl_stats_.rasterize_time += total_duration; + impl_stats_.best_rasterize_time += best_duration; + impl_stats_.rasterized_pixel_count += pixels; if (is_in_pending_tree_now_bin) { - rendering_stats_.total_rasterize_time_for_now_bins_on_pending_tree += + impl_stats_.rasterize_time_for_now_bins_on_pending_tree += total_duration; } } @@ -112,7 +137,7 @@ void RenderingStatsInstrumentation::IncrementImplThreadScrolls() { return; base::AutoLock scoped_lock(lock_); - rendering_stats_.num_impl_thread_scrolls++; + impl_stats_.impl_thread_scroll_count++; } void RenderingStatsInstrumentation::IncrementMainThreadScrolls() { @@ -120,7 +145,7 @@ void RenderingStatsInstrumentation::IncrementMainThreadScrolls() { return; base::AutoLock scoped_lock(lock_); - rendering_stats_.num_main_thread_scrolls++; + impl_stats_.main_thread_scroll_count++; } void RenderingStatsInstrumentation::AddLayersDrawn(int64 amount) { @@ -128,7 +153,7 @@ void RenderingStatsInstrumentation::AddLayersDrawn(int64 amount) { return; base::AutoLock scoped_lock(lock_); - rendering_stats_.num_layers_drawn += amount; + impl_stats_.drawn_layer_count += amount; } void RenderingStatsInstrumentation::AddMissingTiles(int64 amount) { @@ -136,7 +161,7 @@ void RenderingStatsInstrumentation::AddMissingTiles(int64 amount) { return; base::AutoLock scoped_lock(lock_); - rendering_stats_.num_missing_tiles += amount; + impl_stats_.missing_tile_count += amount; } void RenderingStatsInstrumentation::AddDeferredImageDecode( @@ -145,8 +170,8 @@ void RenderingStatsInstrumentation::AddDeferredImageDecode( return; base::AutoLock scoped_lock(lock_); - rendering_stats_.total_deferred_image_decode_time += duration; - rendering_stats_.total_deferred_image_decode_count++; + impl_stats_.deferred_image_decode_time += duration; + impl_stats_.deferred_image_decode_count++; } void RenderingStatsInstrumentation::AddImageGathering( @@ -155,8 +180,8 @@ void RenderingStatsInstrumentation::AddImageGathering( return; base::AutoLock scoped_lock(lock_); - rendering_stats_.total_image_gathering_time += duration; - rendering_stats_.total_image_gathering_count++; + main_stats_.image_gathering_time += duration; + main_stats_.image_gathering_count++; } void RenderingStatsInstrumentation::IncrementDeferredImageCacheHitCount() { @@ -164,7 +189,7 @@ void RenderingStatsInstrumentation::IncrementDeferredImageCacheHitCount() { return; base::AutoLock scoped_lock(lock_); - rendering_stats_.total_deferred_image_cache_hit_count++; + impl_stats_.deferred_image_cache_hit_count++; } void RenderingStatsInstrumentation::AddAnalysisResult( @@ -174,10 +199,10 @@ void RenderingStatsInstrumentation::AddAnalysisResult( return; base::AutoLock scoped_lock(lock_); - rendering_stats_.total_tiles_analyzed++; - rendering_stats_.total_tile_analysis_time += duration; + impl_stats_.tile_analysis_count++; + impl_stats_.tile_analysis_time += duration; if (is_solid_color) - rendering_stats_.solid_color_tiles_analyzed++; + impl_stats_.solid_color_tile_analysis_count++; } } // namespace cc diff --git a/chromium/cc/debug/rendering_stats_instrumentation.h b/chromium/cc/debug/rendering_stats_instrumentation.h index 6f7515a9418..5f74f9e6132 100644 --- a/chromium/cc/debug/rendering_stats_instrumentation.h +++ b/chromium/cc/debug/rendering_stats_instrumentation.h @@ -18,8 +18,33 @@ class CC_EXPORT RenderingStatsInstrumentation { static scoped_ptr<RenderingStatsInstrumentation> Create(); virtual ~RenderingStatsInstrumentation(); + // Return current main thread rendering stats. + MainThreadRenderingStats GetMainThreadRenderingStats() { + return main_stats_; + } + // Return current impl thread rendering stats. + ImplThreadRenderingStats GetImplThreadRenderingStats() { + return impl_stats_; + } + // Return the accumulated, combined rendering stats. RenderingStats GetRenderingStats(); + // Add current main thread rendering stats to accumulator and + // clear current stats. + void AccumulateAndClearMainThreadStats(); + // Add current impl thread rendering stats to accumulator and + // clear current stats. + void AccumulateAndClearImplThreadStats(); + + // Issue trace event for current main thread rendering stats. + void IssueTraceEventForMainThreadStats() { + main_stats_.IssueTraceEvent(); + } + // Issue trace event for current impl thread rendering stats. + void IssueTraceEventForImplThreadStats() { + impl_stats_.IssueTraceEvent(); + } + // Read and write access to the record_rendering_stats_ flag is not locked to // improve performance. The flag is commonly turned off and hardly changes // it's value during runtime. @@ -33,8 +58,8 @@ class CC_EXPORT RenderingStatsInstrumentation { base::TimeDelta EndRecording(base::TimeTicks start_time) const; void IncrementAnimationFrameCount(); - void SetScreenFrameCount(int64 count); - void SetDroppedFrameCount(int64 count); + void IncrementScreenFrameCount(int64 count, bool main_thread); + void IncrementDroppedFrameCount(int64 count); void AddCommit(base::TimeDelta duration); void AddPaint(base::TimeDelta duration, int64 pixels); @@ -61,7 +86,11 @@ class CC_EXPORT RenderingStatsInstrumentation { RenderingStatsInstrumentation(); private: - RenderingStats rendering_stats_; + MainThreadRenderingStats main_stats_; + MainThreadRenderingStats main_stats_accu_; + ImplThreadRenderingStats impl_stats_; + ImplThreadRenderingStats impl_stats_accu_; + bool record_rendering_stats_; base::Lock lock_; diff --git a/chromium/cc/debug/test_context_provider.cc b/chromium/cc/debug/test_context_provider.cc new file mode 100644 index 00000000000..d09ecd86ed9 --- /dev/null +++ b/chromium/cc/debug/test_context_provider.cc @@ -0,0 +1,240 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/debug/test_context_provider.h" + +#include <set> +#include <vector> + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/logging.h" +#include "base/strings/string_split.h" +#include "cc/debug/test_web_graphics_context_3d.h" + +namespace cc { + +class TestContextProvider::LostContextCallbackProxy + : public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback { + public: + explicit LostContextCallbackProxy(TestContextProvider* provider) + : provider_(provider) { + provider_->context3d_->setContextLostCallback(this); + } + + virtual ~LostContextCallbackProxy() { + provider_->context3d_->setContextLostCallback(NULL); + } + + virtual void onContextLost() { + provider_->OnLostContext(); + } + + private: + TestContextProvider* provider_; +}; + +class TestContextProvider::SwapBuffersCompleteCallbackProxy + : public WebKit::WebGraphicsContext3D:: + WebGraphicsSwapBuffersCompleteCallbackCHROMIUM { + public: + explicit SwapBuffersCompleteCallbackProxy(TestContextProvider* provider) + : provider_(provider) { + provider_->context3d_->setSwapBuffersCompleteCallbackCHROMIUM(this); + } + + virtual ~SwapBuffersCompleteCallbackProxy() { + provider_->context3d_->setSwapBuffersCompleteCallbackCHROMIUM(NULL); + } + + virtual void onSwapBuffersComplete() { + provider_->OnSwapBuffersComplete(); + } + + private: + TestContextProvider* provider_; +}; + +// static +scoped_refptr<TestContextProvider> TestContextProvider::Create() { + return Create(TestWebGraphicsContext3D::Create().Pass()); +} + +// static +scoped_refptr<TestContextProvider> TestContextProvider::Create( + const CreateCallback& create_callback) { + scoped_refptr<TestContextProvider> provider = new TestContextProvider; + if (!provider->InitializeOnMainThread(create_callback)) + return NULL; + return provider; +} + +scoped_ptr<TestWebGraphicsContext3D> ReturnScopedContext( + scoped_ptr<TestWebGraphicsContext3D> context) { + return context.Pass(); +} + +// static +scoped_refptr<TestContextProvider> TestContextProvider::Create( + scoped_ptr<TestWebGraphicsContext3D> context) { + return Create(base::Bind(&ReturnScopedContext, base::Passed(&context))); +} + +TestContextProvider::TestContextProvider() + : bound_(false), + destroyed_(false) { + DCHECK(main_thread_checker_.CalledOnValidThread()); + context_thread_checker_.DetachFromThread(); +} + +TestContextProvider::~TestContextProvider() { + DCHECK(main_thread_checker_.CalledOnValidThread() || + context_thread_checker_.CalledOnValidThread()); +} + +bool TestContextProvider::InitializeOnMainThread( + const CreateCallback& create_callback) { + DCHECK(main_thread_checker_.CalledOnValidThread()); + + DCHECK(!context3d_); + DCHECK(!create_callback.is_null()); + context3d_ = create_callback.Run(); + return context3d_; +} + +bool TestContextProvider::BindToCurrentThread() { + DCHECK(context3d_); + + // This is called on the thread the context will be used. + DCHECK(context_thread_checker_.CalledOnValidThread()); + + if (bound_) + return true; + + bound_ = true; + if (!context3d_->makeContextCurrent()) { + base::AutoLock lock(destroyed_lock_); + destroyed_ = true; + return false; + } + + lost_context_callback_proxy_.reset(new LostContextCallbackProxy(this)); + swap_buffers_complete_callback_proxy_.reset( + new SwapBuffersCompleteCallbackProxy(this)); + + return true; +} + +ContextProvider::Capabilities TestContextProvider::ContextCapabilities() { + DCHECK(context3d_); + DCHECK(bound_); + DCHECK(context_thread_checker_.CalledOnValidThread()); + + return context3d_->test_capabilities(); +} + +WebKit::WebGraphicsContext3D* TestContextProvider::Context3d() { + DCHECK(context3d_); + DCHECK(bound_); + DCHECK(context_thread_checker_.CalledOnValidThread()); + + return context3d_.get(); +} + +class GrContext* TestContextProvider::GrContext() { + DCHECK(context3d_); + DCHECK(bound_); + DCHECK(context_thread_checker_.CalledOnValidThread()); + + // TODO(danakj): Make a test GrContext that works with a test Context3d. + return NULL; +} + +void TestContextProvider::VerifyContexts() { + DCHECK(context3d_); + DCHECK(bound_); + DCHECK(context_thread_checker_.CalledOnValidThread()); + + if (context3d_->isContextLost()) { + base::AutoLock lock(destroyed_lock_); + destroyed_ = true; + } +} + +bool TestContextProvider::DestroyedOnMainThread() { + DCHECK(main_thread_checker_.CalledOnValidThread()); + + base::AutoLock lock(destroyed_lock_); + return destroyed_; +} + +void TestContextProvider::OnLostContext() { + DCHECK(context_thread_checker_.CalledOnValidThread()); + { + base::AutoLock lock(destroyed_lock_); + if (destroyed_) + return; + destroyed_ = true; + } + if (!lost_context_callback_.is_null()) + base::ResetAndReturn(&lost_context_callback_).Run(); +} + +void TestContextProvider::OnSwapBuffersComplete() { + DCHECK(context_thread_checker_.CalledOnValidThread()); + if (!swap_buffers_complete_callback_.is_null()) + swap_buffers_complete_callback_.Run(); +} + +TestWebGraphicsContext3D* TestContextProvider::TestContext3d() { + DCHECK(context3d_); + DCHECK(bound_); + DCHECK(context_thread_checker_.CalledOnValidThread()); + + return context3d_.get(); +} + +TestWebGraphicsContext3D* TestContextProvider::UnboundTestContext3d() { + DCHECK(context3d_); + DCHECK(context_thread_checker_.CalledOnValidThread()); + + return context3d_.get(); +} + +void TestContextProvider::SetMemoryAllocation( + const ManagedMemoryPolicy& policy, + bool discard_backbuffer_when_not_visible) { + if (memory_policy_changed_callback_.is_null()) + return; + memory_policy_changed_callback_.Run( + policy, discard_backbuffer_when_not_visible); +} + +void TestContextProvider::SetLostContextCallback( + const LostContextCallback& cb) { + DCHECK(context_thread_checker_.CalledOnValidThread()); + DCHECK(lost_context_callback_.is_null() || cb.is_null()); + lost_context_callback_ = cb; +} + +void TestContextProvider::SetSwapBuffersCompleteCallback( + const SwapBuffersCompleteCallback& cb) { + DCHECK(context_thread_checker_.CalledOnValidThread()); + DCHECK(swap_buffers_complete_callback_.is_null() || cb.is_null()); + swap_buffers_complete_callback_ = cb; +} + +void TestContextProvider::SetMemoryPolicyChangedCallback( + const MemoryPolicyChangedCallback& cb) { + DCHECK(context_thread_checker_.CalledOnValidThread()); + DCHECK(memory_policy_changed_callback_.is_null() || cb.is_null()); + memory_policy_changed_callback_ = cb; +} + +void TestContextProvider::SetMaxTransferBufferUsageBytes( + size_t max_transfer_buffer_usage_bytes) { + context3d_->SetMaxTransferBufferUsageBytes(max_transfer_buffer_usage_bytes); +} + +} // namespace cc diff --git a/chromium/cc/debug/test_context_provider.h b/chromium/cc/debug/test_context_provider.h new file mode 100644 index 00000000000..0401fcf1ba8 --- /dev/null +++ b/chromium/cc/debug/test_context_provider.h @@ -0,0 +1,90 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_DEBUG_TEST_CONTEXT_PROVIDER_H_ +#define CC_DEBUG_TEST_CONTEXT_PROVIDER_H_ + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" +#include "cc/base/cc_export.h" +#include "cc/output/context_provider.h" + +namespace WebKit { class WebGraphicsContext3D; } + +namespace cc { +class TestWebGraphicsContext3D; + +class CC_EXPORT TestContextProvider + : public NON_EXPORTED_BASE(cc::ContextProvider) { + public: + typedef base::Callback<scoped_ptr<TestWebGraphicsContext3D>(void)> + CreateCallback; + + static scoped_refptr<TestContextProvider> Create(); + static scoped_refptr<TestContextProvider> Create( + const CreateCallback& create_callback); + static scoped_refptr<TestContextProvider> Create( + scoped_ptr<TestWebGraphicsContext3D> context); + + virtual bool BindToCurrentThread() OVERRIDE; + virtual Capabilities ContextCapabilities() OVERRIDE; + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE; + virtual class GrContext* GrContext() OVERRIDE; + virtual void VerifyContexts() OVERRIDE; + virtual bool DestroyedOnMainThread() OVERRIDE; + virtual void SetLostContextCallback(const LostContextCallback& cb) OVERRIDE; + virtual void SetSwapBuffersCompleteCallback( + const SwapBuffersCompleteCallback& cb) OVERRIDE; + virtual void SetMemoryPolicyChangedCallback( + const MemoryPolicyChangedCallback& cb) OVERRIDE; + + TestWebGraphicsContext3D* TestContext3d(); + + // This returns the TestWebGraphicsContext3D but is valid to call + // before the context is bound to a thread. This is needed to set up + // state on the test context before binding. Don't call + // makeContextCurrent on the context returned from this method. + TestWebGraphicsContext3D* UnboundTestContext3d(); + + void SetMemoryAllocation(const ManagedMemoryPolicy& policy, + bool discard_backbuffer_when_not_visible); + + void SetMaxTransferBufferUsageBytes(size_t max_transfer_buffer_usage_bytes); + + protected: + TestContextProvider(); + virtual ~TestContextProvider(); + + bool InitializeOnMainThread(const CreateCallback& create_callback); + + void OnLostContext(); + void OnSwapBuffersComplete(); + + scoped_ptr<TestWebGraphicsContext3D> context3d_; + bool bound_; + + base::ThreadChecker main_thread_checker_; + base::ThreadChecker context_thread_checker_; + + base::Lock destroyed_lock_; + bool destroyed_; + + LostContextCallback lost_context_callback_; + SwapBuffersCompleteCallback swap_buffers_complete_callback_; + MemoryPolicyChangedCallback memory_policy_changed_callback_; + + class LostContextCallbackProxy; + scoped_ptr<LostContextCallbackProxy> lost_context_callback_proxy_; + + class SwapBuffersCompleteCallbackProxy; + scoped_ptr<SwapBuffersCompleteCallbackProxy> + swap_buffers_complete_callback_proxy_; +}; + +} // namespace cc + +#endif // CC_DEBUG_TEST_CONTEXT_PROVIDER_H_ + diff --git a/chromium/cc/debug/test_web_graphics_context_3d.cc b/chromium/cc/debug/test_web_graphics_context_3d.cc new file mode 100644 index 00000000000..634778fc4e6 --- /dev/null +++ b/chromium/cc/debug/test_web_graphics_context_3d.cc @@ -0,0 +1,632 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/debug/test_web_graphics_context_3d.h" + +#include <algorithm> +#include <string> + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "third_party/khronos/GLES2/gl2ext.h" + +using WebKit::WGC3Dboolean; +using WebKit::WGC3Dchar; +using WebKit::WGC3Denum; +using WebKit::WGC3Dint; +using WebKit::WGC3Dsizei; +using WebKit::WGC3Dsizeiptr; +using WebKit::WGC3Duint; +using WebKit::WebGLId; +using WebKit::WebGraphicsContext3D; + +namespace cc { + +static const WebGLId kFramebufferId = 1; +static const WebGLId kProgramId = 2; +static const WebGLId kRenderbufferId = 3; +static const WebGLId kShaderId = 4; + +static unsigned s_context_id = 1; + +const WebGLId TestWebGraphicsContext3D::kExternalTextureId = 1337; + +static base::LazyInstance<base::Lock>::Leaky + g_shared_namespace_lock = LAZY_INSTANCE_INITIALIZER; + +TestWebGraphicsContext3D::Namespace* + TestWebGraphicsContext3D::shared_namespace_ = NULL; + +TestWebGraphicsContext3D::Namespace::Namespace() + : next_buffer_id(1), + next_image_id(1), + next_texture_id(1) { +} + +TestWebGraphicsContext3D::Namespace::~Namespace() { + g_shared_namespace_lock.Get().AssertAcquired(); + if (shared_namespace_ == this) + shared_namespace_ = NULL; +} + +// static +scoped_ptr<TestWebGraphicsContext3D> TestWebGraphicsContext3D::Create() { + return make_scoped_ptr(new TestWebGraphicsContext3D()); +} + +TestWebGraphicsContext3D::TestWebGraphicsContext3D() + : FakeWebGraphicsContext3D(), + context_id_(s_context_id++), + times_make_current_succeeds_(-1), + times_bind_texture_succeeds_(-1), + times_end_query_succeeds_(-1), + times_gen_mailbox_succeeds_(-1), + context_lost_(false), + times_map_image_chromium_succeeds_(-1), + times_map_buffer_chromium_succeeds_(-1), + context_lost_callback_(NULL), + swap_buffers_callback_(NULL), + max_texture_size_(2048), + width_(0), + height_(0), + bound_buffer_(0), + weak_ptr_factory_(this) { + CreateNamespace(); + test_capabilities_.swapbuffers_complete_callback = true; +} + +TestWebGraphicsContext3D::TestWebGraphicsContext3D( + const WebGraphicsContext3D::Attributes& attributes) + : FakeWebGraphicsContext3D(), + context_id_(s_context_id++), + attributes_(attributes), + times_make_current_succeeds_(-1), + times_bind_texture_succeeds_(-1), + times_end_query_succeeds_(-1), + times_gen_mailbox_succeeds_(-1), + context_lost_(false), + times_map_image_chromium_succeeds_(-1), + times_map_buffer_chromium_succeeds_(-1), + context_lost_callback_(NULL), + swap_buffers_callback_(NULL), + max_texture_size_(2048), + width_(0), + height_(0), + bound_buffer_(0), + weak_ptr_factory_(this) { + CreateNamespace(); + test_capabilities_.swapbuffers_complete_callback = true; +} + +void TestWebGraphicsContext3D::CreateNamespace() { + if (attributes_.shareResources) { + base::AutoLock lock(g_shared_namespace_lock.Get()); + if (shared_namespace_) { + namespace_ = shared_namespace_; + } else { + namespace_ = new Namespace; + shared_namespace_ = namespace_.get(); + } + } else { + namespace_ = new Namespace; + } +} + +TestWebGraphicsContext3D::~TestWebGraphicsContext3D() { + for (size_t i = 0; i < sync_point_callbacks_.size(); ++i) { + if (sync_point_callbacks_[i] != NULL) + delete sync_point_callbacks_[i]; + } + base::AutoLock lock(g_shared_namespace_lock.Get()); + namespace_ = NULL; +} + +bool TestWebGraphicsContext3D::makeContextCurrent() { + if (times_make_current_succeeds_ >= 0) { + if (!times_make_current_succeeds_) { + loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, + GL_INNOCENT_CONTEXT_RESET_ARB); + } + --times_make_current_succeeds_; + } + return !context_lost_; +} + +int TestWebGraphicsContext3D::width() { + return width_; +} + +int TestWebGraphicsContext3D::height() { + return height_; +} + +void TestWebGraphicsContext3D::reshapeWithScaleFactor( + int width, int height, float scale_factor) { + width_ = width; + height_ = height; +} + +bool TestWebGraphicsContext3D::isContextLost() { + return context_lost_; +} + +WGC3Denum TestWebGraphicsContext3D::getGraphicsResetStatusARB() { + return context_lost_ ? GL_UNKNOWN_CONTEXT_RESET_ARB : GL_NO_ERROR; +} + +WGC3Denum TestWebGraphicsContext3D::checkFramebufferStatus( + WGC3Denum target) { + if (context_lost_) + return GL_FRAMEBUFFER_UNDEFINED_OES; + return GL_FRAMEBUFFER_COMPLETE; +} + +WebGraphicsContext3D::Attributes + TestWebGraphicsContext3D::getContextAttributes() { + return attributes_; +} + +WebKit::WebString TestWebGraphicsContext3D::getString(WGC3Denum name) { + return WebKit::WebString(); +} + +WGC3Dint TestWebGraphicsContext3D::getUniformLocation( + WebGLId program, + const WGC3Dchar* name) { + return 0; +} + +WGC3Dsizeiptr TestWebGraphicsContext3D::getVertexAttribOffset( + WGC3Duint index, + WGC3Denum pname) { + return 0; +} + +WGC3Dboolean TestWebGraphicsContext3D::isBuffer( + WebGLId buffer) { + return false; +} + +WGC3Dboolean TestWebGraphicsContext3D::isEnabled( + WGC3Denum cap) { + return false; +} + +WGC3Dboolean TestWebGraphicsContext3D::isFramebuffer( + WebGLId framebuffer) { + return false; +} + +WGC3Dboolean TestWebGraphicsContext3D::isProgram( + WebGLId program) { + return false; +} + +WGC3Dboolean TestWebGraphicsContext3D::isRenderbuffer( + WebGLId renderbuffer) { + return false; +} + +WGC3Dboolean TestWebGraphicsContext3D::isShader( + WebGLId shader) { + return false; +} + +WGC3Dboolean TestWebGraphicsContext3D::isTexture( + WebGLId texture) { + return false; +} + +WebGLId TestWebGraphicsContext3D::createBuffer() { + return NextBufferId(); +} + +void TestWebGraphicsContext3D::deleteBuffer(WebGLId id) { + base::AutoLock lock(namespace_->lock); + unsigned context_id = id >> 17; + unsigned buffer_id = id & 0x1ffff; + DCHECK(buffer_id && buffer_id < namespace_->next_buffer_id); + DCHECK_EQ(context_id, context_id_); +} + +WebGLId TestWebGraphicsContext3D::createFramebuffer() { + return kFramebufferId | context_id_ << 16; +} + +void TestWebGraphicsContext3D::deleteFramebuffer(WebGLId id) { + DCHECK_EQ(kFramebufferId | context_id_ << 16, id); +} + +WebGLId TestWebGraphicsContext3D::createProgram() { + return kProgramId | context_id_ << 16; +} + +void TestWebGraphicsContext3D::deleteProgram(WebGLId id) { + DCHECK_EQ(kProgramId | context_id_ << 16, id); +} + +WebGLId TestWebGraphicsContext3D::createRenderbuffer() { + return kRenderbufferId | context_id_ << 16; +} + +void TestWebGraphicsContext3D::deleteRenderbuffer(WebGLId id) { + DCHECK_EQ(kRenderbufferId | context_id_ << 16, id); +} + +WebGLId TestWebGraphicsContext3D::createShader(WGC3Denum) { + return kShaderId | context_id_ << 16; +} + +void TestWebGraphicsContext3D::deleteShader(WebGLId id) { + DCHECK_EQ(kShaderId | context_id_ << 16, id); +} + +WebGLId TestWebGraphicsContext3D::createTexture() { + WebGLId texture_id = NextTextureId(); + DCHECK_NE(texture_id, kExternalTextureId); + base::AutoLock lock(namespace_->lock); + namespace_->textures.push_back(texture_id); + return texture_id; +} + +void TestWebGraphicsContext3D::deleteTexture(WebGLId texture_id) { + base::AutoLock lock(namespace_->lock); + std::vector<WebKit::WebGLId>& textures = namespace_->textures; + DCHECK(std::find(textures.begin(), textures.end(), texture_id) != + textures.end()); + textures.erase(std::find(textures.begin(), textures.end(), texture_id)); +} + +void TestWebGraphicsContext3D::attachShader(WebGLId program, WebGLId shader) { + DCHECK_EQ(kProgramId | context_id_ << 16, program); + DCHECK_EQ(kShaderId | context_id_ << 16, shader); +} + +void TestWebGraphicsContext3D::useProgram(WebGLId program) { + if (!program) + return; + DCHECK_EQ(kProgramId | context_id_ << 16, program); +} + +void TestWebGraphicsContext3D::bindFramebuffer( + WGC3Denum target, WebGLId framebuffer) { + if (!framebuffer) + return; + DCHECK_EQ(kFramebufferId | context_id_ << 16, framebuffer); +} + +void TestWebGraphicsContext3D::bindRenderbuffer( + WGC3Denum target, WebGLId renderbuffer) { + if (!renderbuffer) + return; + DCHECK_EQ(kRenderbufferId | context_id_ << 16, renderbuffer); +} + +void TestWebGraphicsContext3D::bindTexture( + WGC3Denum target, WebGLId texture_id) { + if (times_bind_texture_succeeds_ >= 0) { + if (!times_bind_texture_succeeds_) { + loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, + GL_INNOCENT_CONTEXT_RESET_ARB); + } + --times_bind_texture_succeeds_; + } + + if (!texture_id) + return; + if (texture_id == kExternalTextureId) + return; + base::AutoLock lock(namespace_->lock); + std::vector<WebKit::WebGLId>& textures = namespace_->textures; + DCHECK(std::find(textures.begin(), textures.end(), texture_id) != + textures.end()); + used_textures_.insert(texture_id); +} + +void TestWebGraphicsContext3D::endQueryEXT(WGC3Denum target) { + if (times_end_query_succeeds_ >= 0) { + if (!times_end_query_succeeds_) { + loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, + GL_INNOCENT_CONTEXT_RESET_ARB); + } + --times_end_query_succeeds_; + } +} + +void TestWebGraphicsContext3D::getQueryObjectuivEXT( + WebGLId query, + WGC3Denum pname, + WGC3Duint* params) { + // If the context is lost, behave as if result is available. + if (pname == GL_QUERY_RESULT_AVAILABLE_EXT) + *params = 1; +} + +void TestWebGraphicsContext3D::getIntegerv( + WGC3Denum pname, + WebKit::WGC3Dint* value) { + if (pname == GL_MAX_TEXTURE_SIZE) + *value = max_texture_size_; + else if (pname == GL_ACTIVE_TEXTURE) + *value = GL_TEXTURE0; +} + +void TestWebGraphicsContext3D::genMailboxCHROMIUM(WebKit::WGC3Dbyte* mailbox) { + if (times_gen_mailbox_succeeds_ >= 0) { + if (!times_gen_mailbox_succeeds_) { + loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, + GL_INNOCENT_CONTEXT_RESET_ARB); + } + --times_gen_mailbox_succeeds_; + } + if (context_lost_) { + memset(mailbox, 0, 64); + return; + } + + static char mailbox_name1 = '1'; + static char mailbox_name2 = '1'; + mailbox[0] = mailbox_name1; + mailbox[1] = mailbox_name2; + mailbox[2] = '\0'; + if (++mailbox_name1 == 0) { + mailbox_name1 = '1'; + ++mailbox_name2; + } +} + +void TestWebGraphicsContext3D::setContextLostCallback( + WebGraphicsContextLostCallback* callback) { + context_lost_callback_ = callback; +} + +void TestWebGraphicsContext3D::loseContextCHROMIUM(WGC3Denum current, + WGC3Denum other) { + if (context_lost_) + return; + context_lost_ = true; + if (context_lost_callback_) + context_lost_callback_->onContextLost(); + + for (size_t i = 0; i < shared_contexts_.size(); ++i) + shared_contexts_[i]->loseContextCHROMIUM(current, other); + shared_contexts_.clear(); +} + +void TestWebGraphicsContext3D::signalSyncPoint( + unsigned sync_point, + WebGraphicsSyncPointCallback* callback) { + sync_point_callbacks_.push_back(callback); +} + +void TestWebGraphicsContext3D::signalQuery( + WebKit::WebGLId query, + WebGraphicsSyncPointCallback* callback) { + sync_point_callbacks_.push_back(callback); +} + +void TestWebGraphicsContext3D::setSwapBuffersCompleteCallbackCHROMIUM( + WebGraphicsSwapBuffersCompleteCallbackCHROMIUM* callback) { + if (test_capabilities_.swapbuffers_complete_callback) + swap_buffers_callback_ = callback; +} + +void TestWebGraphicsContext3D::prepareTexture() { + if (swap_buffers_callback_) { + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&TestWebGraphicsContext3D::SwapBuffersComplete, + weak_ptr_factory_.GetWeakPtr())); + } + CallAllSyncPointCallbacks(); +} + +void TestWebGraphicsContext3D::finish() { + CallAllSyncPointCallbacks(); +} + +void TestWebGraphicsContext3D::flush() { + CallAllSyncPointCallbacks(); +} + +static void CallAndDestroy( + WebKit::WebGraphicsContext3D::WebGraphicsSyncPointCallback* callback) { + if (!callback) + return; + callback->onSyncPointReached(); + delete callback; +} + +void TestWebGraphicsContext3D::CallAllSyncPointCallbacks() { + for (size_t i = 0; i < sync_point_callbacks_.size(); ++i) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&CallAndDestroy, + sync_point_callbacks_[i])); + } + sync_point_callbacks_.clear(); +} + +void TestWebGraphicsContext3D::SwapBuffersComplete() { + if (swap_buffers_callback_) + swap_buffers_callback_->onSwapBuffersComplete(); +} + +void TestWebGraphicsContext3D::bindBuffer(WebKit::WGC3Denum target, + WebKit::WebGLId buffer) { + bound_buffer_ = buffer; + if (!bound_buffer_) + return; + unsigned context_id = buffer >> 17; + unsigned buffer_id = buffer & 0x1ffff; + base::AutoLock lock(namespace_->lock); + DCHECK(buffer_id && buffer_id < namespace_->next_buffer_id); + DCHECK_EQ(context_id, context_id_); + + base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers; + if (buffers.count(bound_buffer_) == 0) + buffers.set(bound_buffer_, make_scoped_ptr(new Buffer).Pass()); + + buffers.get(bound_buffer_)->target = target; +} + +void TestWebGraphicsContext3D::bufferData(WebKit::WGC3Denum target, + WebKit::WGC3Dsizeiptr size, + const void* data, + WebKit::WGC3Denum usage) { + base::AutoLock lock(namespace_->lock); + base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers; + DCHECK_GT(buffers.count(bound_buffer_), 0u); + DCHECK_EQ(target, buffers.get(bound_buffer_)->target); + Buffer* buffer = buffers.get(bound_buffer_); + if (context_lost_) { + buffer->pixels.reset(); + return; + } + + buffer->pixels.reset(new uint8[size]); + buffer->size = size; + if (data != NULL) + memcpy(buffer->pixels.get(), data, size); +} + +void* TestWebGraphicsContext3D::mapBufferCHROMIUM(WebKit::WGC3Denum target, + WebKit::WGC3Denum access) { + base::AutoLock lock(namespace_->lock); + base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers; + DCHECK_GT(buffers.count(bound_buffer_), 0u); + DCHECK_EQ(target, buffers.get(bound_buffer_)->target); + if (times_map_buffer_chromium_succeeds_ >= 0) { + if (!times_map_buffer_chromium_succeeds_) { + return NULL; + } + --times_map_buffer_chromium_succeeds_; + } + return buffers.get(bound_buffer_)->pixels.get(); +} + +WebKit::WGC3Dboolean TestWebGraphicsContext3D::unmapBufferCHROMIUM( + WebKit::WGC3Denum target) { + base::AutoLock lock(namespace_->lock); + base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers; + DCHECK_GT(buffers.count(bound_buffer_), 0u); + DCHECK_EQ(target, buffers.get(bound_buffer_)->target); + buffers.get(bound_buffer_)->pixels.reset(); + return true; +} + +WebKit::WGC3Duint TestWebGraphicsContext3D::createImageCHROMIUM( + WebKit::WGC3Dsizei width, WebKit::WGC3Dsizei height, + WebKit::WGC3Denum internalformat) { + DCHECK_EQ(GL_RGBA8_OES, static_cast<int>(internalformat)); + WebKit::WGC3Duint image_id = NextImageId(); + base::AutoLock lock(namespace_->lock); + base::ScopedPtrHashMap<unsigned, Image>& images = namespace_->images; + images.set(image_id, make_scoped_ptr(new Image).Pass()); + images.get(image_id)->pixels.reset(new uint8[width * height * 4]); + return image_id; +} + +void TestWebGraphicsContext3D::destroyImageCHROMIUM( + WebKit::WGC3Duint id) { + base::AutoLock lock(namespace_->lock); + unsigned context_id = id >> 17; + unsigned image_id = id & 0x1ffff; + DCHECK(image_id && image_id < namespace_->next_image_id); + DCHECK_EQ(context_id, context_id_); +} + +void TestWebGraphicsContext3D::getImageParameterivCHROMIUM( + WebKit::WGC3Duint image_id, + WebKit::WGC3Denum pname, + WebKit::WGC3Dint* params) { + base::AutoLock lock(namespace_->lock); + DCHECK_GT(namespace_->images.count(image_id), 0u); + DCHECK_EQ(GL_IMAGE_ROWBYTES_CHROMIUM, static_cast<int>(pname)); + *params = 0; +} + +void* TestWebGraphicsContext3D::mapImageCHROMIUM(WebKit::WGC3Duint image_id, + WebKit::WGC3Denum access) { + base::AutoLock lock(namespace_->lock); + base::ScopedPtrHashMap<unsigned, Image>& images = namespace_->images; + DCHECK_GT(images.count(image_id), 0u); + if (times_map_image_chromium_succeeds_ >= 0) { + if (!times_map_image_chromium_succeeds_) { + return NULL; + } + --times_map_image_chromium_succeeds_; + } + return images.get(image_id)->pixels.get(); +} + +void TestWebGraphicsContext3D::unmapImageCHROMIUM( + WebKit::WGC3Duint image_id) { + base::AutoLock lock(namespace_->lock); + DCHECK_GT(namespace_->images.count(image_id), 0u); +} + +size_t TestWebGraphicsContext3D::NumTextures() const { + base::AutoLock lock(namespace_->lock); + return namespace_->textures.size(); +} + +WebKit::WebGLId TestWebGraphicsContext3D::TextureAt(int i) const { + base::AutoLock lock(namespace_->lock); + return namespace_->textures[i]; +} + +WebGLId TestWebGraphicsContext3D::NextTextureId() { + base::AutoLock lock(namespace_->lock); + WebGLId texture_id = namespace_->next_texture_id++; + DCHECK(texture_id < (1 << 16)); + texture_id |= context_id_ << 16; + return texture_id; +} + +WebGLId TestWebGraphicsContext3D::NextBufferId() { + base::AutoLock lock(namespace_->lock); + WebGLId buffer_id = namespace_->next_buffer_id++; + DCHECK(buffer_id < (1 << 17)); + buffer_id |= context_id_ << 17; + return buffer_id; +} + +WebKit::WGC3Duint TestWebGraphicsContext3D::NextImageId() { + base::AutoLock lock(namespace_->lock); + WGC3Duint image_id = namespace_->next_image_id++; + DCHECK(image_id < (1 << 17)); + image_id |= context_id_ << 17; + return image_id; +} + +size_t TestWebGraphicsContext3D::GetTransferBufferMemoryUsedBytes() const { + size_t total_bytes = 0; + base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers; + base::ScopedPtrHashMap<unsigned, Buffer>::iterator it = buffers.begin(); + for (; it != buffers.end(); ++it) { + Buffer* buffer = it->second; + if (buffer->target == GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM) + total_bytes += buffer->size; + } + return total_bytes; +} + +void TestWebGraphicsContext3D::SetMaxTransferBufferUsageBytes( + size_t max_transfer_buffer_usage_bytes) { + test_capabilities_.max_transfer_buffer_usage_bytes = + max_transfer_buffer_usage_bytes; +} + +TestWebGraphicsContext3D::Buffer::Buffer() : target(0), size(0) {} + +TestWebGraphicsContext3D::Buffer::~Buffer() {} + +TestWebGraphicsContext3D::Image::Image() {} + +TestWebGraphicsContext3D::Image::~Image() {} + +} // namespace cc diff --git a/chromium/cc/debug/test_web_graphics_context_3d.h b/chromium/cc/debug/test_web_graphics_context_3d.h new file mode 100644 index 00000000000..4db270c4dd6 --- /dev/null +++ b/chromium/cc/debug/test_web_graphics_context_3d.h @@ -0,0 +1,292 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_DEBUG_TEST_WEB_GRAPHICS_CONTEXT_3D_H_ +#define CC_DEBUG_TEST_WEB_GRAPHICS_CONTEXT_3D_H_ + +#include <vector> + +#include "base/compiler_specific.h" +#include "base/containers/hash_tables.h" +#include "base/containers/scoped_ptr_hash_map.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/stl_util.h" +#include "base/synchronization/lock.h" +#include "cc/base/cc_export.h" +#include "cc/debug/fake_web_graphics_context_3d.h" +#include "cc/output/context_provider.h" +#include "third_party/khronos/GLES2/gl2.h" + +namespace cc { + +class CC_EXPORT TestWebGraphicsContext3D : public FakeWebGraphicsContext3D { + public: + static scoped_ptr<TestWebGraphicsContext3D> Create(); + + virtual ~TestWebGraphicsContext3D(); + + virtual bool makeContextCurrent(); + + virtual int width(); + virtual int height(); + + virtual void reshapeWithScaleFactor( + int width, int height, float scale_factor); + + virtual bool isContextLost(); + virtual WebKit::WGC3Denum getGraphicsResetStatusARB(); + + virtual void attachShader(WebKit::WebGLId program, WebKit::WebGLId shader); + virtual void bindFramebuffer( + WebKit::WGC3Denum target, WebKit::WebGLId framebuffer); + virtual void bindRenderbuffer( + WebKit::WGC3Denum target, WebKit::WebGLId renderbuffer); + virtual void bindTexture( + WebKit::WGC3Denum target, + WebKit::WebGLId texture_id); + + virtual WebKit::WGC3Denum checkFramebufferStatus(WebKit::WGC3Denum target); + + virtual Attributes getContextAttributes(); + + virtual WebKit::WebString getString(WebKit::WGC3Denum name); + virtual WebKit::WGC3Dint getUniformLocation( + WebKit::WebGLId program, + const WebKit::WGC3Dchar* name); + virtual WebKit::WGC3Dsizeiptr getVertexAttribOffset( + WebKit::WGC3Duint index, + WebKit::WGC3Denum pname); + + virtual WebKit::WGC3Dboolean isBuffer(WebKit::WebGLId buffer); + virtual WebKit::WGC3Dboolean isEnabled(WebKit::WGC3Denum cap); + virtual WebKit::WGC3Dboolean isFramebuffer(WebKit::WebGLId framebuffer); + virtual WebKit::WGC3Dboolean isProgram(WebKit::WebGLId program); + virtual WebKit::WGC3Dboolean isRenderbuffer(WebKit::WebGLId renderbuffer); + virtual WebKit::WGC3Dboolean isShader(WebKit::WebGLId shader); + virtual WebKit::WGC3Dboolean isTexture(WebKit::WebGLId texture); + + virtual void useProgram(WebKit::WebGLId program); + + virtual WebKit::WebGLId createBuffer(); + virtual WebKit::WebGLId createFramebuffer(); + virtual WebKit::WebGLId createProgram(); + virtual WebKit::WebGLId createRenderbuffer(); + virtual WebKit::WebGLId createShader(WebKit::WGC3Denum); + virtual WebKit::WebGLId createTexture(); + + virtual void deleteBuffer(WebKit::WebGLId id); + virtual void deleteFramebuffer(WebKit::WebGLId id); + virtual void deleteProgram(WebKit::WebGLId id); + virtual void deleteRenderbuffer(WebKit::WebGLId id); + virtual void deleteShader(WebKit::WebGLId id); + virtual void deleteTexture(WebKit::WebGLId texture_id); + + virtual void endQueryEXT(WebKit::WGC3Denum target); + virtual void getQueryObjectuivEXT( + WebKit::WebGLId query, + WebKit::WGC3Denum pname, + WebKit::WGC3Duint* params); + + virtual void getIntegerv( + WebKit::WGC3Denum pname, + WebKit::WGC3Dint* value); + + virtual void genMailboxCHROMIUM(WebKit::WGC3Dbyte* mailbox); + virtual void produceTextureCHROMIUM(WebKit::WGC3Denum target, + const WebKit::WGC3Dbyte* mailbox) { } + virtual void consumeTextureCHROMIUM(WebKit::WGC3Denum target, + const WebKit::WGC3Dbyte* mailbox) { } + + virtual void setContextLostCallback( + WebGraphicsContextLostCallback* callback); + + virtual void loseContextCHROMIUM(WebKit::WGC3Denum current, + WebKit::WGC3Denum other); + + // Takes ownership of the |callback|. + virtual void signalSyncPoint(unsigned sync_point, + WebGraphicsSyncPointCallback* callback); + virtual void signalQuery(WebKit::WebGLId query, + WebGraphicsSyncPointCallback* callback); + + virtual void setSwapBuffersCompleteCallbackCHROMIUM( + WebGraphicsSwapBuffersCompleteCallbackCHROMIUM* callback); + + virtual void prepareTexture(); + virtual void finish(); + virtual void flush(); + + virtual void bindBuffer(WebKit::WGC3Denum target, WebKit::WebGLId buffer); + virtual void bufferData(WebKit::WGC3Denum target, + WebKit::WGC3Dsizeiptr size, + const void* data, + WebKit::WGC3Denum usage); + virtual void* mapBufferCHROMIUM(WebKit::WGC3Denum target, + WebKit::WGC3Denum access); + virtual WebKit::WGC3Dboolean unmapBufferCHROMIUM(WebKit::WGC3Denum target); + + virtual WebKit::WGC3Duint createImageCHROMIUM( + WebKit::WGC3Dsizei width, + WebKit::WGC3Dsizei height, + WebKit::WGC3Denum internalformat); + virtual void destroyImageCHROMIUM(WebKit::WGC3Duint image_id); + virtual void getImageParameterivCHROMIUM( + WebKit::WGC3Duint image_id, + WebKit::WGC3Denum pname, + WebKit::WGC3Dint* params); + virtual void* mapImageCHROMIUM( + WebKit::WGC3Duint image_id, + WebKit::WGC3Denum access); + virtual void unmapImageCHROMIUM(WebKit::WGC3Duint image_id); + + const ContextProvider::Capabilities& test_capabilities() const { + return test_capabilities_; + } + + // When set, MakeCurrent() will fail after this many times. + void set_times_make_current_succeeds(int times) { + times_make_current_succeeds_ = times; + } + void set_times_bind_texture_succeeds(int times) { + times_bind_texture_succeeds_ = times; + } + void set_times_end_query_succeeds(int times) { + times_end_query_succeeds_ = times; + } + void set_times_gen_mailbox_succeeds(int times) { + times_gen_mailbox_succeeds_ = times; + } + + // When set, mapImageCHROMIUM and mapBufferCHROMIUM will return NULL after + // this many times. + void set_times_map_image_chromium_succeeds(int times) { + times_map_image_chromium_succeeds_ = times; + } + void set_times_map_buffer_chromium_succeeds(int times) { + times_map_buffer_chromium_succeeds_ = times; + } + + size_t NumTextures() const; + WebKit::WebGLId TextureAt(int i) const; + + size_t NumUsedTextures() const { return used_textures_.size(); } + bool UsedTexture(int texture) const { + return ContainsKey(used_textures_, texture); + } + void ResetUsedTextures() { used_textures_.clear(); } + + void set_support_swapbuffers_complete_callback(bool support) { + test_capabilities_.swapbuffers_complete_callback = support; + } + void set_have_extension_io_surface(bool have) { + test_capabilities_.iosurface = have; + } + void set_have_extension_egl_image(bool have) { + test_capabilities_.egl_image_external = have; + } + void set_have_post_sub_buffer(bool have) { + test_capabilities_.post_sub_buffer = have; + } + void set_have_discard_framebuffer(bool have) { + test_capabilities_.discard_framebuffer = have; + } + + // When this context is lost, all contexts in its share group are also lost. + void add_share_group_context(WebKit::WebGraphicsContext3D* context3d) { + shared_contexts_.push_back(context3d); + } + + void set_max_texture_size(int size) { max_texture_size_ = size; } + + static const WebKit::WebGLId kExternalTextureId; + virtual WebKit::WebGLId NextTextureId(); + + virtual WebKit::WebGLId NextBufferId(); + + virtual WebKit::WebGLId NextImageId(); + + size_t GetTransferBufferMemoryUsedBytes() const; + void SetMaxTransferBufferUsageBytes(size_t max_transfer_buffer_usage_bytes); + + protected: + struct Buffer { + Buffer(); + ~Buffer(); + + WebKit::WGC3Denum target; + scoped_ptr<uint8[]> pixels; + size_t size; + + private: + DISALLOW_COPY_AND_ASSIGN(Buffer); + }; + + struct Image { + Image(); + ~Image(); + + scoped_ptr<uint8[]> pixels; + + private: + DISALLOW_COPY_AND_ASSIGN(Image); + }; + + struct Namespace : public base::RefCountedThreadSafe<Namespace> { + Namespace(); + + // Protects all fields. + base::Lock lock; + unsigned next_buffer_id; + unsigned next_image_id; + unsigned next_texture_id; + std::vector<WebKit::WebGLId> textures; + base::ScopedPtrHashMap<unsigned, Buffer> buffers; + base::ScopedPtrHashMap<unsigned, Image> images; + + private: + friend class base::RefCountedThreadSafe<Namespace>; + ~Namespace(); + DISALLOW_COPY_AND_ASSIGN(Namespace); + }; + + TestWebGraphicsContext3D(); + TestWebGraphicsContext3D( + const WebKit::WebGraphicsContext3D::Attributes& attributes); + + void CallAllSyncPointCallbacks(); + void SwapBuffersComplete(); + void CreateNamespace(); + + unsigned context_id_; + Attributes attributes_; + ContextProvider::Capabilities test_capabilities_; + int times_make_current_succeeds_; + int times_bind_texture_succeeds_; + int times_end_query_succeeds_; + int times_gen_mailbox_succeeds_; + bool context_lost_; + int times_map_image_chromium_succeeds_; + int times_map_buffer_chromium_succeeds_; + WebGraphicsContextLostCallback* context_lost_callback_; + WebGraphicsSwapBuffersCompleteCallbackCHROMIUM* swap_buffers_callback_; + std::vector<WebGraphicsSyncPointCallback*> sync_point_callbacks_; + base::hash_set<WebKit::WebGLId> used_textures_; + std::vector<WebKit::WebGraphicsContext3D*> shared_contexts_; + int max_texture_size_; + int width_; + int height_; + + unsigned bound_buffer_; + + scoped_refptr<Namespace> namespace_; + static Namespace* shared_namespace_; + + base::WeakPtrFactory<TestWebGraphicsContext3D> weak_ptr_factory_; +}; + +} // namespace cc + +#endif // CC_DEBUG_TEST_WEB_GRAPHICS_CONTEXT_3D_H_ diff --git a/chromium/cc/debug/traced_picture.cc b/chromium/cc/debug/traced_picture.cc index 7f04d0d783f..0cc2148f5f0 100644 --- a/chromium/cc/debug/traced_picture.cc +++ b/chromium/cc/debug/traced_picture.cc @@ -12,7 +12,8 @@ namespace cc { TracedPicture::TracedPicture(scoped_refptr<Picture> picture) - : picture_(picture) { + : picture_(picture), + is_alias_(false) { } TracedPicture::~TracedPicture() { @@ -25,7 +26,34 @@ scoped_ptr<base::debug::ConvertableToTraceFormat> return result.PassAs<base::debug::ConvertableToTraceFormat>(); } +scoped_ptr<base::debug::ConvertableToTraceFormat> + TracedPicture::AsTraceablePictureAlias(Picture* original) { + TracedPicture* ptr = new TracedPicture(original); + ptr->is_alias_ = true; + scoped_ptr<TracedPicture> result(ptr); + return result.PassAs<base::debug::ConvertableToTraceFormat>(); +} + void TracedPicture::AppendAsTraceFormat(std::string* out) const { + if (is_alias_) + AppendPictureAlias(out); + else + AppendPicture(out); +} + +void TracedPicture::AppendPictureAlias(std::string* out) const { + scoped_ptr<base::DictionaryValue> alias(new base::DictionaryValue()); + alias->SetString("id_ref", base::StringPrintf("%p", picture_.get())); + + scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue()); + res->Set("alias", alias.release()); + + std::string tmp; + base::JSONWriter::Write(res.get(), &tmp); + out->append(tmp); +} + +void TracedPicture::AppendPicture(std::string* out) const { scoped_ptr<base::Value> value = picture_->AsValue(); std::string tmp; base::JSONWriter::Write(value.get(), &tmp); diff --git a/chromium/cc/debug/traced_picture.h b/chromium/cc/debug/traced_picture.h index 9137d8f830e..50511f2034a 100644 --- a/chromium/cc/debug/traced_picture.h +++ b/chromium/cc/debug/traced_picture.h @@ -22,10 +22,17 @@ class TracedPicture : public base::debug::ConvertableToTraceFormat { static scoped_ptr<base::debug::ConvertableToTraceFormat> AsTraceablePicture(Picture* picture); + static scoped_ptr<base::debug::ConvertableToTraceFormat> + AsTraceablePictureAlias(Picture* original); + virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE; private: + void AppendPicture(std::string* out) const; + void AppendPictureAlias(std::string* out) const; + scoped_refptr<Picture> picture_; + bool is_alias_; DISALLOW_COPY_AND_ASSIGN(TracedPicture); }; diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index db265617fe8..d28935f0a39 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -117,7 +117,6 @@ class CC_EXPORT InputHandler { virtual void StartPageScaleAnimation(gfx::Vector2d target_offset, bool anchor_point, float page_scale, - base::TimeTicks start_time, base::TimeDelta duration) = 0; // Request another callback to InputHandlerClient::Animate(). diff --git a/chromium/cc/input/page_scale_animation.cc b/chromium/cc/input/page_scale_animation.cc index a535c87ad97..2b7d84c1f54 100644 --- a/chromium/cc/input/page_scale_animation.cc +++ b/chromium/cc/input/page_scale_animation.cc @@ -45,13 +45,11 @@ scoped_ptr<PageScaleAnimation> PageScaleAnimation::Create( float start_page_scale_factor, gfx::SizeF viewport_size, gfx::SizeF root_layer_size, - double start_time, scoped_ptr<TimingFunction> timing_function) { return make_scoped_ptr(new PageScaleAnimation(start_scroll_offset, start_page_scale_factor, viewport_size, root_layer_size, - start_time, timing_function.Pass())); } @@ -60,7 +58,6 @@ PageScaleAnimation::PageScaleAnimation( float start_page_scale_factor, gfx::SizeF viewport_size, gfx::SizeF root_layer_size, - double start_time, scoped_ptr<TimingFunction> timing_function) : start_page_scale_factor_(start_page_scale_factor), target_page_scale_factor_(0.f), @@ -69,7 +66,7 @@ PageScaleAnimation::PageScaleAnimation( target_anchor_(), viewport_size_(viewport_size), root_layer_size_(root_layer_size), - start_time_(start_time), + start_time_(-1.0), duration_(0.0), timing_function_(timing_function.Pass()) {} @@ -165,19 +162,32 @@ gfx::SizeF PageScaleAnimation::ViewportSizeAt(float interp) const { return gfx::ScaleSize(viewport_size_, 1.f / PageScaleFactorAt(interp)); } +bool PageScaleAnimation::IsAnimationStarted() const { + return start_time_ >= 0; +} + +void PageScaleAnimation::StartAnimation(double time) { + DCHECK_GT(0, start_time_); + start_time_ = time; +} + gfx::Vector2dF PageScaleAnimation::ScrollOffsetAtTime(double time) const { + DCHECK_GE(start_time_, 0); return ScrollOffsetAt(InterpAtTime(time)); } float PageScaleAnimation::PageScaleFactorAtTime(double time) const { + DCHECK_GE(start_time_, 0); return PageScaleFactorAt(InterpAtTime(time)); } bool PageScaleAnimation::IsAnimationCompleteAtTime(double time) const { + DCHECK_GE(start_time_, 0); return time >= end_time(); } float PageScaleAnimation::InterpAtTime(double time) const { + DCHECK_GE(start_time_, 0); DCHECK_GE(time, start_time_); if (IsAnimationCompleteAtTime(time)) return 1.f; diff --git a/chromium/cc/input/page_scale_animation.h b/chromium/cc/input/page_scale_animation.h index b6d35104153..4c30cdcbc52 100644 --- a/chromium/cc/input/page_scale_animation.h +++ b/chromium/cc/input/page_scale_animation.h @@ -29,7 +29,6 @@ class PageScaleAnimation { float start_page_scale_factor, gfx::SizeF viewport_size, gfx::SizeF root_layer_size, - double start_time, scoped_ptr<TimingFunction> timing_function); ~PageScaleAnimation(); @@ -50,6 +49,11 @@ class PageScaleAnimation { float target_page_scale_factor, double duration); + // These should be called before the first frame of animation to initialize + // the start time. StartAnimation should only be called once after creation. + bool IsAnimationStarted() const; + void StartAnimation(double time); + // Call these functions while the animation is in progress to output the // current state. gfx::Vector2dF ScrollOffsetAtTime(double time) const; @@ -69,7 +73,6 @@ class PageScaleAnimation { float start_page_scale_factor, gfx::SizeF viewport_size, gfx::SizeF root_layer_size, - double start_time, scoped_ptr<TimingFunction> timing_function); private: diff --git a/chromium/cc/input/scrollbar.h b/chromium/cc/input/scrollbar.h index 4c7e33837f6..bfefd430213 100644 --- a/chromium/cc/input/scrollbar.h +++ b/chromium/cc/input/scrollbar.h @@ -24,6 +24,7 @@ class Scrollbar { virtual ~Scrollbar() {} virtual ScrollbarOrientation Orientation() const = 0; + virtual bool IsLeftSideVerticalScrollbar() const = 0; virtual gfx::Point Location() const = 0; virtual bool IsOverlay() const = 0; virtual bool HasThumb() const = 0; diff --git a/chromium/cc/layers/compositing_reasons.h b/chromium/cc/layers/compositing_reasons.h index 28c2f7b4dc6..02354a03283 100644 --- a/chromium/cc/layers/compositing_reasons.h +++ b/chromium/cc/layers/compositing_reasons.h @@ -22,7 +22,6 @@ const uint64 kCompositingReasonFilters = GG_UINT64_C(1) << 7; const uint64 kCompositingReasonPositionFixed = GG_UINT64_C(1) << 8; const uint64 kCompositingReasonPositionSticky = GG_UINT64_C(1) << 9; const uint64 kCompositingReasonOverflowScrollingTouch = GG_UINT64_C(1) << 10; -const uint64 kCompositingReasonBlending = GG_UINT64_C(1) << 11; const uint64 kCompositingReasonAssumedOverlap = GG_UINT64_C(1) << 12; const uint64 kCompositingReasonOverlap = GG_UINT64_C(1) << 13; const uint64 kCompositingReasonNegativeZIndexChildren = GG_UINT64_C(1) << 14; @@ -52,6 +51,8 @@ const uint64 kCompositingReasonLayerForScrollingContainer = const uint64 kCompositingReasonLayerForForeground = GG_UINT64_C(1) << 29; const uint64 kCompositingReasonLayerForBackground = GG_UINT64_C(1) << 30; const uint64 kCompositingReasonLayerForMask = GG_UINT64_C(1) << 31; +const uint64 kCompositingReasonOverflowScrollingParent = GG_UINT64_C(1) << 32; +const uint64 kCompositingReasonOutOfFlowClipping = GG_UINT64_C(1) << 33; typedef uint64 CompositingReasons; diff --git a/chromium/cc/layers/content_layer.cc b/chromium/cc/layers/content_layer.cc index 2053ecbeac1..3d611eeef28 100644 --- a/chromium/cc/layers/content_layer.cc +++ b/chromium/cc/layers/content_layer.cc @@ -29,6 +29,12 @@ void ContentLayerPainter::Paint(SkCanvas* canvas, base::TimeTicks paint_start = base::TimeTicks::HighResNow(); client_->PaintContents(canvas, content_rect, opaque); base::TimeTicks paint_end = base::TimeTicks::HighResNow(); + // The start and end times might be the same if the paint was very fast or if + // our timer granularity is poor. Treat this as a very short time duration + // instead of none to avoid dividing by zero. + if (paint_end == paint_start) + paint_end += base::TimeDelta::FromMicroseconds(1); + double pixels_per_sec = (content_rect.width() * content_rect.height()) / (paint_end - paint_start).InSecondsF(); UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.AccelContentPaintDurationMS", @@ -121,9 +127,8 @@ void ContentLayer::CreateUpdaterIfNeeded() { } updater_->SetOpaque(contents_opaque()); - unsigned texture_format = - layer_tree_host()->GetRendererCapabilities().best_texture_format; - SetTextureFormat(texture_format); + SetTextureFormat( + layer_tree_host()->GetRendererCapabilities().best_texture_format); } void ContentLayer::SetContentsOpaque(bool opaque) { @@ -145,4 +150,19 @@ bool ContentLayer::SupportsLCDText() const { return true; } +skia::RefPtr<SkPicture> ContentLayer::GetPicture() const { + if (!DrawsContent()) + return skia::RefPtr<SkPicture>(); + + int width = bounds().width(); + int height = bounds().height(); + gfx::RectF opaque; + + skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture); + SkCanvas* canvas = picture->beginRecording(width, height); + client_->PaintContents(canvas, gfx::Rect(width, height), &opaque); + picture->endRecording(); + return picture; +} + } // namespace cc diff --git a/chromium/cc/layers/content_layer.h b/chromium/cc/layers/content_layer.h index f49215b6b40..a076c31dcac 100644 --- a/chromium/cc/layers/content_layer.h +++ b/chromium/cc/layers/content_layer.h @@ -52,6 +52,8 @@ class CC_EXPORT ContentLayer : public TiledLayer { virtual bool SupportsLCDText() const OVERRIDE; + virtual skia::RefPtr<SkPicture> GetPicture() const OVERRIDE; + protected: explicit ContentLayer(ContentLayerClient* client); virtual ~ContentLayer(); diff --git a/chromium/cc/layers/delegated_renderer_layer.cc b/chromium/cc/layers/delegated_renderer_layer.cc index 66064d9e849..cc49c753e25 100644 --- a/chromium/cc/layers/delegated_renderer_layer.cc +++ b/chromium/cc/layers/delegated_renderer_layer.cc @@ -7,6 +7,9 @@ #include "cc/layers/delegated_renderer_layer_client.h" #include "cc/layers/delegated_renderer_layer_impl.h" #include "cc/output/delegated_frame_data.h" +#include "cc/quads/render_pass_draw_quad.h" +#include "cc/trees/blocking_task_runner.h" +#include "cc/trees/layer_tree_host.h" namespace cc { @@ -19,7 +22,10 @@ scoped_refptr<DelegatedRendererLayer> DelegatedRendererLayer::Create( DelegatedRendererLayer::DelegatedRendererLayer( DelegatedRendererLayerClient* client) : Layer(), - client_(client) {} + client_(client), + needs_filter_context_(false), + main_thread_runner_(BlockingTaskRunner::current()), + weak_ptrs_(this) {} DelegatedRendererLayer::~DelegatedRendererLayer() {} @@ -29,6 +35,26 @@ scoped_ptr<LayerImpl> DelegatedRendererLayer::CreateLayerImpl( tree_impl, layer_id_).PassAs<LayerImpl>(); } +void DelegatedRendererLayer::SetLayerTreeHost(LayerTreeHost* host) { + if (layer_tree_host() == host) { + Layer::SetLayerTreeHost(host); + return; + } + + if (!host) { + // The active frame needs to be removed from the active tree and resources + // returned before the commit is called complete. + // TODO(danakj): Don't need to do this if the last frame commited was empty + // or we never commited a frame with resources. + SetNextCommitWaitsForActivation(); + } else { + if (needs_filter_context_) + host->set_needs_filter_context(); + } + + Layer::SetLayerTreeHost(host); +} + bool DelegatedRendererLayer::DrawsContent() const { return Layer::DrawsContent() && !frame_size_.IsEmpty(); } @@ -41,19 +67,23 @@ void DelegatedRendererLayer::PushPropertiesTo(LayerImpl* impl) { delegated_impl->SetDisplaySize(display_size_); + delegated_impl->CreateChildIdIfNeeded( + base::Bind(&DelegatedRendererLayer::ReceiveUnusedResourcesOnImplThread, + main_thread_runner_, + weak_ptrs_.GetWeakPtr())); + if (frame_data_) delegated_impl->SetFrameData(frame_data_.Pass(), damage_in_frame_); frame_data_.reset(); damage_in_frame_ = gfx::RectF(); - delegated_impl->CollectUnusedResources( - &unused_resources_for_child_compositor_); - + // The ResourceProvider will have the new frame as soon as we push it to the + // pending tree. So unused resources will be returned as well. if (client_) client_->DidCommitFrameData(); - // TODO(danakj): TakeUnusedResourcesForChildCompositor requires a push - // properties to happen in order to collect unused resources returned + // TODO(danakj): The DidCommitFrameData() notification requires a push + // properties to happen in order to notify about unused resources returned // from the parent compositor. crbug.com/259090 needs_push_properties_ = true; } @@ -67,48 +97,74 @@ void DelegatedRendererLayer::SetDisplaySize(gfx::Size size) { void DelegatedRendererLayer::SetFrameData( scoped_ptr<DelegatedFrameData> new_frame_data) { + DCHECK(new_frame_data); + if (frame_data_) { - // Copy the resources from the last provided frame into the new frame, as - // it may use resources that were transferred in the last frame. - new_frame_data->resource_list.insert(new_frame_data->resource_list.end(), - frame_data_->resource_list.begin(), - frame_data_->resource_list.end()); + // Copy the resources from the last provided frame into the unused resources + // list, as the new frame will provide its own resources. + TransferableResource::ReturnResources( + frame_data_->resource_list, + &unused_resources_for_child_compositor_); } frame_data_ = new_frame_data.Pass(); if (!frame_data_->render_pass_list.empty()) { RenderPass* root_pass = frame_data_->render_pass_list.back(); damage_in_frame_.Union(root_pass->damage_rect); frame_size_ = root_pass->output_rect.size(); - - // TODO(danakj): This could be optimized to only add resources to the - // frame_data_ if they are actually used in the frame. For now, it will - // cause the parent (this layer) to hold onto some resources it doesn't - // need to for an extra frame. - for (size_t i = 0; i < unused_resources_for_child_compositor_.size(); ++i) { - frame_data_->resource_list.push_back( - unused_resources_for_child_compositor_[i]); - } - unused_resources_for_child_compositor_.clear(); } else { frame_size_ = gfx::Size(); } + + // If any RenderPassDrawQuad has a filter operation, then we need a filter + // context to draw this layer's content. + for (size_t i = 0; + !needs_filter_context_ && i < frame_data_->render_pass_list.size(); + ++i) { + const QuadList& quad_list = frame_data_->render_pass_list[i]->quad_list; + for (size_t j = 0; !needs_filter_context_ && j < quad_list.size(); ++j) { + if (quad_list[j]->material != DrawQuad::RENDER_PASS) + continue; + const RenderPassDrawQuad* render_pass_quad = + RenderPassDrawQuad::MaterialCast(quad_list[j]); + if (!render_pass_quad->filters.IsEmpty() || + !render_pass_quad->background_filters.IsEmpty()) + needs_filter_context_ = true; + } + } + if (needs_filter_context_ && layer_tree_host()) + layer_tree_host()->set_needs_filter_context(); + SetNeedsCommit(); + // The active frame needs to be replaced and resources returned before the + // commit is called complete. + SetNextCommitWaitsForActivation(); } void DelegatedRendererLayer::TakeUnusedResourcesForChildCompositor( - TransferableResourceArray* array) { + ReturnedResourceArray* array) { DCHECK(array->empty()); array->clear(); array->swap(unused_resources_for_child_compositor_); } -bool DelegatedRendererLayer::BlocksPendingCommit() const { - // The active frame needs to be replaced and resources returned before the - // commit is called complete. This is true even whenever there may be - // resources to return, regardless of if the layer will draw in its new - // state. - return true; +void DelegatedRendererLayer::ReceiveUnusedResources( + const ReturnedResourceArray& unused) { + unused_resources_for_child_compositor_.insert( + unused_resources_for_child_compositor_.end(), + unused.begin(), + unused.end()); +} + +// static +void DelegatedRendererLayer::ReceiveUnusedResourcesOnImplThread( + scoped_refptr<BlockingTaskRunner> task_runner, + base::WeakPtr<DelegatedRendererLayer> self, + const ReturnedResourceArray& unused) { + task_runner->PostTask( + FROM_HERE, + base::Bind( + &DelegatedRendererLayer::ReceiveUnusedResources, self, unused)); } } // namespace cc diff --git a/chromium/cc/layers/delegated_renderer_layer.h b/chromium/cc/layers/delegated_renderer_layer.h index 3f5280eec5f..60e9c3c05d9 100644 --- a/chromium/cc/layers/delegated_renderer_layer.h +++ b/chromium/cc/layers/delegated_renderer_layer.h @@ -5,12 +5,15 @@ #ifndef CC_LAYERS_DELEGATED_RENDERER_LAYER_H_ #define CC_LAYERS_DELEGATED_RENDERER_LAYER_H_ +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/synchronization/lock.h" #include "cc/base/cc_export.h" #include "cc/layers/layer.h" -#include "cc/resources/transferable_resource.h" +#include "cc/resources/returned_resource.h" namespace cc { - +class BlockingTaskRunner; class DelegatedFrameData; class DelegatedRendererLayerClient; @@ -21,6 +24,7 @@ class CC_EXPORT DelegatedRendererLayer : public Layer { virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; + virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* impl) OVERRIDE; virtual bool DrawsContent() const OVERRIDE; @@ -34,25 +38,34 @@ class CC_EXPORT DelegatedRendererLayer : public Layer { // Passes ownership of any unused resources that had been given by the child // compositor to the given array, so they can be given back to the child. - void TakeUnusedResourcesForChildCompositor(TransferableResourceArray* array); - - virtual bool BlocksPendingCommit() const OVERRIDE; + void TakeUnusedResourcesForChildCompositor(ReturnedResourceArray* array); protected: explicit DelegatedRendererLayer(DelegatedRendererLayerClient* client); virtual ~DelegatedRendererLayer(); private: + void ReceiveUnusedResources(const ReturnedResourceArray& unused); + static void ReceiveUnusedResourcesOnImplThread( + scoped_refptr<BlockingTaskRunner> task_runner, + base::WeakPtr<DelegatedRendererLayer> self, + const ReturnedResourceArray& unused); + scoped_ptr<DelegatedFrameData> frame_data_; gfx::RectF damage_in_frame_; gfx::Size frame_size_; gfx::Size display_size_; - TransferableResourceArray unused_resources_for_child_compositor_; DelegatedRendererLayerClient* client_; + bool needs_filter_context_; + + ReturnedResourceArray unused_resources_for_child_compositor_; + scoped_refptr<BlockingTaskRunner> main_thread_runner_; + base::WeakPtrFactory<DelegatedRendererLayer> weak_ptrs_; DISALLOW_COPY_AND_ASSIGN(DelegatedRendererLayer); }; } // namespace cc + #endif // CC_LAYERS_DELEGATED_RENDERER_LAYER_H_ diff --git a/chromium/cc/layers/delegated_renderer_layer_impl.cc b/chromium/cc/layers/delegated_renderer_layer_impl.cc index 2dfdac6eb2d..34a1cd86755 100644 --- a/chromium/cc/layers/delegated_renderer_layer_impl.cc +++ b/chromium/cc/layers/delegated_renderer_layer_impl.cc @@ -47,7 +47,7 @@ bool DelegatedRendererLayerImpl::HasContributingDelegatedRenderPasses() const { static ResourceProvider::ResourceId ResourceRemapHelper( bool* invalid_frame, const ResourceProvider::ResourceIdMap& child_to_parent_map, - ResourceProvider::ResourceIdSet *remapped_resources, + ResourceProvider::ResourceIdArray* resources_in_frame, ResourceProvider::ResourceId id) { ResourceProvider::ResourceIdMap::const_iterator it = @@ -59,7 +59,7 @@ static ResourceProvider::ResourceId ResourceRemapHelper( DCHECK_EQ(it->first, id); ResourceProvider::ResourceId remapped_id = it->second; - remapped_resources->insert(remapped_id); + resources_in_frame->push_back(id); return remapped_id; } @@ -85,23 +85,31 @@ void DelegatedRendererLayerImpl::PushPropertiesTo(LayerImpl* layer) { have_render_passes_to_push_ = false; } - // This is just a copy for testing since we keep the data on the pending layer - // for returning resources to the child for now. + // This is just a copy for testing, since resources are added to the + // ResourceProvider in the pending tree. delegated_layer->resources_ = resources_; } +void DelegatedRendererLayerImpl::CreateChildIdIfNeeded( + const ReturnCallback& return_callback) { + if (child_id_) + return; + + ResourceProvider* resource_provider = layer_tree_impl()->resource_provider(); + child_id_ = resource_provider->CreateChild(return_callback); + own_child_id_ = true; +} + void DelegatedRendererLayerImpl::SetFrameData( scoped_ptr<DelegatedFrameData> frame_data, gfx::RectF damage_in_frame) { DCHECK(frame_data); + DCHECK(child_id_) << "CreateChildIdIfNeeded must be called first."; // A frame with an empty root render pass is invalid. DCHECK(frame_data->render_pass_list.empty() || !frame_data->render_pass_list.back()->output_rect.IsEmpty()); - CreateChildIdIfNeeded(); - DCHECK(child_id_); - ResourceProvider* resource_provider = layer_tree_impl()->resource_provider(); const ResourceProvider::ResourceIdMap& resource_map = resource_provider->GetChildToParentMap(child_id_); @@ -109,12 +117,12 @@ void DelegatedRendererLayerImpl::SetFrameData( resource_provider->ReceiveFromChild(child_id_, frame_data->resource_list); bool invalid_frame = false; - ResourceProvider::ResourceIdSet used_resources; + ResourceProvider::ResourceIdArray resources_in_frame; DrawQuad::ResourceIteratorCallback remap_resources_to_parent_callback = base::Bind(&ResourceRemapHelper, &invalid_frame, resource_map, - &used_resources); + &resources_in_frame); for (size_t i = 0; i < frame_data->render_pass_list.size(); ++i) { RenderPass* pass = frame_data->render_pass_list[i]; for (size_t j = 0; j < pass->quad_list.size(); ++j) { @@ -123,8 +131,15 @@ void DelegatedRendererLayerImpl::SetFrameData( } } - if (invalid_frame) + if (invalid_frame) { + // Declare we are still using the last frame's resources. + resource_provider->DeclareUsedResourcesFromChild(child_id_, resources_); return; + } + + // Declare we are using the new frame's resources. + resources_.swap(resources_in_frame); + resource_provider->DeclareUsedResourcesFromChild(child_id_, resources_); // Display size is already set so we can compute what the damage rect // will be in layer space. @@ -140,33 +155,9 @@ void DelegatedRendererLayerImpl::SetFrameData( // Save the remapped quads on the layer. This steals the quads and render // passes from the frame_data. SetRenderPasses(&frame_data->render_pass_list); - resources_.swap(used_resources); have_render_passes_to_push_ = true; } -void DelegatedRendererLayerImpl::CollectUnusedResources( - TransferableResourceArray* resources_for_ack) { - CreateChildIdIfNeeded(); - DCHECK(child_id_); - - ResourceProvider* resource_provider = layer_tree_impl()->resource_provider(); - const ResourceProvider::ResourceIdMap& resource_map = - resource_provider->GetChildToParentMap(child_id_); - - ResourceProvider::ResourceIdArray unused_resources; - for (ResourceProvider::ResourceIdMap::const_iterator it = - resource_map.begin(); - it != resource_map.end(); - ++it) { - bool resource_is_in_current_frame = resources_.count(it->second) > 0; - bool resource_is_in_use = resource_provider->InUseByConsumer(it->second); - if (!resource_is_in_current_frame && !resource_is_in_use) - unused_resources.push_back(it->second); - } - resource_provider->PrepareSendToChild( - child_id_, unused_resources, resources_for_ack); -} - void DelegatedRendererLayerImpl::SetDisplaySize(gfx::Size size) { if (display_size_ == size) return; @@ -176,10 +167,6 @@ void DelegatedRendererLayerImpl::SetDisplaySize(gfx::Size size) { void DelegatedRendererLayerImpl::SetRenderPasses( ScopedPtrVector<RenderPass>* render_passes_in_draw_order) { - gfx::RectF old_root_damage; - if (!render_passes_in_draw_order_.empty()) - old_root_damage = render_passes_in_draw_order_.back()->damage_rect; - ClearRenderPasses(); for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) { @@ -192,9 +179,6 @@ void DelegatedRendererLayerImpl::SetRenderPasses( render_passes_in_draw_order_.push_back(taken_render_pass.Pass()); } - if (!render_passes_in_draw_order_.empty()) - render_passes_in_draw_order_.back()->damage_rect.Union(old_root_damage); - // Give back an empty array instead of nulls. render_passes_in_draw_order->clear(); } @@ -240,13 +224,19 @@ RenderPass::Id DelegatedRendererLayerImpl::NextContributingRenderPassId( return RenderPass::Id(previous.layer_id, previous.index + 1); } -RenderPass::Id DelegatedRendererLayerImpl::ConvertDelegatedRenderPassId( - RenderPass::Id delegated_render_pass_id) const { +bool DelegatedRendererLayerImpl::ConvertDelegatedRenderPassId( + RenderPass::Id delegated_render_pass_id, + RenderPass::Id* output_render_pass_id) const { base::hash_map<RenderPass::Id, int>::const_iterator found = render_passes_index_by_id_.find(delegated_render_pass_id); - DCHECK(found != render_passes_index_by_id_.end()); + if (found == render_passes_index_by_id_.end()) { + // Be robust against a RenderPass id that isn't part of the frame. + return false; + } unsigned delegated_render_pass_index = found->second; - return RenderPass::Id(id(), IndexToId(delegated_render_pass_index)); + *output_render_pass_id = + RenderPass::Id(id(), IndexToId(delegated_render_pass_index)); + return true; } void DelegatedRendererLayerImpl::AppendContributingRenderPasses( @@ -254,10 +244,14 @@ void DelegatedRendererLayerImpl::AppendContributingRenderPasses( DCHECK(HasContributingDelegatedRenderPasses()); for (size_t i = 0; i < render_passes_in_draw_order_.size() - 1; ++i) { - RenderPass::Id output_render_pass_id = - ConvertDelegatedRenderPassId(render_passes_in_draw_order_[i]->id); + RenderPass::Id output_render_pass_id(-1, -1); + bool present = + ConvertDelegatedRenderPassId(render_passes_in_draw_order_[i]->id, + &output_render_pass_id); // Don't clash with the RenderPass we generate if we own a RenderSurface. + DCHECK(present) << render_passes_in_draw_order_[i]->id.layer_id << ", " + << render_passes_in_draw_order_[i]->id.index; DCHECK_GT(output_render_pass_id.index, 0); render_pass_sink->AppendRenderPass( @@ -288,13 +282,13 @@ void DelegatedRendererLayerImpl::AppendQuads( DCHECK(root_delegated_render_pass->output_rect.origin().IsOrigin()); gfx::Size frame_size = root_delegated_render_pass->output_rect.size(); - // If the index of the EenderPassId is 0, then it is a RenderPass generated - // for a layer in this compositor, not the delegated renderer. Then we want to - // merge our root RenderPass with the target RenderPass. Otherwise, it is some - // RenderPass which we added from the delegated renderer. + // If the index of the RenderPassId is 0, then it is a RenderPass generated + // for a layer in this compositor, not the delegating renderer. Then we want + // to merge our root RenderPass with the target RenderPass. Otherwise, it is + // some RenderPass which we added from the delegating renderer. bool should_merge_root_render_pass_with_target = !target_render_pass_id.index; if (should_merge_root_render_pass_with_target) { - // Verify that the RenderPass we are appending to is created our + // Verify that the RenderPass we are appending to is created by our // render_target. DCHECK(target_render_pass_id.layer_id == render_target()->id()); @@ -421,6 +415,7 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads( if (render_target() == this) { DCHECK(!is_clipped()); DCHECK(render_surface()); + DCHECK_EQ(0, num_unclipped_descendants()); output_shared_quad_state->clip_rect = MathUtil::MapClippedRect( delegated_frame_to_target_transform, output_shared_quad_state->clip_rect); @@ -446,18 +441,26 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads( } else { RenderPass::Id delegated_contributing_render_pass_id = RenderPassDrawQuad::MaterialCast(delegated_quad)->render_pass_id; - RenderPass::Id output_contributing_render_pass_id = - ConvertDelegatedRenderPassId(delegated_contributing_render_pass_id); - DCHECK(output_contributing_render_pass_id != - append_quads_data->render_pass_id); - - output_quad = RenderPassDrawQuad::MaterialCast(delegated_quad)->Copy( - output_shared_quad_state, - output_contributing_render_pass_id).PassAs<DrawQuad>(); + RenderPass::Id output_contributing_render_pass_id(-1, -1); + + bool present = + ConvertDelegatedRenderPassId(delegated_contributing_render_pass_id, + &output_contributing_render_pass_id); + + // The frame may have a RenderPassDrawQuad that points to a RenderPass not + // part of the frame. Just ignore these quads. + if (present) { + DCHECK(output_contributing_render_pass_id != + append_quads_data->render_pass_id); + + output_quad = RenderPassDrawQuad::MaterialCast(delegated_quad)->Copy( + output_shared_quad_state, + output_contributing_render_pass_id).PassAs<DrawQuad>(); + } } - DCHECK(output_quad.get()); - quad_sink->Append(output_quad.Pass(), append_quads_data); + if (output_quad) + quad_sink->Append(output_quad.Pass(), append_quads_data); } } @@ -465,15 +468,6 @@ const char* DelegatedRendererLayerImpl::LayerTypeAsString() const { return "cc::DelegatedRendererLayerImpl"; } -void DelegatedRendererLayerImpl::CreateChildIdIfNeeded() { - if (child_id_) - return; - - ResourceProvider* resource_provider = layer_tree_impl()->resource_provider(); - child_id_ = resource_provider->CreateChild(); - own_child_id_ = true; -} - void DelegatedRendererLayerImpl::ClearChildId() { if (!child_id_) return; diff --git a/chromium/cc/layers/delegated_renderer_layer_impl.h b/chromium/cc/layers/delegated_renderer_layer_impl.h index 9cfdcb6a351..7d774f1c357 100644 --- a/chromium/cc/layers/delegated_renderer_layer_impl.h +++ b/chromium/cc/layers/delegated_renderer_layer_impl.h @@ -40,11 +40,14 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl { void AppendContributingRenderPasses(RenderPassSink* render_pass_sink); + // Creates an ID with the resource provider for the child renderer + // that will be sending quads to the layer. Registers the callback to + // inform when resources are no longer in use. + void CreateChildIdIfNeeded(const ReturnCallback& return_callback); + void SetFrameData(scoped_ptr<DelegatedFrameData> frame_data, gfx::RectF damage_in_frame); - void CollectUnusedResources(TransferableResourceArray* resources_for_ack); - void SetDisplaySize(gfx::Size size); protected: @@ -54,14 +57,11 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl { const ScopedPtrVector<RenderPass>& RenderPassesInDrawOrderForTesting() const { return render_passes_in_draw_order_; } - const ResourceProvider::ResourceIdSet& ResourcesForTesting() const { + const ResourceProvider::ResourceIdArray& ResourcesForTesting() const { return resources_; } private: - // Creates an ID with the resource provider for the child renderer - // that will be sending quads to the layer. - void CreateChildIdIfNeeded(); void ClearChildId(); void AppendRainbowDebugBorder(QuadSink* quad_sink, @@ -71,8 +71,11 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl { ScopedPtrVector<RenderPass>* render_passes_in_draw_order); void ClearRenderPasses(); - RenderPass::Id ConvertDelegatedRenderPassId( - RenderPass::Id delegated_render_pass_id) const; + // Returns |true| if the delegated_render_pass_id is part of the current + // frame and can be converted. + bool ConvertDelegatedRenderPassId( + RenderPass::Id delegated_render_pass_id, + RenderPass::Id* output_render_pass_id) const; gfx::Transform DelegatedFrameToLayerSpaceTransform(gfx::Size frame_size) const; @@ -89,7 +92,7 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl { bool have_render_passes_to_push_; ScopedPtrVector<RenderPass> render_passes_in_draw_order_; base::hash_map<RenderPass::Id, int> render_passes_index_by_id_; - ResourceProvider::ResourceIdSet resources_; + ResourceProvider::ResourceIdArray resources_; gfx::Size display_size_; int child_id_; diff --git a/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc b/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc index 1584782f109..a641e00eaf0 100644 --- a/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc +++ b/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc @@ -5,6 +5,7 @@ #include "cc/layers/delegated_renderer_layer_impl.h" #include "cc/base/scoped_ptr_vector.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/quad_sink.h" #include "cc/layers/solid_color_layer_impl.h" @@ -20,7 +21,6 @@ #include "cc/test/mock_quad_culler.h" #include "cc/test/render_pass_test_common.h" #include "cc/test/render_pass_test_utils.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" @@ -38,20 +38,15 @@ class DelegatedRendererLayerImplTest : public testing::Test { LayerTreeSettings settings; settings.minimum_occlusion_tracking_size = gfx::Size(); - host_impl_ = LayerTreeHostImpl::Create(settings, - &client_, - &proxy_, - &stats_instrumentation_); + host_impl_.reset(new FakeLayerTreeHostImpl(settings, &proxy_)); host_impl_->InitializeRenderer(CreateFakeOutputSurface()); host_impl_->SetViewportSize(gfx::Size(10, 10)); } protected: FakeProxy proxy_; - FakeLayerTreeHostImplClient client_; DebugScopedSetImplThreadAndMainThreadBlocked always_impl_thread_and_main_thread_blocked_; - FakeRenderingStatsInstrumentation stats_instrumentation_; scoped_ptr<LayerTreeHostImpl> host_impl_; }; @@ -472,8 +467,8 @@ class DelegatedRendererLayerImplTestTransform root_layer->SetBounds(gfx::Size(100, 100)); delegated_renderer_layer->SetPosition(gfx::Point(20, 20)); - delegated_renderer_layer->SetBounds(gfx::Size(30, 30)); - delegated_renderer_layer->SetContentBounds(gfx::Size(30, 30)); + delegated_renderer_layer->SetBounds(gfx::Size(75, 75)); + delegated_renderer_layer->SetContentBounds(gfx::Size(75, 75)); delegated_renderer_layer->SetDrawsContent(true); gfx::Transform transform; transform.Scale(2.0, 2.0); @@ -518,8 +513,8 @@ class DelegatedRendererLayerImplTestTransform quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data); } - gfx::Size root_pass_content_bounds(50, 50); - gfx::Rect root_pass_rect(0, 0, 50, 50); + gfx::Size root_pass_content_bounds(100, 100); + gfx::Rect root_pass_rect(0, 0, 100, 100); gfx::Transform root_pass_transform; root_pass_transform.Scale(1.5, 1.5); root_pass_transform.Translate(7.0, 7.0); @@ -653,7 +648,7 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_NoSurface) { // When the quads don't have a clip of their own, the clip rect is set to // the drawable_content_rect of the delegated renderer layer. - EXPECT_EQ(gfx::Rect(42, 42, 120, 120).ToString(), + EXPECT_EQ(delegated_renderer_layer_->drawable_content_rect().ToString(), root_delegated_shared_quad_state->clip_rect.ToString()); // Even though the quads in the root pass have no clip of their own, they @@ -665,12 +660,12 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_NoSurface) { // Device scale factor is 2. expected.Scale(2.0, 2.0); // This is the transform from the layer's space to its target. - // The position (20) - the width / scale (30 / 2) = 20 - 15 = 5 - expected.Translate(5.0, 5.0); + // The position (20) - the width / scale (75 / 2) = 20 - 37.5 = -17.5 + expected.Translate(-17.5, -17.5); expected.Scale(2.0, 2.0); expected.Translate(8.0, 8.0); - // The frame has size 50x50 but the layer's bounds are 30x30. - expected.Scale(30.0 / 50.0, 30.0 / 50.0); + // The frame has size 100x100 but the layer's bounds are 75x75. + expected.Scale(75.0 / 100.0, 75.0 / 100.0); // This is the transform within the source frame. expected.Scale(1.5, 1.5); expected.Translate(7.0, 7.0); @@ -710,15 +705,15 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_NoSurface) { // Since the quads have a clip_rect it should be modified by delegated // renderer layer's draw_transform. // The position of the resulting clip_rect is: - // (clip rect position (10) * scale to layer (30/50) + translate (8)) * - // layer scale (2) + layer position (20) = 48 - // But the layer is centered, so: 48 - (width / 2) = 48 - 30 / 2 = 33 - // The device scale is 2, so everything gets doubled, giving 66. + // (clip rect position (10) * scale to layer (75/100) + translate (8)) * + // layer scale (2) + layer position (20) = 51 + // But the layer is centered, so: 51 - (75 / 2) = 51 - 75 / 2 = 13.5 + // The device scale is 2, so everything gets doubled, giving 27. // - // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from - // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42. The device scale - // doubles this to 84. - EXPECT_EQ(gfx::Rect(66, 66, 84, 84).ToString(), + // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from + // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5. The device + // scale doubles this to 105. + EXPECT_EQ(gfx::Rect(27, 27, 105, 105).ToString(), root_delegated_shared_quad_state->clip_rect.ToString()); // The quads had a clip and it should be preserved. @@ -728,12 +723,12 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_NoSurface) { // Device scale factor is 2. expected.Scale(2.0, 2.0); // This is the transform from the layer's space to its target. - // The position (20) - the width / scale (30 / 2) = 20 - 15 = 5 - expected.Translate(5.0, 5.0); + // The position (20) - the width / scale (75 / 2) = 20 - 37.5 = -17.5 + expected.Translate(-17.5, -17.5); expected.Scale(2.0, 2.0); expected.Translate(8.0, 8.0); - // The frame has size 50x50 but the layer's bounds are 30x30. - expected.Scale(30.0 / 50.0, 30.0 / 50.0); + // The frame has size 100x100 but the layer's bounds are 75x75. + expected.Scale(75.0 / 100.0, 75.0 / 100.0); // This is the transform within the source frame. expected.Scale(1.5, 1.5); expected.Translate(7.0, 7.0); @@ -775,10 +770,10 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_Surface) { // When the layer owns a surface, then its position and translation are not // a part of its draw transform. // The position of the resulting clip_rect is: - // (clip rect position (10) * scale to layer (30/50)) * device scale (2) = 12 - // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from - // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42. - EXPECT_EQ(gfx::Rect(12, 12, 42, 42).ToString(), + // (clip rect position (10) * scale to layer (75/100)) * device scale (2) = 15 + // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from + // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5. + EXPECT_EQ(gfx::Rect(15, 15, 53, 53).ToString(), root_delegated_shared_quad_state->clip_rect.ToString()); // Since the layer owns a surface it doesn't need to clip its quads, so @@ -788,8 +783,8 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_Surface) { gfx::Transform expected; // Device scale factor is 2. expected.Scale(2.0, 2.0); - // The frame has size 50x50 but the layer's bounds are 30x30. - expected.Scale(30.0 / 50.0, 30.0 / 50.0); + // The frame has size 100x100 but the layer's bounds are 75x75. + expected.Scale(75.0 / 100.0, 75.0 / 100.0); // This is the transform within the source frame. expected.Scale(1.5, 1.5); expected.Translate(7.0, 7.0); @@ -831,10 +826,10 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_Surface) { // When the layer owns a surface, then its position and translation are not // a part of its draw transform. // The position of the resulting clip_rect is: - // (clip rect position (10) * scale to layer (30/50)) * device scale (2) = 12 - // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from - // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42. - EXPECT_EQ(gfx::Rect(12, 12, 42, 42).ToString(), + // (clip rect position (10) * scale to layer (75/100)) * device scale (2) = 15 + // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from + // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5. + EXPECT_EQ(gfx::Rect(15, 15, 53, 53).ToString(), root_delegated_shared_quad_state->clip_rect.ToString()); // The quads had a clip and it should be preserved. @@ -843,8 +838,8 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_Surface) { gfx::Transform expected; // Device scale factor is 2. expected.Scale(2.0, 2.0); - // The frame has size 50x50 but the layer's bounds are 30x30. - expected.Scale(30.0 / 50.0, 30.0 / 50.0); + // The frame has size 100x100 but the layer's bounds are 75x75. + expected.Scale(75.0 / 100.0, 75.0 / 100.0); // This is the transform within the source frame. expected.Scale(1.5, 1.5); expected.Translate(7.0, 7.0); @@ -1250,5 +1245,58 @@ TEST_F(DelegatedRendererLayerImplTestClip, QuadsClipped_LayerClipped_Surface) { host_impl_->DidDrawAllLayers(frame); } +TEST_F(DelegatedRendererLayerImplTest, InvalidRenderPassDrawQuad) { + scoped_ptr<LayerImpl> root_layer = LayerImpl::Create( + host_impl_->active_tree(), 1).PassAs<LayerImpl>(); + scoped_ptr<FakeDelegatedRendererLayerImpl> delegated_renderer_layer = + FakeDelegatedRendererLayerImpl::Create(host_impl_->active_tree(), 4); + + host_impl_->SetViewportSize(gfx::Size(100, 100)); + + delegated_renderer_layer->SetPosition(gfx::Point(3, 3)); + delegated_renderer_layer->SetBounds(gfx::Size(10, 10)); + delegated_renderer_layer->SetContentBounds(gfx::Size(10, 10)); + delegated_renderer_layer->SetDrawsContent(true); + + ScopedPtrVector<RenderPass> delegated_render_passes; + TestRenderPass* pass1 = AddRenderPass( + &delegated_render_passes, + RenderPass::Id(9, 6), + gfx::Rect(0, 0, 10, 10), + gfx::Transform()); + AddQuad(pass1, gfx::Rect(0, 0, 6, 6), 33u); + + // This render pass isn't part of the frame. + scoped_ptr<TestRenderPass> missing_pass(TestRenderPass::Create()); + missing_pass->SetNew(RenderPass::Id(9, 7), + gfx::Rect(7, 7, 7, 7), + gfx::Rect(7, 7, 7, 7), + gfx::Transform()); + + // But a render pass quad refers to it. + AddRenderPassQuad(pass1, missing_pass.get()); + + delegated_renderer_layer->SetFrameDataForRenderPasses( + &delegated_render_passes); + + // The RenderPasses should be taken by the layer. + EXPECT_EQ(0u, delegated_render_passes.size()); + + root_layer->AddChild(delegated_renderer_layer.PassAs<LayerImpl>()); + host_impl_->active_tree()->SetRootLayer(root_layer.Pass()); + + LayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); + + // The DelegatedRendererLayerImpl should drop the bad RenderPassDrawQuad. + ASSERT_EQ(1u, frame.render_passes.size()); + ASSERT_EQ(1u, frame.render_passes[0]->quad_list.size()); + EXPECT_EQ(DrawQuad::SOLID_COLOR, + frame.render_passes[0]->quad_list[0]->material); + + host_impl_->DrawLayers(&frame, base::TimeTicks::Now()); + host_impl_->DidDrawAllLayers(frame); +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/draw_properties.h b/chromium/cc/layers/draw_properties.h index bb80dce0adf..181d900f267 100644 --- a/chromium/cc/layers/draw_properties.h +++ b/chromium/cc/layers/draw_properties.h @@ -27,6 +27,7 @@ struct CC_EXPORT DrawProperties { contents_scale_x(1.f), contents_scale_y(1.f), num_descendants_that_draw_content(0), + num_unclipped_descendants(0), descendants_can_clip_selves(false), can_draw_directly_to_backbuffer(false), layer_or_descendant_has_copy_request(false) {} @@ -88,6 +89,10 @@ struct CC_EXPORT DrawProperties { // Does not include this layer itself, only its children and descendants. int num_descendants_that_draw_content; + // Number of descendants with a clip parent that is our ancestor. NB - this + // does not include our clip children because they are clipped by us. + int num_unclipped_descendants; + // If true, every descendant in the sub-tree can clip itself without the // need to use hardware sissoring or a new render target. bool descendants_can_clip_selves; diff --git a/chromium/cc/layers/heads_up_display_layer.cc b/chromium/cc/layers/heads_up_display_layer.cc index 92486cc8515..d9fab902ef9 100644 --- a/chromium/cc/layers/heads_up_display_layer.cc +++ b/chromium/cc/layers/heads_up_display_layer.cc @@ -55,4 +55,8 @@ scoped_ptr<LayerImpl> HeadsUpDisplayLayer::CreateLayerImpl( PassAs<LayerImpl>(); } +std::string HeadsUpDisplayLayer::DebugName() { + return std::string("Heads Up Display Layer"); +} + } // namespace cc diff --git a/chromium/cc/layers/heads_up_display_layer.h b/chromium/cc/layers/heads_up_display_layer.h index 7038cd480c3..f13d56bba67 100644 --- a/chromium/cc/layers/heads_up_display_layer.h +++ b/chromium/cc/layers/heads_up_display_layer.h @@ -5,6 +5,8 @@ #ifndef CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_ #define CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_ +#include <string> + #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/layers/contents_scaling_layer.h" @@ -23,6 +25,8 @@ class CC_EXPORT HeadsUpDisplayLayer : public ContentsScalingLayer { virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; + virtual std::string DebugName() OVERRIDE; + protected: HeadsUpDisplayLayer(); diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index 8728d704ca4..c1fa9f37dbc 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -94,8 +94,9 @@ bool HeadsUpDisplayLayerImpl::WillDraw(DrawMode draw_mode, hud_resource_->Free(); if (!hud_resource_->id()) { - hud_resource_->Allocate( - content_bounds(), GL_RGBA, ResourceProvider::TextureUsageAny); + hud_resource_->Allocate(content_bounds(), + ResourceProvider::TextureUsageAny, + RGBA_8888); } return LayerImpl::WillDraw(draw_mode, resource_provider); diff --git a/chromium/cc/layers/image_layer.cc b/chromium/cc/layers/image_layer.cc index 8ba1908131d..bbf1aa82e34 100644 --- a/chromium/cc/layers/image_layer.cc +++ b/chromium/cc/layers/image_layer.cc @@ -56,9 +56,8 @@ void ImageLayer::CreateUpdaterIfNeeded() { return; updater_ = ImageLayerUpdater::Create(); - GLenum texture_format = - layer_tree_host()->GetRendererCapabilities().best_texture_format; - SetTextureFormat(texture_format); + SetTextureFormat( + layer_tree_host()->GetRendererCapabilities().best_texture_format); } LayerUpdater* ImageLayer::Updater() const { diff --git a/chromium/cc/layers/io_surface_layer_impl.cc b/chromium/cc/layers/io_surface_layer_impl.cc index f8577a37765..e8c627be08b 100644 --- a/chromium/cc/layers/io_surface_layer_impl.cc +++ b/chromium/cc/layers/io_surface_layer_impl.cc @@ -39,11 +39,11 @@ void IOSurfaceLayerImpl::DestroyTexture() { } if (io_surface_texture_id_) { - OutputSurface* output_surface = layer_tree_impl()->output_surface(); + ContextProvider* context_provider = + layer_tree_impl()->output_surface()->context_provider().get(); // TODO(skaslev): Implement this path for software compositing. - WebKit::WebGraphicsContext3D* context3d = output_surface->context3d(); - if (context3d) - context3d->deleteTexture(io_surface_texture_id_); + if (context_provider) + context_provider->Context3d()->deleteTexture(io_surface_texture_id_); io_surface_texture_id_ = 0; } } @@ -67,13 +67,15 @@ bool IOSurfaceLayerImpl::WillDraw(DrawMode draw_mode, return false; if (io_surface_changed_) { - WebKit::WebGraphicsContext3D* context3d = - resource_provider->GraphicsContext3D(); - if (!context3d) { + ContextProvider* context_provider = + layer_tree_impl()->output_surface()->context_provider().get(); + if (!context_provider) { // TODO(skaslev): Implement this path for software compositing. return false; } + WebKit::WebGraphicsContext3D* context3d = context_provider->Context3d(); + // TODO(ernstm): Do this in a way that we can track memory usage. if (!io_surface_texture_id_) { io_surface_texture_id_ = context3d->createTexture(); diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index aa4a002dff4..86887a6ad2c 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -6,12 +6,14 @@ #include <algorithm> +#include "base/debug/trace_event.h" #include "base/location.h" #include "base/metrics/histogram.h" #include "base/single_thread_task_runner.h" #include "cc/animation/animation.h" #include "cc/animation/animation_events.h" #include "cc/animation/layer_animation_controller.h" +#include "cc/layers/layer_client.h" #include "cc/layers/layer_impl.h" #include "cc/output/copy_output_request.h" #include "cc/output/copy_output_result.h" @@ -54,8 +56,11 @@ Layer::Layer() use_parent_backface_visibility_(false), draw_checkerboard_for_missing_tiles_(false), force_render_surface_(false), + scroll_parent_(NULL), + clip_parent_(NULL), replica_layer_(NULL), - raster_scale_(0.f) { + raster_scale_(0.f), + client_(NULL) { if (layer_id_ < 0) { s_next_layer_id = 1; layer_id_ = s_next_layer_id++; @@ -85,6 +90,9 @@ Layer::~Layer() { DCHECK_EQ(this, replica_layer_->parent()); replica_layer_->RemoveFromParent(); } + + RemoveFromScrollTree(); + RemoveFromClipTree(); } void Layer::SetLayerTreeHost(LayerTreeHost* host) { @@ -144,14 +152,11 @@ void Layer::SetNeedsFullTreeSync() { layer_tree_host_->SetNeedsFullTreeSync(); } -bool Layer::IsPropertyChangeAllowed() const { +void Layer::SetNextCommitWaitsForActivation() { if (!layer_tree_host_) - return true; - - if (!layer_tree_host_->settings().strict_layer_property_change_checking) - return true; + return; - return !layer_tree_host_->in_paint_layer_contents(); + layer_tree_host_->SetNextCommitWaitsForActivation(); } void Layer::SetNeedsPushProperties() { @@ -179,6 +184,16 @@ void Layer::RemoveDependentNeedsPushProperties() { parent_->RemoveDependentNeedsPushProperties(); } +bool Layer::IsPropertyChangeAllowed() const { + if (!layer_tree_host_) + return true; + + if (!layer_tree_host_->settings().strict_layer_property_change_checking) + return true; + + return !layer_tree_host_->in_paint_layer_contents(); +} + gfx::Rect Layer::LayerRectToContentRect(const gfx::RectF& layer_rect) const { gfx::RectF content_rect = gfx::ScaleRect(layer_rect, contents_scale_x(), contents_scale_y()); @@ -188,28 +203,14 @@ gfx::Rect Layer::LayerRectToContentRect(const gfx::RectF& layer_rect) const { return gfx::ToEnclosingRect(content_rect); } -bool Layer::BlocksPendingCommit() const { - return false; +skia::RefPtr<SkPicture> Layer::GetPicture() const { + return skia::RefPtr<SkPicture>(); } bool Layer::CanClipSelf() const { return false; } -bool Layer::BlocksPendingCommitRecursive() const { - if (BlocksPendingCommit()) - return true; - if (mask_layer() && mask_layer()->BlocksPendingCommitRecursive()) - return true; - if (replica_layer() && replica_layer()->BlocksPendingCommitRecursive()) - return true; - for (size_t i = 0; i < children_.size(); ++i) { - if (children_[i]->BlocksPendingCommitRecursive()) - return true; - } - return false; -} - void Layer::SetParent(Layer* layer) { DCHECK(!layer || !layer->HasAncestor(this)); @@ -559,6 +560,66 @@ bool Layer::TransformIsAnimating() const { return layer_animation_controller_->IsAnimatingProperty(Animation::Transform); } +void Layer::SetScrollParent(Layer* parent) { + DCHECK(IsPropertyChangeAllowed()); + if (scroll_parent_ == parent) + return; + + if (scroll_parent_) + scroll_parent_->RemoveScrollChild(this); + + scroll_parent_ = parent; + + if (scroll_parent_) + scroll_parent_->AddScrollChild(this); + + SetNeedsCommit(); +} + +void Layer::AddScrollChild(Layer* child) { + if (!scroll_children_) + scroll_children_.reset(new std::set<Layer*>); + scroll_children_->insert(child); + SetNeedsCommit(); +} + +void Layer::RemoveScrollChild(Layer* child) { + scroll_children_->erase(child); + if (scroll_children_->empty()) + scroll_children_.reset(); + SetNeedsCommit(); +} + +void Layer::SetClipParent(Layer* ancestor) { + DCHECK(IsPropertyChangeAllowed()); + if (clip_parent_ == ancestor) + return; + + if (clip_parent_) + clip_parent_->RemoveClipChild(this); + + clip_parent_ = ancestor; + + if (clip_parent_) + clip_parent_->AddClipChild(this); + + SetNeedsCommit(); +} + +void Layer::AddClipChild(Layer* child) { + if (!clip_children_) + clip_children_.reset(new std::set<Layer*>); + clip_children_->insert(child); + SetNeedsCommit(); +} + +void Layer::RemoveClipChild(Layer* child) { + clip_children_->erase(child); + if (clip_children_->empty()) + clip_children_.reset(); + SetNeedsCommit(); +} + void Layer::SetScrollOffset(gfx::Vector2d scroll_offset) { DCHECK(IsPropertyChangeAllowed()); if (scroll_offset_ == scroll_offset) @@ -627,6 +688,7 @@ void Layer::SetTouchEventHandlerRegion(const Region& region) { if (touch_event_handler_region_ == region) return; touch_event_handler_region_ = region; + SetNeedsCommit(); } void Layer::SetDrawCheckerboardForMissingTiles(bool checkerboard) { @@ -742,7 +804,15 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { : bounds_); layer->SetContentBounds(content_bounds()); layer->SetContentsScale(contents_scale_x(), contents_scale_y()); - layer->SetDebugName(debug_name_); + + bool is_tracing; + TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + &is_tracing); + if (is_tracing) + layer->SetDebugName(DebugName()); + else + layer->SetDebugName(std::string()); + layer->SetCompositingReasons(compositing_reasons_); layer->SetDoubleSided(double_sided_); layer->SetDrawCheckerboardForMissingTiles( @@ -775,9 +845,50 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { DCHECK(!(TransformIsAnimating() && layer->TransformIsAnimatingOnImplOnly())); layer->SetScrollable(scrollable_); - layer->SetScrollOffset(scroll_offset_); layer->SetMaxScrollOffset(max_scroll_offset_); + LayerImpl* scroll_parent = NULL; + if (scroll_parent_) + scroll_parent = layer->layer_tree_impl()->LayerById(scroll_parent_->id()); + + layer->SetScrollParent(scroll_parent); + if (scroll_children_) { + std::set<LayerImpl*>* scroll_children = new std::set<LayerImpl*>; + for (std::set<Layer*>::iterator it = scroll_children_->begin(); + it != scroll_children_->end(); ++it) + scroll_children->insert(layer->layer_tree_impl()->LayerById((*it)->id())); + layer->SetScrollChildren(scroll_children); + } + + LayerImpl* clip_parent = NULL; + if (clip_parent_) { + clip_parent = + layer->layer_tree_impl()->LayerById(clip_parent_->id()); + } + + layer->SetClipParent(clip_parent); + if (clip_children_) { + std::set<LayerImpl*>* clip_children = new std::set<LayerImpl*>; + for (std::set<Layer*>::iterator it = clip_children_->begin(); + it != clip_children_->end(); ++it) { + LayerImpl* clip_child = layer->layer_tree_impl()->LayerById((*it)->id()); + DCHECK(clip_child); + clip_children->insert(clip_child); + } + layer->SetClipChildren(clip_children); + } + + // Adjust the scroll delta to be just the scrolls that have happened since + // the begin frame was sent. This happens for impl-side painting + // in LayerImpl::ApplyScrollDeltasSinceBeginFrame in a separate tree walk. + if (layer->layer_tree_impl()->settings().impl_side_painting) { + layer->SetScrollOffset(scroll_offset_); + } else { + layer->SetScrollOffsetAndDelta( + scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta()); + layer->SetSentScrollDelta(gfx::Vector2d()); + } + // Wrap the copy_requests_ in a PostTask to the main thread. ScopedPtrVector<CopyOutputRequest> main_thread_copy_requests; for (ScopedPtrVector<CopyOutputRequest>::iterator it = copy_requests_.begin(); @@ -805,23 +916,6 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { update_rect_.Union(layer->update_rect()); layer->set_update_rect(update_rect_); - if (layer->layer_tree_impl()->settings().impl_side_painting) { - DCHECK(layer->layer_tree_impl()->IsPendingTree()); - LayerImpl* active_twin = - layer->layer_tree_impl()->FindActiveTreeLayerById(id()); - // Update the scroll delta from the active layer, which may have - // adjusted its scroll delta prior to this pending layer being created. - // This code is identical to that in LayerImpl::SetScrollDelta. - if (active_twin) { - DCHECK(layer->sent_scroll_delta().IsZero()); - layer->SetScrollDelta(active_twin->ScrollDelta() - - active_twin->sent_scroll_delta()); - } - } else { - layer->SetScrollDelta(layer->ScrollDelta() - layer->sent_scroll_delta()); - layer->SetSentScrollDelta(gfx::Vector2d()); - } - layer->SetStackingOrderChanged(stacking_order_changed_); layer_animation_controller_->PushAnimationUpdatesTo( @@ -868,9 +962,8 @@ bool Layer::NeedMoreUpdates() { return false; } -void Layer::SetDebugName(const std::string& debug_name) { - debug_name_ = debug_name; - SetNeedsCommit(); +std::string Layer::DebugName() { + return client_ ? client_->DebugName() : std::string(); } void Layer::SetCompositingReasons(CompositingReasons reasons) { @@ -967,7 +1060,7 @@ Region Layer::VisibleContentOpaqueRegion() const { return Region(); } -ScrollbarLayer* Layer::ToScrollbarLayer() { +ScrollbarLayerInterface* Layer::ToScrollbarLayer() { return NULL; } @@ -979,4 +1072,30 @@ bool Layer::SupportsLCDText() const { return false; } +void Layer::RemoveFromScrollTree() { + if (scroll_children_.get()) { + for (std::set<Layer*>::iterator it = scroll_children_->begin(); + it != scroll_children_->end(); ++it) + (*it)->scroll_parent_ = NULL; + } + + if (scroll_parent_) + scroll_parent_->RemoveScrollChild(this); + + scroll_parent_ = NULL; +} + +void Layer::RemoveFromClipTree() { + if (clip_children_.get()) { + for (std::set<Layer*>::iterator it = clip_children_->begin(); + it != clip_children_->end(); ++it) + (*it)->clip_parent_ = NULL; + } + + if (clip_parent_) + clip_parent_->RemoveClipChild(this); + + clip_parent_ = NULL; +} + } // namespace cc diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 035e071a5fd..737e83df067 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -5,6 +5,7 @@ #ifndef CC_LAYERS_LAYER_H_ #define CC_LAYERS_LAYER_H_ +#include <set> #include <string> #include "base/callback.h" @@ -26,10 +27,15 @@ #include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkPicture.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_f.h" #include "ui/gfx/transform.h" +namespace gfx { +class BoxF; +} + namespace cc { class Animation; @@ -38,13 +44,14 @@ struct AnimationEvent; class CopyOutputRequest; class LayerAnimationDelegate; class LayerAnimationEventObserver; +class LayerClient; class LayerImpl; class LayerTreeHost; class LayerTreeImpl; class PriorityCalculator; class RenderingStatsInstrumentation; class ResourceUpdateQueue; -class ScrollbarLayer; +class ScrollbarLayerInterface; struct AnimationEvent; // Base class for composited layers. Special layer types are derived from @@ -151,6 +158,34 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, const gfx::Transform& transform() const { return transform_; } bool TransformIsAnimating() const; + void SetScrollParent(Layer* parent); + + Layer* scroll_parent() { return scroll_parent_; } + const Layer* scroll_parent() const { return scroll_parent_; } + + void AddScrollChild(Layer* child); + void RemoveScrollChild(Layer* child); + + std::set<Layer*>* scroll_children() { return scroll_children_.get(); } + const std::set<Layer*>* scroll_children() const { + return scroll_children_.get(); + } + + void SetClipParent(Layer* ancestor); + + Layer* clip_parent() { return clip_parent_; } + const Layer* clip_parent() const { + return clip_parent_; + } + + void AddClipChild(Layer* child); + void RemoveClipChild(Layer* child); + + std::set<Layer*>* clip_children() { return clip_children_.get(); } + const std::set<Layer*>* clip_children() const { + return clip_children_.get(); + } + DrawProperties<Layer, RenderSurface>& draw_properties() { return draw_properties_; } @@ -201,6 +236,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, RenderSurface* render_surface() const { return draw_properties_.render_surface.get(); } + int num_unclipped_descendants() const { + return draw_properties_.num_unclipped_descendants; + } void SetScrollOffset(gfx::Vector2d scroll_offset); gfx::Vector2d scroll_offset() const { return scroll_offset_; } @@ -292,7 +330,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, virtual void SetIsMask(bool is_mask) {} virtual void ReduceMemoryUsage() {} - void SetDebugName(const std::string& debug_name); + virtual std::string DebugName(); + + void SetLayerClient(LayerClient* client) { client_ = client; } + void SetCompositingReasons(CompositingReasons reasons); virtual void PushPropertiesTo(LayerImpl* layer); @@ -316,7 +357,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, float* contents_scale_y, gfx::Size* content_bounds); - LayerTreeHost* layer_tree_host() const { return layer_tree_host_; } + LayerTreeHost* layer_tree_host() { return layer_tree_host_; } + const LayerTreeHost* layer_tree_host() const { return layer_tree_host_; } // Set the priority of all desired textures in this layer. virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) {} @@ -328,6 +370,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, void SuspendAnimations(double monotonic_time); void ResumeAnimations(double monotonic_time); + bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds) { + return layer_animation_controller_->AnimatedBoundsForBox(box, bounds); + } + LayerAnimationController* layer_animation_controller() { return layer_animation_controller_.get(); } @@ -347,16 +393,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, virtual Region VisibleContentOpaqueRegion() const; - virtual ScrollbarLayer* ToScrollbarLayer(); + virtual ScrollbarLayerInterface* ToScrollbarLayer(); gfx::Rect LayerRectToContentRect(const gfx::RectF& layer_rect) const; - // In impl-side painting, this returns true if this layer type is not - // compatible with the main thread running freely, such as a double-buffered - // canvas that doesn't want to be triple-buffered across all three trees. - virtual bool BlocksPendingCommit() const; - // Returns true if anything in this tree blocksPendingCommit. - bool BlocksPendingCommitRecursive() const; + virtual skia::RefPtr<SkPicture> GetPicture() const; virtual bool CanClipSelf() const; @@ -407,7 +448,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, // Called when there's been a change in layer structure. Implies both // SetNeedsUpdate and SetNeedsCommit, but not SetNeedsPushProperties. void SetNeedsFullTreeSync(); - bool IsPropertyChangeAllowed() const; + + // Called when the next commit should wait until the pending tree is activated + // before finishing the commit and unblocking the main thread. Used to ensure + // unused resources on the impl thread are returned before commit completes. + void SetNextCommitWaitsForActivation(); void SetNeedsPushProperties(); void AddDependentNeedsPushProperties(); @@ -416,6 +461,16 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, return needs_push_properties() || descendant_needs_push_properties(); } + bool IsPropertyChangeAllowed() const; + + // If this layer has a scroll parent, it removes |this| from its list of + // scroll children. + void RemoveFromScrollTree(); + + // If this layer has a clip parent, it removes |this| from its list of clip + // children. + void RemoveFromClipTree(); + void reset_raster_scale_to_unknown() { raster_scale_ = 0.f; } // This flag is set when the layer needs to push properties to the impl @@ -486,7 +541,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, gfx::PointF position_; gfx::PointF anchor_point_; SkColor background_color_; - std::string debug_name_; CompositingReasons compositing_reasons_; float opacity_; skia::RefPtr<SkImageFilter> filter_; @@ -504,6 +558,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, bool use_parent_backface_visibility_; bool draw_checkerboard_for_missing_tiles_; bool force_render_surface_; + Layer* scroll_parent_; + scoped_ptr<std::set<Layer*> > scroll_children_; + + Layer* clip_parent_; + scoped_ptr<std::set<Layer*> > clip_children_; gfx::Transform transform_; gfx::Transform sublayer_transform_; @@ -514,6 +573,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, // Transient properties. float raster_scale_; + LayerClient* client_; + ScopedPtrVector<CopyOutputRequest> copy_requests_; base::Closure did_scroll_callback_; diff --git a/chromium/cc/layers/layer_client.h b/chromium/cc/layers/layer_client.h new file mode 100644 index 00000000000..73c0fd2514b --- /dev/null +++ b/chromium/cc/layers/layer_client.h @@ -0,0 +1,24 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_LAYER_CLIENT_H_ +#define CC_LAYERS_LAYER_CLIENT_H_ + +#include <string> + +#include "cc/base/cc_export.h" + +namespace cc { + +class CC_EXPORT LayerClient { + public: + virtual std::string DebugName() = 0; + + protected: + virtual ~LayerClient() {} +}; + +} // namespace cc + +#endif // CC_LAYERS_LAYER_CLIENT_H_ diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index b78cf1a45a8..b0c5399d32e 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -9,13 +9,14 @@ #include "cc/animation/animation_registrar.h" #include "cc/animation/scrollbar_animation_controller.h" #include "cc/animation/scrollbar_animation_controller_linear_fade.h" +#include "cc/animation/scrollbar_animation_controller_thinning.h" #include "cc/base/math_util.h" #include "cc/debug/debug_colors.h" #include "cc/debug/layer_tree_debug_state.h" #include "cc/debug/traced_value.h" #include "cc/input/layer_scroll_offset_delegate.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" #include "cc/layers/quad_sink.h" -#include "cc/layers/scrollbar_layer_impl.h" #include "cc/output/copy_output_request.h" #include "cc/quads/debug_border_draw_quad.h" #include "cc/trees/layer_tree_impl.h" @@ -29,6 +30,8 @@ namespace cc { LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) : parent_(NULL), + scroll_parent_(NULL), + clip_parent_(NULL), mask_layer_id_(-1), replica_layer_id_(-1), layer_id_(id), @@ -73,6 +76,24 @@ LayerImpl::~LayerImpl() { layer_tree_impl_->UnregisterLayer(this); layer_animation_controller_->RemoveValueObserver(this); + + if (scroll_children_) { + for (std::set<LayerImpl*>::iterator it = scroll_children_->begin(); + it != scroll_children_->end(); ++it) + (*it)->scroll_parent_ = NULL; + } + + if (scroll_parent_) + scroll_parent_->RemoveScrollChild(this); + + if (clip_children_) { + for (std::set<LayerImpl*>::iterator it = clip_children_->begin(); + it != clip_children_->end(); ++it) + (*it)->clip_parent_ = NULL; + } + + if (clip_parent_) + clip_parent_->RemoveClipChild(this); } void LayerImpl::AddChild(scoped_ptr<LayerImpl> child) { @@ -104,6 +125,67 @@ void LayerImpl::ClearChildList() { layer_tree_impl()->set_needs_update_draw_properties(); } +bool LayerImpl::HasAncestor(const LayerImpl* ancestor) const { + if (!ancestor) + return false; + + for (const LayerImpl* layer = this; layer; layer = layer->parent()) { + if (layer == ancestor) + return true; + } + + return false; +} + +void LayerImpl::SetScrollParent(LayerImpl* parent) { + if (scroll_parent_ == parent) + return; + + // Having both a scroll parent and a scroll offset delegate is unsupported. + DCHECK(!scroll_offset_delegate_); + + if (scroll_parent_) + scroll_parent_->RemoveScrollChild(this); + + scroll_parent_ = parent; +} + +void LayerImpl::SetScrollChildren(std::set<LayerImpl*>* children) { + if (scroll_children_.get() == children) + return; + scroll_children_.reset(children); +} + +void LayerImpl::RemoveScrollChild(LayerImpl* child) { + DCHECK(scroll_children_); + scroll_children_->erase(child); + if (scroll_children_->empty()) + scroll_children_.reset(); +} + +void LayerImpl::SetClipParent(LayerImpl* ancestor) { + if (clip_parent_ == ancestor) + return; + + if (clip_parent_) + clip_parent_->RemoveClipChild(this); + + clip_parent_ = ancestor; +} + +void LayerImpl::SetClipChildren(std::set<LayerImpl*>* children) { + if (clip_children_.get() == children) + return; + clip_children_.reset(children); +} + +void LayerImpl::RemoveClipChild(LayerImpl* child) { + DCHECK(clip_children_); + clip_children_->erase(child); + if (clip_children_->empty()) + clip_children_.reset(); +} + void LayerImpl::PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests) { if (requests->empty()) return; @@ -272,7 +354,7 @@ gfx::Vector2dF LayerImpl::ScrollBy(gfx::Vector2dF scroll) { return unscrolled; } -void LayerImpl::ApplySentScrollDeltas() { +void LayerImpl::ApplySentScrollDeltasFromAbortedCommit() { // Pending tree never has sent scroll deltas DCHECK(layer_tree_impl()->IsActiveTree()); @@ -292,6 +374,25 @@ void LayerImpl::ApplySentScrollDeltas() { sent_scroll_delta_ = gfx::Vector2d(); } +void LayerImpl::ApplyScrollDeltasSinceBeginFrame() { + // Only the pending tree can have missing scrolls. + DCHECK(layer_tree_impl()->IsPendingTree()); + if (!scrollable()) + return; + + // Pending tree should never have sent scroll deltas. + DCHECK(sent_scroll_delta().IsZero()); + + LayerImpl* active_twin = layer_tree_impl()->FindActiveTreeLayerById(id()); + if (active_twin) { + // Scrolls that happens after begin frame (where the sent scroll delta + // comes from) and commit need to be applied to the pending tree + // so that it is up to date with the total scroll. + SetScrollDelta(active_twin->ScrollDelta() - + active_twin->sent_scroll_delta()); + } +} + InputHandler::ScrollStatus LayerImpl::TryScroll( gfx::PointF screen_space_point, InputHandler::ScrollInputType type) const { @@ -419,9 +520,40 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->SetTransform(transform_); layer->SetScrollable(scrollable_); - layer->SetScrollOffset(scroll_offset_); + layer->SetScrollOffsetAndDelta( + scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta()); + layer->SetSentScrollDelta(gfx::Vector2d()); + layer->SetMaxScrollOffset(max_scroll_offset_); + LayerImpl* scroll_parent = NULL; + if (scroll_parent_) + scroll_parent = layer->layer_tree_impl()->LayerById(scroll_parent_->id()); + + layer->SetScrollParent(scroll_parent); + if (scroll_children_) { + std::set<LayerImpl*>* scroll_children = new std::set<LayerImpl*>; + for (std::set<LayerImpl*>::iterator it = scroll_children_->begin(); + it != scroll_children_->end(); ++it) + scroll_children->insert(layer->layer_tree_impl()->LayerById((*it)->id())); + layer->SetScrollChildren(scroll_children); + } + + LayerImpl* clip_parent = NULL; + if (clip_parent_) { + clip_parent = layer->layer_tree_impl()->LayerById( + clip_parent_->id()); + } + + layer->SetClipParent(clip_parent); + if (clip_children_) { + std::set<LayerImpl*>* clip_children = new std::set<LayerImpl*>; + for (std::set<LayerImpl*>::iterator it = clip_children_->begin(); + it != clip_children_->end(); ++it) + clip_children->insert(layer->layer_tree_impl()->LayerById((*it)->id())); + layer->SetClipChildren(clip_children); + } + layer->PassCopyRequests(©_requests_); // If the main thread commits multiple times before the impl thread actually @@ -431,9 +563,6 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { update_rect_.Union(layer->update_rect()); layer->set_update_rect(update_rect_); - layer->SetScrollDelta(layer->ScrollDelta() - layer->sent_scroll_delta()); - layer->SetSentScrollDelta(gfx::Vector2d()); - layer->SetStackingOrderChanged(stacking_order_changed_); // Reset any state that should be cleared for the next update. @@ -622,7 +751,7 @@ scoped_ptr<LayerImpl> LayerImpl::TakeReplicaLayer() { return replica_layer_.Pass(); } -ScrollbarLayerImpl* LayerImpl::ToScrollbarLayer() { +ScrollbarLayerImplBase* LayerImpl::ToScrollbarLayer() { return NULL; } @@ -847,10 +976,11 @@ void LayerImpl::UpdateScrollbarPositions() { return; last_scroll_offset_ = current_offset; - if (scrollbar_animation_controller_ && - !scrollbar_animation_controller_->IsScrollGestureInProgress()) { - scrollbar_animation_controller_->DidProgrammaticallyUpdateScroll( + if (scrollbar_animation_controller_) { + bool should_animate = scrollbar_animation_controller_->DidScrollUpdate( layer_tree_impl_->CurrentPhysicalTimeTicks()); + if (should_animate) + layer_tree_impl_->StartScrollbarAnimation(); } // Get the current_offset_.y() value for a sanity-check on scrolling @@ -863,6 +993,8 @@ void LayerImpl::UpdateScrollbarPositions() { void LayerImpl::SetScrollOffsetDelegate( LayerScrollOffsetDelegate* scroll_offset_delegate) { + // Having both a scroll parent and a scroll offset delegate is unsupported. + DCHECK(!scroll_parent_); if (!scroll_offset_delegate && scroll_offset_delegate_) { scroll_delta_ = scroll_offset_delegate_->GetTotalScrollOffset() - scroll_offset_; @@ -874,16 +1006,49 @@ void LayerImpl::SetScrollOffsetDelegate( } void LayerImpl::SetScrollOffset(gfx::Vector2d scroll_offset) { - if (scroll_offset_ == scroll_offset) - return; + SetScrollOffsetAndDelta(scroll_offset, ScrollDelta()); +} - scroll_offset_ = scroll_offset; +void LayerImpl::SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset, + gfx::Vector2dF scroll_delta) { + bool changed = false; - if (scroll_offset_delegate_) - scroll_offset_delegate_->SetTotalScrollOffset(TotalScrollOffset()); + if (scroll_offset_ != scroll_offset) { + changed = true; + scroll_offset_ = scroll_offset; - NoteLayerPropertyChangedForSubtree(); - UpdateScrollbarPositions(); + if (scroll_offset_delegate_) + scroll_offset_delegate_->SetTotalScrollOffset(TotalScrollOffset()); + } + + if (ScrollDelta() != scroll_delta) { + changed = true; + if (layer_tree_impl()->IsActiveTree()) { + LayerImpl* pending_twin = + layer_tree_impl()->FindPendingTreeLayerById(id()); + if (pending_twin) { + // The pending twin can't mirror the scroll delta of the active + // layer. Although the delta - sent scroll delta difference is + // identical for both twins, the sent scroll delta for the pending + // layer is zero, as anything that has been sent has been baked + // into the layer's position/scroll offset as a part of commit. + DCHECK(pending_twin->sent_scroll_delta().IsZero()); + pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta()); + } + } + + if (scroll_offset_delegate_) { + scroll_offset_delegate_->SetTotalScrollOffset(scroll_offset_ + + scroll_delta); + } else { + scroll_delta_ = scroll_delta; + } + } + + if (changed) { + NoteLayerPropertyChangedForSubtree(); + UpdateScrollbarPositions(); + } } gfx::Vector2dF LayerImpl::ScrollDelta() const { @@ -893,31 +1058,7 @@ gfx::Vector2dF LayerImpl::ScrollDelta() const { } void LayerImpl::SetScrollDelta(gfx::Vector2dF scroll_delta) { - if (ScrollDelta() == scroll_delta) - return; - - if (layer_tree_impl()->IsActiveTree()) { - LayerImpl* pending_twin = layer_tree_impl()->FindPendingTreeLayerById(id()); - if (pending_twin) { - // The pending twin can't mirror the scroll delta of the active - // layer. Although the delta - sent scroll delta difference is - // identical for both twins, the sent scroll delta for the pending - // layer is zero, as anything that has been sent has been baked - // into the layer's position/scroll offset as a part of commit. - DCHECK(pending_twin->sent_scroll_delta().IsZero()); - pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta()); - } - } - - if (scroll_offset_delegate_) { - scroll_offset_delegate_->SetTotalScrollOffset( - scroll_offset_ + scroll_delta); - } else { - scroll_delta_ = scroll_delta; - } - - NoteLayerPropertyChangedForSubtree(); - UpdateScrollbarPositions(); + SetScrollOffsetAndDelta(scroll_offset_, scroll_delta); } gfx::Vector2dF LayerImpl::TotalScrollOffset() const { @@ -951,42 +1092,55 @@ void LayerImpl::SetMaxScrollOffset(gfx::Vector2d max_scroll_offset) { UpdateScrollbarPositions(); } -void LayerImpl::SetScrollbarOpacity(float opacity) { - if (horizontal_scrollbar_layer_) - horizontal_scrollbar_layer_->SetOpacity(opacity); - if (vertical_scrollbar_layer_) - vertical_scrollbar_layer_->SetOpacity(opacity); -} - void LayerImpl::DidBecomeActive() { - if (!layer_tree_impl_->settings().use_linear_fade_scrollbar_animator) + if (layer_tree_impl_->settings().scrollbar_animator == + LayerTreeSettings::NoAnimator) { return; + } bool need_scrollbar_animation_controller = horizontal_scrollbar_layer_ || vertical_scrollbar_layer_; - if (need_scrollbar_animation_controller) { - if (!scrollbar_animation_controller_) { - base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds( - layer_tree_impl_->settings().scrollbar_linear_fade_delay_ms); - base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds( - layer_tree_impl_->settings().scrollbar_linear_fade_length_ms); - scrollbar_animation_controller_ = - ScrollbarAnimationControllerLinearFade::Create( - this, fadeout_delay, fadeout_length) - .PassAs<ScrollbarAnimationController>(); - } - } else { + if (!need_scrollbar_animation_controller) { scrollbar_animation_controller_.reset(); + return; + } + + if (scrollbar_animation_controller_) + return; + + switch (layer_tree_impl_->settings().scrollbar_animator) { + case LayerTreeSettings::LinearFade: { + base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds( + layer_tree_impl_->settings().scrollbar_linear_fade_delay_ms); + base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds( + layer_tree_impl_->settings().scrollbar_linear_fade_length_ms); + + scrollbar_animation_controller_ = + ScrollbarAnimationControllerLinearFade::Create( + this, fadeout_delay, fadeout_length) + .PassAs<ScrollbarAnimationController>(); + break; + } + case LayerTreeSettings::Thinning: { + scrollbar_animation_controller_ = + ScrollbarAnimationControllerThinning::Create(this) + .PassAs<ScrollbarAnimationController>(); + break; + } + case LayerTreeSettings::NoAnimator: + NOTREACHED(); + break; } } void LayerImpl::SetHorizontalScrollbarLayer( - ScrollbarLayerImpl* scrollbar_layer) { + ScrollbarLayerImplBase* scrollbar_layer) { horizontal_scrollbar_layer_ = scrollbar_layer; if (horizontal_scrollbar_layer_) horizontal_scrollbar_layer_->set_scroll_layer_id(id()); } -void LayerImpl::SetVerticalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer) { +void LayerImpl::SetVerticalScrollbarLayer( + ScrollbarLayerImplBase* scrollbar_layer) { vertical_scrollbar_layer_ = scrollbar_layer; if (vertical_scrollbar_layer_) vertical_scrollbar_layer_->set_scroll_layer_id(id()); @@ -1034,9 +1188,6 @@ CompositingReasonsAsValue(CompositingReasons reasons) { if (reasons & kCompositingReasonOverflowScrollingTouch) reason_list->AppendString("Is a scrollable overflow element"); - if (reasons & kCompositingReasonBlending) - reason_list->AppendString("Has a blend mode"); - if (reasons & kCompositingReasonAssumedOverlap) reason_list->AppendString("Might overlap a composited animation"); @@ -1109,12 +1260,19 @@ CompositingReasonsAsValue(CompositingReasons reasons) { if (reasons & kCompositingReasonLayerForMask) reason_list->AppendString("Is a mask layer"); + if (reasons & kCompositingReasonOverflowScrollingParent) + reason_list->AppendString("Scroll parent is not an ancestor"); + + if (reasons & kCompositingReasonOutOfFlowClipping) + reason_list->AppendString("Has clipping ancestor"); + return reason_list.PassAs<base::Value>(); } void LayerImpl::AsValueInto(base::DictionaryValue* state) const { TracedValue::MakeDictIntoImplicitSnapshot(state, LayerTypeAsString(), this); state->SetInteger("layer_id", id()); + state->SetString("layer_name", debug_name()); state->Set("bounds", MathUtil::AsValue(bounds()).release()); state->SetInteger("draws_content", DrawsContent()); state->SetInteger("gpu_memory_usage", GPUMemoryUsageInBytes()); @@ -1128,6 +1286,20 @@ void LayerImpl::AsValueInto(base::DictionaryValue* state) const { &clipped); state->Set("layer_quad", MathUtil::AsValue(layer_quad).release()); + if (!touch_event_handler_region_.IsEmpty()) { + state->Set("touch_event_handler_region", + touch_event_handler_region_.AsValue().release()); + } + if (have_wheel_event_handlers_) { + gfx::Rect wheel_rect(content_bounds()); + Region wheel_region(wheel_rect); + state->Set("wheel_event_handler_region", + wheel_region.AsValue().release()); + } + if (!non_fast_scrollable_region_.IsEmpty()) { + state->Set("non_fast_scrollable_region", + non_fast_scrollable_region_.AsValue().release()); + } scoped_ptr<base::ListValue> children_list(new base::ListValue()); for (size_t i = 0; i < children_.size(); ++i) @@ -1137,6 +1309,12 @@ void LayerImpl::AsValueInto(base::DictionaryValue* state) const { state->Set("mask_layer", mask_layer_->AsValue().release()); if (replica_layer_) state->Set("replica_layer", replica_layer_->AsValue().release()); + + if (scroll_parent_) + state->SetInteger("scroll_parent", scroll_parent_->id()); + + if (clip_parent_) + state->SetInteger("clip_parent", clip_parent_->id()); } size_t LayerImpl::GPUMemoryUsageInBytes() const { return 0; } diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h index 90da41a1c6c..ba0ff7b8216 100644 --- a/chromium/cc/layers/layer_impl.h +++ b/chromium/cc/layers/layer_impl.h @@ -5,6 +5,7 @@ #ifndef CC_LAYERS_LAYER_IMPL_H_ #define CC_LAYERS_LAYER_IMPL_H_ +#include <set> #include <string> #include "base/logging.h" @@ -44,7 +45,7 @@ class LayerTreeImpl; class QuadSink; class Renderer; class ScrollbarAnimationController; -class ScrollbarLayerImpl; +class ScrollbarLayerImplBase; class Layer; struct AppendQuadsData; @@ -83,6 +84,38 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { // Warning: This does not preserve tree structure invariants. void ClearChildList(); + bool HasAncestor(const LayerImpl* ancestor) const; + + void SetScrollParent(LayerImpl* parent); + + LayerImpl* scroll_parent() { return scroll_parent_; } + const LayerImpl* scroll_parent() const { return scroll_parent_; } + + void SetScrollChildren(std::set<LayerImpl*>* children); + void RemoveScrollChild(LayerImpl* child); + + std::set<LayerImpl*>* scroll_children() { return scroll_children_.get(); } + const std::set<LayerImpl*>* scroll_children() const { + return scroll_children_.get(); + } + + void SetClipParent(LayerImpl* ancestor); + + LayerImpl* clip_parent() { + return clip_parent_; + } + const LayerImpl* clip_parent() const { + return clip_parent_; + } + + void SetClipChildren(std::set<LayerImpl*>* children); + void RemoveClipChild(LayerImpl* child); + + std::set<LayerImpl*>* clip_children() { return clip_children_.get(); } + const std::set<LayerImpl*>* clip_children() const { + return clip_children_.get(); + } + void PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests); void TakeCopyRequestsAndTransformToTarget( ScopedPtrVector<CopyOutputRequest>* request); @@ -128,7 +161,7 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { virtual void UpdateTilePriorities() {} - virtual ScrollbarLayerImpl* ToScrollbarLayer(); + virtual ScrollbarLayerImplBase* ToScrollbarLayer(); // Returns true if this layer has content to draw. void SetDrawsContent(bool draws_content); @@ -284,6 +317,9 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { RenderSurfaceImpl* render_surface() const { return draw_properties_.render_surface.get(); } + int num_unclipped_descendants() const { + return draw_properties_.num_unclipped_descendants; + } // The client should be responsible for setting bounds, content bounds and // contents scale to appropriate values. LayerImpl doesn't calculate any of @@ -310,6 +346,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { void SetScrollOffsetDelegate( LayerScrollOffsetDelegate* scroll_offset_delegate); void SetScrollOffset(gfx::Vector2d scroll_offset); + void SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset, + gfx::Vector2dF scroll_delta); gfx::Vector2d scroll_offset() const { return scroll_offset_; } void SetMaxScrollOffset(gfx::Vector2d max_scroll_offset); @@ -330,7 +368,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { void SetScrollable(bool scrollable) { scrollable_ = scrollable; } bool scrollable() const { return scrollable_; } - void ApplySentScrollDeltas(); + void ApplySentScrollDeltasFromAbortedCommit(); + void ApplyScrollDeltasSinceBeginFrame(); void SetShouldScrollOnMainThread(bool should_scroll_on_main_thread) { should_scroll_on_main_thread_ = should_scroll_on_main_thread; @@ -413,15 +452,13 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { return scrollbar_animation_controller_.get(); } - void SetScrollbarOpacity(float opacity); - - void SetHorizontalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer); - ScrollbarLayerImpl* horizontal_scrollbar_layer() { + void SetHorizontalScrollbarLayer(ScrollbarLayerImplBase* scrollbar_layer); + ScrollbarLayerImplBase* horizontal_scrollbar_layer() { return horizontal_scrollbar_layer_; } - void SetVerticalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer); - ScrollbarLayerImpl* vertical_scrollbar_layer() { + void SetVerticalScrollbarLayer(ScrollbarLayerImplBase* scrollbar_layer); + ScrollbarLayerImplBase* vertical_scrollbar_layer() { return vertical_scrollbar_layer_; } @@ -475,6 +512,18 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { // Properties internal to LayerImpl LayerImpl* parent_; OwnedLayerImplList children_; + + LayerImpl* scroll_parent_; + + // Storing a pointer to a set rather than a set since this will be rarely + // used. If this pointer turns out to be too heavy, we could have this (and + // the scroll parent above) be stored in a LayerImpl -> scroll_info + // map somewhere. + scoped_ptr<std::set<LayerImpl*> > scroll_children_; + + LayerImpl* clip_parent_; + scoped_ptr<std::set<LayerImpl*> > clip_children_; + // mask_layer_ can be temporarily stolen during tree sync, we need this ID to // confirm newly assigned layer is still the previous one int mask_layer_id_; @@ -567,8 +616,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { // Weak pointers to this layer's scrollbars, if it has them. Updated during // tree synchronization. - ScrollbarLayerImpl* horizontal_scrollbar_layer_; - ScrollbarLayerImpl* vertical_scrollbar_layer_; + ScrollbarLayerImplBase* horizontal_scrollbar_layer_; + ScrollbarLayerImplBase* vertical_scrollbar_layer_; ScopedPtrVector<CopyOutputRequest> copy_requests_; diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc index 9c6dbfe3790..1ab7a71b2ca 100644 --- a/chromium/cc/layers/layer_impl_unittest.cc +++ b/chromium/cc/layers/layer_impl_unittest.cc @@ -502,7 +502,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsNoDelegate) { EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); - layer()->ApplySentScrollDeltas(); + layer()->ApplySentScrollDeltasFromAbortedCommit(); EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset()); EXPECT_VECTOR_EQ(scroll_delta - sent_scroll_delta, layer()->ScrollDelta()); @@ -527,7 +527,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsWithIgnoringDelegate) { EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); - layer()->ApplySentScrollDeltas(); + layer()->ApplySentScrollDeltasFromAbortedCommit(); EXPECT_VECTOR_EQ(fixed_offset, layer()->TotalScrollOffset()); EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset()); @@ -551,7 +551,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsWithAcceptingDelegate) { EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset()); EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta()); - layer()->ApplySentScrollDeltas(); + layer()->ApplySentScrollDeltasFromAbortedCommit(); EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset()); EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset()); diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index bd1e16fa658..c5c5286d2aa 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -559,7 +559,8 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) { EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTransform( gfx::Transform(0.0, 0.0, 0.0, 0.0, 0.0, 0.0))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDoubleSided(false)); - EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDebugName("Test Layer")); + EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTouchEventHandlerRegion( + gfx::Rect(10, 10))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDrawCheckerboardForMissingTiles( !test_layer->DrawCheckerboardForMissingTiles())); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetForceRenderSurface(true)); diff --git a/chromium/cc/layers/nine_patch_layer.cc b/chromium/cc/layers/nine_patch_layer.cc index e32f6e0b125..5e000072f28 100644 --- a/chromium/cc/layers/nine_patch_layer.cc +++ b/chromium/cc/layers/nine_patch_layer.cc @@ -8,16 +8,55 @@ #include "cc/resources/prioritized_resource.h" #include "cc/resources/resource_update.h" #include "cc/resources/resource_update_queue.h" +#include "cc/resources/scoped_ui_resource.h" +#include "cc/resources/ui_resource_bitmap.h" #include "cc/trees/layer_tree_host.h" namespace cc { + +namespace { + +class ScopedUIResourceHolder : public NinePatchLayer::UIResourceHolder { + public: + static scoped_ptr<ScopedUIResourceHolder> Create(LayerTreeHost* host, + const SkBitmap& skbitmap) { + return make_scoped_ptr(new ScopedUIResourceHolder(host, skbitmap)); + } + virtual UIResourceId id() OVERRIDE { return resource_->id(); } + + private: + ScopedUIResourceHolder(LayerTreeHost* host, const SkBitmap& skbitmap) { + resource_ = ScopedUIResource::Create(host, UIResourceBitmap(skbitmap)); + } + + scoped_ptr<ScopedUIResource> resource_; +}; + +class SharedUIResourceHolder : public NinePatchLayer::UIResourceHolder { + public: + static scoped_ptr<SharedUIResourceHolder> Create(UIResourceId id) { + return make_scoped_ptr(new SharedUIResourceHolder(id)); + } + + virtual UIResourceId id() OVERRIDE { return id_; } + + private: + explicit SharedUIResourceHolder(UIResourceId id) : id_(id) {} + + UIResourceId id_; +}; + +} // anonymous namespace + + +NinePatchLayer::UIResourceHolder::~UIResourceHolder() {} + scoped_refptr<NinePatchLayer> NinePatchLayer::Create() { return make_scoped_refptr(new NinePatchLayer()); } -NinePatchLayer::NinePatchLayer() - : bitmap_dirty_(false) {} +NinePatchLayer::NinePatchLayer() : fill_center_(false) {} NinePatchLayer::~NinePatchLayer() {} @@ -26,90 +65,89 @@ scoped_ptr<LayerImpl> NinePatchLayer::CreateLayerImpl( return NinePatchLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>(); } -void NinePatchLayer::SetTexturePriorities( - const PriorityCalculator& priority_calc) { - if (resource_ && !resource_->texture()->resource_manager()) { - // Release the resource here, as it is no longer tied to a resource manager. - resource_.reset(); - if (!bitmap_.isNull()) - CreateResource(); - } else if (bitmap_dirty_ && DrawsContent()) { - CreateResource(); - } +void NinePatchLayer::SetLayerTreeHost(LayerTreeHost* host) { + if (host == layer_tree_host()) + return; - if (resource_) { - resource_->texture()->set_request_priority( - PriorityCalculator::UIPriority(true)); - GLenum texture_format = - layer_tree_host()->GetRendererCapabilities().best_texture_format; - resource_->texture()->SetDimensions( - gfx::Size(bitmap_.width(), bitmap_.height()), texture_format); - } -} + Layer::SetLayerTreeHost(host); -void NinePatchLayer::SetBitmap(const SkBitmap& bitmap, gfx::Rect aperture) { - bitmap_ = bitmap; - image_aperture_ = aperture; - bitmap_dirty_ = true; - SetNeedsDisplay(); + // Recreate the resource hold against the new LTH. + RecreateUIResourceHolder(); } -bool NinePatchLayer::Update(ResourceUpdateQueue* queue, - const OcclusionTracker* occlusion) { - bool updated = Layer::Update(queue, occlusion); - - CreateUpdaterIfNeeded(); - - if (resource_ && - (bitmap_dirty_ || resource_->texture()->resource_id() == 0)) { - gfx::Rect content_rect(0, 0, bitmap_.width(), bitmap_.height()); - ResourceUpdate upload = ResourceUpdate::Create(resource_->texture(), - &bitmap_, - content_rect, - content_rect, - gfx::Vector2d()); - queue->AppendFullUpload(upload); - bitmap_dirty_ = false; - updated = true; - } - return updated; +void NinePatchLayer::RecreateUIResourceHolder() { + ui_resource_holder_.reset(); + if (!layer_tree_host() || bitmap_.empty()) + return; + + ui_resource_holder_ = + ScopedUIResourceHolder::Create(layer_tree_host(), bitmap_); } -void NinePatchLayer::CreateUpdaterIfNeeded() { - if (updater_.get()) +void NinePatchLayer::SetBorder(gfx::Rect border) { + if (border == border_) return; + border_ = border; + SetNeedsCommit(); +} - updater_ = ImageLayerUpdater::Create(); +void NinePatchLayer::SetBitmap(const SkBitmap& skbitmap, gfx::Rect aperture) { + image_aperture_ = aperture; + bitmap_ = skbitmap; + + // TODO(ccameron): Remove this. This provides the default border that was + // provided before borders were required to be explicitly provided. Once Blink + // fixes its callers to call SetBorder, this can be removed. + SetBorder(gfx::Rect(aperture.x(), + aperture.y(), + skbitmap.width() - aperture.width(), + skbitmap.height() - aperture.height())); + RecreateUIResourceHolder(); + SetNeedsCommit(); } -void NinePatchLayer::CreateResource() { - DCHECK(!bitmap_.isNull()); - CreateUpdaterIfNeeded(); - updater_->SetBitmap(bitmap_); +void NinePatchLayer::SetUIResourceId(UIResourceId resource_id, + gfx::Rect aperture) { + if (ui_resource_holder_ && ui_resource_holder_->id() == resource_id && + image_aperture_ == aperture) + return; - if (!resource_) { - resource_ = updater_->CreateResource( - layer_tree_host()->contents_texture_manager()); + image_aperture_ = aperture; + if (resource_id) { + ui_resource_holder_ = SharedUIResourceHolder::Create(resource_id); + } else { + ui_resource_holder_.reset(); } + + SetNeedsCommit(); +} + +void NinePatchLayer::SetFillCenter(bool fill_center) { + if (fill_center_ == fill_center) + return; + + fill_center_ = fill_center; + SetNeedsCommit(); } bool NinePatchLayer::DrawsContent() const { - bool draws = !bitmap_.isNull() && - Layer::DrawsContent() && - bitmap_.width() && - bitmap_.height(); - return draws; + return ui_resource_holder_ && ui_resource_holder_->id() && + Layer::DrawsContent(); } void NinePatchLayer::PushPropertiesTo(LayerImpl* layer) { Layer::PushPropertiesTo(layer); NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer); - if (resource_) { - DCHECK(!bitmap_.isNull()); - layer_impl->SetResourceId(resource_->texture()->resource_id()); - layer_impl->SetLayout( - gfx::Size(bitmap_.width(), bitmap_.height()), image_aperture_); + if (!ui_resource_holder_) { + layer_impl->SetUIResourceId(0); + } else { + DCHECK(layer_tree_host()); + + gfx::Size image_size = + layer_tree_host()->GetUIResourceSize(ui_resource_holder_->id()); + layer_impl->SetUIResourceId(ui_resource_holder_->id()); + layer_impl->SetLayout(image_size, image_aperture_, border_, fill_center_); } } diff --git a/chromium/cc/layers/nine_patch_layer.h b/chromium/cc/layers/nine_patch_layer.h index 35d08812cff..5880635389a 100644 --- a/chromium/cc/layers/nine_patch_layer.h +++ b/chromium/cc/layers/nine_patch_layer.h @@ -8,47 +8,60 @@ #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/layers/layer.h" -#include "cc/resources/image_layer_updater.h" -#include "third_party/skia/include/core/SkBitmap.h" +#include "cc/resources/ui_resource_client.h" #include "ui/gfx/rect.h" namespace cc { -class ResourceUpdateQueue; +class LayerTreeHost; +class ScopedUIResource; class CC_EXPORT NinePatchLayer : public Layer { public: static scoped_refptr<NinePatchLayer> Create(); virtual bool DrawsContent() const OVERRIDE; - virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) - OVERRIDE; - virtual bool Update(ResourceUpdateQueue* queue, - const OcclusionTracker* occlusion) OVERRIDE; + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE; + + // |border| is the space around the center rectangular region in layer space + // (known as aperture in image space). |border.x()| and |border.y()| are the + // size of the left and top boundary, respectively. + // |border.width()-border.x()| and |border.height()-border.y()| are the size + // of the right and bottom boundary, respectively. + void SetBorder(gfx::Rect border); + // aperture is in the pixel space of the bitmap resource and refers to // the center patch of the ninepatch (which is unused in this // implementation). We split off eight rects surrounding it and stick them // on the edges of the layer. The corners are unscaled, the top and bottom // rects are x-stretched to fit, and the left and right rects are // y-stretched to fit. - void SetBitmap(const SkBitmap& bitmap, gfx::Rect aperture); + void SetBitmap(const SkBitmap& skbitmap, gfx::Rect aperture); + + // An alternative way of setting the resource to allow for sharing. + void SetUIResourceId(UIResourceId resource_id, gfx::Rect aperture); + void SetFillCenter(bool fill_center); + + class UIResourceHolder { + public: + virtual UIResourceId id() = 0; + virtual ~UIResourceHolder(); + }; private: NinePatchLayer(); virtual ~NinePatchLayer(); virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; + void RecreateUIResourceHolder(); - void CreateUpdaterIfNeeded(); - void CreateResource(); - - scoped_refptr<ImageLayerUpdater> updater_; - scoped_ptr<LayerUpdater::Resource> resource_; - + gfx::Rect border_; + bool fill_center_; + scoped_ptr<UIResourceHolder> ui_resource_holder_; SkBitmap bitmap_; - bool bitmap_dirty_; // The transparent center region that shows the parent layer's contents in // image space. diff --git a/chromium/cc/layers/nine_patch_layer_impl.cc b/chromium/cc/layers/nine_patch_layer_impl.cc index 103f7656eb8..f609c3d2fda 100644 --- a/chromium/cc/layers/nine_patch_layer_impl.cc +++ b/chromium/cc/layers/nine_patch_layer_impl.cc @@ -6,15 +6,18 @@ #include "base/strings/stringprintf.h" #include "base/values.h" +#include "cc/base/math_util.h" #include "cc/layers/quad_sink.h" #include "cc/quads/texture_draw_quad.h" +#include "cc/trees/layer_tree_impl.h" #include "ui/gfx/rect_f.h" namespace cc { NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id), - resource_id_(0) {} + fill_center_(false), + ui_resource_id_(0) {} NinePatchLayerImpl::~NinePatchLayerImpl() {} @@ -31,11 +34,8 @@ void NinePatchLayerImpl::PushPropertiesTo(LayerImpl* layer) { LayerImpl::PushPropertiesTo(layer); NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer); - if (!resource_id_) - return; - - layer_impl->SetResourceId(resource_id_); - layer_impl->SetLayout(image_bounds_, image_aperture_); + layer_impl->SetUIResourceId(ui_resource_id_); + layer_impl->SetLayout(image_bounds_, image_aperture_, border_, fill_center_); } static gfx::RectF NormalizedRect(float x, @@ -50,120 +50,188 @@ static gfx::RectF NormalizedRect(float x, height / total_height); } -void NinePatchLayerImpl::SetLayout(gfx::Size image_bounds, gfx::Rect aperture) { +void NinePatchLayerImpl::SetUIResourceId(UIResourceId uid) { + if (uid == ui_resource_id_) + return; + ui_resource_id_ = uid; + NoteLayerPropertyChanged(); +} + +void NinePatchLayerImpl::SetLayout(gfx::Size image_bounds, + gfx::Rect aperture, + gfx::Rect border, + bool fill_center) { + // This check imposes an ordering on the call sequence. An UIResource must + // exist before SetLayout can be called. + DCHECK(ui_resource_id_); + + // TODO(ccameron): the following "greater than or equal to" (GE) checks should + // be greater than (GT) to avoid degenerate nine-patches. The relaxed + // condition "equal to" is a workaround for the overhang shadow use case and + // should be investigated further. + + // |border| is in layer space. It cannot exceed the bounds of the layer. + DCHECK(!border.size().IsEmpty()); + DCHECK_GE(bounds().width(), border.width()); + DCHECK_GE(bounds().height(), border.height()); + + // Sanity Check on |border| + DCHECK_LT(border.x(), border.width()); + DCHECK_LT(border.y(), border.height()); + DCHECK_GE(border.x(), 0); + DCHECK_GE(border.y(), 0); + + // |aperture| is in image space. It cannot exceed the bounds of the bitmap. + DCHECK(!aperture.size().IsEmpty()); + DCHECK(gfx::Rect(image_bounds.width(), image_bounds.height()) + .Contains(aperture)); + + // Avoid the degenerate cases where the aperture touches the edge of the + // image. + DCHECK_LT(aperture.width(), image_bounds.width() - 1); + DCHECK_LT(aperture.height(), image_bounds.height() - 1); + DCHECK_GT(aperture.x(), 0); + DCHECK_GT(aperture.y(), 0); + + if (image_bounds_ == image_bounds && image_aperture_ == aperture && + border_ == border && fill_center_ == fill_center) + return; + image_bounds_ = image_bounds; image_aperture_ = aperture; + border_ = border; + fill_center_ = fill_center; + + NoteLayerPropertyChanged(); } bool NinePatchLayerImpl::WillDraw(DrawMode draw_mode, ResourceProvider* resource_provider) { - if (!resource_id_ || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) + if (!ui_resource_id_ || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE) return false; return LayerImpl::WillDraw(draw_mode, resource_provider); } void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, AppendQuadsData* append_quads_data) { - DCHECK(resource_id_); - SharedQuadState* shared_quad_state = quad_sink->UseSharedQuadState(CreateSharedQuadState()); AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); + if (!ui_resource_id_) + return; + + ResourceProvider::ResourceId resource = + layer_tree_impl()->ResourceIdForUIResource(ui_resource_id_); + + if (!resource) + return; + static const bool flipped = false; static const bool premultiplied_alpha = true; DCHECK(!bounds().IsEmpty()); - // NinePatch border widths in bitmap pixel space - int left_width = image_aperture_.x(); - int top_height = image_aperture_.y(); - int right_width = image_bounds_.width() - image_aperture_.right(); - int bottom_height = image_bounds_.height() - image_aperture_.bottom(); + // NinePatch border widths in layer space. + int layer_left_width = border_.x(); + int layer_top_height = border_.y(); + int layer_right_width = border_.width() - layer_left_width; + int layer_bottom_height = border_.height() - layer_top_height; - // If layer can't fit the corners, clip to show the outer edges of the - // image. - int corner_total_width = left_width + right_width; - int middle_width = bounds().width() - corner_total_width; - if (middle_width < 0) { - float left_width_proportion = - static_cast<float>(left_width) / corner_total_width; - int left_width_crop = middle_width * left_width_proportion; - left_width += left_width_crop; - right_width = bounds().width() - left_width; - middle_width = 0; - } - int corner_total_height = top_height + bottom_height; - int middle_height = bounds().height() - corner_total_height; - if (middle_height < 0) { - float top_height_proportion = - static_cast<float>(top_height) / corner_total_height; - int top_height_crop = middle_height * top_height_proportion; - top_height += top_height_crop; - bottom_height = bounds().height() - top_height; - middle_height = 0; - } + int layer_middle_width = bounds().width() - border_.width(); + int layer_middle_height = bounds().height() - border_.height(); // Patch positions in layer space - gfx::Rect top_left(0, 0, left_width, top_height); - gfx::Rect top_right( - bounds().width() - right_width, 0, right_width, top_height); - gfx::Rect bottom_left( - 0, bounds().height() - bottom_height, left_width, bottom_height); - gfx::Rect bottom_right( - top_right.x(), bottom_left.y(), right_width, bottom_height); - gfx::Rect top(top_left.right(), 0, middle_width, top_height); - gfx::Rect left(0, top_left.bottom(), left_width, middle_height); - gfx::Rect right(top_right.x(), - top_right.bottom(), - right_width, - left.height()); - gfx::Rect bottom(top.x(), bottom_left.y(), top.width(), bottom_height); - - float img_width = image_bounds_.width(); - float img_height = image_bounds_.height(); - + gfx::Rect layer_top_left(0, 0, layer_left_width, layer_top_height); + gfx::Rect layer_top_right(bounds().width() - layer_right_width, + 0, + layer_right_width, + layer_top_height); + gfx::Rect layer_bottom_left(0, + bounds().height() - layer_bottom_height, + layer_left_width, + layer_bottom_height); + gfx::Rect layer_bottom_right(layer_top_right.x(), + layer_bottom_left.y(), + layer_right_width, + layer_bottom_height); + gfx::Rect layer_top( + layer_top_left.right(), 0, layer_middle_width, layer_top_height); + gfx::Rect layer_left( + 0, layer_top_left.bottom(), layer_left_width, layer_middle_height); + gfx::Rect layer_right(layer_top_right.x(), + layer_top_right.bottom(), + layer_right_width, + layer_left.height()); + gfx::Rect layer_bottom(layer_top.x(), + layer_bottom_left.y(), + layer_top.width(), + layer_bottom_height); + gfx::Rect layer_center(layer_left_width, + layer_top_height, + layer_middle_width, + layer_middle_height); + + // Note the following values are in image (bitmap) space. + float image_width = image_bounds_.width(); + float image_height = image_bounds_.height(); + + int image_aperture_left_width = image_aperture_.x(); + int image_aperture_top_height = image_aperture_.y(); + int image_aperture_right_width = image_width - image_aperture_.right(); + int image_aperture_bottom_height = image_height - image_aperture_.bottom(); // Patch positions in bitmap UV space (from zero to one) gfx::RectF uv_top_left = NormalizedRect(0, 0, - left_width, - top_height, - img_width, - img_height); - gfx::RectF uv_top_right = NormalizedRect(img_width - right_width, - 0, - right_width, - top_height, - img_width, - img_height); - gfx::RectF uv_bottom_left = NormalizedRect(0, - img_height - bottom_height, - left_width, - bottom_height, - img_width, - img_height); - gfx::RectF uv_bottom_right = NormalizedRect(img_width - right_width, - img_height - bottom_height, - right_width, - bottom_height, - img_width, - img_height); - gfx::RectF uv_top(uv_top_left.right(), - 0, - (img_width - left_width - right_width) / img_width, - (top_height) / img_height); + image_aperture_left_width, + image_aperture_top_height, + image_width, + image_height); + gfx::RectF uv_top_right = + NormalizedRect(image_width - image_aperture_right_width, + 0, + image_aperture_right_width, + image_aperture_top_height, + image_width, + image_height); + gfx::RectF uv_bottom_left = + NormalizedRect(0, + image_height - image_aperture_bottom_height, + image_aperture_left_width, + image_aperture_bottom_height, + image_width, + image_height); + gfx::RectF uv_bottom_right = + NormalizedRect(image_width - image_aperture_right_width, + image_height - image_aperture_bottom_height, + image_aperture_right_width, + image_aperture_bottom_height, + image_width, + image_height); + gfx::RectF uv_top( + uv_top_left.right(), + 0, + (image_width - image_aperture_left_width - image_aperture_right_width) / + image_width, + (image_aperture_top_height) / image_height); gfx::RectF uv_left(0, - uv_top_left.bottom(), - left_width / img_width, - (img_height - top_height - bottom_height) / img_height); + uv_top_left.bottom(), + image_aperture_left_width / image_width, + (image_height - image_aperture_top_height - + image_aperture_bottom_height) / + image_height); gfx::RectF uv_right(uv_top_right.x(), - uv_top_right.bottom(), - right_width / img_width, - uv_left.height()); + uv_top_right.bottom(), + image_aperture_right_width / image_width, + uv_left.height()); gfx::RectF uv_bottom(uv_top.x(), - uv_bottom_left.y(), - uv_top.width(), - bottom_height / img_height); + uv_bottom_left.y(), + uv_top.width(), + image_aperture_bottom_height / image_height); + gfx::RectF uv_center(uv_top_left.right(), + uv_top_left.bottom(), + uv_top.width(), + uv_left.height()); // Nothing is opaque here. // TODO(danakj): Should we look at the SkBitmaps to determine opaqueness? @@ -173,9 +241,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - top_left, + layer_top_left, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_top_left.origin(), uv_top_left.bottom_right(), @@ -186,9 +254,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - top_right, + layer_top_right, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_top_right.origin(), uv_top_right.bottom_right(), @@ -199,9 +267,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - bottom_left, + layer_bottom_left, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_bottom_left.origin(), uv_bottom_left.bottom_right(), @@ -212,9 +280,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - bottom_right, + layer_bottom_right, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_bottom_right.origin(), uv_bottom_right.bottom_right(), @@ -225,9 +293,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - top, + layer_top, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_top.origin(), uv_top.bottom_right(), @@ -238,9 +306,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - left, + layer_left, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_left.origin(), uv_left.bottom_right(), @@ -251,9 +319,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - right, + layer_right, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_right.origin(), uv_right.bottom_right(), @@ -264,9 +332,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, quad = TextureDrawQuad::Create(); quad->SetNew(shared_quad_state, - bottom, + layer_bottom, opaque_rect, - resource_id_, + resource, premultiplied_alpha, uv_bottom.origin(), uv_bottom.bottom_right(), @@ -274,10 +342,21 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink, vertex_opacity, flipped); quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); -} -void NinePatchLayerImpl::DidLoseOutputSurface() { - resource_id_ = 0; + if (fill_center_) { + quad = TextureDrawQuad::Create(); + quad->SetNew(shared_quad_state, + layer_center, + opaque_rect, + resource, + premultiplied_alpha, + uv_center.origin(), + uv_center.bottom_right(), + SK_ColorTRANSPARENT, + vertex_opacity, + flipped); + quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + } } const char* NinePatchLayerImpl::LayerTypeAsString() const { @@ -294,10 +373,12 @@ base::DictionaryValue* NinePatchLayerImpl::LayerTreeAsJson() const { list->AppendInteger(image_aperture_.size().height()); result->Set("ImageAperture", list); - list = new base::ListValue; - list->AppendInteger(image_bounds_.width()); - list->AppendInteger(image_bounds_.height()); - result->Set("ImageBounds", list); + result->Set("ImageBounds", MathUtil::AsValue(image_bounds_).release()); + result->Set("Border", MathUtil::AsValue(border_).release()); + + base::FundamentalValue* fill_center = + base::Value::CreateBooleanValue(fill_center_); + result->Set("FillCenter", fill_center); return result; } diff --git a/chromium/cc/layers/nine_patch_layer_impl.h b/chromium/cc/layers/nine_patch_layer_impl.h index bc442d43f33..ba414aed391 100644 --- a/chromium/cc/layers/nine_patch_layer_impl.h +++ b/chromium/cc/layers/nine_patch_layer_impl.h @@ -10,6 +10,7 @@ #include "cc/base/cc_export.h" #include "cc/layers/layer_impl.h" #include "cc/resources/resource_provider.h" +#include "cc/resources/ui_resource_client.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" @@ -27,8 +28,37 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl { } virtual ~NinePatchLayerImpl(); - void SetResourceId(unsigned id) { resource_id_ = id; } - void SetLayout(gfx::Size image_bounds, gfx::Rect aperture); + + void SetUIResourceId(UIResourceId uid); + + // The bitmap stretches out the bounds of the layer. The following picture + // illustrates the parameters associated with the dimensions. + // + // Layer space layout Bitmap space layout + // + // ------------------------ ~~~~~~~~~~ W ~~~~~~~~~~ + // | : | : : | + // | C | : Y | + // | : | : : | + // | ------------ | :~~X~~------------ | + // | | | | : | : | + // | | | | : | : | + // |~~A~~| |~~B~~| H | Q | + // | | | | : | : | + // | ------------ | : ~~~~~P~~~~~ | + // | : | : | + // | D | : | + // | : | : | + // ------------------------ ------------------------ + // + // |image_bounds| = (W, H) + // |image_aperture| = (X, Y, P, Q) + // |border| = (A, C, A + B, C + D) + // |fill_center| indicates whether to draw the center quad or not. + void SetLayout(gfx::Size image_bounds, + gfx::Rect image_aperture, + gfx::Rect border, + bool fill_center); virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; @@ -39,7 +69,6 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl { virtual void AppendQuads(QuadSink* quad_sink, AppendQuadsData* append_quads_data) OVERRIDE; virtual ResourceProvider::ResourceId ContentsResourceId() const OVERRIDE; - virtual void DidLoseOutputSurface() OVERRIDE; virtual base::DictionaryValue* LayerTreeAsJson() const OVERRIDE; @@ -56,7 +85,12 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl { // image space. gfx::Rect image_aperture_; - ResourceProvider::ResourceId resource_id_; + // An inset border that the patches will be mapped to. + gfx::Rect border_; + + bool fill_center_; + + UIResourceId ui_resource_id_; DISALLOW_COPY_AND_ASSIGN(NinePatchLayerImpl); }; diff --git a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc index 1985bd4c29c..0fbc645be1b 100644 --- a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc +++ b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <stdio.h> - +#include "base/containers/hash_tables.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/nine_patch_layer_impl.h" #include "cc/quads/texture_draw_quad.h" +#include "cc/resources/ui_resource_bitmap.h" +#include "cc/resources/ui_resource_client.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" @@ -22,6 +23,40 @@ namespace cc { namespace { +class FakeUIResourceLayerTreeHostImpl : public FakeLayerTreeHostImpl { + public: + explicit FakeUIResourceLayerTreeHostImpl(Proxy* proxy) + : FakeLayerTreeHostImpl(proxy), fake_next_resource_id_(1) {} + + virtual void CreateUIResource( + UIResourceId uid, + const UIResourceBitmap& bitmap) OVERRIDE { + if (ResourceIdForUIResource(uid)) + DeleteUIResource(uid); + fake_ui_resource_map_[uid] = fake_next_resource_id_; + } + + virtual void DeleteUIResource(UIResourceId uid) OVERRIDE { + ResourceProvider::ResourceId id = ResourceIdForUIResource(uid); + if (id) + fake_ui_resource_map_.erase(uid); + } + + virtual ResourceProvider::ResourceId ResourceIdForUIResource( + UIResourceId uid) const OVERRIDE { + UIResourceMap::const_iterator iter = fake_ui_resource_map_.find(uid); + if (iter != fake_ui_resource_map_.end()) + return iter->second; + return 0; + } + + private: + ResourceProvider::ResourceId fake_next_resource_id_; + typedef base::hash_map<UIResourceId, ResourceProvider::ResourceId> + UIResourceMap; + UIResourceMap fake_ui_resource_map_; +}; + gfx::Rect ToRoundedIntRect(gfx::RectF rect_f) { return gfx::Rect(gfx::ToRoundedInt(rect_f.x()), gfx::ToRoundedInt(rect_f.y()), @@ -29,19 +64,21 @@ gfx::Rect ToRoundedIntRect(gfx::RectF rect_f) { gfx::ToRoundedInt(rect_f.height())); } -TEST(NinePatchLayerImplTest, VerifyDrawQuads) { - // Input is a 100x100 bitmap with a 40x50 aperture at x=20, y=30. - // The bounds of the layer are set to 400x400, so the draw quads - // generated should leave the border width (40) intact. +void NinePatchLayerLayoutTest(gfx::Size bitmap_size, + gfx::Rect aperture_rect, + gfx::Size layer_size, + gfx::Rect border, + bool fill_center, + size_t expected_quad_size) { MockQuadCuller quad_culler; - gfx::Size bitmap_size(100, 100); - gfx::Size layer_size(400, 400); gfx::Rect visible_content_rect(layer_size); - gfx::Rect aperture_rect(20, 30, 40, 50); - gfx::Rect scaled_aperture_non_uniform(20, 30, 340, 350); + gfx::Rect expected_remaining(border.x(), + border.y(), + layer_size.width() - border.width(), + layer_size.height() - border.height()); FakeImplProxy proxy; - FakeLayerTreeHostImpl host_impl(&proxy); + FakeUIResourceLayerTreeHostImpl host_impl(&proxy); scoped_ptr<NinePatchLayerImpl> layer = NinePatchLayerImpl::Create(host_impl.active_tree(), 1); layer->draw_properties().visible_content_rect = visible_content_rect; @@ -49,21 +86,26 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) { layer->SetContentBounds(layer_size); layer->CreateRenderSurface(); layer->draw_properties().render_target = layer.get(); - layer->SetLayout(bitmap_size, aperture_rect); - layer->SetResourceId(1); - // This scale should not affect the generated quad geometry, but only - // the shared draw transform. - gfx::Transform transform; - transform.Scale(10, 10); - layer->draw_properties().target_space_transform = transform; + UIResourceId uid = 1; + SkBitmap skbitmap; + skbitmap.setConfig( + SkBitmap::kARGB_8888_Config, bitmap_size.width(), bitmap_size.height()); + skbitmap.allocPixels(); + skbitmap.setImmutable(); + UIResourceBitmap bitmap(skbitmap); + + host_impl.CreateUIResource(uid, bitmap); + layer->SetUIResourceId(uid); + layer->SetLayout(bitmap_size, aperture_rect, border, fill_center); AppendQuadsData data; layer->AppendQuads(&quad_culler, &data); // Verify quad rects const QuadList& quads = quad_culler.quad_list(); - EXPECT_EQ(8u, quads.size()); + EXPECT_EQ(expected_quad_size, quads.size()); + Region remaining(visible_content_rect); for (size_t i = 0; i < quads.size(); ++i) { DrawQuad* quad = quads[i]; @@ -71,12 +113,16 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) { EXPECT_TRUE(visible_content_rect.Contains(quad_rect)) << i; EXPECT_TRUE(remaining.Contains(quad_rect)) << i; - EXPECT_EQ(transform, quad->quadTransform()); remaining.Subtract(Region(quad_rect)); } - EXPECT_RECT_EQ(scaled_aperture_non_uniform, remaining.bounds()); - Region scaled_aperture_region(scaled_aperture_non_uniform); - EXPECT_EQ(scaled_aperture_region, remaining); + + // Check if the left-over quad is the same size as the mapped aperture quad in + // layer space. + if (!fill_center) { + EXPECT_RECT_EQ(expected_remaining, gfx::ToEnclosedRect(remaining.bounds())); + } else { + EXPECT_TRUE(remaining.bounds().IsEmpty()); + } // Verify UV rects gfx::Rect bitmap_rect(bitmap_size); @@ -89,66 +135,59 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) { tex_rect.Scale(bitmap_size.width(), bitmap_size.height()); tex_remaining.Subtract(Region(ToRoundedIntRect(tex_rect))); } - EXPECT_RECT_EQ(aperture_rect, tex_remaining.bounds()); - Region aperture_region(aperture_rect); - EXPECT_EQ(aperture_region, tex_remaining); -} - -TEST(NinePatchLayerImplTest, VerifyDrawQuadsForSqueezedLayer) { - // Test with a layer much smaller than the bitmap. - MockQuadCuller quad_culler; - gfx::Size bitmap_size(101, 101); - gfx::Size layer_size(51, 51); - gfx::Rect visible_content_rect(layer_size); - gfx::Rect aperture_rect(20, 30, 40, 45); // rightWidth: 40, botHeight: 25 - - FakeImplProxy proxy; - FakeLayerTreeHostImpl host_impl(&proxy); - scoped_ptr<NinePatchLayerImpl> layer = - NinePatchLayerImpl::Create(host_impl.active_tree(), 1); - layer->draw_properties().visible_content_rect = visible_content_rect; - layer->SetBounds(layer_size); - layer->SetContentBounds(layer_size); - layer->CreateRenderSurface(); - layer->draw_properties().render_target = layer.get(); - layer->SetLayout(bitmap_size, aperture_rect); - layer->SetResourceId(1); - AppendQuadsData data; - layer->AppendQuads(&quad_culler, &data); - - // Verify corner rects fill the layer and don't overlap - const QuadList& quads = quad_culler.quad_list(); - EXPECT_EQ(4u, quads.size()); - Region filled; - for (size_t i = 0; i < quads.size(); ++i) { - DrawQuad* quad = quads[i]; - gfx::Rect quad_rect = quad->rect; - - EXPECT_FALSE(filled.Intersects(quad_rect)); - filled.Union(quad_rect); + if (!fill_center) { + EXPECT_RECT_EQ(aperture_rect, tex_remaining.bounds()); + Region aperture_region(aperture_rect); + EXPECT_EQ(aperture_region, tex_remaining); + } else { + EXPECT_TRUE(remaining.bounds().IsEmpty()); } - Region expected_full(visible_content_rect); - EXPECT_EQ(expected_full, filled); +} - // Verify UV rects cover the corners of the bitmap and the crop is weighted - // proportionately to the relative corner sizes (for uneven apertures). - gfx::Rect bitmap_rect(bitmap_size); - Region tex_remaining(bitmap_rect); - for (size_t i = 0; i < quads.size(); ++i) { - DrawQuad* quad = quads[i]; - const TextureDrawQuad* tex_quad = TextureDrawQuad::MaterialCast(quad); - gfx::RectF tex_rect = - gfx::BoundingRect(tex_quad->uv_top_left, tex_quad->uv_bottom_right); - tex_rect.Scale(bitmap_size.width(), bitmap_size.height()); - tex_remaining.Subtract(Region(ToRoundedIntRect(tex_rect))); - } - Region expected_remaining_region = Region(gfx::Rect(bitmap_size)); - expected_remaining_region.Subtract(gfx::Rect(0, 0, 17, 28)); - expected_remaining_region.Subtract(gfx::Rect(67, 0, 34, 28)); - expected_remaining_region.Subtract(gfx::Rect(0, 78, 17, 23)); - expected_remaining_region.Subtract(gfx::Rect(67, 78, 34, 23)); - EXPECT_EQ(expected_remaining_region, tex_remaining); +TEST(NinePatchLayerImplTest, VerifyDrawQuads) { + // Input is a 100x100 bitmap with a 40x50 aperture at x=20, y=30. + // The bounds of the layer are set to 400x400. + gfx::Size bitmap_size(100, 100); + gfx::Size layer_size(400, 500); + gfx::Rect aperture_rect(20, 30, 40, 50); + gfx::Rect border(40, 40, 80, 80); + bool fill_center = false; + size_t expected_quad_size = 8; + NinePatchLayerLayoutTest(bitmap_size, + aperture_rect, + layer_size, + border, + fill_center, + expected_quad_size); + + // The bounds of the layer are set to less than the bitmap size. + bitmap_size = gfx::Size(100, 100); + layer_size = gfx::Size(40, 50); + aperture_rect = gfx::Rect(20, 30, 40, 50); + border = gfx::Rect(10, 10, 25, 15); + fill_center = true; + expected_quad_size = 9; + NinePatchLayerLayoutTest(bitmap_size, + aperture_rect, + layer_size, + border, + fill_center, + expected_quad_size); + + // Layer and image sizes are equal. + bitmap_size = gfx::Size(100, 100); + layer_size = gfx::Size(100, 100); + aperture_rect = gfx::Rect(20, 30, 40, 50); + border = gfx::Rect(20, 30, 40, 50); + fill_center = true; + expected_quad_size = 9; + NinePatchLayerLayoutTest(bitmap_size, + aperture_rect, + layer_size, + border, + fill_center, + expected_quad_size); } } // namespace diff --git a/chromium/cc/layers/nine_patch_layer_unittest.cc b/chromium/cc/layers/nine_patch_layer_unittest.cc index 5268d02472f..101146c0077 100644 --- a/chromium/cc/layers/nine_patch_layer_unittest.cc +++ b/chromium/cc/layers/nine_patch_layer_unittest.cc @@ -8,9 +8,11 @@ #include "cc/resources/prioritized_resource_manager.h" #include "cc/resources/resource_provider.h" #include "cc/resources/resource_update_queue.h" +#include "cc/resources/scoped_ui_resource.h" #include "cc/scheduler/texture_uploader.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/geometry_test_utils.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/occlusion_tracker.h" @@ -54,7 +56,7 @@ class NinePatchLayerTest : public testing::Test { FakeLayerTreeHostClient fake_client_; }; -TEST_F(NinePatchLayerTest, TriggerFullUploadOnceWhenChangingBitmap) { +TEST_F(NinePatchLayerTest, SetBitmap) { scoped_refptr<NinePatchLayer> test_layer = NinePatchLayer::Create(); ASSERT_TRUE(test_layer.get()); test_layer->SetIsDrawable(true); @@ -66,80 +68,60 @@ TEST_F(NinePatchLayerTest, TriggerFullUploadOnceWhenChangingBitmap) { layer_tree_host_->InitializeOutputSurfaceIfNeeded(); - PriorityCalculator calculator; ResourceUpdateQueue queue; OcclusionTracker occlusion_tracker(gfx::Rect(), false); - - // No bitmap set should not trigger any uploads. test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); test_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(0u, queue.FullUploadSize()); - EXPECT_EQ(0u, queue.PartialUploadSize()); - // Setting a bitmap set should trigger a single full upload. + EXPECT_FALSE(test_layer->DrawsContent()); + SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); bitmap.allocPixels(); - test_layer->SetBitmap(bitmap, gfx::Rect(5, 5, 1, 1)); - test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); + bitmap.setImmutable(); + + gfx::Rect aperture(5, 5, 1, 1); + bool fill_center = false; + test_layer->SetBitmap(bitmap, aperture); + test_layer->SetFillCenter(fill_center); test_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(1u, queue.FullUploadSize()); - EXPECT_EQ(0u, queue.PartialUploadSize()); - ResourceUpdate params = queue.TakeFirstFullUpload(); - EXPECT_TRUE(params.texture != NULL); - - // Upload the texture. - layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( - 1024 * 1024); - layer_tree_host_->contents_texture_manager()->PrioritizeTextures(); - - scoped_ptr<OutputSurface> output_surface; - scoped_ptr<ResourceProvider> resource_provider; - { - DebugScopedSetImplThread impl_thread(Proxy()); - DebugScopedSetMainThreadBlocked main_thread_blocked(Proxy()); - output_surface = CreateFakeOutputSurface(); - resource_provider = ResourceProvider::Create(output_surface.get(), 0); - params.texture->AcquireBackingTexture(resource_provider.get()); - ASSERT_TRUE(params.texture->have_backing_texture()); - } - // Nothing changed, so no repeated upload. + EXPECT_TRUE(test_layer->DrawsContent()); +} + +TEST_F(NinePatchLayerTest, SetUIResourceId) { + scoped_refptr<NinePatchLayer> test_layer = NinePatchLayer::Create(); + ASSERT_TRUE(test_layer.get()); + test_layer->SetIsDrawable(true); + test_layer->SetBounds(gfx::Size(100, 100)); + + layer_tree_host_->SetRootLayer(test_layer); + Mock::VerifyAndClearExpectations(layer_tree_host_.get()); + EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get()); + + layer_tree_host_->InitializeOutputSurfaceIfNeeded(); + + ResourceUpdateQueue queue; + OcclusionTracker occlusion_tracker(gfx::Rect(), false); test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); test_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(0u, queue.FullUploadSize()); - EXPECT_EQ(0u, queue.PartialUploadSize()); - { - DebugScopedSetImplThread impl_thread(Proxy()); - DebugScopedSetMainThreadBlocked main_thread_blocked(Proxy()); - layer_tree_host_->contents_texture_manager()->ClearAllMemory( - resource_provider.get()); - } - // Reupload after eviction - test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); + EXPECT_FALSE(test_layer->DrawsContent()); + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); + bitmap.allocPixels(); + bitmap.setImmutable(); + + scoped_ptr<ScopedUIResource> resource = ScopedUIResource::Create( + layer_tree_host_.get(), UIResourceBitmap(bitmap)); + gfx::Rect aperture(5, 5, 1, 1); + bool fill_center = true; + test_layer->SetUIResourceId(resource->id(), aperture); + test_layer->SetFillCenter(fill_center); test_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(1u, queue.FullUploadSize()); - EXPECT_EQ(0u, queue.PartialUploadSize()); - // PrioritizedResourceManager clearing - layer_tree_host_->contents_texture_manager()->UnregisterTexture( - params.texture); - EXPECT_EQ(NULL, params.texture->resource_manager()); - test_layer->SavePaintProperties(); - test_layer->SetTexturePriorities(calculator); - ResourceUpdateQueue queue2; - test_layer->Update(&queue2, &occlusion_tracker); - EXPECT_EQ(1u, queue2.FullUploadSize()); - EXPECT_EQ(0u, queue2.PartialUploadSize()); - params = queue2.TakeFirstFullUpload(); - EXPECT_TRUE(params.texture != NULL); - EXPECT_EQ(params.texture->resource_manager(), - layer_tree_host_->contents_texture_manager()); + EXPECT_TRUE(test_layer->DrawsContent()); } } // namespace diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc new file mode 100644 index 00000000000..4272fe688cc --- /dev/null +++ b/chromium/cc/layers/painted_scrollbar_layer.cc @@ -0,0 +1,244 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/painted_scrollbar_layer.h" + +#include "base/auto_reset.h" +#include "base/basictypes.h" +#include "base/debug/trace_event.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" +#include "cc/resources/ui_resource_bitmap.h" +#include "cc/trees/layer_tree_host.h" +#include "cc/trees/layer_tree_impl.h" +#include "skia/ext/platform_canvas.h" +#include "skia/ext/refptr.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkSize.h" +#include "ui/gfx/skia_util.h" + +namespace cc { + +scoped_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return PaintedScrollbarLayerImpl::Create( + tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>(); +} + +scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create( + scoped_ptr<Scrollbar> scrollbar, + int scroll_layer_id) { + return make_scoped_refptr( + new PaintedScrollbarLayer(scrollbar.Pass(), scroll_layer_id)); +} + +PaintedScrollbarLayer::PaintedScrollbarLayer( + scoped_ptr<Scrollbar> scrollbar, + int scroll_layer_id) + : scrollbar_(scrollbar.Pass()), + scroll_layer_id_(scroll_layer_id), + thumb_thickness_(scrollbar_->ThumbThickness()), + thumb_length_(scrollbar_->ThumbLength()) { + if (!scrollbar_->IsOverlay()) + SetShouldScrollOnMainThread(true); +} + +PaintedScrollbarLayer::~PaintedScrollbarLayer() {} + +int PaintedScrollbarLayer::ScrollLayerId() const { + return scroll_layer_id_; +} + +void PaintedScrollbarLayer::SetScrollLayerId(int id) { + if (id == scroll_layer_id_) + return; + + scroll_layer_id_ = id; + SetNeedsFullTreeSync(); +} + +bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const { + return scrollbar_->IsOverlay(); +} + +ScrollbarOrientation PaintedScrollbarLayer::orientation() const { + return scrollbar_->Orientation(); +} + +int PaintedScrollbarLayer::MaxTextureSize() { + DCHECK(layer_tree_host()); + return layer_tree_host()->GetRendererCapabilities().max_texture_size; +} + +float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) { + // If the scaled content_bounds() is bigger than the max texture size of the + // device, we need to clamp it by rescaling, since content_bounds() is used + // below to set the texture size. + gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale); + if (scaled_bounds.width() > MaxTextureSize() || + scaled_bounds.height() > MaxTextureSize()) { + if (scaled_bounds.width() > scaled_bounds.height()) + return (MaxTextureSize() - 1) / static_cast<float>(bounds().width()); + else + return (MaxTextureSize() - 1) / static_cast<float>(bounds().height()); + } + return scale; +} + +void PaintedScrollbarLayer::CalculateContentsScale( + float ideal_contents_scale, + float device_scale_factor, + float page_scale_factor, + bool animating_transform_to_screen, + float* contents_scale_x, + float* contents_scale_y, + gfx::Size* content_bounds) { + ContentsScalingLayer::CalculateContentsScale( + ClampScaleToMaxTextureSize(ideal_contents_scale), + device_scale_factor, + page_scale_factor, + animating_transform_to_screen, + contents_scale_x, + contents_scale_y, + content_bounds); +} + +void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { + ContentsScalingLayer::PushPropertiesTo(layer); + + PaintedScrollbarLayerImpl* scrollbar_layer = + static_cast<PaintedScrollbarLayerImpl*>(layer); + + scrollbar_layer->SetThumbThickness(thumb_thickness_); + scrollbar_layer->SetThumbLength(thumb_length_); + if (orientation() == HORIZONTAL) { + scrollbar_layer->SetTrackStart( + track_rect_.x() - location_.x()); + scrollbar_layer->SetTrackLength(track_rect_.width()); + } else { + scrollbar_layer->SetTrackStart( + track_rect_.y() - location_.y()); + scrollbar_layer->SetTrackLength(track_rect_.height()); + } + + if (track_resource_.get()) + scrollbar_layer->set_track_ui_resource_id(track_resource_->id()); + if (thumb_resource_.get()) + scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id()); + + scrollbar_layer->set_is_overlay_scrollbar(scrollbar_->IsOverlay()); + + // PaintedScrollbarLayer must push properties every frame. crbug.com/259095 + needs_push_properties_ = true; +} + +ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() { + return this; +} + +void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { + // When the LTH is set to null or has changed, then this layer should remove + // all of its associated resources. + if (!host || host != layer_tree_host()) { + track_resource_.reset(); + thumb_resource_.reset(); + } + + ContentsScalingLayer::SetLayerTreeHost(host); +} + +gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect( + gfx::Rect layer_rect) const { + // Don't intersect with the bounds as in LayerRectToContentRect() because + // layer_rect here might be in coordinates of the containing layer. + gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect( + layer_rect, contents_scale_y(), contents_scale_y()); + // We should never return a rect bigger than the content_bounds(). + gfx::Size clamped_size = expanded_rect.size(); + clamped_size.SetToMin(content_bounds()); + expanded_rect.set_size(clamped_size); + return expanded_rect; +} + +gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const { + gfx::Size thumb_size; + if (orientation() == HORIZONTAL) { + thumb_size = + gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness()); + } else { + thumb_size = + gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength()); + } + return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size)); +} + +void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() { + track_rect_ = scrollbar_->TrackRect(); + location_ = scrollbar_->Location(); + if (scrollbar_->HasThumb()) { + thumb_thickness_ = scrollbar_->ThumbThickness(); + thumb_length_ = scrollbar_->ThumbLength(); + } +} + +bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue, + const OcclusionTracker* occlusion) { + UpdateThumbAndTrackGeometry(); + + gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect( + gfx::Rect(location_, bounds())); + + if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty()) + return false; + + { + base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_, + true); + ContentsScalingLayer::Update(queue, occlusion); + } + + track_resource_ = ScopedUIResource::Create( + layer_tree_host(), RasterizeScrollbarPart(scaled_track_rect, TRACK)); + gfx::Rect thumb_rect = OriginThumbRect(); + + if (scrollbar_->HasThumb() && !thumb_rect.IsEmpty()) { + thumb_resource_ = ScopedUIResource::Create( + layer_tree_host(), RasterizeScrollbarPart(thumb_rect, THUMB)); + } + + return true; +} + +UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart( + gfx::Rect rect, + ScrollbarPart part) { + DCHECK(!rect.size().IsEmpty()); + + SkBitmap skbitmap; + skbitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height()); + skbitmap.allocPixels(); + + SkCanvas skcanvas(skbitmap); + skcanvas.translate(SkFloatToScalar(-rect.x()), SkFloatToScalar(-rect.y())); + skcanvas.scale(SkFloatToScalar(contents_scale_x()), + SkFloatToScalar(contents_scale_y())); + + gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( + rect, 1.f / contents_scale_x(), 1.f / contents_scale_y()); + SkRect layer_skrect = RectToSkRect(layer_rect); + SkPaint paint; + paint.setAntiAlias(false); + paint.setXfermodeMode(SkXfermode::kClear_Mode); + skcanvas.drawRect(layer_skrect, paint); + skcanvas.clipRect(layer_skrect); + + scrollbar_->PaintPart(&skcanvas, part, layer_rect); + // Make sure that the pixels are no longer mutable to unavoid unnecessary + // allocation and copying. + skbitmap.setImmutable(); + + return UIResourceBitmap(skbitmap); +} + +} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer.h b/chromium/cc/layers/painted_scrollbar_layer.h index 6d0e58f7a4e..e84377d1e5a 100644 --- a/chromium/cc/layers/scrollbar_layer.h +++ b/chromium/cc/layers/painted_scrollbar_layer.h @@ -1,40 +1,41 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CC_LAYERS_SCROLLBAR_LAYER_H_ -#define CC_LAYERS_SCROLLBAR_LAYER_H_ +#ifndef CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_ +#define CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_ #include "cc/base/cc_export.h" #include "cc/input/scrollbar.h" #include "cc/layers/contents_scaling_layer.h" +#include "cc/layers/scrollbar_layer_interface.h" #include "cc/layers/scrollbar_theme_painter.h" #include "cc/resources/layer_updater.h" +#include "cc/resources/scoped_ui_resource.h" namespace cc { -class CachingBitmapContentLayerUpdater; -class ResourceUpdateQueue; class ScrollbarThemeComposite; -class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer { +class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface, + public ContentsScalingLayer { public: virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; - static scoped_refptr<ScrollbarLayer> Create( + static scoped_refptr<PaintedScrollbarLayer> Create( scoped_ptr<Scrollbar> scrollbar, int scroll_layer_id); - int scroll_layer_id() const { return scroll_layer_id_; } - void SetScrollLayerId(int id); - virtual bool OpacityCanAnimateOnImplThread() const OVERRIDE; + virtual ScrollbarLayerInterface* ToScrollbarLayer() OVERRIDE; + + // ScrollbarLayerInterface + virtual int ScrollLayerId() const OVERRIDE; + virtual void SetScrollLayerId(int id) OVERRIDE; - ScrollbarOrientation Orientation() const; + virtual ScrollbarOrientation orientation() const OVERRIDE; // Layer interface - virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) - OVERRIDE; virtual bool Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) OVERRIDE; virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE; @@ -47,49 +48,45 @@ class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer { float* contents_scale_y, gfx::Size* content_bounds) OVERRIDE; - virtual ScrollbarLayer* ToScrollbarLayer() OVERRIDE; - protected: - ScrollbarLayer(scoped_ptr<Scrollbar> scrollbar, - int scroll_layer_id); - virtual ~ScrollbarLayer(); + PaintedScrollbarLayer(scoped_ptr<Scrollbar> scrollbar, int scroll_layer_id); + virtual ~PaintedScrollbarLayer(); + + // For unit tests + UIResourceId track_resource_id() { + return track_resource_.get() ? track_resource_->id() : 0; + } + UIResourceId thumb_resource_id() { + return thumb_resource_.get() ? thumb_resource_->id() : 0; + } + void UpdateThumbAndTrackGeometry(); private: - bool UpdatePart(CachingBitmapContentLayerUpdater* painter, - LayerUpdater::Resource* resource, - gfx::Rect rect, - ResourceUpdateQueue* queue); - void CreateUpdaterIfNeeded(); gfx::Rect ScrollbarLayerRectToContentRect(gfx::Rect layer_rect) const; gfx::Rect OriginThumbRect() const; - bool is_dirty() const { return !dirty_rect_.IsEmpty(); } - int MaxTextureSize(); float ClampScaleToMaxTextureSize(float scale); + UIResourceBitmap RasterizeScrollbarPart(gfx::Rect rect, + ScrollbarPart part); + scoped_ptr<Scrollbar> scrollbar_; + int scroll_layer_id_; + // Snapshot of properties taken in UpdateThumbAndTrackGeometry and used in + // PushPropertiesTo. int thumb_thickness_; int thumb_length_; - gfx::Rect track_rect_; gfx::Point location_; - int scroll_layer_id_; - - unsigned texture_format_; - - gfx::RectF dirty_rect_; - - scoped_refptr<CachingBitmapContentLayerUpdater> track_updater_; - scoped_refptr<CachingBitmapContentLayerUpdater> thumb_updater_; + gfx::Rect track_rect_; - // All the parts of the scrollbar except the thumb - scoped_ptr<LayerUpdater::Resource> track_; - scoped_ptr<LayerUpdater::Resource> thumb_; + scoped_ptr<ScopedUIResource> track_resource_; + scoped_ptr<ScopedUIResource> thumb_resource_; - DISALLOW_COPY_AND_ASSIGN(ScrollbarLayer); + DISALLOW_COPY_AND_ASSIGN(PaintedScrollbarLayer); }; } // namespace cc -#endif // CC_LAYERS_SCROLLBAR_LAYER_H_ +#endif // CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_ diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc new file mode 100644 index 00000000000..e38a1a640be --- /dev/null +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc @@ -0,0 +1,181 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/painted_scrollbar_layer_impl.h" + +#include <algorithm> + +#include "cc/animation/scrollbar_animation_controller.h" +#include "cc/layers/layer.h" +#include "cc/layers/quad_sink.h" +#include "cc/quads/solid_color_draw_quad.h" +#include "cc/quads/texture_draw_quad.h" +#include "cc/trees/layer_tree_impl.h" +#include "cc/trees/layer_tree_settings.h" +#include "ui/gfx/rect_conversions.h" + +namespace cc { + +scoped_ptr<PaintedScrollbarLayerImpl> PaintedScrollbarLayerImpl::Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation) { + return make_scoped_ptr( + new PaintedScrollbarLayerImpl(tree_impl, id, orientation)); +} + +PaintedScrollbarLayerImpl::PaintedScrollbarLayerImpl( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation) + : ScrollbarLayerImplBase(tree_impl, id, orientation, false), + track_ui_resource_id_(0), + thumb_ui_resource_id_(0), + thumb_thickness_(0), + thumb_length_(0), + track_start_(0), + track_length_(0), + vertical_adjust_(0.f), + scroll_layer_id_(Layer::INVALID_ID) {} + +PaintedScrollbarLayerImpl::~PaintedScrollbarLayerImpl() {} + +scoped_ptr<LayerImpl> PaintedScrollbarLayerImpl::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return PaintedScrollbarLayerImpl::Create(tree_impl, id(), orientation()) + .PassAs<LayerImpl>(); +} + +void PaintedScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { + ScrollbarLayerImplBase::PushPropertiesTo(layer); + + PaintedScrollbarLayerImpl* scrollbar_layer = + static_cast<PaintedScrollbarLayerImpl*>(layer); + + scrollbar_layer->SetThumbThickness(thumb_thickness_); + scrollbar_layer->SetThumbLength(thumb_length_); + scrollbar_layer->SetTrackStart(track_start_); + scrollbar_layer->SetTrackLength(track_length_); + + scrollbar_layer->set_track_ui_resource_id(track_ui_resource_id_); + scrollbar_layer->set_thumb_ui_resource_id(thumb_ui_resource_id_); +} + +bool PaintedScrollbarLayerImpl::WillDraw(DrawMode draw_mode, + ResourceProvider* resource_provider) { + DCHECK(draw_mode != DRAW_MODE_RESOURCELESS_SOFTWARE); + return LayerImpl::WillDraw(draw_mode, resource_provider); +} + +void PaintedScrollbarLayerImpl::AppendQuads( + QuadSink* quad_sink, + AppendQuadsData* append_quads_data) { + bool premultipled_alpha = true; + bool flipped = false; + gfx::PointF uv_top_left(0.f, 0.f); + gfx::PointF uv_bottom_right(1.f, 1.f); + gfx::Rect bounds_rect(bounds()); + gfx::Rect content_bounds_rect(content_bounds()); + + SharedQuadState* shared_quad_state = + quad_sink->UseSharedQuadState(CreateSharedQuadState()); + AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); + + gfx::Rect thumb_quad_rect = ComputeThumbQuadRect(); + + ResourceProvider::ResourceId thumb_resource_id = + layer_tree_impl()->ResourceIdForUIResource(thumb_ui_resource_id_); + ResourceProvider::ResourceId track_resource_id = + layer_tree_impl()->ResourceIdForUIResource(track_ui_resource_id_); + + if (thumb_resource_id && !thumb_quad_rect.IsEmpty()) { + gfx::Rect opaque_rect; + const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; + scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); + quad->SetNew(shared_quad_state, + thumb_quad_rect, + opaque_rect, + thumb_resource_id, + premultipled_alpha, + uv_top_left, + uv_bottom_right, + SK_ColorTRANSPARENT, + opacity, + flipped); + quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + } + + gfx::Rect track_quad_rect = content_bounds_rect; + if (track_resource_id && !track_quad_rect.IsEmpty()) { + gfx::Rect opaque_rect(contents_opaque() ? track_quad_rect : gfx::Rect()); + const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; + scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); + quad->SetNew(shared_quad_state, + track_quad_rect, + opaque_rect, + track_resource_id, + premultipled_alpha, + uv_top_left, + uv_bottom_right, + SK_ColorTRANSPARENT, + opacity, + flipped); + quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + } +} + +void PaintedScrollbarLayerImpl::DidLoseOutputSurface() { + track_ui_resource_id_ = 0; + thumb_ui_resource_id_ = 0; +} + +void PaintedScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) { + if (thumb_thickness_ == thumb_thickness) + return; + thumb_thickness_ = thumb_thickness; + NoteLayerPropertyChanged(); +} + +int PaintedScrollbarLayerImpl::ThumbThickness() const { + return thumb_thickness_; +} + +void PaintedScrollbarLayerImpl::SetThumbLength(int thumb_length) { + if (thumb_length_ == thumb_length) + return; + thumb_length_ = thumb_length; + NoteLayerPropertyChanged(); +} + +int PaintedScrollbarLayerImpl::ThumbLength() const { + return thumb_length_; +} + +void PaintedScrollbarLayerImpl::SetTrackStart(int track_start) { + if (track_start_ == track_start) + return; + track_start_ = track_start; + NoteLayerPropertyChanged(); +} + +int PaintedScrollbarLayerImpl::TrackStart() const { + return track_start_; +} + +void PaintedScrollbarLayerImpl::SetTrackLength(int track_length) { + if (track_length_ == track_length) + return; + track_length_ = track_length; + NoteLayerPropertyChanged(); +} + +float PaintedScrollbarLayerImpl::TrackLength() const { + return track_length_ + (orientation() == VERTICAL ? vertical_adjust() : 0); +} + +const char* PaintedScrollbarLayerImpl::LayerTypeAsString() const { + return "cc::PaintedScrollbarLayerImpl"; +} + +} // namespace cc diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.h b/chromium/cc/layers/painted_scrollbar_layer_impl.h new file mode 100644 index 00000000000..c9d1f3dc0fc --- /dev/null +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.h @@ -0,0 +1,82 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_ +#define CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_ + +#include "cc/base/cc_export.h" +#include "cc/input/scrollbar.h" +#include "cc/layers/scrollbar_layer_impl_base.h" +#include "cc/resources/ui_resource_client.h" + +namespace cc { + +class LayerTreeImpl; +class ScrollView; + +class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase { + public: + static scoped_ptr<PaintedScrollbarLayerImpl> Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation); + virtual ~PaintedScrollbarLayerImpl(); + + // LayerImpl implementation. + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + + virtual bool WillDraw(DrawMode draw_mode, + ResourceProvider* resource_provider) OVERRIDE; + virtual void AppendQuads(QuadSink* quad_sink, + AppendQuadsData* append_quads_data) OVERRIDE; + + virtual void DidLoseOutputSurface() OVERRIDE; + + void SetThumbThickness(int thumb_thickness); + void SetThumbLength(int thumb_length); + void SetTrackStart(int track_start); + void SetTrackLength(int track_length); + + void set_track_ui_resource_id(UIResourceId uid) { + track_ui_resource_id_ = uid; + } + void set_thumb_ui_resource_id(UIResourceId uid) { + thumb_ui_resource_id_ = uid; + } + + protected: + PaintedScrollbarLayerImpl(LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation); + + // ScrollbarLayerImplBase implementation. + virtual int ThumbThickness() const OVERRIDE; + virtual int ThumbLength() const OVERRIDE; + virtual float TrackLength() const OVERRIDE; + virtual int TrackStart() const OVERRIDE; + + private: + virtual const char* LayerTypeAsString() const OVERRIDE; + + UIResourceId track_ui_resource_id_; + UIResourceId thumb_ui_resource_id_; + + int thumb_thickness_; + int thumb_length_; + int track_start_; + int track_length_; + + // Difference between the clip layer's height and the visible viewport + // height (which may differ in the presence of top-controls hiding). + float vertical_adjust_; + + int scroll_layer_id_; + + DISALLOW_COPY_AND_ASSIGN(PaintedScrollbarLayerImpl); +}; + +} // namespace cc +#endif // CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_ diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc index 889e2fbd3f6..4226f1ba713 100644 --- a/chromium/cc/layers/picture_layer.cc +++ b/chromium/cc/layers/picture_layer.cc @@ -6,6 +6,7 @@ #include "cc/debug/benchmark_instrumentation.h" #include "cc/debug/devtools_instrumentation.h" +#include "cc/layers/content_layer_client.h" #include "cc/layers/picture_layer_impl.h" #include "cc/trees/layer_tree_impl.h" #include "ui/gfx/rect_conversions.h" @@ -36,25 +37,26 @@ scoped_ptr<LayerImpl> PictureLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) { void PictureLayer::PushPropertiesTo(LayerImpl* base_layer) { Layer::PushPropertiesTo(base_layer); - PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer); - // This should be first so others can use it. - layer_impl->UpdateTwinLayer(); + + if (layer_impl->bounds().IsEmpty()) { + // Update may not get called for an empty layer, so resize here instead. + // Using layer_impl because either bounds() or paint_properties().bounds + // may disagree and either one could have been pushed to layer_impl. + pile_->Resize(layer_impl->bounds()); + pile_->UpdateRecordedRegion(); + } + + if (DrawsContent()) { + DCHECK(paint_properties().bounds == pile_->size()); + } layer_impl->SetIsMask(is_mask_); - layer_impl->CreateTilingSetIfNeeded(); // Unlike other properties, invalidation must always be set on layer_impl. // See PictureLayerImpl::PushPropertiesTo for more details. layer_impl->invalidation_.Clear(); layer_impl->invalidation_.Swap(&pile_invalidation_); layer_impl->pile_ = PicturePileImpl::CreateFromOther(pile_.get()); - layer_impl->SyncFromActiveLayer(); - - // PictureLayer must push properties every frame. - // TODO(danakj): If we can avoid requiring to do CreateTilingSetIfNeeded() and - // SyncFromActiveLayer() on every commit then this could go away, maybe - // conditionally. crbug.com/259402 - needs_push_properties_ = true; } void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) { @@ -114,11 +116,14 @@ bool PictureLayer::Update(ResourceUpdateQueue* queue, pile_invalidation_, visible_layer_rect, rendering_stats_instrumentation()); - if (!updated) { + if (updated) { + SetNeedsPushProperties(); + } else { // If this invalidation did not affect the pile, then it can be cleared as // an optimization. pile_invalidation_.Clear(); } + return updated; } @@ -130,4 +135,22 @@ bool PictureLayer::SupportsLCDText() const { return true; } +skia::RefPtr<SkPicture> PictureLayer::GetPicture() const { + // We could either flatten the PicturePile into a single SkPicture, + // or paint a fresh one depending on what we intend to do with the + // picture. For now we just paint a fresh one to get consistent results. + if (!DrawsContent()) + return skia::RefPtr<SkPicture>(); + + int width = bounds().width(); + int height = bounds().height(); + gfx::RectF opaque; + + skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture); + SkCanvas* canvas = picture->beginRecording(width, height); + client_->PaintContents(canvas, gfx::Rect(width, height), &opaque); + picture->endRecording(); + return picture; +} + } // namespace cc diff --git a/chromium/cc/layers/picture_layer.h b/chromium/cc/layers/picture_layer.h index 7a9a427d427..d7943307ddc 100644 --- a/chromium/cc/layers/picture_layer.h +++ b/chromium/cc/layers/picture_layer.h @@ -34,6 +34,7 @@ class CC_EXPORT PictureLayer : public Layer { const OcclusionTracker* occlusion) OVERRIDE; virtual void SetIsMask(bool is_mask) OVERRIDE; virtual bool SupportsLCDText() const OVERRIDE; + virtual skia::RefPtr<SkPicture> GetPicture() const OVERRIDE; protected: explicit PictureLayer(ContentLayerClient* client); diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index 2f1878bc3a6..95a4a076520 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -45,11 +45,11 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id) raster_contents_scale_(0.f), low_res_raster_contents_scale_(0.f), raster_source_scale_was_animating_(false), - is_using_lcd_text_(tree_impl->settings().can_use_lcd_text) { -} + is_using_lcd_text_(tree_impl->settings().can_use_lcd_text), + needs_post_commit_initialization_(true), + should_update_tile_priorities_(false) {} -PictureLayerImpl::~PictureLayerImpl() { -} +PictureLayerImpl::~PictureLayerImpl() {} const char* PictureLayerImpl::LayerTypeAsString() const { return "cc::PictureLayerImpl"; @@ -60,13 +60,11 @@ scoped_ptr<LayerImpl> PictureLayerImpl::CreateLayerImpl( return PictureLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>(); } -void PictureLayerImpl::CreateTilingSetIfNeeded() { - DCHECK(layer_tree_impl()->IsPendingTree()); - if (!tilings_) - tilings_.reset(new PictureLayerTilingSet(this, bounds())); -} - void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { + // It's possible this layer was never drawn or updated (e.g. because it was + // a descendant of an opacity 0 layer). + DoPostCommitInitializationIfNeeded(); + LayerImpl::PushPropertiesTo(base_layer); PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer); @@ -78,8 +76,9 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { layer_impl->SetIsMask(is_mask_); layer_impl->pile_ = pile_; - pile_ = NULL; + // Tilings would be expensive to push, so we swap. This optimization requires + // an extra invalidation in SyncFromActiveLayer. layer_impl->tilings_.swap(tilings_); layer_impl->tilings_->SetClient(layer_impl); if (tilings_) @@ -92,19 +91,19 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { layer_impl->low_res_raster_contents_scale_ = low_res_raster_contents_scale_; layer_impl->UpdateLCDTextStatus(is_using_lcd_text_); + layer_impl->needs_post_commit_initialization_ = false; - // As an optimization, don't make a copy of this potentially complex region, - // and swap it directly from the pending to the active layer. In general, any - // property pushed to a LayerImpl continues to live on that LayerImpl. - // However, invalidation is the difference between two main thread frames, so - // it no longer makes sense once the pending tree gets recycled. It will - // always get pushed during PictureLayer::PushPropertiesTo. + // The invalidation on this soon-to-be-recycled layer must be cleared to + // mirror clearing the invalidation in PictureLayer's version of this function + // in case push properties is skipped. layer_impl->invalidation_.Swap(&invalidation_); invalidation_.Clear(); + needs_post_commit_initialization_ = true; } void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, AppendQuadsData* append_quads_data) { + DCHECK(!needs_post_commit_initialization_); gfx::Rect rect(visible_content_rect()); gfx::Rect content_rect(content_bounds()); @@ -137,7 +136,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, opaque_rect, texture_rect, texture_size, - false, + RGBA_8888, quad_content_rect, contents_scale, draw_direct_to_backbuffer, @@ -149,11 +148,6 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); - bool clipped = false; - gfx::QuadF target_quad = MathUtil::MapQuad( - draw_transform(), - gfx::QuadF(rect), - &clipped); if (ShowDebugBorders()) { for (PictureLayerTilingSet::CoverageIterator iter( tilings_.get(), contents_scale_x(), rect, ideal_contents_scale_); @@ -228,6 +222,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, const ManagedTileState::TileVersion& tile_version = iter->GetTileVersionForDrawing(); + scoped_ptr<DrawQuad> draw_quad; switch (tile_version.mode()) { case ManagedTileState::TileVersion::RESOURCE_MODE: { gfx::RectF texture_rect = iter.texture_rect(); @@ -245,7 +240,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, texture_rect, iter.texture_size(), tile_version.contents_swizzled()); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + draw_quad = quad.PassAs<DrawQuad>(); break; } case ManagedTileState::TileVersion::PICTURE_PILE_MODE: { @@ -253,22 +248,22 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, gfx::Rect opaque_rect = iter->opaque_rect(); opaque_rect.Intersect(content_rect); + ResourceProvider* resource_provider = + layer_tree_impl()->resource_provider(); + ResourceFormat format = + resource_provider->memory_efficient_texture_format(); scoped_ptr<PictureDrawQuad> quad = PictureDrawQuad::Create(); quad->SetNew(shared_quad_state, geometry_rect, opaque_rect, texture_rect, iter.texture_size(), - // TODO(reveman): This assumes the renderer will use - // GL_RGBA as format of temporary resource. The need - // to swizzle should instead be determined by the - // renderer. - !PlatformColor::SameComponentOrder(GL_RGBA), + format, iter->content_rect(), iter->contents_scale(), draw_direct_to_backbuffer, pile_); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + draw_quad = quad.PassAs<DrawQuad>(); break; } case ManagedTileState::TileVersion::SOLID_COLOR_MODE: { @@ -277,14 +272,15 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, geometry_rect, tile_version.get_solid_color(), false); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); + draw_quad = quad.PassAs<DrawQuad>(); break; } - default: - NOTREACHED(); } - if (!seen_tilings.size() || seen_tilings.back() != iter.CurrentTiling()) + DCHECK(draw_quad); + quad_sink->Append(draw_quad.Pass(), append_quads_data); + + if (seen_tilings.empty() || seen_tilings.back() != iter.CurrentTiling()) seen_tilings.push_back(iter.CurrentTiling()); } @@ -296,6 +292,15 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink, } void PictureLayerImpl::UpdateTilePriorities() { + DCHECK(!needs_post_commit_initialization_); + CHECK(should_update_tile_priorities_); + + if (!layer_tree_impl()->device_viewport_valid_for_tile_management()) { + for (size_t i = 0; i < tilings_->num_tilings(); ++i) + DCHECK(tilings_->tiling_at(i)->has_ever_been_updated()); + return; + } + if (!tilings_->num_tilings()) return; @@ -314,19 +319,17 @@ void PictureLayerImpl::UpdateTilePriorities() { if (!tiling_needs_update) return; - // At this point, tile priorities are going to be modified. - layer_tree_impl()->WillModifyTilePriorities(); - UpdateLCDTextStatus(can_use_lcd_text()); gfx::Transform current_screen_space_transform = screen_space_transform(); + gfx::Size viewport_size = layer_tree_impl()->DrawViewportSize(); gfx::Rect viewport_in_content_space; gfx::Transform screen_to_layer(gfx::Transform::kSkipInitialization); if (screen_space_transform().GetInverse(&screen_to_layer)) { - gfx::Rect device_viewport(layer_tree_impl()->device_viewport_size()); - viewport_in_content_space = gfx::ToEnclosingRect( - MathUtil::ProjectClippedRect(screen_to_layer, device_viewport)); + viewport_in_content_space = + gfx::ToEnclosingRect(MathUtil::ProjectClippedRect( + screen_to_layer, gfx::Rect(viewport_size))); } WhichTree tree = @@ -335,7 +338,7 @@ void PictureLayerImpl::UpdateTilePriorities() { layer_tree_impl()->settings().max_tiles_for_interest_area; tilings_->UpdateTilePriorities( tree, - layer_tree_impl()->device_viewport_size(), + viewport_size, viewport_in_content_space, visible_content_rect(), last_bounds_, @@ -353,12 +356,15 @@ void PictureLayerImpl::UpdateTilePriorities() { last_screen_space_transform_ = current_screen_space_transform; last_bounds_ = bounds(); last_content_scale_ = contents_scale_x(); + + // Tile priorities were modified. + layer_tree_impl()->DidModifyTilePriorities(); } void PictureLayerImpl::DidBecomeActive() { LayerImpl::DidBecomeActive(); tilings_->DidBecomeActive(); - layer_tree_impl()->WillModifyTilePriorities(); + layer_tree_impl()->DidModifyTilePriorities(); } void PictureLayerImpl::DidBeginTracing() { @@ -380,6 +386,12 @@ void PictureLayerImpl::CalculateContentsScale( float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) { + DoPostCommitInitializationIfNeeded(); + + // This function sets valid raster scales and manages tilings, so tile + // priorities can now be updated. + should_update_tile_priorities_ = true; + if (!CanHaveTilings()) { ideal_page_scale_ = page_scale_factor; ideal_device_scale_ = device_scale_factor; @@ -525,14 +537,10 @@ gfx::Size PictureLayerImpl::CalculateTileSize( return default_tile_size; } -void PictureLayerImpl::SyncFromActiveLayer() { - DCHECK(layer_tree_impl()->IsPendingTree()); - - if (twin_layer_) - SyncFromActiveLayer(twin_layer_); -} - void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) { + DCHECK(!other->needs_post_commit_initialization_); + DCHECK(other->tilings_); + UpdateLCDTextStatus(other->is_using_lcd_text_); if (!DrawsContent()) { @@ -581,6 +589,8 @@ void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) { } else { tilings_->RemoveAllTilings(); } + + SanityCheckTilingState(); } void PictureLayerImpl::SyncTiling( @@ -593,19 +603,11 @@ void PictureLayerImpl::SyncTiling( // get updated prior to drawing or activation. If this tree does not // need update draw properties, then its transforms are up to date and // we can create tiles for this tiling immediately. - if (!layer_tree_impl()->needs_update_draw_properties()) + if (!layer_tree_impl()->needs_update_draw_properties() && + should_update_tile_priorities_) UpdateTilePriorities(); } -void PictureLayerImpl::UpdateTwinLayer() { - DCHECK(layer_tree_impl()->IsPendingTree()); - - twin_layer_ = static_cast<PictureLayerImpl*>( - layer_tree_impl()->FindActiveTreeLayerById(id())); - if (twin_layer_) - twin_layer_->twin_layer_ = this; -} - void PictureLayerImpl::SetIsMask(bool is_mask) { if (is_mask_ == is_mask) return; @@ -617,27 +619,24 @@ void PictureLayerImpl::SetIsMask(bool is_mask) { ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const { gfx::Rect content_rect(content_bounds()); float scale = contents_scale_x(); - for (PictureLayerTilingSet::CoverageIterator - iter(tilings_.get(), scale, content_rect, ideal_contents_scale_); - iter; - ++iter) { - // Mask resource not ready yet. - if (!*iter) - return 0; + PictureLayerTilingSet::CoverageIterator iter( + tilings_.get(), scale, content_rect, ideal_contents_scale_); - const ManagedTileState::TileVersion& tile_version = - iter->GetTileVersionForDrawing(); - if (!tile_version.IsReadyToDraw() || - tile_version.mode() != ManagedTileState::TileVersion::RESOURCE_MODE) - return 0; + // Mask resource not ready yet. + if (!iter || !*iter) + return 0; - // Masks only supported if they fit on exactly one tile. - if (iter.geometry_rect() != content_rect) - return 0; + // Masks only supported if they fit on exactly one tile. + if (iter.geometry_rect() != content_rect) + return 0; - return tile_version.get_resource_id(); - } - return 0; + const ManagedTileState::TileVersion& tile_version = + iter->GetTileVersionForDrawing(); + if (!tile_version.IsReadyToDraw() || + tile_version.mode() != ManagedTileState::TileVersion::RESOURCE_MODE) + return 0; + + return tile_version.get_resource_id(); } void PictureLayerImpl::MarkVisibleResourcesAsRequired() const { @@ -695,7 +694,7 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const { continue; missing_region.Subtract(iter.geometry_rect()); - iter->mark_required_for_activation(); + iter->MarkRequiredForActivation(); } } @@ -720,8 +719,30 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const { if (!missing_region.Intersects(iter.geometry_rect())) continue; - iter->mark_required_for_activation(); + iter->MarkRequiredForActivation(); + } +} + +void PictureLayerImpl::DoPostCommitInitialization() { + DCHECK(needs_post_commit_initialization_); + DCHECK(layer_tree_impl()->IsPendingTree()); + + if (!tilings_) + tilings_.reset(new PictureLayerTilingSet(this, bounds())); + + DCHECK(!twin_layer_); + twin_layer_ = static_cast<PictureLayerImpl*>( + layer_tree_impl()->FindActiveTreeLayerById(id())); + if (twin_layer_) { + DCHECK(!twin_layer_->twin_layer_); + twin_layer_->twin_layer_ = this; + // If the twin has never been pushed to, do not sync from it. + // This can happen if this function is called during activation. + if (!twin_layer_->needs_post_commit_initialization_) + SyncFromActiveLayer(twin_layer_); } + + needs_post_commit_initialization_ = false; } PictureLayerTiling* PictureLayerImpl::AddTiling(float contents_scale) { @@ -747,6 +768,7 @@ void PictureLayerImpl::RemoveTiling(float contents_scale) { break; } } + SanityCheckTilingState(); } namespace { @@ -775,6 +797,7 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) { DCHECK(ideal_device_scale_); DCHECK(ideal_source_scale_); DCHECK(CanHaveTilings()); + DCHECK(!needs_post_commit_initialization_); bool change_target_tiling = raster_page_scale_ == 0.f || @@ -790,6 +813,9 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) { if (!change_target_tiling) return; + if (!layer_tree_impl()->device_viewport_valid_for_tile_management()) + return; + raster_page_scale_ = ideal_page_scale_; raster_device_scale_ = ideal_device_scale_; raster_source_scale_ = ideal_source_scale_; @@ -829,12 +855,13 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) { low_res != high_res) low_res = AddTiling(low_res_raster_contents_scale_); - if (high_res) - high_res->set_resolution(HIGH_RESOLUTION); + high_res->set_resolution(HIGH_RESOLUTION); if (low_res && low_res != high_res) low_res->set_resolution(LOW_RESOLUTION); else if (!low_res && previous_low_res) previous_low_res->set_resolution(LOW_RESOLUTION); + + SanityCheckTilingState(); } bool PictureLayerImpl::ShouldAdjustRasterScale( @@ -947,6 +974,8 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer( twin->RemoveTiling(to_remove[i]->contents_scale()); tilings_->Remove(to_remove[i]); } + + SanityCheckTilingState(); } float PictureLayerImpl::MinimumContentsScale() const { @@ -981,6 +1010,10 @@ void PictureLayerImpl::ResetRasterScale() { raster_source_scale_ = 0.f; raster_contents_scale_ = 0.f; low_res_raster_contents_scale_ = 0.f; + + // When raster scales aren't valid, don't update tile priorities until + // this layer has been updated via UpdateDrawProperties. + should_update_tile_priorities_ = false; } bool PictureLayerImpl::CanHaveTilings() const { @@ -1002,6 +1035,22 @@ bool PictureLayerImpl::CanHaveTilingWithScale(float contents_scale) const { return true; } +void PictureLayerImpl::SanityCheckTilingState() const { + if (!DCHECK_IS_ON()) + return; + + if (!CanHaveTilings()) { + DCHECK_EQ(0u, tilings_->num_tilings()); + return; + } + if (tilings_->num_tilings() == 0) + return; + + // MarkVisibleResourcesAsRequired depends on having exactly 1 high res + // tiling to mark its tiles as being required for activation. + DCHECK_EQ(1, tilings_->NumHighResTilings()); +} + void PictureLayerImpl::GetDebugBorderProperties( SkColor* color, float* width) const { @@ -1012,6 +1061,7 @@ void PictureLayerImpl::GetDebugBorderProperties( void PictureLayerImpl::AsValueInto(base::DictionaryValue* state) const { LayerImpl::AsValueInto(state); state->SetDouble("ideal_contents_scale", ideal_contents_scale_); + state->SetDouble("geometry_contents_scale", contents_scale_x()); state->Set("tilings", tilings_->AsValue().release()); state->Set("pictures", pile_->AsValue().release()); state->Set("invalidation", invalidation_.AsValue().release()); @@ -1019,7 +1069,7 @@ void PictureLayerImpl::AsValueInto(base::DictionaryValue* state) const { scoped_ptr<base::ListValue> coverage_tiles(new base::ListValue); for (PictureLayerTilingSet::CoverageIterator iter(tilings_.get(), contents_scale_x(), - gfx::Rect(bounds()), + gfx::Rect(content_bounds()), ideal_contents_scale_); iter; ++iter) { diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index 25cc5d0b7e7..fd5c0677a98 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -62,11 +62,7 @@ class CC_EXPORT PictureLayerImpl const PictureLayerTiling* tiling) OVERRIDE; // PushPropertiesTo active tree => pending tree. - void SyncFromActiveLayer(); void SyncTiling(const PictureLayerTiling* tiling); - void UpdateTwinLayer(); - - void CreateTilingSetIfNeeded(); // Mask-related functions void SetIsMask(bool is_mask); @@ -92,9 +88,15 @@ class CC_EXPORT PictureLayerImpl void UpdateLCDTextStatus(bool new_status); void ResetRasterScale(); void MarkVisibleResourcesAsRequired() const; + void DoPostCommitInitializationIfNeeded() { + if (needs_post_commit_initialization_) + DoPostCommitInitialization(); + } + void DoPostCommitInitialization(); bool CanHaveTilings() const; bool CanHaveTilingWithScale(float contents_scale) const; + void SanityCheckTilingState() const; virtual void GetDebugBorderProperties( SkColor* color, float* width) const OVERRIDE; @@ -124,6 +126,10 @@ class CC_EXPORT PictureLayerImpl bool raster_source_scale_was_animating_; bool is_using_lcd_text_; + bool needs_post_commit_initialization_; + // A sanity state check to make sure UpdateTilePriorities only gets called + // after a CalculateContentsScale/ManageTilings. + bool should_update_tile_priorities_; friend class PictureLayer; DISALLOW_COPY_AND_ASSIGN(PictureLayerImpl); diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index 047a529924a..e1cd39ada41 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -6,6 +6,7 @@ #include <utility> +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/picture_layer.h" #include "cc/test/fake_content_layer_client.h" @@ -19,7 +20,7 @@ #include "cc/test/mock_quad_culler.h" #include "cc/trees/layer_tree_impl.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkDevice.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" #include "ui/gfx/rect_conversions.h" namespace cc { @@ -27,7 +28,7 @@ namespace { class MockCanvas : public SkCanvas { public: - explicit MockCanvas(SkDevice* device) : SkCanvas(device) {} + explicit MockCanvas(SkBaseDevice* device) : SkCanvas(device) {} virtual void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE { // Capture calls before SkCanvas quickReject() kicks in. @@ -40,14 +41,22 @@ class MockCanvas : public SkCanvas { class PictureLayerImplTest : public testing::Test { public: PictureLayerImplTest() - : host_impl_(ImplSidePaintingSettings(), &proxy_), - id_(7) { - host_impl_.InitializeRenderer(CreateFakeOutputSurface()); - } + : host_impl_(ImplSidePaintingSettings(), &proxy_), id_(7) {} + + explicit PictureLayerImplTest(const LayerTreeSettings& settings) + : host_impl_(settings, &proxy_), id_(7) {} virtual ~PictureLayerImplTest() { } + virtual void SetUp() OVERRIDE { + InitializeRenderer(); + } + + virtual void InitializeRenderer() { + host_impl_.InitializeRenderer(CreateFakeOutputSurface()); + } + void SetupDefaultTrees(gfx::Size layer_bounds) { gfx::Size tile_size(100, 100); @@ -69,7 +78,6 @@ class PictureLayerImplTest : public testing::Test { host_impl_.active_tree()->LayerById(id_)); SetupPendingTree(pending_pile); - pending_layer_->UpdateTwinLayer(); } void AddDefaultTilingsWithInvalidation(const Region& invalidation) { @@ -79,7 +87,6 @@ class PictureLayerImplTest : public testing::Test { for (size_t i = 0; i < active_layer_->tilings()->num_tilings(); ++i) active_layer_->tilings()->tiling_at(i)->CreateAllTilesForTesting(); pending_layer_->set_invalidation(invalidation); - pending_layer_->SyncFromActiveLayer(); for (size_t i = 0; i < pending_layer_->tilings()->num_tilings(); ++i) pending_layer_->tilings()->tiling_at(i)->CreateAllTilesForTesting(); } @@ -98,6 +105,7 @@ class PictureLayerImplTest : public testing::Test { pending_layer_ = static_cast<FakePictureLayerImpl*>( host_impl_.pending_tree()->LayerById(id_)); + pending_layer_->DoPostCommitInitializationIfNeeded(); } static void VerifyAllTilesExistAndHavePile( @@ -180,7 +188,7 @@ class PictureLayerImplTest : public testing::Test { SkBitmap store; store.setConfig(SkBitmap::kNo_Config, 1000, 1000); - SkDevice device(store); + SkBitmapDevice device(store); std::vector<SkRect>::const_iterator rect_iter = rects.begin(); for (tile_iter = tiles.begin(); tile_iter < tiles.end(); tile_iter++) { @@ -241,6 +249,62 @@ TEST_F(PictureLayerImplTest, CloneNoInvalidation) { VerifyAllTilesExistAndHavePile(tilings->tiling_at(i), active_pile.get()); } +TEST_F(PictureLayerImplTest, SuppressUpdateTilePriorities) { + base::TimeTicks time_ticks; + host_impl_.SetCurrentFrameTimeTicks(time_ticks); + + gfx::Size tile_size(100, 100); + gfx::Size layer_bounds(400, 400); + + scoped_refptr<FakePicturePileImpl> pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + scoped_refptr<FakePicturePileImpl> active_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + + SetupTrees(pending_pile, active_pile); + + Region invalidation; + AddDefaultTilingsWithInvalidation(invalidation); + float dummy_contents_scale_x; + float dummy_contents_scale_y; + gfx::Size dummy_content_bounds; + active_layer_->CalculateContentsScale(1.f, + 1.f, + 1.f, + false, + &dummy_contents_scale_x, + &dummy_contents_scale_y, + &dummy_content_bounds); + + EXPECT_TRUE(host_impl_.manage_tiles_needed()); + active_layer_->UpdateTilePriorities(); + host_impl_.ManageTiles(); + EXPECT_FALSE(host_impl_.manage_tiles_needed()); + + time_ticks += base::TimeDelta::FromMilliseconds(200); + host_impl_.SetCurrentFrameTimeTicks(time_ticks); + + // Setting this boolean should cause an early out in UpdateTilePriorities. + bool valid_for_tile_management = false; + host_impl_.SetExternalDrawConstraints(gfx::Transform(), + gfx::Rect(layer_bounds), + gfx::Rect(layer_bounds), + valid_for_tile_management); + active_layer_->UpdateTilePriorities(); + EXPECT_FALSE(host_impl_.manage_tiles_needed()); + + time_ticks += base::TimeDelta::FromMilliseconds(200); + host_impl_.SetCurrentFrameTimeTicks(time_ticks); + + valid_for_tile_management = true; + host_impl_.SetExternalDrawConstraints(gfx::Transform(), + gfx::Rect(layer_bounds), + gfx::Rect(layer_bounds), + valid_for_tile_management); + active_layer_->UpdateTilePriorities(); + EXPECT_TRUE(host_impl_.manage_tiles_needed()); +} + TEST_F(PictureLayerImplTest, ClonePartialInvalidation) { gfx::Size tile_size(100, 100); gfx::Size layer_bounds(400, 400); @@ -822,7 +886,7 @@ TEST_F(PictureLayerImplTest, ClampTilesToToMaxTileSize) { TestWebGraphicsContext3D::Create(); context->set_max_texture_size(140); host_impl_.InitializeRenderer(FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>()); + context.Pass()).PassAs<OutputSurface>()); pending_layer_->CalculateContentsScale( 1.f, 1.f, 1.f, false, &result_scale_x, &result_scale_y, &result_bounds); @@ -873,7 +937,7 @@ TEST_F(PictureLayerImplTest, ClampSingleTileToToMaxTileSize) { TestWebGraphicsContext3D::Create(); context->set_max_texture_size(140); host_impl_.InitializeRenderer(FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>()); + context.Pass()).PassAs<OutputSurface>()); pending_layer_->CalculateContentsScale( 1.f, 1.f, 1.f, false, &result_scale_x, &result_scale_y, &result_bounds); @@ -1007,5 +1071,98 @@ TEST_F(PictureLayerImplTest, MarkRequiredOffscreenTiles) { EXPECT_GT(num_offscreen, 0); } +TEST_F(PictureLayerImplTest, ActivateUninitializedLayer) { + gfx::Size tile_size(100, 100); + gfx::Size layer_bounds(400, 400); + scoped_refptr<FakePicturePileImpl> pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + + host_impl_.CreatePendingTree(); + LayerTreeImpl* pending_tree = host_impl_.pending_tree(); + + scoped_ptr<FakePictureLayerImpl> pending_layer = + FakePictureLayerImpl::CreateWithPile(pending_tree, id_, pending_pile); + pending_layer->SetDrawsContent(true); + pending_tree->SetRootLayer(pending_layer.PassAs<LayerImpl>()); + + pending_layer_ = static_cast<FakePictureLayerImpl*>( + host_impl_.pending_tree()->LayerById(id_)); + + // Set some state on the pending layer, make sure it is not clobbered + // by a sync from the active layer. This could happen because if the + // pending layer has not been post-commit initialized it will attempt + // to sync from the active layer. + bool default_lcd_text_setting = pending_layer_->is_using_lcd_text(); + pending_layer_->force_set_lcd_text(!default_lcd_text_setting); + EXPECT_TRUE(pending_layer_->needs_post_commit_initialization()); + + host_impl_.ActivatePendingTree(); + + active_layer_ = static_cast<FakePictureLayerImpl*>( + host_impl_.active_tree()->LayerById(id_)); + + EXPECT_EQ(0u, active_layer_->num_tilings()); + EXPECT_EQ(!default_lcd_text_setting, active_layer_->is_using_lcd_text()); + EXPECT_FALSE(active_layer_->needs_post_commit_initialization()); +} + +// Solid color scrollbar setting is required for deferred initialization. +class ImplSidePaintingSolidColorScrollbarSettings + : public ImplSidePaintingSettings { + public: + ImplSidePaintingSolidColorScrollbarSettings() { + solid_color_scrollbars = true; + } +}; + +class DeferredInitPictureLayerImplTest : public PictureLayerImplTest { + public: + DeferredInitPictureLayerImplTest() + : PictureLayerImplTest(ImplSidePaintingSolidColorScrollbarSettings()) {} + + virtual void InitializeRenderer() OVERRIDE { + host_impl_.InitializeRenderer(FakeOutputSurface::CreateDeferredGL( + scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice)) + .PassAs<OutputSurface>()); + } + + virtual void SetUp() OVERRIDE { + PictureLayerImplTest::SetUp(); + + // Create some default active and pending trees. + gfx::Size tile_size(100, 100); + gfx::Size layer_bounds(400, 400); + + scoped_refptr<FakePicturePileImpl> pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + scoped_refptr<FakePicturePileImpl> active_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + + SetupTrees(pending_pile, active_pile); + } +}; + +// This test is really a LayerTreeHostImpl test, in that it makes sure +// that trees need update draw properties after deferred initialization. +// However, this is also a regression test for PictureLayerImpl in that +// not having this update will cause a crash. +TEST_F(DeferredInitPictureLayerImplTest, + PreventUpdateTilePrioritiesDuringLostContext) { + host_impl_.pending_tree()->UpdateDrawProperties(); + host_impl_.active_tree()->UpdateDrawProperties(); + EXPECT_FALSE(host_impl_.pending_tree()->needs_update_draw_properties()); + EXPECT_FALSE(host_impl_.active_tree()->needs_update_draw_properties()); + + FakeOutputSurface* fake_output_surface = + static_cast<FakeOutputSurface*>(host_impl_.output_surface()); + ASSERT_TRUE(fake_output_surface->InitializeAndSetContext3d( + TestContextProvider::Create(), NULL)); + + // These will crash PictureLayerImpl if this is not true. + ASSERT_TRUE(host_impl_.pending_tree()->needs_update_draw_properties()); + ASSERT_TRUE(host_impl_.active_tree()->needs_update_draw_properties()); + host_impl_.active_tree()->UpdateDrawProperties(); +} + } // namespace } // namespace cc diff --git a/chromium/cc/layers/picture_layer_unittest.cc b/chromium/cc/layers/picture_layer_unittest.cc new file mode 100644 index 00000000000..c3dd244c2ef --- /dev/null +++ b/chromium/cc/layers/picture_layer_unittest.cc @@ -0,0 +1,69 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/picture_layer.h" + +#include "cc/layers/content_layer_client.h" +#include "cc/layers/picture_layer_impl.h" +#include "cc/resources/resource_update_queue.h" +#include "cc/test/fake_layer_tree_host.h" +#include "cc/test/fake_picture_layer_impl.h" +#include "cc/test/fake_proxy.h" +#include "cc/test/impl_side_painting_settings.h" +#include "cc/trees/occlusion_tracker.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class MockContentLayerClient : public ContentLayerClient { + public: + virtual void PaintContents(SkCanvas* canvas, + gfx::Rect clip, + gfx::RectF* opaque) OVERRIDE {} + virtual void DidChangeLayerCanUseLCDText() OVERRIDE {} +}; + +TEST(PictureLayerTest, NoTilesIfEmptyBounds) { + MockContentLayerClient client; + scoped_refptr<PictureLayer> layer = PictureLayer::Create(&client); + layer->SetBounds(gfx::Size(10, 10)); + + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + host->SetRootLayer(layer); + layer->SetIsDrawable(true); + layer->SavePaintProperties(); + + OcclusionTracker occlusion(gfx::Rect(0, 0, 1000, 1000), false); + scoped_ptr<ResourceUpdateQueue> queue(new ResourceUpdateQueue); + layer->Update(queue.get(), &occlusion); + + layer->SetBounds(gfx::Size(0, 0)); + layer->SavePaintProperties(); + // Intentionally skipping Update since it would normally be skipped on + // a layer with empty bounds. + + FakeProxy proxy; +#ifndef NDEBUG + proxy.SetCurrentThreadIsImplThread(true); +#endif + { + FakeLayerTreeHostImpl host_impl(ImplSidePaintingSettings(), &proxy); + host_impl.CreatePendingTree(); + scoped_ptr<FakePictureLayerImpl> layer_impl = + FakePictureLayerImpl::Create(host_impl.pending_tree(), 1); + + layer->PushPropertiesTo(layer_impl.get()); + EXPECT_FALSE(layer_impl->CanHaveTilings()); + EXPECT_TRUE(layer_impl->bounds() == gfx::Size(0, 0)); + EXPECT_TRUE(layer_impl->pile()->size() == gfx::Size(0, 0)); + EXPECT_TRUE(layer_impl->pile()->recorded_region().IsEmpty()); + } +#ifndef NDEBUG + proxy.SetCurrentThreadIsImplThread(false); +#endif +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer.cc b/chromium/cc/layers/scrollbar_layer.cc deleted file mode 100644 index 95eb6384732..00000000000 --- a/chromium/cc/layers/scrollbar_layer.cc +++ /dev/null @@ -1,353 +0,0 @@ - -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/layers/scrollbar_layer.h" - -#include "base/auto_reset.h" -#include "base/basictypes.h" -#include "base/debug/trace_event.h" -#include "cc/layers/scrollbar_layer_impl.h" -#include "cc/resources/caching_bitmap_content_layer_updater.h" -#include "cc/resources/layer_painter.h" -#include "cc/resources/prioritized_resource.h" -#include "cc/resources/resource_update_queue.h" -#include "cc/trees/layer_tree_host.h" -#include "ui/gfx/rect_conversions.h" - -namespace cc { - -scoped_ptr<LayerImpl> ScrollbarLayer::CreateLayerImpl( - LayerTreeImpl* tree_impl) { - return ScrollbarLayerImpl::Create( - tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>(); -} - -scoped_refptr<ScrollbarLayer> ScrollbarLayer::Create( - scoped_ptr<Scrollbar> scrollbar, - int scroll_layer_id) { - return make_scoped_refptr(new ScrollbarLayer(scrollbar.Pass(), - scroll_layer_id)); -} - -ScrollbarLayer::ScrollbarLayer( - scoped_ptr<Scrollbar> scrollbar, - int scroll_layer_id) - : scrollbar_(scrollbar.Pass()), - scroll_layer_id_(scroll_layer_id), - texture_format_(GL_INVALID_ENUM) { - if (!scrollbar_->IsOverlay()) - SetShouldScrollOnMainThread(true); -} - -ScrollbarLayer::~ScrollbarLayer() {} - -void ScrollbarLayer::SetScrollLayerId(int id) { - if (id == scroll_layer_id_) - return; - - scroll_layer_id_ = id; - SetNeedsFullTreeSync(); -} - -bool ScrollbarLayer::OpacityCanAnimateOnImplThread() const { - return scrollbar_->IsOverlay(); -} - -ScrollbarOrientation ScrollbarLayer::Orientation() const { - return scrollbar_->Orientation(); -} - -int ScrollbarLayer::MaxTextureSize() { - DCHECK(layer_tree_host()); - return layer_tree_host()->GetRendererCapabilities().max_texture_size; -} - -float ScrollbarLayer::ClampScaleToMaxTextureSize(float scale) { - if (layer_tree_host()->settings().solid_color_scrollbars) - return scale; - - // If the scaled content_bounds() is bigger than the max texture size of the - // device, we need to clamp it by rescaling, since content_bounds() is used - // below to set the texture size. - gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale); - if (scaled_bounds.width() > MaxTextureSize() || - scaled_bounds.height() > MaxTextureSize()) { - if (scaled_bounds.width() > scaled_bounds.height()) - return (MaxTextureSize() - 1) / static_cast<float>(bounds().width()); - else - return (MaxTextureSize() - 1) / static_cast<float>(bounds().height()); - } - return scale; -} - -void ScrollbarLayer::CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - bool animating_transform_to_screen, - float* contents_scale_x, - float* contents_scale_y, - gfx::Size* content_bounds) { - ContentsScalingLayer::CalculateContentsScale( - ClampScaleToMaxTextureSize(ideal_contents_scale), - device_scale_factor, - page_scale_factor, - animating_transform_to_screen, - contents_scale_x, - contents_scale_y, - content_bounds); -} - -void ScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { - ContentsScalingLayer::PushPropertiesTo(layer); - - ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer); - - if (layer_tree_host() && - layer_tree_host()->settings().solid_color_scrollbars) { - int thickness_override = - layer_tree_host()->settings().solid_color_scrollbar_thickness_dip; - if (thickness_override != -1) { - scrollbar_layer->SetThumbThickness(thickness_override); - } else { - if (Orientation() == HORIZONTAL) - scrollbar_layer->SetThumbThickness(bounds().height()); - else - scrollbar_layer->SetThumbThickness(bounds().width()); - } - } else { - scrollbar_layer->SetThumbThickness(thumb_thickness_); - } - scrollbar_layer->SetThumbLength(thumb_length_); - if (Orientation() == HORIZONTAL) { - scrollbar_layer->SetTrackStart(track_rect_.x() - location_.x()); - scrollbar_layer->SetTrackLength(track_rect_.width()); - } else { - scrollbar_layer->SetTrackStart(track_rect_.y() - location_.y()); - scrollbar_layer->SetTrackLength(track_rect_.height()); - } - - if (track_ && track_->texture()->have_backing_texture()) - scrollbar_layer->set_track_resource_id(track_->texture()->resource_id()); - else - scrollbar_layer->set_track_resource_id(0); - - if (thumb_ && thumb_->texture()->have_backing_texture()) - scrollbar_layer->set_thumb_resource_id(thumb_->texture()->resource_id()); - else - scrollbar_layer->set_thumb_resource_id(0); - - scrollbar_layer->set_is_overlay_scrollbar(scrollbar_->IsOverlay()); - - // ScrollbarLayer must push properties every frame. crbug.com/259095 - needs_push_properties_ = true; -} - -ScrollbarLayer* ScrollbarLayer::ToScrollbarLayer() { - return this; -} - -void ScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { - if (!host || host != layer_tree_host()) { - track_updater_ = NULL; - track_.reset(); - thumb_updater_ = NULL; - thumb_.reset(); - } - - ContentsScalingLayer::SetLayerTreeHost(host); -} - -class ScrollbarPartPainter : public LayerPainter { - public: - ScrollbarPartPainter(Scrollbar* scrollbar, ScrollbarPart part) - : scrollbar_(scrollbar), - part_(part) {} - virtual ~ScrollbarPartPainter() {} - - // LayerPainter implementation - virtual void Paint(SkCanvas* canvas, - gfx::Rect content_rect, - gfx::RectF* opaque) OVERRIDE { - scrollbar_->PaintPart(canvas, part_, content_rect); - } - - private: - Scrollbar* scrollbar_; - ScrollbarPart part_; -}; - -void ScrollbarLayer::CreateUpdaterIfNeeded() { - if (layer_tree_host()->settings().solid_color_scrollbars) - return; - - texture_format_ = - layer_tree_host()->GetRendererCapabilities().best_texture_format; - - if (!track_updater_.get()) { - track_updater_ = CachingBitmapContentLayerUpdater::Create( - scoped_ptr<LayerPainter>( - new ScrollbarPartPainter(scrollbar_.get(), TRACK)) - .Pass(), - rendering_stats_instrumentation(), - id()); - } - if (!track_) { - track_ = track_updater_->CreateResource( - layer_tree_host()->contents_texture_manager()); - } - - if (!thumb_updater_.get()) { - thumb_updater_ = CachingBitmapContentLayerUpdater::Create( - scoped_ptr<LayerPainter>( - new ScrollbarPartPainter(scrollbar_.get(), THUMB)) - .Pass(), - rendering_stats_instrumentation(), - id()); - } - if (!thumb_ && scrollbar_->HasThumb()) { - thumb_ = thumb_updater_->CreateResource( - layer_tree_host()->contents_texture_manager()); - } -} - -bool ScrollbarLayer::UpdatePart(CachingBitmapContentLayerUpdater* painter, - LayerUpdater::Resource* resource, - gfx::Rect rect, - ResourceUpdateQueue* queue) { - if (layer_tree_host()->settings().solid_color_scrollbars) - return false; - - // Skip painting and uploading if there are no invalidations and - // we already have valid texture data. - if (resource->texture()->have_backing_texture() && - resource->texture()->size() == rect.size() && - !is_dirty()) - return false; - - // We should always have enough memory for UI. - DCHECK(resource->texture()->can_acquire_backing_texture()); - if (!resource->texture()->can_acquire_backing_texture()) - return false; - - // Paint and upload the entire part. - gfx::Rect painted_opaque_rect; - painter->PrepareToUpdate(rect, - rect.size(), - contents_scale_x(), - contents_scale_y(), - &painted_opaque_rect); - if (!painter->pixels_did_change() && - resource->texture()->have_backing_texture()) { - TRACE_EVENT_INSTANT0("cc", - "ScrollbarLayer::UpdatePart no texture upload needed", - TRACE_EVENT_SCOPE_THREAD); - return false; - } - - bool partial_updates_allowed = - layer_tree_host()->settings().max_partial_texture_updates > 0; - if (!partial_updates_allowed) - resource->texture()->ReturnBackingTexture(); - - gfx::Vector2d dest_offset(0, 0); - resource->Update(queue, rect, dest_offset, partial_updates_allowed); - return true; -} - -gfx::Rect ScrollbarLayer::ScrollbarLayerRectToContentRect( - gfx::Rect layer_rect) const { - // Don't intersect with the bounds as in LayerRectToContentRect() because - // layer_rect here might be in coordinates of the containing layer. - gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect( - layer_rect, contents_scale_y(), contents_scale_y()); - // We should never return a rect bigger than the content_bounds(). - gfx::Size clamped_size = expanded_rect.size(); - clamped_size.SetToMin(content_bounds()); - expanded_rect.set_size(clamped_size); - return expanded_rect; -} - -void ScrollbarLayer::SetTexturePriorities( - const PriorityCalculator& priority_calc) { - if (layer_tree_host()->settings().solid_color_scrollbars) - return; - - if (content_bounds().IsEmpty()) - return; - DCHECK_LE(content_bounds().width(), MaxTextureSize()); - DCHECK_LE(content_bounds().height(), MaxTextureSize()); - - CreateUpdaterIfNeeded(); - - bool draws_to_root = !render_target()->parent(); - if (track_) { - track_->texture()->SetDimensions(content_bounds(), texture_format_); - track_->texture()->set_request_priority( - PriorityCalculator::UIPriority(draws_to_root)); - } - if (thumb_) { - gfx::Size thumb_size = OriginThumbRect().size(); - thumb_->texture()->SetDimensions(thumb_size, texture_format_); - thumb_->texture()->set_request_priority( - PriorityCalculator::UIPriority(draws_to_root)); - } -} - -bool ScrollbarLayer::Update(ResourceUpdateQueue* queue, - const OcclusionTracker* occlusion) { - track_rect_ = scrollbar_->TrackRect(); - location_ = scrollbar_->Location(); - - if (layer_tree_host()->settings().solid_color_scrollbars) - return false; - - bool updated = false; - - { - base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_, - true); - updated = ContentsScalingLayer::Update(queue, occlusion); - } - - dirty_rect_.Union(update_rect_); - if (content_bounds().IsEmpty()) - return false; - if (visible_content_rect().IsEmpty()) - return false; - - CreateUpdaterIfNeeded(); - - gfx::Rect content_rect = ScrollbarLayerRectToContentRect( - gfx::Rect(scrollbar_->Location(), bounds())); - updated |= UpdatePart(track_updater_.get(), track_.get(), content_rect, - queue); - - if (scrollbar_->HasThumb()) { - thumb_thickness_ = scrollbar_->ThumbThickness(); - thumb_length_ = scrollbar_->ThumbLength(); - gfx::Rect origin_thumb_rect = OriginThumbRect(); - if (!origin_thumb_rect.IsEmpty()) { - updated |= UpdatePart(thumb_updater_.get(), thumb_.get(), - origin_thumb_rect, queue); - } - } - - dirty_rect_ = gfx::RectF(); - return updated; -} - -gfx::Rect ScrollbarLayer::OriginThumbRect() const { - gfx::Size thumb_size; - if (Orientation() == HORIZONTAL) { - thumb_size = gfx::Size(scrollbar_->ThumbLength(), - scrollbar_->ThumbThickness()); - } else { - thumb_size = gfx::Size(scrollbar_->ThumbThickness(), - scrollbar_->ThumbLength()); - } - return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size)); -} - -} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_impl.cc b/chromium/cc/layers/scrollbar_layer_impl.cc deleted file mode 100644 index 1de185a77dd..00000000000 --- a/chromium/cc/layers/scrollbar_layer_impl.cc +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/layers/scrollbar_layer_impl.h" - -#include <algorithm> - -#include "cc/animation/scrollbar_animation_controller.h" -#include "cc/layers/layer.h" -#include "cc/layers/quad_sink.h" -#include "cc/quads/solid_color_draw_quad.h" -#include "cc/quads/texture_draw_quad.h" -#include "cc/trees/layer_tree_impl.h" -#include "cc/trees/layer_tree_settings.h" -#include "ui/gfx/rect_conversions.h" - -namespace cc { - -scoped_ptr<ScrollbarLayerImpl> ScrollbarLayerImpl::Create( - LayerTreeImpl* tree_impl, - int id, - ScrollbarOrientation orientation) { - return make_scoped_ptr(new ScrollbarLayerImpl(tree_impl, - id, - orientation)); -} - -ScrollbarLayerImpl::ScrollbarLayerImpl( - LayerTreeImpl* tree_impl, - int id, - ScrollbarOrientation orientation) - : LayerImpl(tree_impl, id), - track_resource_id_(0), - thumb_resource_id_(0), - current_pos_(0.f), - maximum_(0), - thumb_thickness_(0), - thumb_length_(0), - track_start_(0), - track_length_(0), - orientation_(orientation), - vertical_adjust_(0.f), - visible_to_total_length_ratio_(1.f), - scroll_layer_id_(Layer::INVALID_ID), - is_overlay_scrollbar_(false) {} - -ScrollbarLayerImpl::~ScrollbarLayerImpl() {} - -ScrollbarLayerImpl* ScrollbarLayerImpl::ToScrollbarLayer() { - return this; -} - -scoped_ptr<LayerImpl> ScrollbarLayerImpl::CreateLayerImpl( - LayerTreeImpl* tree_impl) { - return ScrollbarLayerImpl::Create(tree_impl, - id(), - orientation_).PassAs<LayerImpl>(); -} - -void ScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { - LayerImpl::PushPropertiesTo(layer); - - ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer); - - scrollbar_layer->SetThumbThickness(thumb_thickness_); - scrollbar_layer->SetThumbLength(thumb_length_); - scrollbar_layer->SetTrackStart(track_start_); - scrollbar_layer->SetTrackLength(track_length_); - scrollbar_layer->set_is_overlay_scrollbar(is_overlay_scrollbar_); - - scrollbar_layer->set_track_resource_id(track_resource_id_); - scrollbar_layer->set_thumb_resource_id(thumb_resource_id_); -} - -bool ScrollbarLayerImpl::WillDraw(DrawMode draw_mode, - ResourceProvider* resource_provider) { - if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE && - !layer_tree_impl()->settings().solid_color_scrollbars) - return false; - return LayerImpl::WillDraw(draw_mode, resource_provider); -} - -void ScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink, - AppendQuadsData* append_quads_data) { - bool premultipled_alpha = true; - bool flipped = false; - gfx::PointF uv_top_left(0.f, 0.f); - gfx::PointF uv_bottom_right(1.f, 1.f); - gfx::Rect bounds_rect(bounds()); - gfx::Rect content_bounds_rect(content_bounds()); - - SharedQuadState* shared_quad_state = - quad_sink->UseSharedQuadState(CreateSharedQuadState()); - AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); - - gfx::Rect thumb_quad_rect = ComputeThumbQuadRect(); - - if (layer_tree_impl()->settings().solid_color_scrollbars) { - scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); - quad->SetNew(shared_quad_state, - thumb_quad_rect, - layer_tree_impl()->settings().solid_color_scrollbar_color, - false); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); - return; - } - - if (thumb_resource_id_ && !thumb_quad_rect.IsEmpty()) { - gfx::Rect opaque_rect; - const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; - scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); - quad->SetNew(shared_quad_state, - thumb_quad_rect, - opaque_rect, - thumb_resource_id_, - premultipled_alpha, - uv_top_left, - uv_bottom_right, - SK_ColorTRANSPARENT, - opacity, - flipped); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); - } - - if (!track_resource_id_) - return; - - // Order matters here: since the back track texture is being drawn to the - // entire contents rect, we must append it after the thumb and fore track - // quads. The back track texture contains (and displays) the buttons. - if (!content_bounds_rect.IsEmpty()) { - gfx::Rect quad_rect(content_bounds_rect); - gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect()); - const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; - scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); - quad->SetNew(shared_quad_state, - quad_rect, - opaque_rect, - track_resource_id_, - premultipled_alpha, - uv_top_left, - uv_bottom_right, - SK_ColorTRANSPARENT, - opacity, - flipped); - quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); - } -} - -ScrollbarOrientation ScrollbarLayerImpl::Orientation() const { - return orientation_; -} - -float ScrollbarLayerImpl::CurrentPos() const { - return current_pos_; -} - -int ScrollbarLayerImpl::Maximum() const { - return maximum_; -} - -gfx::Rect ScrollbarLayerImpl::ScrollbarLayerRectToContentRect( - gfx::RectF layer_rect) const { - // Don't intersect with the bounds as in layerRectToContentRect() because - // layer_rect here might be in coordinates of the containing layer. - gfx::RectF content_rect = gfx::ScaleRect(layer_rect, - contents_scale_x(), - contents_scale_y()); - return gfx::ToEnclosingRect(content_rect); -} - -void ScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) { - if (thumb_thickness_ == thumb_thickness) - return; - thumb_thickness_ = thumb_thickness; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetThumbLength(int thumb_length) { - if (thumb_length_ == thumb_length) - return; - thumb_length_ = thumb_length; - NoteLayerPropertyChanged(); -} -void ScrollbarLayerImpl::SetTrackStart(int track_start) { - if (track_start_ == track_start) - return; - track_start_ = track_start; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetTrackLength(int track_length) { - if (track_length_ == track_length) - return; - track_length_ = track_length; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetVerticalAdjust(float vertical_adjust) { - if (vertical_adjust_ == vertical_adjust) - return; - vertical_adjust_ = vertical_adjust; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetVisibleToTotalLengthRatio(float ratio) { - if (visible_to_total_length_ratio_ == ratio) - return; - visible_to_total_length_ratio_ = ratio; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetCurrentPos(float current_pos) { - if (current_pos_ == current_pos) - return; - current_pos_ = current_pos; - NoteLayerPropertyChanged(); -} - -void ScrollbarLayerImpl::SetMaximum(int maximum) { - if (maximum_ == maximum) - return; - maximum_ = maximum; - NoteLayerPropertyChanged(); -} - -gfx::Rect ScrollbarLayerImpl::ComputeThumbQuadRect() const { - // Thumb extent is the length of the thumb in the scrolling direction, thumb - // thickness is in the perpendicular direction. Here's an example of a - // horizontal scrollbar - inputs are above the scrollbar, computed values - // below: - // - // |<------------------- track_length_ ------------------->| - // - // |--| <-- start_offset - // - // +--+----------------------------+------------------+-------+--+ - // |<|| |##################| ||>| - // +--+----------------------------+------------------+-------+--+ - // - // |<- thumb_length ->| - // - // |<------- thumb_offset -------->| - // - // For painted, scrollbars, the length is fixed. For solid color scrollbars we - // have to compute it. The ratio of the thumb's length to the track's length - // is the same as that of the visible viewport to the total viewport, unless - // that would make the thumb's length less than its thickness. - // - // vertical_adjust_ is used when the layer geometry from the main thread is - // not in sync with what the user sees. For instance on Android scrolling the - // top bar controls out of view reveals more of the page content. We want the - // root layer scrollbars to reflect what the user sees even if we haven't - // received new layer geometry from the main thread. If the user has scrolled - // down by 50px and the initial viewport size was 950px the geometry would - // look something like this: - // - // vertical_adjust_ = 50, scroll position 0, visible ratios 99% - // Layer geometry: Desired thumb positions: - // +--------------------+-+ +----------------------+ <-- 0px - // | |v| | #| - // | |e| | #| - // | |r| | #| - // | |t| | #| - // | |i| | #| - // | |c| | #| - // | |a| | #| - // | |l| | #| - // | | | | #| - // | |l| | #| - // | |a| | #| - // | |y| | #| - // | |e| | #| - // | |r| | #| - // +--------------------+-+ | #| - // | horizontal layer | | | #| - // +--------------------+-+ | #| <-- 950px - // | | | #| - // | | |##################### | - // +----------------------+ +----------------------+ <-- 1000px - // - // The layer geometry is set up for a 950px tall viewport, but the user can - // actually see down to 1000px. Thus we have to move the quad for the - // horizontal scrollbar down by the vertical_adjust_ factor and lay the - // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This - // means the quads may extend outside the layer's bounds. - - int thumb_length = thumb_length_; - float track_length = track_length_; - if (orientation_ == VERTICAL) - track_length += vertical_adjust_; - - if (layer_tree_impl()->settings().solid_color_scrollbars) { - thumb_length = std::max( - static_cast<int>(visible_to_total_length_ratio_ * track_length), - thumb_thickness_); - } - - // With the length known, we can compute the thumb's position. - float clamped_current_pos = - std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_)); - float ratio = clamped_current_pos / maximum_; - float max_offset = track_length - thumb_length; - int thumb_offset = static_cast<int>(ratio * max_offset) + track_start_; - - gfx::RectF thumb_rect; - if (orientation_ == HORIZONTAL) { - thumb_rect = gfx::RectF(thumb_offset, vertical_adjust_, - thumb_length, thumb_thickness_); - } else { - thumb_rect = gfx::RectF(0.f, thumb_offset, - thumb_thickness_, thumb_length); - } - - return ScrollbarLayerRectToContentRect(thumb_rect); -} - -void ScrollbarLayerImpl::DidLoseOutputSurface() { - track_resource_id_ = 0; - thumb_resource_id_ = 0; -} - -const char* ScrollbarLayerImpl::LayerTypeAsString() const { - return "cc::ScrollbarLayerImpl"; -} - -} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_impl.h b/chromium/cc/layers/scrollbar_layer_impl.h deleted file mode 100644 index 6347c413dfc..00000000000 --- a/chromium/cc/layers/scrollbar_layer_impl.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_ -#define CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_ - -#include "cc/base/cc_export.h" -#include "cc/input/scrollbar.h" -#include "cc/layers/layer_impl.h" - -namespace cc { - -class LayerTreeImpl; -class ScrollView; - -class CC_EXPORT ScrollbarLayerImpl : public LayerImpl { - public: - static scoped_ptr<ScrollbarLayerImpl> Create( - LayerTreeImpl* tree_impl, - int id, - ScrollbarOrientation orientation); - virtual ~ScrollbarLayerImpl(); - - // LayerImpl implementation. - virtual ScrollbarLayerImpl* ToScrollbarLayer() OVERRIDE; - virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) - OVERRIDE; - virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; - - virtual bool WillDraw(DrawMode draw_mode, - ResourceProvider* resource_provider) OVERRIDE; - virtual void AppendQuads(QuadSink* quad_sink, - AppendQuadsData* append_quads_data) OVERRIDE; - - virtual void DidLoseOutputSurface() OVERRIDE; - - int scroll_layer_id() const { return scroll_layer_id_; } - void set_scroll_layer_id(int id) { scroll_layer_id_ = id; } - - ScrollbarOrientation Orientation() const; - float CurrentPos() const; - int Maximum() const; - - void SetThumbThickness(int thumb_thickness); - int thumb_thickness() const { return thumb_thickness_; } - void SetThumbLength(int thumb_length); - void SetTrackStart(int track_start); - void SetTrackLength(int track_length); - void SetVerticalAdjust(float vertical_adjust); - void set_track_resource_id(ResourceProvider::ResourceId id) { - track_resource_id_ = id; - } - void set_thumb_resource_id(ResourceProvider::ResourceId id) { - thumb_resource_id_ = id; - } - void SetVisibleToTotalLengthRatio(float ratio); - void set_is_overlay_scrollbar(bool is_overlay_scrollbar) { - is_overlay_scrollbar_ = is_overlay_scrollbar; - } - bool is_overlay_scrollbar() const { return is_overlay_scrollbar_; } - - void SetCurrentPos(float current_pos); - void SetMaximum(int maximum); - - gfx::Rect ComputeThumbQuadRect() const; - - protected: - ScrollbarLayerImpl(LayerTreeImpl* tree_impl, - int id, - ScrollbarOrientation orientation); - - private: - virtual const char* LayerTypeAsString() const OVERRIDE; - - gfx::Rect ScrollbarLayerRectToContentRect(gfx::RectF layer_rect) const; - - ResourceProvider::ResourceId track_resource_id_; - ResourceProvider::ResourceId thumb_resource_id_; - - float current_pos_; - int maximum_; - int thumb_thickness_; - int thumb_length_; - int track_start_; - int track_length_; - ScrollbarOrientation orientation_; - - // Difference between the clip layer's height and the visible viewport - // height (which may differ in the presence of top-controls hiding). - float vertical_adjust_; - - float visible_to_total_length_ratio_; - - int scroll_layer_id_; - - bool is_overlay_scrollbar_; - - DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerImpl); -}; - -} // namespace cc -#endif // CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_ diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.cc b/chromium/cc/layers/scrollbar_layer_impl_base.cc new file mode 100644 index 00000000000..c1e7126ff52 --- /dev/null +++ b/chromium/cc/layers/scrollbar_layer_impl_base.cc @@ -0,0 +1,170 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/scrollbar_layer_impl_base.h" + +#include <algorithm> +#include "cc/layers/layer.h" +#include "ui/gfx/rect_conversions.h" + +namespace cc { + +ScrollbarLayerImplBase::ScrollbarLayerImplBase( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + bool is_left_side_vertical_scrollbar) + : LayerImpl(tree_impl, id), + scroll_layer_id_(Layer::INVALID_ID), + is_overlay_scrollbar_(false), + thumb_thickness_scale_factor_(1.f), + current_pos_(0.f), + maximum_(0), + orientation_(orientation), + is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar), + vertical_adjust_(0.f), + visible_to_total_length_ratio_(1.f) {} + +void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) { + LayerImpl::PushPropertiesTo(layer); +} + +ScrollbarLayerImplBase* ScrollbarLayerImplBase::ToScrollbarLayer() { + return this; +} + +gfx::Rect ScrollbarLayerImplBase::ScrollbarLayerRectToContentRect( + gfx::RectF layer_rect) const { + // Don't intersect with the bounds as in LayerRectToContentRect() because + // layer_rect here might be in coordinates of the containing layer. + gfx::RectF content_rect = gfx::ScaleRect(layer_rect, + contents_scale_x(), + contents_scale_y()); + return gfx::ToEnclosingRect(content_rect); +} + +void ScrollbarLayerImplBase::SetCurrentPos(float current_pos) { + if (current_pos_ == current_pos) + return; + current_pos_ = current_pos; + NoteLayerPropertyChanged(); +} + +void ScrollbarLayerImplBase::SetMaximum(int maximum) { + if (maximum_ == maximum) + return; + maximum_ = maximum; + NoteLayerPropertyChanged(); +} + +void ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) { + if (vertical_adjust_ == vertical_adjust) + return; + vertical_adjust_ = vertical_adjust; + NoteLayerPropertyChanged(); +} + +void ScrollbarLayerImplBase::SetVisibleToTotalLengthRatio(float ratio) { + if (visible_to_total_length_ratio_ == ratio) + return; + visible_to_total_length_ratio_ = ratio; + NoteLayerPropertyChanged(); +} + +gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const { + // Thumb extent is the length of the thumb in the scrolling direction, thumb + // thickness is in the perpendicular direction. Here's an example of a + // horizontal scrollbar - inputs are above the scrollbar, computed values + // below: + // + // |<------------------- track_length_ ------------------->| + // + // |--| <-- start_offset + // + // +--+----------------------------+------------------+-------+--+ + // |<|| |##################| ||>| + // +--+----------------------------+------------------+-------+--+ + // + // |<- thumb_length ->| + // + // |<------- thumb_offset -------->| + // + // For painted, scrollbars, the length is fixed. For solid color scrollbars we + // have to compute it. The ratio of the thumb's length to the track's length + // is the same as that of the visible viewport to the total viewport, unless + // that would make the thumb's length less than its thickness. + // + // vertical_adjust_ is used when the layer geometry from the main thread is + // not in sync with what the user sees. For instance on Android scrolling the + // top bar controls out of view reveals more of the page content. We want the + // root layer scrollbars to reflect what the user sees even if we haven't + // received new layer geometry from the main thread. If the user has scrolled + // down by 50px and the initial viewport size was 950px the geometry would + // look something like this: + // + // vertical_adjust_ = 50, scroll position 0, visible ratios 99% + // Layer geometry: Desired thumb positions: + // +--------------------+-+ +----------------------+ <-- 0px + // | |v| | #| + // | |e| | #| + // | |r| | #| + // | |t| | #| + // | |i| | #| + // | |c| | #| + // | |a| | #| + // | |l| | #| + // | | | | #| + // | |l| | #| + // | |a| | #| + // | |y| | #| + // | |e| | #| + // | |r| | #| + // +--------------------+-+ | #| + // | horizontal layer | | | #| + // +--------------------+-+ | #| <-- 950px + // | | | #| + // | | |##################### | + // +----------------------+ +----------------------+ <-- 1000px + // + // The layer geometry is set up for a 950px tall viewport, but the user can + // actually see down to 1000px. Thus we have to move the quad for the + // horizontal scrollbar down by the vertical_adjust_ factor and lay the + // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This + // means the quads may extend outside the layer's bounds. + + // With the length known, we can compute the thumb's position. + float track_length = TrackLength(); + int thumb_length = ThumbLength(); + int thumb_thickness = ThumbThickness(); + + // With the length known, we can compute the thumb's position. + float clamped_current_pos = + std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_)); + float ratio = clamped_current_pos / maximum_; + float max_offset = track_length - thumb_length; + int thumb_offset = static_cast<int>(ratio * max_offset) + TrackStart(); + + float thumb_thickness_adjustment = + thumb_thickness * (1.f - thumb_thickness_scale_factor_); + + gfx::RectF thumb_rect; + if (orientation_ == HORIZONTAL) { + thumb_rect = gfx::RectF(thumb_offset, + vertical_adjust_ + thumb_thickness_adjustment, + thumb_length, + thumb_thickness - thumb_thickness_adjustment); + } else { + thumb_rect = gfx::RectF( + is_left_side_vertical_scrollbar_ + ? bounds().width() - thumb_thickness + : thumb_thickness_adjustment, + thumb_offset, + thumb_thickness - thumb_thickness_adjustment, + thumb_length); + } + + return ScrollbarLayerRectToContentRect(thumb_rect); +} + +} // namespace cc diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.h b/chromium/cc/layers/scrollbar_layer_impl_base.h new file mode 100644 index 00000000000..55ade6ab04f --- /dev/null +++ b/chromium/cc/layers/scrollbar_layer_impl_base.h @@ -0,0 +1,91 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_ +#define CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_ + +#include "cc/base/cc_export.h" +#include "cc/input/scrollbar.h" +#include "cc/layers/layer_impl.h" + +namespace cc { + +class LayerTreeImpl; + +class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl { + public: + int ScrollLayerId() const { return scroll_layer_id_; } + void set_scroll_layer_id(int id) { scroll_layer_id_ = id; } + + float current_pos() const { return current_pos_; } + void SetCurrentPos(float current_pos); + int maximum() const { return maximum_; } + void SetMaximum(int maximum); + + void SetVerticalAdjust(float vertical_adjust); + + bool is_overlay_scrollbar() const { return is_overlay_scrollbar_; } + void set_is_overlay_scrollbar(bool is_overlay) { + is_overlay_scrollbar_ = is_overlay; + } + + ScrollbarOrientation orientation() const { return orientation_; } + bool is_left_side_vertical_scrollbar() { + return is_left_side_vertical_scrollbar_; + } + + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + virtual ScrollbarLayerImplBase* ToScrollbarLayer() OVERRIDE; + + void SetVisibleToTotalLengthRatio(float ratio); + virtual gfx::Rect ComputeThumbQuadRect() const; + + float thumb_thickness_scale_factor() { + return thumb_thickness_scale_factor_; + } + void set_thumb_thickness_scale_factor(float thumb_thickness_scale_factor) { + thumb_thickness_scale_factor_ = thumb_thickness_scale_factor; + } + + protected: + ScrollbarLayerImplBase(LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + bool is_left_side_vertical_scrollbar); + virtual ~ScrollbarLayerImplBase() {} + + gfx::Rect ScrollbarLayerRectToContentRect(gfx::RectF layer_rect) const; + + float visible_to_total_length_ratio() const { + return visible_to_total_length_ratio_; + } + float vertical_adjust() const { return vertical_adjust_; } + + virtual int ThumbThickness() const = 0; + virtual int ThumbLength() const = 0; + virtual float TrackLength() const = 0; + virtual int TrackStart() const = 0; + + private: + int scroll_layer_id_; + bool is_overlay_scrollbar_; + + float thumb_thickness_scale_factor_; + float current_pos_; + int maximum_; + ScrollbarOrientation orientation_; + bool is_left_side_vertical_scrollbar_; + + // Difference between the clip layer's height and the visible viewport + // height (which may differ in the presence of top-controls hiding). + float vertical_adjust_; + + float visible_to_total_length_ratio_; + + DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerImplBase); +}; + +} // namespace cc + +#endif // CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_ diff --git a/chromium/cc/layers/scrollbar_layer_interface.h b/chromium/cc/layers/scrollbar_layer_interface.h new file mode 100644 index 00000000000..bbb0da6ae2b --- /dev/null +++ b/chromium/cc/layers/scrollbar_layer_interface.h @@ -0,0 +1,30 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_ +#define CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_ + +#include "cc/base/cc_export.h" +#include "cc/input/scrollbar.h" + +namespace cc { + +class CC_EXPORT ScrollbarLayerInterface { + public: + virtual int ScrollLayerId() const = 0; + virtual void SetScrollLayerId(int id) = 0; + + virtual ScrollbarOrientation orientation() const = 0; + + protected: + ScrollbarLayerInterface() {} + virtual ~ScrollbarLayerInterface() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerInterface); +}; + +} // namespace cc + +#endif // CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_ diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc index b7981041f09..2e1b4b6f483 100644 --- a/chromium/cc/layers/scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/scrollbar_layer_unittest.cc @@ -2,24 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "cc/layers/scrollbar_layer.h" - +#include "base/containers/hash_tables.h" #include "cc/animation/scrollbar_animation_controller.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/layers/append_quads_data.h" -#include "cc/layers/scrollbar_layer_impl.h" +#include "cc/layers/painted_scrollbar_layer.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" +#include "cc/layers/scrollbar_layer_interface.h" +#include "cc/layers/solid_color_scrollbar_layer.h" +#include "cc/layers/solid_color_scrollbar_layer_impl.h" #include "cc/quads/solid_color_draw_quad.h" -#include "cc/resources/prioritized_resource_manager.h" -#include "cc/resources/priority_calculator.h" #include "cc/resources/resource_update_queue.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/fake_painted_scrollbar_layer.h" #include "cc/test/fake_scrollbar.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_tree_test.h" #include "cc/test/mock_quad_culler.h" -#include "cc/test/test_web_graphics_context_3d.h" +#include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" #include "cc/trees/tree_synchronizer.h" @@ -32,12 +35,20 @@ namespace { LayerImpl* LayerImplForScrollAreaAndScrollbar( FakeLayerTreeHost* host, scoped_ptr<Scrollbar> scrollbar, - bool reverse_order) { + bool reverse_order, + bool use_solid_color_scrollbar, + int thumb_thickness) { scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> child1 = Layer::Create(); - scoped_refptr<Layer> child2 = - ScrollbarLayer::Create(scrollbar.Pass(), - child1->id()); + scoped_refptr<Layer> child2; + if (use_solid_color_scrollbar) { + const bool kIsLeftSideVerticalScrollbar = false; + child2 = SolidColorScrollbarLayer::Create( + scrollbar->Orientation(), thumb_thickness, + kIsLeftSideVerticalScrollbar, child1->id()); + } else { + child2 = PaintedScrollbarLayer::Create(scrollbar.Pass(), child1->id()); + } layer_tree_root->AddChild(child1); layer_tree_root->InsertChild(child2, reverse_order ? 0 : 1); host->SetRootLayer(layer_tree_root); @@ -47,12 +58,13 @@ LayerImpl* LayerImplForScrollAreaAndScrollbar( TEST(ScrollbarLayerTest, ResolveScrollLayerPointer) { scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, false, 0); LayerImpl* cc_child1 = layer_impl_tree_root->children()[0]; - ScrollbarLayerImpl* cc_child2 = static_cast<ScrollbarLayerImpl*>( - layer_impl_tree_root->children()[1]); + PaintedScrollbarLayerImpl* cc_child2 = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); EXPECT_EQ(cc_child1->horizontal_scrollbar_layer(), cc_child2); } @@ -60,11 +72,12 @@ TEST(ScrollbarLayerTest, ResolveScrollLayerPointer) { TEST(ScrollbarLayerTest, ResolveScrollLayerPointer_ReverseOrder) { scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), true); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), true, false, 0); - ScrollbarLayerImpl* cc_child1 = static_cast<ScrollbarLayerImpl*>( - layer_impl_tree_root->children()[0]); + PaintedScrollbarLayerImpl* cc_child1 = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[0]); LayerImpl* cc_child2 = layer_impl_tree_root->children()[1]; EXPECT_EQ(cc_child2->horizontal_scrollbar_layer(), cc_child1); @@ -75,10 +88,11 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) { // Create and attach a non-overlay scrollbar. scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); - ScrollbarLayerImpl* scrollbar_layer_impl = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, false, 0); + PaintedScrollbarLayerImpl* scrollbar_layer_impl = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); // When the scrollbar is not an overlay scrollbar, the scroll should be // responded to on the main thread as the compositor does not yet implement @@ -90,10 +104,10 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) { // Create and attach an overlay scrollbar. scrollbar.reset(new FakeScrollbar(false, false, true)); - layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); - scrollbar_layer_impl = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); + layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, false, 0); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); // The user shouldn't be able to drag an overlay scrollbar and the scroll // may be handled in the compositor. @@ -102,15 +116,14 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) { InputHandler::Gesture)); } -TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) { +TEST(PaintedScrollbarLayerTest, ScrollOffsetSynchronization) { scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); scoped_refptr<Layer> scrollbar_layer = - ScrollbarLayer::Create(scrollbar.Pass(), - layer_tree_root->id()); + PaintedScrollbarLayer::Create(scrollbar.Pass(), layer_tree_root->id()); layer_tree_root->SetScrollable(true); layer_tree_root->SetScrollOffset(gfx::Vector2d(10, 20)); @@ -127,11 +140,12 @@ TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) { LayerImpl* layer_impl_tree_root = host->CommitAndCreateLayerImplTree(); - ScrollbarLayerImpl* cc_scrollbar_layer = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); + ScrollbarLayerImplBase* cc_scrollbar_layer = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); - EXPECT_EQ(10.f, cc_scrollbar_layer->CurrentPos()); - EXPECT_EQ(30, cc_scrollbar_layer->Maximum()); + EXPECT_EQ(10.f, cc_scrollbar_layer->current_pos()); + EXPECT_EQ(30, cc_scrollbar_layer->maximum()); layer_tree_root->SetScrollOffset(gfx::Vector2d(100, 200)); layer_tree_root->SetMaxScrollOffset(gfx::Vector2d(300, 500)); @@ -146,31 +160,124 @@ TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) { EXPECT_EQ(scrollbar_controller, layer_impl_tree_root->scrollbar_animation_controller()); - EXPECT_EQ(100.f, cc_scrollbar_layer->CurrentPos()); - EXPECT_EQ(300, cc_scrollbar_layer->Maximum()); + EXPECT_EQ(100.f, cc_scrollbar_layer->current_pos()); + EXPECT_EQ(300, cc_scrollbar_layer->maximum()); layer_impl_tree_root->ScrollBy(gfx::Vector2d(12, 34)); - EXPECT_EQ(112.f, cc_scrollbar_layer->CurrentPos()); - EXPECT_EQ(300, cc_scrollbar_layer->Maximum()); + EXPECT_EQ(112.f, cc_scrollbar_layer->current_pos()); + EXPECT_EQ(300, cc_scrollbar_layer->maximum()); +} + +TEST(ScrollbarLayerTest, ThumbRect) { + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + scoped_refptr<Layer> root_layer = Layer::Create(); + scoped_refptr<Layer> content_layer = Layer::Create(); + scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = + FakePaintedScrollbarLayer::Create(false, true, root_layer->id()); + + root_layer->SetScrollable(true); + root_layer->SetMaxScrollOffset(gfx::Vector2d(80, 0)); + root_layer->SetBounds(gfx::Size(100, 50)); + content_layer->SetBounds(gfx::Size(100, 50)); + + host->SetRootLayer(root_layer); + root_layer->AddChild(content_layer); + root_layer->AddChild(scrollbar_layer); + + root_layer->SetScrollOffset(gfx::Vector2d(0, 0)); + scrollbar_layer->SetBounds(gfx::Size(70, 10)); + scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10)); + scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10)); + scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10); + scrollbar_layer->fake_scrollbar()->set_thumb_length(4); + scrollbar_layer->UpdateThumbAndTrackGeometry(); + LayerImpl* root_layer_impl = NULL; + PaintedScrollbarLayerImpl* scrollbar_layer_impl = NULL; + + // Thumb is at the edge of the scrollbar (should be inset to + // the start of the track within the scrollbar layer's + // position). + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Under-scroll (thumb position should clamp and be unchanged). + root_layer->SetScrollOffset(gfx::Vector2d(-5, 0)); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Over-scroll (thumb position should clamp on the far side). + root_layer->SetScrollOffset(gfx::Vector2d(85, 0)); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(56, 0, 4, 10).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Change thumb thickness and length. + scrollbar_layer->fake_scrollbar()->set_thumb_thickness(4); + scrollbar_layer->fake_scrollbar()->set_thumb_length(6); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(54, 0, 6, 4).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Shrink the scrollbar layer to cover only the track. + scrollbar_layer->SetBounds(gfx::Size(50, 10)); + scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(30, 10)); + scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10)); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); + + // Shrink the track in the non-scrolling dimension so that it only covers the + // middle third of the scrollbar layer (this does not affect the thumb + // position). + scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 12, 50, 6)); + + scrollbar_layer->UpdateThumbAndTrackGeometry(); + root_layer_impl = host->CommitAndCreateLayerImplTree(); + scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( + root_layer_impl->children()[1]); + EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(), + scrollbar_layer_impl->ComputeThumbQuadRect().ToString()); } TEST(ScrollbarLayerTest, SolidColorDrawQuads) { + const int kThumbThickness = 3; + const int kTrackLength = 100; + LayerTreeSettings layer_tree_settings; - layer_tree_settings.solid_color_scrollbars = true; - layer_tree_settings.solid_color_scrollbar_thickness_dip = 3; scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(layer_tree_settings); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true)); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); - ScrollbarLayerImpl* scrollbar_layer_impl = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); - scrollbar_layer_impl->SetThumbThickness(3); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, true, kThumbThickness); + ScrollbarLayerImplBase* scrollbar_layer_impl = + static_cast<SolidColorScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); + scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness)); scrollbar_layer_impl->SetCurrentPos(10.f); scrollbar_layer_impl->SetMaximum(100); - scrollbar_layer_impl->SetTrackLength(100); scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.4f); // Thickness should be overridden to 3. @@ -217,20 +324,21 @@ TEST(ScrollbarLayerTest, SolidColorDrawQuads) { } TEST(ScrollbarLayerTest, LayerDrivenSolidColorDrawQuads) { + const int kThumbThickness = 3; + const int kTrackLength = 10; + LayerTreeSettings layer_tree_settings; - layer_tree_settings.solid_color_scrollbars = true; - layer_tree_settings.solid_color_scrollbar_thickness_dip = 3; scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(layer_tree_settings); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true)); - LayerImpl* layer_impl_tree_root = - LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false); - ScrollbarLayerImpl* scrollbar_layer_impl = - static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]); + LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar( + host.get(), scrollbar.Pass(), false, true, kThumbThickness); + ScrollbarLayerImplBase* scrollbar_layer_impl = + static_cast<PaintedScrollbarLayerImpl*>( + layer_impl_tree_root->children()[1]); - scrollbar_layer_impl->SetThumbThickness(3); - scrollbar_layer_impl->SetTrackLength(10); + scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness)); scrollbar_layer_impl->SetCurrentPos(4.f); scrollbar_layer_impl->SetMaximum(8); @@ -259,40 +367,43 @@ class ScrollbarLayerSolidColorThumbTest : public testing::Test { layer_tree_settings.solid_color_scrollbars = true; host_impl_.reset(new FakeLayerTreeHostImpl(layer_tree_settings, &proxy_)); - horizontal_scrollbar_layer_ = ScrollbarLayerImpl::Create( - host_impl_->active_tree(), 1, HORIZONTAL); - vertical_scrollbar_layer_ = ScrollbarLayerImpl::Create( - host_impl_->active_tree(), 2, VERTICAL); + const int kThumbThickness = 3; + const bool kIsLeftSideVerticalScrollbar = false; + + horizontal_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create( + host_impl_->active_tree(), 1, HORIZONTAL, kThumbThickness, + kIsLeftSideVerticalScrollbar); + vertical_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create( + host_impl_->active_tree(), 2, VERTICAL, kThumbThickness, + kIsLeftSideVerticalScrollbar); } protected: FakeImplProxy proxy_; scoped_ptr<FakeLayerTreeHostImpl> host_impl_; - scoped_ptr<ScrollbarLayerImpl> horizontal_scrollbar_layer_; - scoped_ptr<ScrollbarLayerImpl> vertical_scrollbar_layer_; + scoped_ptr<SolidColorScrollbarLayerImpl> horizontal_scrollbar_layer_; + scoped_ptr<SolidColorScrollbarLayerImpl> vertical_scrollbar_layer_; }; TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbLength) { horizontal_scrollbar_layer_->SetCurrentPos(0); horizontal_scrollbar_layer_->SetMaximum(10); - horizontal_scrollbar_layer_->SetThumbThickness(3); // Simple case - one third of the scrollable area is visible, so the thumb // should be one third as long as the track. horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.33f); - horizontal_scrollbar_layer_->SetTrackLength(100); + horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3)); EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width()); // The thumb's length should never be less than its thickness. horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.01f); - horizontal_scrollbar_layer_->SetTrackLength(100); + horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3)); EXPECT_EQ(3, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width()); } TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) { - horizontal_scrollbar_layer_->SetTrackLength(100); + horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3)); horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.1f); - horizontal_scrollbar_layer_->SetThumbThickness(3); horizontal_scrollbar_layer_->SetCurrentPos(0); horizontal_scrollbar_layer_->SetMaximum(100); @@ -311,15 +422,15 @@ TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) { } TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbVerticalAdjust) { - ScrollbarLayerImpl* layers[2] = + SolidColorScrollbarLayerImpl* layers[2] = { horizontal_scrollbar_layer_.get(), vertical_scrollbar_layer_.get() }; for (size_t i = 0; i < 2; ++i) { - layers[i]->SetTrackLength(100); layers[i]->SetVisibleToTotalLengthRatio(0.2f); - layers[i]->SetThumbThickness(3); layers[i]->SetCurrentPos(25); layers[i]->SetMaximum(100); } + layers[0]->SetBounds(gfx::Size(100, 3)); + layers[1]->SetBounds(gfx::Size(3, 100)); EXPECT_RECT_EQ(gfx::RectF(20.f, 0.f, 20.f, 3.f), horizontal_scrollbar_layer_->ComputeThumbQuadRect()); @@ -347,7 +458,7 @@ class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest { virtual void BeginTest() OVERRIDE { scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar); - scrollbar_layer_ = ScrollbarLayer::Create(scrollbar.Pass(), 1); + scrollbar_layer_ = PaintedScrollbarLayer::Create(scrollbar.Pass(), 1); scrollbar_layer_->SetLayerTreeHost(layer_tree_host()); scrollbar_layer_->SetBounds(bounds_); layer_tree_host()->root_layer()->AddChild(scrollbar_layer_); @@ -377,7 +488,7 @@ class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest { virtual void AfterTest() OVERRIDE {} private: - scoped_refptr<ScrollbarLayer> scrollbar_layer_; + scoped_refptr<PaintedScrollbarLayer> scrollbar_layer_; scoped_refptr<Layer> scroll_layer_; gfx::Size bounds_; }; @@ -404,9 +515,49 @@ class MockLayerTreeHost : public LayerTreeHost { public: MockLayerTreeHost(LayerTreeHostClient* client, const LayerTreeSettings& settings) - : LayerTreeHost(client, settings) { + : LayerTreeHost(client, settings), + next_id_(1), + total_ui_resource_created_(0), + total_ui_resource_deleted_(0) { Initialize(NULL); } + + virtual UIResourceId CreateUIResource(UIResourceClient* content) OVERRIDE { + total_ui_resource_created_++; + UIResourceId nid = next_id_++; + ui_resource_bitmap_map_.insert( + std::make_pair(nid, content->GetBitmap(nid, false))); + return nid; + } + + // Deletes a UI resource. May safely be called more than once. + virtual void DeleteUIResource(UIResourceId id) OVERRIDE { + UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id); + if (iter != ui_resource_bitmap_map_.end()) { + ui_resource_bitmap_map_.erase(iter); + total_ui_resource_deleted_++; + } + } + + size_t UIResourceCount() { return ui_resource_bitmap_map_.size(); } + int TotalUIResourceDeleted() { return total_ui_resource_deleted_; } + int TotalUIResourceCreated() { return total_ui_resource_created_; } + + gfx::Size ui_resource_size(UIResourceId id) { + UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id); + if (iter != ui_resource_bitmap_map_.end()) + return iter->second.GetSize(); + return gfx::Size(); + } + + private: + typedef base::hash_map<UIResourceId, UIResourceBitmap> + UIResourceBitmapMap; + UIResourceBitmapMap ui_resource_bitmap_map_; + + int next_id_; + int total_ui_resource_created_; + int total_ui_resource_deleted_; }; @@ -415,21 +566,34 @@ class ScrollbarLayerTestResourceCreation : public testing::Test { ScrollbarLayerTestResourceCreation() : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {} - void TestResourceUpload(size_t expected_resources) { + void TestResourceUpload(int num_updates, + size_t expected_resources, + int expected_created, + int expected_deleted, + bool use_solid_color_scrollbar) { layer_tree_host_.reset( new MockLayerTreeHost(&fake_client_, layer_tree_settings_)); scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false)); scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); - scoped_refptr<Layer> scrollbar_layer = - ScrollbarLayer::Create(scrollbar.Pass(), layer_tree_root->id()); + scoped_refptr<Layer> scrollbar_layer; + if (use_solid_color_scrollbar) { + const int kThumbThickness = 3; + const bool kIsLeftSideVerticalScrollbar = false; + scrollbar_layer = + SolidColorScrollbarLayer::Create(scrollbar->Orientation(), + kThumbThickness, + kIsLeftSideVerticalScrollbar, + layer_tree_root->id()); + } else { + scrollbar_layer = PaintedScrollbarLayer::Create(scrollbar.Pass(), + layer_tree_root->id()); + } layer_tree_root->AddChild(content_layer); layer_tree_root->AddChild(scrollbar_layer); layer_tree_host_->InitializeOutputSurfaceIfNeeded(); - layer_tree_host_->contents_texture_manager()-> - SetMaxMemoryLimitBytes(1024 * 1024); layer_tree_host_->SetRootLayer(layer_tree_root); scrollbar_layer->SetIsDrawable(true); @@ -447,16 +611,17 @@ class ScrollbarLayerTestResourceCreation : public testing::Test { testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get()); - PriorityCalculator calculator; ResourceUpdateQueue queue; OcclusionTracker occlusion_tracker(gfx::Rect(), false); scrollbar_layer->SavePaintProperties(); - scrollbar_layer->SetTexturePriorities(calculator); - layer_tree_host_->contents_texture_manager()->PrioritizeTextures(); - scrollbar_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(0u, queue.FullUploadSize()); - EXPECT_EQ(expected_resources, queue.PartialUploadSize()); + for (int update_counter = 0; update_counter < num_updates; update_counter++) + scrollbar_layer->Update(&queue, &occlusion_tracker); + + // A non-solid-color scrollbar should have requested two textures. + EXPECT_EQ(expected_resources, layer_tree_host_->UIResourceCount()); + EXPECT_EQ(expected_created, layer_tree_host_->TotalUIResourceCreated()); + EXPECT_EQ(expected_deleted, layer_tree_host_->TotalUIResourceDeleted()); testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); @@ -470,13 +635,22 @@ class ScrollbarLayerTestResourceCreation : public testing::Test { }; TEST_F(ScrollbarLayerTestResourceCreation, ResourceUpload) { - layer_tree_settings_.solid_color_scrollbars = false; - TestResourceUpload(2); + bool use_solid_color_scrollbars = false; + TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars); + int num_updates[3] = {1, 5, 10}; + for (int j = 0; j < 3; j++) { + TestResourceUpload(num_updates[j], + 2, + num_updates[j] * 2, + (num_updates[j] - 1) * 2, + use_solid_color_scrollbars); + } } TEST_F(ScrollbarLayerTestResourceCreation, SolidColorNoResourceUpload) { - layer_tree_settings_.solid_color_scrollbars = true; - TestResourceUpload(0); + bool use_solid_color_scrollbars = true; + TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars); + TestResourceUpload(1, 0, 0, 0, use_solid_color_scrollbars); } class ScaledScrollbarLayerTestResourceCreation : public testing::Test { @@ -484,25 +658,20 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test { ScaledScrollbarLayerTestResourceCreation() : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {} - void TestResourceUpload(size_t expected_resources, const float test_scale) { + void TestResourceUpload(const float test_scale) { layer_tree_host_.reset( new MockLayerTreeHost(&fake_client_, layer_tree_settings_)); gfx::Point scrollbar_location(0, 185); - scoped_ptr<FakeScrollbar> scrollbar(new FakeScrollbar(false, true, false)); - scrollbar->set_location(scrollbar_location); - scoped_refptr<Layer> layer_tree_root = Layer::Create(); scoped_refptr<Layer> content_layer = Layer::Create(); - scoped_refptr<Layer> scrollbar_layer = - ScrollbarLayer::Create(scrollbar.PassAs<cc::Scrollbar>(), - layer_tree_root->id()); + scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer = + FakePaintedScrollbarLayer::Create(false, true, layer_tree_root->id()); + layer_tree_root->AddChild(content_layer); layer_tree_root->AddChild(scrollbar_layer); layer_tree_host_->InitializeOutputSurfaceIfNeeded(); - layer_tree_host_->contents_texture_manager()-> - SetMaxMemoryLimitBytes(1024 * 1024); layer_tree_host_->SetRootLayer(layer_tree_root); scrollbar_layer->SetIsDrawable(true); @@ -529,30 +698,23 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test { testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get()); - PriorityCalculator calculator; ResourceUpdateQueue queue; OcclusionTracker occlusion_tracker(gfx::Rect(), false); - scrollbar_layer->SavePaintProperties(); - scrollbar_layer->SetTexturePriorities(calculator); - layer_tree_host_->contents_texture_manager()->PrioritizeTextures(); scrollbar_layer->Update(&queue, &occlusion_tracker); - EXPECT_EQ(expected_resources, queue.PartialUploadSize()); // Verify that we have not generated any content uploads that are larger // than their destination textures. - while (queue.HasMoreUpdates()) { - ResourceUpdate update = queue.TakeFirstPartialUpload(); - EXPECT_LE(update.texture->size().width(), - scrollbar_layer->content_bounds().width()); - EXPECT_LE(update.texture->size().height(), - scrollbar_layer->content_bounds().height()); - - EXPECT_LE(update.dest_offset.x() + update.content_rect.width(), - update.texture->size().width()); - EXPECT_LE(update.dest_offset.y() + update.content_rect.height(), - update.texture->size().height()); - } + + gfx::Size track_size = layer_tree_host_->ui_resource_size( + scrollbar_layer->track_resource_id()); + gfx::Size thumb_size = layer_tree_host_->ui_resource_size( + scrollbar_layer->thumb_resource_id()); + + EXPECT_LE(track_size.width(), scrollbar_layer->content_bounds().width()); + EXPECT_LE(track_size.height(), scrollbar_layer->content_bounds().height()); + EXPECT_LE(thumb_size.width(), scrollbar_layer->content_bounds().width()); + EXPECT_LE(thumb_size.height(), scrollbar_layer->content_bounds().height()); testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get()); @@ -566,10 +728,11 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test { }; TEST_F(ScaledScrollbarLayerTestResourceCreation, ScaledResourceUpload) { - layer_tree_settings_.solid_color_scrollbars = false; // Pick a test scale that moves the scrollbar's (non-zero) position to // a non-pixel-aligned location. - TestResourceUpload(2, 1.41f); + TestResourceUpload(.041f); + TestResourceUpload(1.41f); + TestResourceUpload(4.1f); } } // namespace diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.cc b/chromium/cc/layers/solid_color_scrollbar_layer.cc new file mode 100644 index 00000000000..d9ed922e5d8 --- /dev/null +++ b/chromium/cc/layers/solid_color_scrollbar_layer.cc @@ -0,0 +1,68 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/solid_color_scrollbar_layer.h" + +#include "base/memory/scoped_ptr.h" +#include "cc/layers/layer_impl.h" +#include "cc/layers/solid_color_scrollbar_layer_impl.h" + +namespace cc { + +scoped_ptr<LayerImpl> SolidColorScrollbarLayer::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return SolidColorScrollbarLayerImpl::Create( + tree_impl, id(), orientation(), thumb_thickness_, + is_left_side_vertical_scrollbar_).PassAs<LayerImpl>(); +} + +scoped_refptr<SolidColorScrollbarLayer> SolidColorScrollbarLayer::Create( + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar, + int scroll_layer_id) { + return make_scoped_refptr(new SolidColorScrollbarLayer( + orientation, + thumb_thickness, + is_left_side_vertical_scrollbar, + scroll_layer_id)); +} + +SolidColorScrollbarLayer::SolidColorScrollbarLayer( + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar, + int scroll_layer_id) + : scroll_layer_id_(scroll_layer_id), + orientation_(orientation), + thumb_thickness_(thumb_thickness), + is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar) {} + +SolidColorScrollbarLayer::~SolidColorScrollbarLayer() {} + +ScrollbarLayerInterface* SolidColorScrollbarLayer::ToScrollbarLayer() { + return this; +} + +bool SolidColorScrollbarLayer::OpacityCanAnimateOnImplThread() const { + return true; +} + +int SolidColorScrollbarLayer::ScrollLayerId() const { + return scroll_layer_id_; +} + +void SolidColorScrollbarLayer::SetScrollLayerId(int id) { + if (id == scroll_layer_id_) + return; + + scroll_layer_id_ = id; + SetNeedsFullTreeSync(); +} + +ScrollbarOrientation SolidColorScrollbarLayer::orientation() const { + return orientation_; +} + +} // namespace cc diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.h b/chromium/cc/layers/solid_color_scrollbar_layer.h new file mode 100644 index 00000000000..9c0dc1ed473 --- /dev/null +++ b/chromium/cc/layers/solid_color_scrollbar_layer.h @@ -0,0 +1,54 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_ +#define CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_ + +#include "cc/base/cc_export.h" +#include "cc/layers/layer.h" +#include "cc/layers/scrollbar_layer_interface.h" + +namespace cc { + +class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface, + public Layer { + public: + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + + static scoped_refptr<SolidColorScrollbarLayer> Create( + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar, + int scroll_layer_id); + + // Layer overrides. + virtual bool OpacityCanAnimateOnImplThread() const OVERRIDE; + virtual ScrollbarLayerInterface* ToScrollbarLayer() OVERRIDE; + + // ScrollbarLayerInterface + virtual int ScrollLayerId() const OVERRIDE; + virtual void SetScrollLayerId(int id) OVERRIDE; + + virtual ScrollbarOrientation orientation() const OVERRIDE; + + protected: + SolidColorScrollbarLayer(ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar, + int scroll_layer_id); + virtual ~SolidColorScrollbarLayer(); + + private: + int scroll_layer_id_; + ScrollbarOrientation orientation_; + int thumb_thickness_; + bool is_left_side_vertical_scrollbar_; + + DISALLOW_COPY_AND_ASSIGN(SolidColorScrollbarLayer); +}; + +} // namespace cc + +#endif // CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_ diff --git a/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc b/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc new file mode 100644 index 00000000000..9f46a8080f5 --- /dev/null +++ b/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc @@ -0,0 +1,88 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/quad_sink.h" +#include "cc/layers/solid_color_scrollbar_layer_impl.h" +#include "cc/quads/solid_color_draw_quad.h" +#include "cc/trees/layer_tree_impl.h" +#include "cc/trees/layer_tree_settings.h" + +namespace cc { + +scoped_ptr<SolidColorScrollbarLayerImpl> SolidColorScrollbarLayerImpl::Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar) { + return make_scoped_ptr(new SolidColorScrollbarLayerImpl( + tree_impl, id, orientation, thumb_thickness, + is_left_side_vertical_scrollbar)); +} + +SolidColorScrollbarLayerImpl::~SolidColorScrollbarLayerImpl() {} + +scoped_ptr<LayerImpl> SolidColorScrollbarLayerImpl::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return SolidColorScrollbarLayerImpl::Create( + tree_impl, id(), orientation(), thumb_thickness_, + is_left_side_vertical_scrollbar()).PassAs<LayerImpl>(); +} + +SolidColorScrollbarLayerImpl::SolidColorScrollbarLayerImpl( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar) + : ScrollbarLayerImplBase(tree_impl, id, orientation, + is_left_side_vertical_scrollbar), + thumb_thickness_(thumb_thickness), + color_(tree_impl->settings().solid_color_scrollbar_color) {} + +void SolidColorScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { + ScrollbarLayerImplBase::PushPropertiesTo(layer); +} + +int SolidColorScrollbarLayerImpl::ThumbThickness() const { + if (thumb_thickness_ != -1) + return thumb_thickness_; + + if (orientation() == HORIZONTAL) + return bounds().height(); + else + return bounds().width(); +} + +int SolidColorScrollbarLayerImpl::ThumbLength() const { + return std::max( + static_cast<int>(visible_to_total_length_ratio() * TrackLength()), + ThumbThickness()); +} + +float SolidColorScrollbarLayerImpl::TrackLength() const { + if (orientation() == HORIZONTAL) + return bounds().width(); + else + return bounds().height() + vertical_adjust(); +} + +int SolidColorScrollbarLayerImpl::TrackStart() const { + return 0; +} + +void SolidColorScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink, + AppendQuadsData* append_quads_data) { + gfx::Rect thumb_quad_rect = ComputeThumbQuadRect(); + + SharedQuadState* shared_quad_state = + quad_sink->UseSharedQuadState(CreateSharedQuadState()); + AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); + + scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); + quad->SetNew(shared_quad_state, thumb_quad_rect, color_, false); + quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); +} + +} // namespace cc diff --git a/chromium/cc/layers/solid_color_scrollbar_layer_impl.h b/chromium/cc/layers/solid_color_scrollbar_layer_impl.h new file mode 100644 index 00000000000..56cbacd888c --- /dev/null +++ b/chromium/cc/layers/solid_color_scrollbar_layer_impl.h @@ -0,0 +1,52 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_ +#define CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_ + +#include "cc/base/cc_export.h" +#include "cc/layers/scrollbar_layer_impl_base.h" + +namespace cc { + +class CC_EXPORT SolidColorScrollbarLayerImpl : public ScrollbarLayerImplBase { + public: + static scoped_ptr<SolidColorScrollbarLayerImpl> Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar); + virtual ~SolidColorScrollbarLayerImpl(); + + // LayerImpl overrides. + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + + virtual void AppendQuads(QuadSink* quad_sink, + AppendQuadsData* append_quads_data) OVERRIDE; + + + protected: + SolidColorScrollbarLayerImpl(LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + int thumb_thickness, + bool is_left_side_vertical_scrollbar); + + // ScrollbarLayerImplBase implementation. + virtual int ThumbThickness() const OVERRIDE; + virtual int ThumbLength() const OVERRIDE; + virtual float TrackLength() const OVERRIDE; + virtual int TrackStart() const OVERRIDE; + + private: + int thumb_thickness_; + SkColor color_; +}; + +} // namespace cc + +#endif // CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_ diff --git a/chromium/cc/layers/texture_layer.cc b/chromium/cc/layers/texture_layer.cc index 11009fa1b68..a845709448f 100644 --- a/chromium/cc/layers/texture_layer.cc +++ b/chromium/cc/layers/texture_layer.cc @@ -5,10 +5,13 @@ #include "cc/layers/texture_layer.h" #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/synchronization/lock.h" #include "cc/layers/texture_layer_client.h" #include "cc/layers/texture_layer_impl.h" +#include "cc/resources/single_release_callback.h" +#include "cc/trees/blocking_task_runner.h" #include "cc/trees/layer_tree_host.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" @@ -51,7 +54,7 @@ void TextureLayer::ClearClient() { layer_tree_host()->StopRateLimiter(client_->Context3d()); client_ = NULL; if (uses_mailbox_) - SetTextureMailbox(TextureMailbox()); + SetTextureMailbox(TextureMailbox(), scoped_ptr<SingleReleaseCallback>()); else SetTextureId(0); } @@ -125,23 +128,34 @@ void TextureLayer::SetTextureId(unsigned id) { layer_tree_host()->AcquireLayerTextures(); texture_id_ = id; SetNeedsCommit(); + // The texture id needs to be removed from the active tree before the + // commit is called complete. + SetNextCommitWaitsForActivation(); } -void TextureLayer::SetTextureMailbox(const TextureMailbox& mailbox) { +void TextureLayer::SetTextureMailbox( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { DCHECK(uses_mailbox_); DCHECK(!mailbox.IsValid() || !holder_ref_ || !mailbox.Equals(holder_ref_->holder()->mailbox())); + DCHECK_EQ(mailbox.IsValid(), !!release_callback); + // If we never commited the mailbox, we need to release it here. if (mailbox.IsValid()) - holder_ref_ = MailboxHolder::Create(mailbox); + holder_ref_ = MailboxHolder::Create(mailbox, release_callback.Pass()); else holder_ref_.reset(); needs_set_mailbox_ = true; SetNeedsCommit(); + // The active frame needs to be replaced and the mailbox returned before the + // commit is called complete. + SetNextCommitWaitsForActivation(); } void TextureLayer::WillModifyTexture() { - if (layer_tree_host() && (DrawsContent() || content_committed_)) { + if (!uses_mailbox_ && layer_tree_host() && (DrawsContent() || + content_committed_)) { layer_tree_host()->AcquireLayerTextures(); content_committed_ = false; } @@ -161,16 +175,24 @@ void TextureLayer::SetLayerTreeHost(LayerTreeHost* host) { } if (layer_tree_host()) { - if (texture_id_) + if (texture_id_) { layer_tree_host()->AcquireLayerTextures(); + // The texture id needs to be removed from the active tree before the + // commit is called complete. + SetNextCommitWaitsForActivation(); + } if (rate_limit_context_ && client_) layer_tree_host()->StopRateLimiter(client_->Context3d()); } // If we're removed from the tree, the TextureLayerImpl will be destroyed, and // we will need to set the mailbox again on a new TextureLayerImpl the next // time we push. - if (!host && uses_mailbox_ && holder_ref_) + if (!host && uses_mailbox_ && holder_ref_) { needs_set_mailbox_ = true; + // The active frame needs to be replaced and the mailbox returned before the + // commit is called complete. + SetNextCommitWaitsForActivation(); + } Layer::SetLayerTreeHost(host); } @@ -184,19 +206,25 @@ bool TextureLayer::Update(ResourceUpdateQueue* queue, if (client_) { if (uses_mailbox_) { TextureMailbox mailbox; + scoped_ptr<SingleReleaseCallback> release_callback; if (client_->PrepareTextureMailbox( - &mailbox, layer_tree_host()->UsingSharedMemoryResources())) { - SetTextureMailbox(mailbox); + &mailbox, + &release_callback, + layer_tree_host()->UsingSharedMemoryResources())) { + SetTextureMailbox(mailbox, release_callback.Pass()); updated = true; } } else { - DCHECK(client_->Context3d()); texture_id_ = client_->PrepareTexture(); + DCHECK_EQ(!!texture_id_, !!client_->Context3d()); if (client_->Context3d() && client_->Context3d()->getGraphicsResetStatusARB() != GL_NO_ERROR) texture_id_ = 0; updated = true; SetNeedsPushProperties(); + // The texture id needs to be removed from the active tree before the + // commit is called complete. + SetNextCommitWaitsForActivation(); } } @@ -218,18 +246,18 @@ void TextureLayer::PushPropertiesTo(LayerImpl* layer) { texture_layer->set_blend_background_color(blend_background_color_); if (uses_mailbox_ && needs_set_mailbox_) { TextureMailbox texture_mailbox; + scoped_ptr<SingleReleaseCallback> release_callback; if (holder_ref_) { MailboxHolder* holder = holder_ref_->holder(); - TextureMailbox::ReleaseCallback callback = - holder->GetCallbackForImplThread(); - texture_mailbox = holder->mailbox().CopyWithNewCallback(callback); + texture_mailbox = holder->mailbox(); + release_callback = holder->GetCallbackForImplThread(); } - texture_layer->SetTextureMailbox(texture_mailbox); + texture_layer->SetTextureMailbox(texture_mailbox, release_callback.Pass()); needs_set_mailbox_ = false; } else { texture_layer->set_texture_id(texture_id_); + content_committed_ = DrawsContent(); } - content_committed_ = DrawsContent(); } Region TextureLayer::VisibleContentOpaqueRegion() const { @@ -242,13 +270,6 @@ Region TextureLayer::VisibleContentOpaqueRegion() const { return Region(); } -bool TextureLayer::BlocksPendingCommit() const { - // Double-buffered texture layers need to be blocked until they can be made - // triple-buffered. Single-buffered layers already prevent draws, so - // can block too for simplicity. - return DrawsContent(); -} - bool TextureLayer::CanClipSelf() const { return true; } @@ -263,10 +284,13 @@ TextureLayer::MailboxHolder::MainThreadReference::~MainThreadReference() { holder_->InternalRelease(); } -TextureLayer::MailboxHolder::MailboxHolder(const TextureMailbox& mailbox) - : message_loop_(base::MessageLoopProxy::current()), +TextureLayer::MailboxHolder::MailboxHolder( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) + : message_loop_(BlockingTaskRunner::current()), internal_references_(0), mailbox_(mailbox), + release_callback_(release_callback.Pass()), sync_point_(mailbox.sync_point()), is_lost_(false) { } @@ -276,23 +300,27 @@ TextureLayer::MailboxHolder::~MailboxHolder() { } scoped_ptr<TextureLayer::MailboxHolder::MainThreadReference> -TextureLayer::MailboxHolder::Create(const TextureMailbox& mailbox) { +TextureLayer::MailboxHolder::Create( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { return scoped_ptr<MainThreadReference>(new MainThreadReference( - new MailboxHolder(mailbox))); + new MailboxHolder(mailbox, release_callback.Pass()))); } void TextureLayer::MailboxHolder::Return(unsigned sync_point, bool is_lost) { + base::AutoLock lock(arguments_lock_); sync_point_ = sync_point; is_lost_ = is_lost; } -TextureMailbox::ReleaseCallback +scoped_ptr<SingleReleaseCallback> TextureLayer::MailboxHolder::GetCallbackForImplThread() { // We can't call GetCallbackForImplThread if we released the main thread // reference. DCHECK_GT(internal_references_, 0u); InternalAddRef(); - return base::Bind(&MailboxHolder::ReturnAndReleaseOnImplThread, this); + return SingleReleaseCallback::Create( + base::Bind(&MailboxHolder::ReturnAndReleaseOnImplThread, this)); } void TextureLayer::MailboxHolder::InternalAddRef() { @@ -302,23 +330,17 @@ void TextureLayer::MailboxHolder::InternalAddRef() { void TextureLayer::MailboxHolder::InternalRelease() { DCHECK(message_loop_->BelongsToCurrentThread()); if (!--internal_references_) { - mailbox_.RunReleaseCallback(sync_point_, is_lost_); + release_callback_->Run(sync_point_, is_lost_); mailbox_ = TextureMailbox(); + release_callback_.reset(); } } -void TextureLayer::MailboxHolder::ReturnAndReleaseOnMainThread( - unsigned sync_point, bool is_lost) { - DCHECK(message_loop_->BelongsToCurrentThread()); - Return(sync_point, is_lost); - InternalRelease(); -} - void TextureLayer::MailboxHolder::ReturnAndReleaseOnImplThread( unsigned sync_point, bool is_lost) { - message_loop_->PostTask(FROM_HERE, base::Bind( - &MailboxHolder::ReturnAndReleaseOnMainThread, - this, sync_point, is_lost)); + Return(sync_point, is_lost); + message_loop_->PostTask(FROM_HERE, + base::Bind(&MailboxHolder::InternalRelease, this)); } } // namespace cc diff --git a/chromium/cc/layers/texture_layer.h b/chromium/cc/layers/texture_layer.h index 9e63b6825eb..5fb214c2c13 100644 --- a/chromium/cc/layers/texture_layer.h +++ b/chromium/cc/layers/texture_layer.h @@ -8,23 +8,80 @@ #include <string> #include "base/callback.h" +#include "base/synchronization/lock.h" #include "cc/base/cc_export.h" #include "cc/layers/layer.h" #include "cc/resources/texture_mailbox.h" namespace WebKit { class WebGraphicsContext3D; } -namespace base { -class MessageLoopProxy; -} - namespace cc { - +class BlockingTaskRunner; +class SingleReleaseCallback; class TextureLayerClient; // A Layer containing a the rendered output of a plugin instance. class CC_EXPORT TextureLayer : public Layer { public: + class CC_EXPORT MailboxHolder + : public base::RefCountedThreadSafe<MailboxHolder> { + public: + class CC_EXPORT MainThreadReference { + public: + explicit MainThreadReference(MailboxHolder* holder); + ~MainThreadReference(); + MailboxHolder* holder() { return holder_.get(); } + + private: + scoped_refptr<MailboxHolder> holder_; + DISALLOW_COPY_AND_ASSIGN(MainThreadReference); + }; + + const TextureMailbox& mailbox() const { return mailbox_; } + void Return(unsigned sync_point, bool is_lost); + + // Gets a ReleaseCallback that can be called from another thread. Note: the + // caller must ensure the callback is called. + scoped_ptr<SingleReleaseCallback> GetCallbackForImplThread(); + + protected: + friend class TextureLayer; + + // Protected visiblity so only TextureLayer and unit tests can create these. + static scoped_ptr<MainThreadReference> Create( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); + virtual ~MailboxHolder(); + + private: + friend class base::RefCountedThreadSafe<MailboxHolder>; + friend class MainThreadReference; + explicit MailboxHolder(const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); + + void InternalAddRef(); + void InternalRelease(); + void ReturnAndReleaseOnImplThread(unsigned sync_point, bool is_lost); + + // This member is thread safe, and is accessed on main and impl threads. + const scoped_refptr<BlockingTaskRunner> message_loop_; + + // These members are only accessed on the main thread, or on the impl thread + // during commit where the main thread is blocked. + unsigned internal_references_; + TextureMailbox mailbox_; + scoped_ptr<SingleReleaseCallback> release_callback_; + + // This lock guards the sync_point_ and is_lost_ fields because they can be + // accessed on both the impl and main thread. We do this to ensure that the + // values of these fields are well-ordered such that the last call to + // ReturnAndReleaseOnImplThread() defines their values. + base::Lock arguments_lock_; + unsigned sync_point_; + bool is_lost_; + DISALLOW_COPY_AND_ASSIGN(MailboxHolder); + }; + // If this texture layer requires special preparation logic for each frame // driven by the compositor, pass in a non-nil client. Pass in a nil client // pointer if texture updates are driven by an external process. @@ -67,11 +124,13 @@ class CC_EXPORT TextureLayer : public Layer { void SetRateLimitContext(bool rate_limit); // Code path for plugins which supply their own texture ID. + // DEPRECATED. DO NOT USE. void SetTextureId(unsigned texture_id); // Code path for plugins which supply their own mailbox. bool uses_mailbox() const { return uses_mailbox_; } - void SetTextureMailbox(const TextureMailbox& mailbox); + void SetTextureMailbox(const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); void WillModifyTexture(); @@ -83,7 +142,6 @@ class CC_EXPORT TextureLayer : public Layer { const OcclusionTracker* occlusion) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; virtual Region VisibleContentOpaqueRegion() const OVERRIDE; - virtual bool BlocksPendingCommit() const OVERRIDE; virtual bool CanClipSelf() const OVERRIDE; @@ -92,50 +150,6 @@ class CC_EXPORT TextureLayer : public Layer { virtual ~TextureLayer(); private: - class MailboxHolder : public base::RefCountedThreadSafe<MailboxHolder> { - public: - class MainThreadReference { - public: - explicit MainThreadReference(MailboxHolder* holder); - ~MainThreadReference(); - MailboxHolder* holder() { return holder_.get(); } - - private: - scoped_refptr<MailboxHolder> holder_; - DISALLOW_COPY_AND_ASSIGN(MainThreadReference); - }; - - static scoped_ptr<MainThreadReference> Create( - const TextureMailbox& mailbox); - - const TextureMailbox& mailbox() const { return mailbox_; } - void Return(unsigned sync_point, bool is_lost); - - // Gets a ReleaseCallback that can be called from another thread. Note: the - // caller must ensure the callback is called. - TextureMailbox::ReleaseCallback GetCallbackForImplThread(); - - private: - friend class base::RefCountedThreadSafe<MailboxHolder>; - friend class MainThreadReference; - explicit MailboxHolder(const TextureMailbox& mailbox); - ~MailboxHolder(); - void InternalAddRef(); - void InternalRelease(); - void ReturnAndReleaseOnMainThread(unsigned sync_point, bool is_lost); - void ReturnAndReleaseOnImplThread(unsigned sync_point, bool is_lost); - - // Thread safety notes: except for the thread-safe message_loop_, all fields - // are only used on the main thread, or on the impl thread during commit - // where the main thread is blocked. - const scoped_refptr<base::MessageLoopProxy> message_loop_; - unsigned internal_references_; - TextureMailbox mailbox_; - unsigned sync_point_; - bool is_lost_; - DISALLOW_COPY_AND_ASSIGN(MailboxHolder); - }; - TextureLayerClient* client_; bool uses_mailbox_; diff --git a/chromium/cc/layers/texture_layer_client.h b/chromium/cc/layers/texture_layer_client.h index 73d34b30775..187b12f16c4 100644 --- a/chromium/cc/layers/texture_layer_client.h +++ b/chromium/cc/layers/texture_layer_client.h @@ -5,6 +5,8 @@ #ifndef CC_LAYERS_TEXTURE_LAYER_CLIENT_H_ #define CC_LAYERS_TEXTURE_LAYER_CLIENT_H_ +#include "cc/resources/single_release_callback.h" + namespace WebKit { class WebGraphicsContext3D; } namespace cc { @@ -24,8 +26,10 @@ class TextureLayerClient { // Returns true and provides a mailbox if a new frame is available. // Returns false if no new data is available // and the old mailbox is to be reused. - virtual bool PrepareTextureMailbox(TextureMailbox* mailbox, - bool use_shared_memory) = 0; + virtual bool PrepareTextureMailbox( + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) = 0; protected: virtual ~TextureLayerClient() {} diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc index 851bf9a145b..e16fd3c3f18 100644 --- a/chromium/cc/layers/texture_layer_impl.cc +++ b/chromium/cc/layers/texture_layer_impl.cc @@ -4,12 +4,15 @@ #include "cc/layers/texture_layer_impl.h" +#include <vector> + #include "base/strings/stringprintf.h" #include "cc/layers/quad_sink.h" #include "cc/output/renderer.h" #include "cc/quads/texture_draw_quad.h" #include "cc/resources/platform_color.h" #include "cc/resources/scoped_resource.h" +#include "cc/resources/single_release_callback.h" #include "cc/trees/layer_tree_impl.h" namespace cc { @@ -36,10 +39,14 @@ TextureLayerImpl::TextureLayerImpl(LayerTreeImpl* tree_impl, TextureLayerImpl::~TextureLayerImpl() { FreeTextureMailbox(); } -void TextureLayerImpl::SetTextureMailbox(const TextureMailbox& mailbox) { +void TextureLayerImpl::SetTextureMailbox( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { DCHECK(uses_mailbox_); + DCHECK_EQ(mailbox.IsValid(), !!release_callback); FreeTextureMailbox(); texture_mailbox_ = mailbox; + release_callback_ = release_callback.Pass(); own_mailbox_ = true; valid_texture_copy_ = false; } @@ -60,7 +67,8 @@ void TextureLayerImpl::PushPropertiesTo(LayerImpl* layer) { texture_layer->set_vertex_opacity(vertex_opacity_); texture_layer->set_premultiplied_alpha(premultiplied_alpha_); if (uses_mailbox_ && own_mailbox_) { - texture_layer->SetTextureMailbox(texture_mailbox_); + texture_layer->SetTextureMailbox(texture_mailbox_, + release_callback_.Pass()); own_mailbox_ = false; } else { texture_layer->set_texture_id(texture_id_); @@ -80,7 +88,8 @@ bool TextureLayerImpl::WillDraw(DrawMode draw_mode, texture_mailbox_.IsSharedMemory())) { external_texture_resource_ = resource_provider->CreateResourceFromTextureMailbox( - texture_mailbox_); + texture_mailbox_, + release_callback_.Pass()); DCHECK(external_texture_resource_); texture_copy_.reset(); valid_texture_copy_ = false; @@ -102,8 +111,8 @@ bool TextureLayerImpl::WillDraw(DrawMode draw_mode, if (!texture_copy_->id()) { texture_copy_->Allocate(texture_mailbox_.shared_memory_size(), - resource_provider->best_texture_format(), - ResourceProvider::TextureUsageAny); + ResourceProvider::TextureUsageAny, + resource_provider->best_texture_format()); } if (texture_copy_->id()) { @@ -229,7 +238,10 @@ void TextureLayerImpl::FreeTextureMailbox() { return; if (own_mailbox_) { DCHECK(!external_texture_resource_); - texture_mailbox_.RunReleaseCallback(texture_mailbox_.sync_point(), false); + if (release_callback_) + release_callback_->Run(texture_mailbox_.sync_point(), false); + texture_mailbox_ = TextureMailbox(); + release_callback_.reset(); } else if (external_texture_resource_) { DCHECK(!own_mailbox_); ResourceProvider* resource_provider = diff --git a/chromium/cc/layers/texture_layer_impl.h b/chromium/cc/layers/texture_layer_impl.h index f85720688a6..90c26923c8e 100644 --- a/chromium/cc/layers/texture_layer_impl.h +++ b/chromium/cc/layers/texture_layer_impl.h @@ -12,6 +12,7 @@ #include "cc/layers/layer_impl.h" namespace cc { +class SingleReleaseCallback; class ScopedResource; class CC_EXPORT TextureLayerImpl : public LayerImpl { @@ -61,7 +62,8 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { virtual bool CanClipSelf() const OVERRIDE; - void SetTextureMailbox(const TextureMailbox& mailbox); + void SetTextureMailbox(const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); private: TextureLayerImpl(LayerTreeImpl* tree_impl, int id, bool uses_mailbox); @@ -81,6 +83,7 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl { scoped_ptr<ScopedResource> texture_copy_; TextureMailbox texture_mailbox_; + scoped_ptr<SingleReleaseCallback> release_callback_; bool uses_mailbox_; bool own_mailbox_; bool valid_texture_copy_; diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc index aa08e9f81ee..239c5b8fa22 100644 --- a/chromium/cc/layers/texture_layer_unittest.cc +++ b/chromium/cc/layers/texture_layer_unittest.cc @@ -4,16 +4,28 @@ #include "cc/layers/texture_layer.h" +#include <algorithm> #include <string> +#include "base/bind.h" #include "base/callback.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "cc/debug/test_web_graphics_context_3d.h" +#include "cc/layers/solid_color_layer.h" #include "cc/layers/texture_layer_client.h" #include "cc/layers/texture_layer_impl.h" +#include "cc/output/compositor_frame_ack.h" +#include "cc/output/context_provider.h" +#include "cc/resources/returned_resource.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/fake_output_surface.h" #include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" +#include "cc/trees/blocking_task_runner.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" @@ -187,7 +199,7 @@ TEST_F(TextureLayerTest, SyncImplWhenRemovingFromTree) { TEST_F(TextureLayerTest, CheckPropertyChangeCausesCorrectBehavior) { scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL); - layer_tree_host_->SetRootLayer(test_layer); + EXPECT_SET_NEEDS_COMMIT(1, layer_tree_host_->SetRootLayer(test_layer)); // Test properties that should call SetNeedsCommit. All properties need to // be set to new values in order for SetNeedsCommit to be called. @@ -243,9 +255,12 @@ class FakeTextureLayerClient : public TextureLayerClient { return context_.get(); } - virtual bool PrepareTextureMailbox(TextureMailbox* mailbox, - bool use_shared_memory) OVERRIDE { + virtual bool PrepareTextureMailbox( + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { *mailbox = TextureMailbox(); + *release_callback = scoped_ptr<SingleReleaseCallback>(); return true; } @@ -328,25 +343,25 @@ struct CommonMailboxObjects { mailbox_name2_); gpu::Mailbox m1; m1.SetName(reinterpret_cast<const int8*>(mailbox_name1_.data())); - mailbox1_ = TextureMailbox(m1, release_mailbox1_, sync_point1_); + mailbox1_ = TextureMailbox(m1, sync_point1_); gpu::Mailbox m2; m2.SetName(reinterpret_cast<const int8*>(mailbox_name2_.data())); - mailbox2_ = TextureMailbox(m2, release_mailbox2_, sync_point2_); + mailbox2_ = TextureMailbox(m2, sync_point2_); gfx::Size size(128, 128); EXPECT_TRUE(shared_memory_->CreateAndMapAnonymous(4 * size.GetArea())); release_mailbox3_ = base::Bind(&MockMailboxCallback::Release2, base::Unretained(&mock_callback_), shared_memory_.get()); - mailbox3_ = TextureMailbox(shared_memory_.get(), size, release_mailbox3_); + mailbox3_ = TextureMailbox(shared_memory_.get(), size); } std::string mailbox_name1_; std::string mailbox_name2_; MockMailboxCallback mock_callback_; - TextureMailbox::ReleaseCallback release_mailbox1_; - TextureMailbox::ReleaseCallback release_mailbox2_; - TextureMailbox::ReleaseCallback release_mailbox3_; + ReleaseCallback release_mailbox1_; + ReleaseCallback release_mailbox2_; + ReleaseCallback release_mailbox3_; TextureMailbox mailbox1_; TextureMailbox mailbox2_; TextureMailbox mailbox3_; @@ -355,6 +370,14 @@ struct CommonMailboxObjects { scoped_ptr<base::SharedMemory> shared_memory_; }; +class TestMailboxHolder : public TextureLayer::MailboxHolder { + public: + using TextureLayer::MailboxHolder::Create; + + protected: + virtual ~TestMailboxHolder() {} +}; + class TextureLayerWithMailboxTest : public TextureLayerTest { protected: virtual void TearDown() { @@ -380,7 +403,9 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); - test_layer->SetTextureMailbox(test_data_.mailbox1_); + test_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); @@ -390,7 +415,9 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { test_data_.sync_point1_, false)) .Times(1); - test_layer->SetTextureMailbox(test_data_.mailbox2_); + test_layer->SetTextureMailbox( + test_data_.mailbox2_, + SingleReleaseCallback::Create(test_data_.release_mailbox2_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -401,11 +428,16 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { test_data_.sync_point2_, false)) .Times(1); - test_layer->SetTextureMailbox(TextureMailbox()); + test_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); - test_layer->SetTextureMailbox(test_data_.mailbox3_); + EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); + test_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -415,13 +447,309 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { Release2(test_data_.shared_memory_.get(), 0, false)) .Times(1); - test_layer->SetTextureMailbox(TextureMailbox()); + test_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); // Test destructor. EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); - test_layer->SetTextureMailbox(test_data_.mailbox1_); + test_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); +} + +class TextureLayerMailboxHolderTest : public TextureLayerTest { + public: + TextureLayerMailboxHolderTest() + : main_thread_("MAIN") { + main_thread_.Start(); + } + + void Wait(const base::Thread& thread) { + bool manual_reset = false; + bool initially_signaled = false; + base::WaitableEvent event(manual_reset, initially_signaled); + thread.message_loop()->PostTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event))); + event.Wait(); + } + + void CreateMainRef() { + main_ref_ = TestMailboxHolder::Create( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)).Pass(); + } + + void ReleaseMainRef() { + main_ref_.reset(); + } + + void CreateImplRef(scoped_ptr<SingleReleaseCallback>* impl_ref) { + *impl_ref = main_ref_->holder()->GetCallbackForImplThread(); + } + + void CapturePostTasksAndWait(base::WaitableEvent* begin_capture, + base::WaitableEvent* wait_for_capture, + base::WaitableEvent* stop_capture) { + begin_capture->Wait(); + BlockingTaskRunner::CapturePostTasks capture; + wait_for_capture->Signal(); + stop_capture->Wait(); + } + + protected: + scoped_ptr<TestMailboxHolder::MainThreadReference> + main_ref_; + base::Thread main_thread_; + CommonMailboxObjects test_data_; +}; + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_BothReleaseThenMain) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The compositors both destroy their impl trees before the main thread layer + // is destroyed. + compositor1->Run(100, false); + compositor2->Run(200, false); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread ref is the last one, so the mailbox is released back to the + // embedder, with the last sync point provided by the impl trees. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, false)).Times(1); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleaseBetween) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // One compositor destroys their impl tree. + compositor1->Run(100, false); + + // Then the main thread reference is destroyed. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The second impl reference is destroyed last, causing the mailbox to be + // released back to the embedder with the last sync point from the impl tree. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, true)).Times(1); + + compositor2->Run(200, true); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleasedFirst) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread reference is destroyed first. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + // One compositor destroys their impl tree. + compositor2->Run(200, false); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The second impl reference is destroyed last, causing the mailbox to be + // released back to the embedder with the last sync point from the impl tree. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 100, true)).Times(1); + + compositor1->Run(100, true); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_SecondImplRefShortcut) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread reference is destroyed first. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, true)).Times(1); + + bool manual_reset = false; + bool initially_signaled = false; + base::WaitableEvent begin_capture(manual_reset, initially_signaled); + base::WaitableEvent wait_for_capture(manual_reset, initially_signaled); + base::WaitableEvent stop_capture(manual_reset, initially_signaled); + + // Post a task to start capturing tasks on the main thread. This will block + // the main thread until we signal the |stop_capture| event. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CapturePostTasksAndWait, + base::Unretained(this), + &begin_capture, + &wait_for_capture, + &stop_capture)); + + // Before the main thread capturing starts, one compositor destroys their + // impl reference. Since capturing did not start, this gets post-tasked to + // the main thread. + compositor1->Run(100, false); + + // Start capturing on the main thread. + begin_capture.Signal(); + wait_for_capture.Wait(); + + // Meanwhile, the second compositor released its impl reference, but this task + // gets shortcutted directly to the main thread. This means the reference is + // released before compositor1, whose reference will be released later when + // the post-task is serviced. But since it was destroyed _on the impl thread_ + // last, its sync point values should be used. + compositor2->Run(200, true); + + stop_capture.Signal(); + Wait(main_thread_); + + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); } class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { @@ -432,21 +760,24 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { // Make sure callback is received on main and doesn't block the impl thread. void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerImplWithMailboxThreadedCallback::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + gfx::Size bounds(100, 100); root_ = Layer::Create(); root_->SetAnchorPoint(gfx::PointF()); @@ -481,61 +812,58 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { EXPECT_EQ(1, callback_count_); break; case 2: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(1, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 3: EXPECT_EQ(2, callback_count_); // Case #3: change mailbox when the layer doesn't draw. The old // mailbox should be released during the next commit. layer_->SetBounds(gfx::Size()); SetMailbox('4'); break; - case 4: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(2, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 5: + case 3: EXPECT_EQ(3, callback_count_); // Case #4: release mailbox that was committed but never drawn. The // old mailbox should be released during the next commit. - layer_->SetTextureMailbox(TextureMailbox()); - break; - case 6: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(3, callback_count_); - layer_tree_host()->SetNeedsCommit(); + layer_->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); break; - case 7: + case 4: + if (layer_tree_host()->settings().impl_side_painting) { + // With impl painting, the texture mailbox will still be on the impl + // thread when the commit finishes, because the layer is not drawble + // when it has no texture mailbox, and thus does not block the commit + // on activation. So, we wait for activation. + // TODO(danakj): fix this. crbug.com/277953 + layer_tree_host()->SetNeedsCommit(); + break; + } else { + ++commit_count_; + } + case 5: EXPECT_EQ(4, callback_count_); // Restore a mailbox for the next step. SetMailbox('5'); break; - case 8: + case 6: // Case #5: remove layer from tree. Callback should *not* be called, the // mailbox is returned to the main thread. EXPECT_EQ(4, callback_count_); layer_->RemoveFromParent(); break; - case 9: - // Mailbox was released to the main thread, task was posted, but won't - // execute until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(4, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 10: + case 7: + if (layer_tree_host()->settings().impl_side_painting) { + // With impl painting, the texture mailbox will still be on the impl + // thread when the commit finishes, because the layer is not around to + // block the commit on activation anymore. So, we wait for activation. + // TODO(danakj): fix this. crbug.com/277953 + layer_tree_host()->SetNeedsCommit(); + break; + } else { + ++commit_count_; + } + case 8: EXPECT_EQ(4, callback_count_); // Resetting the mailbox will call the callback now. - layer_->SetTextureMailbox(TextureMailbox()); + layer_->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_EQ(5, callback_count_); EndTest(); break; @@ -548,6 +876,7 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { virtual void AfterTest() OVERRIDE {} private: + base::ThreadChecker main_thread_; int callback_count_; int commit_count_; scoped_refptr<Layer> root_; @@ -557,6 +886,247 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( TextureLayerImplWithMailboxThreadedCallback); + +class TextureLayerNoMailboxIsActivatedDuringCommit : public LayerTreeTest, + public TextureLayerClient { + protected: + TextureLayerNoMailboxIsActivatedDuringCommit() + : wait_thread_("WAIT"), + wait_event_(false, false) { + wait_thread_.Start(); + } + + virtual void BeginTest() OVERRIDE { + activate_count_ = 0; + + gfx::Size bounds(100, 100); + root_ = Layer::Create(); + root_->SetAnchorPoint(gfx::PointF()); + root_->SetBounds(bounds); + + layer_ = TextureLayer::Create(this); + layer_->SetIsDrawable(true); + layer_->SetAnchorPoint(gfx::PointF()); + layer_->SetBounds(bounds); + + root_->AddChild(layer_); + layer_tree_host()->SetRootLayer(root_); + layer_tree_host()->SetViewportSize(bounds); + + PostSetNeedsCommitToMainThread(); + } + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + return OffscreenContextProviderForMainThread() + ->Context3d()->createTexture(); + } + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + return OffscreenContextProviderForMainThread()->Context3d(); + } + virtual bool PrepareTextureMailbox( + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + return false; + } + + virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // Slow down activation so the main thread DidCommit() will run if + // not blocked. + wait_thread_.message_loop()->PostDelayedTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&wait_event_)), + base::TimeDelta::FromMilliseconds(10)); + wait_event_.Wait(); + + base::AutoLock lock(activate_lock_); + ++activate_count_; + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // The main thread is awake now, and will run DidCommit() immediately. + // Run DidActivate() afterwards by posting it now. + proxy()->MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerNoMailboxIsActivatedDuringCommit::DidActivate, + base::Unretained(this))); + } + + void DidActivate() { + base::AutoLock lock(activate_lock_); + switch (activate_count_) { + case 1: + // The first texture has been activated. Invalidate the layer so it + // grabs a new texture id from the client. + layer_->SetNeedsDisplay(); + // So this commit number should complete after the second activate. + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + break; + case 2: + // The second mailbox has been activated. Remove the layer from + // the tree to cause another commit/activation. The commit should + // finish *after* the layer is removed from the active tree. + layer_->RemoveFromParent(); + // So this commit number should complete after the third activate. + EXPECT_EQ(2, layer_tree_host()->source_frame_number()); + break; + case 3: + EndTest(); + break; + } + } + + virtual void DidCommit() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 2: { + // The activate for the 2nd texture should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(2, activate_count_); + break; + } + case 3: { + // The activate to remove the layer should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(3, activate_count_); + break; + } + } + } + + + virtual void AfterTest() OVERRIDE {} + + base::Thread wait_thread_; + base::WaitableEvent wait_event_; + base::Lock activate_lock_; + int activate_count_; + int activate_commit_; + scoped_refptr<Layer> root_; + scoped_refptr<TextureLayer> layer_; +}; + +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + TextureLayerNoMailboxIsActivatedDuringCommit); + +class TextureLayerMailboxIsActivatedDuringCommit : public LayerTreeTest { + protected: + TextureLayerMailboxIsActivatedDuringCommit() + : wait_thread_("WAIT"), + wait_event_(false, false) { + wait_thread_.Start(); + } + + static void ReleaseCallback(unsigned sync_point, bool lost_resource) {} + + void SetMailbox(char mailbox_char) { + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind( + &TextureLayerMailboxIsActivatedDuringCommit::ReleaseCallback)); + layer_->SetTextureMailbox(mailbox, callback.Pass()); + } + + virtual void BeginTest() OVERRIDE { + activate_count_ = 0; + + gfx::Size bounds(100, 100); + root_ = Layer::Create(); + root_->SetAnchorPoint(gfx::PointF()); + root_->SetBounds(bounds); + + layer_ = TextureLayer::CreateForMailbox(NULL); + layer_->SetIsDrawable(true); + layer_->SetAnchorPoint(gfx::PointF()); + layer_->SetBounds(bounds); + + root_->AddChild(layer_); + layer_tree_host()->SetRootLayer(root_); + layer_tree_host()->SetViewportSize(bounds); + SetMailbox('1'); + + PostSetNeedsCommitToMainThread(); + } + + virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // Slow down activation so the main thread DidCommit() will run if + // not blocked. + wait_thread_.message_loop()->PostDelayedTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&wait_event_)), + base::TimeDelta::FromMilliseconds(10)); + wait_event_.Wait(); + + base::AutoLock lock(activate_lock_); + ++activate_count_; + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // The main thread is awake now, and will run DidCommit() immediately. + // Run DidActivate() afterwards by posting it now. + proxy()->MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxIsActivatedDuringCommit::DidActivate, + base::Unretained(this))); + } + + void DidActivate() { + base::AutoLock lock(activate_lock_); + switch (activate_count_) { + case 1: + // The first mailbox has been activated. Set a new mailbox, and + // expect the next commit to finish *after* it is activated. + SetMailbox('2'); + // So this commit number should complete after the second activate. + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + break; + case 2: + // The second mailbox has been activated. Remove the layer from + // the tree to cause another commit/activation. The commit should + // finish *after* the layer is removed from the active tree. + layer_->RemoveFromParent(); + // So this commit number should complete after the third activate. + EXPECT_EQ(2, layer_tree_host()->source_frame_number()); + break; + case 3: + EndTest(); + break; + } + } + + virtual void DidCommit() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 2: { + // The activate for the 2nd mailbox should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(2, activate_count_); + break; + } + case 3: { + // The activate to remove the layer should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(3, activate_count_); + break; + } + } + } + + + virtual void AfterTest() OVERRIDE {} + + base::Thread wait_thread_; + base::WaitableEvent wait_event_; + base::Lock activate_lock_; + int activate_count_; + scoped_refptr<Layer> root_; + scoped_refptr<TextureLayer> layer_; +}; + +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + TextureLayerMailboxIsActivatedDuringCommit); + class TextureLayerImplWithMailboxTest : public TextureLayerTest { protected: TextureLayerImplWithMailboxTest() @@ -596,14 +1166,17 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } @@ -611,15 +1184,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { // Software resource. scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox3_); + impl_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } @@ -635,14 +1212,17 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } @@ -650,15 +1230,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { // Software resource. scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox3_); + impl_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } @@ -674,15 +1258,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE)); } @@ -698,7 +1286,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { pending_layer->CreateLayerImpl(host_impl_.active_tree())); ASSERT_TRUE(active_layer); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); // Test multiple commits without an activation. EXPECT_CALL(test_data_.mock_callback_, @@ -706,7 +1296,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { test_data_.sync_point1_, false)) .Times(1); - pending_layer->SetTextureMailbox(test_data_.mailbox2_); + pending_layer->SetTextureMailbox( + test_data_.mailbox2_, + SingleReleaseCallback::Create(test_data_.release_mailbox2_)); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); // Test callback after activation. @@ -714,7 +1306,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { active_layer->DidBecomeActive(); EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); EXPECT_CALL(test_data_.mock_callback_, @@ -728,7 +1322,8 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - pending_layer->SetTextureMailbox(TextureMailbox()); + pending_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); pending_layer->PushPropertiesTo(active_layer.get()); active_layer->DidBecomeActive(); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -739,7 +1334,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { test_data_.sync_point1_, false)) .Times(1); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); } TEST_F(TextureLayerImplWithMailboxTest, @@ -751,18 +1348,23 @@ TEST_F(TextureLayerImplWithMailboxTest, EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); impl_layer->DidBecomeActive(); EXPECT_TRUE(impl_layer->WillDraw( DRAW_MODE_HARDWARE, host_impl_.active_tree()->resource_provider())); impl_layer->DidDraw(host_impl_.active_tree()->resource_provider()); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); } TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) { ResourceProvider* provider = host_impl_.active_tree()->resource_provider(); ResourceProvider::ResourceId id = - provider->CreateResourceFromTextureMailbox(test_data_.mailbox1_); + provider->CreateResourceFromTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); provider->AllocateForTesting(id); // Transfer some resources to the parent. @@ -777,7 +1379,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) { EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - provider->ReceiveFromParent(list); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + provider->ReceiveReturnsFromParent(returned); } // Check that ClearClient correctly clears the state so that the impl side @@ -799,8 +1403,7 @@ class TextureLayerClientTest TestWebGraphicsContext3D::Create()); context_ = context.get(); texture_ = context->createTexture(); - return FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>(); + return FakeOutputSurface::Create3d(context.Pass()).PassAs<OutputSurface>(); } virtual unsigned PrepareTexture() OVERRIDE { @@ -812,7 +1415,9 @@ class TextureLayerClientTest } virtual bool PrepareTextureMailbox( - cc::TextureMailbox* mailbox, bool use_shared_memory) OVERRIDE { + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { return false; } @@ -899,6 +1504,343 @@ class TextureLayerClientTest // renderer. SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerClientTest); + +// Checks that changing a texture in the client for a TextureLayer that's +// invisible correctly works without drawing a deleted texture. See +// crbug.com/266628 +class TextureLayerChangeInvisibleTest + : public LayerTreeTest, + public TextureLayerClient { + public: + TextureLayerChangeInvisibleTest() + : client_context_(TestWebGraphicsContext3D::Create()), + texture_(client_context_->createTexture()), + texture_to_delete_on_next_commit_(0), + prepare_called_(0), + commit_count_(0), + expected_texture_on_draw_(0) {} + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + ++prepare_called_; + return texture_; + } + + // TextureLayerClient implementation. + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + return client_context_.get(); + } + + // TextureLayerClient implementation. + virtual bool PrepareTextureMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + return false; + } + + virtual void SetupTree() OVERRIDE { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(10, 10)); + root->SetAnchorPoint(gfx::PointF()); + root->SetIsDrawable(true); + + solid_layer_ = SolidColorLayer::Create(); + solid_layer_->SetBounds(gfx::Size(10, 10)); + solid_layer_->SetIsDrawable(true); + solid_layer_->SetBackgroundColor(SK_ColorWHITE); + root->AddChild(solid_layer_); + + parent_layer_ = Layer::Create(); + parent_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->SetIsDrawable(true); + root->AddChild(parent_layer_); + + texture_layer_ = TextureLayer::Create(this); + texture_layer_->SetBounds(gfx::Size(10, 10)); + texture_layer_->SetAnchorPoint(gfx::PointF()); + texture_layer_->SetIsDrawable(true); + parent_layer_->AddChild(texture_layer_); + + layer_tree_host()->SetRootLayer(root); + LayerTreeTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + ++commit_count_; + switch (commit_count_) { + case 1: + // We should have updated the layer, committing the texture. + EXPECT_EQ(1, prepare_called_); + // Make layer invisible. + parent_layer_->SetOpacity(0.f); + break; + case 2: { + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // Change the texture. + texture_to_delete_on_next_commit_ = texture_; + texture_ = client_context_->createTexture(); + texture_layer_->SetNeedsDisplay(); + // Force a change to make sure we draw a frame. + solid_layer_->SetBackgroundColor(SK_ColorGRAY); + break; + } + case 3: + EXPECT_EQ(1, prepare_called_); + client_context_->deleteTexture(texture_to_delete_on_next_commit_); + texture_to_delete_on_next_commit_ = 0; + // Make layer visible again. + parent_layer_->SetOpacity(1.f); + break; + case 4: { + // Layer should have been updated. + EXPECT_EQ(2, prepare_called_); + texture_layer_->ClearClient(); + client_context_->deleteTexture(texture_); + texture_ = 0; + break; + } + case 5: + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } + + virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + ASSERT_TRUE(proxy()->IsMainThreadBlocked()); + // This is the only texture that can be drawn this frame. + expected_texture_on_draw_ = texture_; + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + ContextForImplThread(host_impl)->ResetUsedTextures(); + return true; + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + ASSERT_TRUE(result); + TestWebGraphicsContext3D* context = ContextForImplThread(host_impl); + int used_textures = context->NumUsedTextures(); + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + EXPECT_EQ(1, used_textures); + EXPECT_TRUE(context->UsedTexture(expected_texture_on_draw_)); + break; + case 1: + case 2: + EXPECT_EQ(0, used_textures); + break; + case 3: + EXPECT_EQ(1, used_textures); + EXPECT_TRUE(context->UsedTexture(expected_texture_on_draw_)); + break; + default: + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + TestWebGraphicsContext3D* ContextForImplThread(LayerTreeHostImpl* host_impl) { + return static_cast<TestWebGraphicsContext3D*>( + host_impl->output_surface()->context_provider()->Context3d()); + } + + scoped_refptr<SolidColorLayer> solid_layer_; + scoped_refptr<Layer> parent_layer_; + scoped_refptr<TextureLayer> texture_layer_; + scoped_ptr<TestWebGraphicsContext3D> client_context_; + + // Used on the main thread, and on the impl thread while the main thread is + // blocked. + unsigned texture_; + + // Used on the main thread. + unsigned texture_to_delete_on_next_commit_; + int prepare_called_; + int commit_count_; + + // Used on the compositor thread. + unsigned expected_texture_on_draw_; +}; + +// The TextureLayerChangeInvisibleTest does not use mailboxes, so can't use a +// delegating renderer. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerChangeInvisibleTest); + +// Checks that changing a mailbox in the client for a TextureLayer that's +// invisible correctly works and uses the new mailbox as soon as the layer +// becomes visible (and returns the old one). +class TextureLayerChangeInvisibleMailboxTest + : public LayerTreeTest, + public TextureLayerClient { + public: + TextureLayerChangeInvisibleMailboxTest() + : mailbox_changed_(true), + mailbox_returned_(0), + prepare_called_(0), + commit_count_(0) { + mailbox_ = MakeMailbox('1'); + } + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + NOTREACHED(); + return 0; + } + + // TextureLayerClient implementation. + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + NOTREACHED(); + return NULL; + } + + // TextureLayerClient implementation. + virtual bool PrepareTextureMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + ++prepare_called_; + if (!mailbox_changed_) + return false; + *mailbox = mailbox_; + *release_callback = SingleReleaseCallback::Create( + base::Bind(&TextureLayerChangeInvisibleMailboxTest::MailboxReleased, + base::Unretained(this))); + return true; + } + + TextureMailbox MakeMailbox(char name) { + return TextureMailbox(std::string(64, name)); + } + + void MailboxReleased(unsigned sync_point, bool lost_resource) { + ++mailbox_returned_; + } + + virtual void SetupTree() OVERRIDE { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(10, 10)); + root->SetAnchorPoint(gfx::PointF()); + root->SetIsDrawable(true); + + solid_layer_ = SolidColorLayer::Create(); + solid_layer_->SetBounds(gfx::Size(10, 10)); + solid_layer_->SetIsDrawable(true); + solid_layer_->SetBackgroundColor(SK_ColorWHITE); + root->AddChild(solid_layer_); + + parent_layer_ = Layer::Create(); + parent_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->SetIsDrawable(true); + root->AddChild(parent_layer_); + + texture_layer_ = TextureLayer::CreateForMailbox(this); + texture_layer_->SetBounds(gfx::Size(10, 10)); + texture_layer_->SetAnchorPoint(gfx::PointF()); + texture_layer_->SetIsDrawable(true); + parent_layer_->AddChild(texture_layer_); + + layer_tree_host()->SetRootLayer(root); + LayerTreeTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + ++commit_count_; + switch (commit_count_) { + case 1: + // We should have updated the layer, committing the texture. + EXPECT_EQ(1, prepare_called_); + // Make layer invisible. + parent_layer_->SetOpacity(0.f); + break; + case 2: + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // Change the texture. + mailbox_ = MakeMailbox('2'); + mailbox_changed_ = true; + texture_layer_->SetNeedsDisplay(); + // Force a change to make sure we draw a frame. + solid_layer_->SetBackgroundColor(SK_ColorGRAY); + break; + case 3: + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // So the old mailbox isn't returned yet. + EXPECT_EQ(0, mailbox_returned_); + // Make layer visible again. + parent_layer_->SetOpacity(1.f); + break; + case 4: + // Layer should have been updated. + EXPECT_EQ(2, prepare_called_); + // So the old mailbox should have been returned already. + EXPECT_EQ(1, mailbox_returned_); + texture_layer_->ClearClient(); + break; + case 5: + EXPECT_EQ(2, mailbox_returned_); + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + ASSERT_TRUE(result); + DelegatedFrameData* delegated_frame_data = + output_surface()->last_sent_frame().delegated_frame_data.get(); + if (!delegated_frame_data) + return; + + // Return all resources immediately. + TransferableResourceArray resources_to_return = + output_surface()->resources_held_by_parent(); + + CompositorFrameAck ack; + for (size_t i = 0; i < resources_to_return.size(); ++i) + output_surface()->ReturnResource(resources_to_return[i].id, &ack); + host_impl->ReclaimResources(&ack); + host_impl->OnSwapBuffersComplete(); + } + + virtual void AfterTest() OVERRIDE {} + + private: + scoped_refptr<SolidColorLayer> solid_layer_; + scoped_refptr<Layer> parent_layer_; + scoped_refptr<TextureLayer> texture_layer_; + + // Used on the main thread. + bool mailbox_changed_; + TextureMailbox mailbox_; + int mailbox_returned_; + int prepare_called_; + int commit_count_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerChangeInvisibleMailboxTest); + // Test recovering from a lost context. class TextureLayerLostContextTest : public LayerTreeTest, @@ -928,7 +1870,9 @@ class TextureLayerLostContextTest } virtual bool PrepareTextureMailbox( - cc::TextureMailbox* mailbox, bool use_shared_memory) OVERRIDE { + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { return false; } @@ -981,19 +1925,20 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerLostContextTest); class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { public: void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; EndTest(); } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerWithMailboxMainThreadDeleted::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void SetupTree() OVERRIDE { @@ -1013,6 +1958,8 @@ class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + callback_count_ = 0; // Set the mailbox on the main thread. @@ -1038,6 +1985,7 @@ class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { } private: + base::ThreadChecker main_thread_; int callback_count_; scoped_refptr<Layer> root_; scoped_refptr<TextureLayer> layer_; @@ -1049,19 +1997,20 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { public: void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; EndTest(); } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerWithMailboxImplThreadDeleted::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void SetupTree() OVERRIDE { @@ -1081,6 +2030,8 @@ class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + callback_count_ = 0; // Set the mailbox on the main thread. @@ -1109,6 +2060,7 @@ class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { } private: + base::ThreadChecker main_thread_; int callback_count_; scoped_refptr<Layer> root_; scoped_refptr<TextureLayer> layer_; diff --git a/chromium/cc/layers/tiled_layer.cc b/chromium/cc/layers/tiled_layer.cc index bac12f0d422..8bbf2271bc4 100644 --- a/chromium/cc/layers/tiled_layer.cc +++ b/chromium/cc/layers/tiled_layer.cc @@ -86,7 +86,7 @@ class UpdatableTile : public LayerTilingData::Tile { TiledLayer::TiledLayer() : ContentsScalingLayer(), - texture_format_(GL_INVALID_ENUM), + texture_format_(RGBA_8888), skips_draw_(false), failed_update_(false), tiling_option_(AUTO_TILE) { @@ -235,9 +235,7 @@ void TiledLayer::PushPropertiesTo(LayerImpl* layer) { needs_push_properties_ = true; } -bool TiledLayer::BlocksPendingCommit() const { return true; } - -PrioritizedResourceManager* TiledLayer::ResourceManager() const { +PrioritizedResourceManager* TiledLayer::ResourceManager() { if (!layer_tree_host()) return NULL; return layer_tree_host()->contents_texture_manager(); @@ -731,6 +729,10 @@ bool TiledLayer::Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) { DCHECK(!skips_draw_ && !failed_update_); // Did ResetUpdateState get skipped? + // Tiled layer always causes commits to wait for activation, as it does + // not support pending trees. + SetNextCommitWaitsForActivation(); + bool updated = false; { diff --git a/chromium/cc/layers/tiled_layer.h b/chromium/cc/layers/tiled_layer.h index 8ad7df95e38..3870d69956b 100644 --- a/chromium/cc/layers/tiled_layer.h +++ b/chromium/cc/layers/tiled_layer.h @@ -8,6 +8,7 @@ #include "cc/base/cc_export.h" #include "cc/layers/contents_scaling_layer.h" #include "cc/resources/layer_tiling_data.h" +#include "cc/resources/resource_format.h" namespace cc { class LayerUpdater; @@ -26,7 +27,6 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { // Layer implementation. virtual void SetIsMask(bool is_mask) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; - virtual bool BlocksPendingCommit() const OVERRIDE; virtual bool DrawsContent() const OVERRIDE; virtual void ReduceMemoryUsage() OVERRIDE; virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE; @@ -46,7 +46,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { // Exposed to subclasses for testing. void SetTileSize(gfx::Size size); - void SetTextureFormat(unsigned texture_format) { + void SetTextureFormat(ResourceFormat texture_format) { texture_format_ = texture_format; } void SetBorderTexelOption(LayerTilingData::BorderTexelOption option); @@ -68,7 +68,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { bool SkipsDraw() const { return skips_draw_; } // Virtual for testing - virtual PrioritizedResourceManager* ResourceManager() const; + virtual PrioritizedResourceManager* ResourceManager(); const LayerTilingData* TilerForTesting() const { return tiler_.get(); } const PrioritizedResource* ResourceAtForTesting(int i, int j) const; @@ -121,7 +121,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { bool IsSmallAnimatedLayer() const; - unsigned texture_format_; + ResourceFormat texture_format_; bool skips_draw_; bool failed_update_; diff --git a/chromium/cc/layers/tiled_layer_unittest.cc b/chromium/cc/layers/tiled_layer_unittest.cc index a2f0ca7a64d..b6da0769f95 100644 --- a/chromium/cc/layers/tiled_layer_unittest.cc +++ b/chromium/cc/layers/tiled_layer_unittest.cc @@ -16,6 +16,7 @@ #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_proxy.h" #include "cc/test/fake_rendering_stats_instrumentation.h" #include "cc/test/geometry_test_utils.h" @@ -47,7 +48,7 @@ class TiledLayerTest : public testing::Test { public: TiledLayerTest() : proxy_(NULL), - output_surface_(CreateFakeOutputSurface()), + output_surface_(FakeOutputSurface::Create3d()), queue_(make_scoped_ptr(new ResourceUpdateQueue)), fake_layer_impl_tree_host_client_(FakeLayerTreeHostClient::DIRECT_3D), occlusion_(NULL) { @@ -64,9 +65,12 @@ class TiledLayerTest : public testing::Test { layer_tree_host_->InitializeOutputSurfaceIfNeeded(); layer_tree_host_->SetRootLayer(Layer::Create()); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + DebugScopedSetImplThreadAndMainThreadBlocked - impl_thread_and_main_thread_blocked(proxy_); - resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0); + impl_thread_and_main_thread_blocked(proxy_); + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false); host_impl_ = make_scoped_ptr(new FakeLayerTreeHostImpl(proxy_)); } @@ -183,6 +187,7 @@ class TiledLayerTest : public testing::Test { public: Proxy* proxy_; LayerTreeSettings settings_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<OutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; scoped_ptr<ResourceUpdateQueue> queue_; @@ -951,8 +956,10 @@ TEST_F(TiledLayerTest, SkipsDrawGetsReset) { layer_tree_host_->SetRootLayer(root_layer); layer_tree_host_->SetViewportSize(gfx::Size(300, 300)); + layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( + memory_limit); - layer_tree_host_->UpdateLayers(queue_.get(), memory_limit); + layer_tree_host_->UpdateLayers(queue_.get()); // We'll skip the root layer. EXPECT_TRUE(root_layer->SkipsDraw()); @@ -963,7 +970,7 @@ TEST_F(TiledLayerTest, SkipsDrawGetsReset) { // Remove the child layer. root_layer->RemoveAllChildren(); - layer_tree_host_->UpdateLayers(queue_.get(), memory_limit); + layer_tree_host_->UpdateLayers(queue_.get()); EXPECT_FALSE(root_layer->SkipsDraw()); ResourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(), @@ -1029,8 +1036,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { layer_tree_host_->SetViewportSize(gfx::Size(300, 200)); // Full update of all 6 tiles. - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); @@ -1046,8 +1052,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { // Full update of 3 tiles and partial update of 3 tiles. layer->InvalidateContentRect(gfx::Rect(0, 0, 300, 150)); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); @@ -1066,8 +1071,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); EXPECT_EQ(2u, queue_->FullUploadSize()); EXPECT_EQ(4u, queue_->PartialUploadSize()); UpdateTextures(); @@ -1092,8 +1096,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); EXPECT_EQ(6u, queue_->FullUploadSize()); EXPECT_EQ(0u, queue_->PartialUploadSize()); UpdateTextures(); @@ -1109,8 +1112,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) { { scoped_ptr<FakeTiledLayerImpl> layer_impl = make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1)); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); EXPECT_EQ(0u, queue_->FullUploadSize()); EXPECT_EQ(4u, queue_->PartialUploadSize()); UpdateTextures(); @@ -1708,8 +1710,7 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) { root->InvalidateContentRect(root_rect); child->InvalidateContentRect(child_rect); child2->InvalidateContentRect(child2_rect); - layer_tree_host_->UpdateLayers(queue_.get(), - std::numeric_limits<size_t>::max()); + layer_tree_host_->UpdateLayers(queue_.get()); { UpdateTextures(); EXPECT_EQ(6, root->fake_layer_updater()->update_count()); @@ -1747,8 +1748,11 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) { root->InvalidateContentRect(root_rect); child->InvalidateContentRect(child_rect); child2->InvalidateContentRect(child2_rect); - layer_tree_host_->UpdateLayers(queue_.get(), - (3 * 2 + 3 * 1) * (100 * 100) * 4); + + size_t memory_limit = (3 * 2 + 3 * 1) * (100 * 100) * 4; + layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( + memory_limit); + layer_tree_host_->UpdateLayers(queue_.get()); { UpdateTextures(); EXPECT_EQ(6, root->fake_layer_updater()->update_count()); @@ -1786,7 +1790,11 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) { root->InvalidateContentRect(root_rect); child->InvalidateContentRect(child_rect); child2->InvalidateContentRect(child2_rect); - layer_tree_host_->UpdateLayers(queue_.get(), (3 * 1) * (100 * 100) * 4); + + memory_limit = (3 * 1) * (100 * 100) * 4; + layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( + memory_limit); + layer_tree_host_->UpdateLayers(queue_.get()); { UpdateTextures(); EXPECT_EQ(0, root->fake_layer_updater()->update_count()); diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc index 4b5671ff90e..28e470e7c3b 100644 --- a/chromium/cc/layers/video_layer_impl.cc +++ b/chromium/cc/layers/video_layer_impl.cc @@ -13,6 +13,7 @@ #include "cc/quads/texture_draw_quad.h" #include "cc/quads/yuv_video_draw_quad.h" #include "cc/resources/resource_provider.h" +#include "cc/resources/single_release_callback.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/proxy.h" #include "media/base/video_frame.h" @@ -93,8 +94,11 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode, if (!LayerImpl::WillDraw(draw_mode, resource_provider)) return false; - if (!updater_) - updater_.reset(new VideoResourceUpdater(resource_provider)); + if (!updater_) { + updater_.reset( + new VideoResourceUpdater(layer_tree_impl()->context_provider(), + layer_tree_impl()->resource_provider())); + } VideoFrameExternalResources external_resources = updater_->CreateExternalResourcesFromVideoFrame(frame_); @@ -108,10 +112,13 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode, return true; } + DCHECK_EQ(external_resources.mailboxes.size(), + external_resources.release_callbacks.size()); for (size_t i = 0; i < external_resources.mailboxes.size(); ++i) { - frame_resources_.push_back( - resource_provider->CreateResourceFromTextureMailbox( - external_resources.mailboxes[i])); + unsigned resource_id = resource_provider->CreateResourceFromTextureMailbox( + external_resources.mailboxes[i], + SingleReleaseCallback::Create(external_resources.release_callbacks[i])); + frame_resources_.push_back(resource_id); } return true; diff --git a/chromium/cc/layers/video_layer_impl.h b/chromium/cc/layers/video_layer_impl.h index b07187115ed..b4df1417bf1 100644 --- a/chromium/cc/layers/video_layer_impl.h +++ b/chromium/cc/layers/video_layer_impl.h @@ -9,6 +9,7 @@ #include "cc/base/cc_export.h" #include "cc/layers/layer_impl.h" +#include "cc/resources/release_callback.h" #include "cc/resources/video_resource_updater.h" namespace media { @@ -59,7 +60,8 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl { // TODO(danakj): Remove these, hide software path inside ResourceProvider and // ExternalResource (aka TextureMailbox) classes. std::vector<unsigned> software_resources_; - TextureMailbox::ReleaseCallback software_release_callback_; + // Called once for each software resource. + ReleaseCallback software_release_callback_; DISALLOW_COPY_AND_ASSIGN(VideoLayerImpl); }; diff --git a/chromium/cc/output/begin_frame_args.cc b/chromium/cc/output/begin_frame_args.cc index cefb22bdc13..d7da76c3bcb 100644 --- a/chromium/cc/output/begin_frame_args.cc +++ b/chromium/cc/output/begin_frame_args.cc @@ -48,10 +48,14 @@ BeginFrameArgs BeginFrameArgs::CreateExpiredForTesting() { DefaultInterval()); } +// This is a hard-coded deadline adjustment that assumes 60Hz, to be used in +// cases where a good estimated draw time is not known. Using 1/3 of the vsync +// as the default adjustment gives the Browser the last 1/3 of a frame to +// produce output, the Renderer Impl thread the middle 1/3 of a frame to produce +// ouput, and the Renderer Main thread the first 1/3 of a frame to produce +// output. base::TimeDelta BeginFrameArgs::DefaultDeadlineAdjustment() { - // Using a large deadline adjustment will effectively revert BeginFrame - // scheduling to the hard vsync scheduling we used to have. - return base::TimeDelta::FromSeconds(-1); + return base::TimeDelta::FromMicroseconds(-16666 / 3); } base::TimeDelta BeginFrameArgs::DefaultInterval() { @@ -62,5 +66,4 @@ base::TimeDelta BeginFrameArgs::DefaultRetroactiveBeginFramePeriod() { return base::TimeDelta::FromMicroseconds(4444); } - } // namespace cc diff --git a/chromium/cc/output/begin_frame_args.h b/chromium/cc/output/begin_frame_args.h index 22693dbef94..025a4075c25 100644 --- a/chromium/cc/output/begin_frame_args.h +++ b/chromium/cc/output/begin_frame_args.h @@ -37,9 +37,7 @@ struct CC_EXPORT BeginFrameArgs { // retroactively. static base::TimeDelta DefaultRetroactiveBeginFramePeriod(); - bool IsValid() const { - return interval >= base::TimeDelta(); - } + bool IsValid() const { return interval >= base::TimeDelta(); } base::TimeTicks frame_time; base::TimeTicks deadline; diff --git a/chromium/cc/output/compositor_frame_ack.h b/chromium/cc/output/compositor_frame_ack.h index 276ecf0d96e..943a206e391 100644 --- a/chromium/cc/output/compositor_frame_ack.h +++ b/chromium/cc/output/compositor_frame_ack.h @@ -8,7 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/output/gl_frame_data.h" -#include "cc/resources/transferable_resource.h" +#include "cc/resources/returned_resource.h" namespace cc { @@ -17,7 +17,7 @@ class CC_EXPORT CompositorFrameAck { CompositorFrameAck(); ~CompositorFrameAck(); - TransferableResourceArray resources; + ReturnedResourceArray resources; scoped_ptr<GLFrameData> gl_frame_data; unsigned last_software_frame_id; diff --git a/chromium/cc/output/compositor_frame_metadata.h b/chromium/cc/output/compositor_frame_metadata.h index 4e569bbb2c7..c084151643b 100644 --- a/chromium/cc/output/compositor_frame_metadata.h +++ b/chromium/cc/output/compositor_frame_metadata.h @@ -6,7 +6,7 @@ #define CC_OUTPUT_COMPOSITOR_FRAME_METADATA_H_ #include "cc/base/cc_export.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" #include "ui/gfx/size_f.h" #include "ui/gfx/vector2d_f.h" diff --git a/chromium/cc/output/context_provider.cc b/chromium/cc/output/context_provider.cc new file mode 100644 index 00000000000..7f46f7ccc67 --- /dev/null +++ b/chromium/cc/output/context_provider.cc @@ -0,0 +1,30 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/output/context_provider.h" + +#include <limits> + +namespace cc { + +ContextProvider::Capabilities::Capabilities() + : bind_uniform_location(false), + discard_backbuffer(false), + egl_image_external(false), + fast_npot_mo8_textures(false), + iosurface(false), + map_image(false), + map_sub(false), + post_sub_buffer(false), + set_visibility(false), + shallow_flush(false), + swapbuffers_complete_callback(false), + texture_format_bgra8888(false), + texture_rectangle(false), + texture_storage(false), + texture_usage(false), + discard_framebuffer(false), + max_transfer_buffer_usage_bytes(std::numeric_limits<size_t>::max()) {} + +} // namespace cc diff --git a/chromium/cc/output/context_provider.h b/chromium/cc/output/context_provider.h index 13b7df5948b..c873b58c8d6 100644 --- a/chromium/cc/output/context_provider.h +++ b/chromium/cc/output/context_provider.h @@ -7,11 +7,13 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" +#include "cc/base/cc_export.h" class GrContext; namespace WebKit { class WebGraphicsContext3D; } namespace cc { +struct ManagedMemoryPolicy; class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> { public: @@ -24,6 +26,30 @@ class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> { virtual WebKit::WebGraphicsContext3D* Context3d() = 0; virtual class GrContext* GrContext() = 0; + struct Capabilities { + bool bind_uniform_location; + bool discard_backbuffer; + bool egl_image_external; + bool fast_npot_mo8_textures; + bool iosurface; + bool map_image; + bool map_sub; + bool post_sub_buffer; + bool set_visibility; + bool shallow_flush; + bool swapbuffers_complete_callback; + bool texture_format_bgra8888; + bool texture_rectangle; + bool texture_storage; + bool texture_usage; + bool discard_framebuffer; + size_t max_transfer_buffer_usage_bytes; + + CC_EXPORT Capabilities(); + }; + // Returns the capabilities of the currently bound 3d context. + virtual Capabilities ContextCapabilities() = 0; + // Ask the provider to check if the contexts are valid or lost. If they are, // this should invalidate the provider so that it can be replaced with a new // one. @@ -41,6 +67,20 @@ class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> { virtual void SetLostContextCallback( const LostContextCallback& lost_context_callback) = 0; + // Sets a callback to be called when swap buffers completes. This should be + // called from the same thread that the context is bound to. + typedef base::Closure SwapBuffersCompleteCallback; + virtual void SetSwapBuffersCompleteCallback( + const SwapBuffersCompleteCallback& swap_buffers_complete_callback) = 0; + + // Sets a callback to be called when the memory policy changes. This should be + // called from the same thread that the context is bound to. + typedef base::Callback<void( + const cc::ManagedMemoryPolicy& policy, + bool discard_backbuffer_when_not_visible)> MemoryPolicyChangedCallback; + virtual void SetMemoryPolicyChangedCallback( + const MemoryPolicyChangedCallback& memory_policy_changed_callback) = 0; + protected: friend class base::RefCountedThreadSafe<ContextProvider>; virtual ~ContextProvider() {} diff --git a/chromium/cc/output/copy_output_request.cc b/chromium/cc/output/copy_output_request.cc index 995c3f4cfcc..6163d5d3c60 100644 --- a/chromium/cc/output/copy_output_request.cc +++ b/chromium/cc/output/copy_output_request.cc @@ -8,6 +8,7 @@ #include "base/callback_helpers.h" #include "base/logging.h" #include "cc/output/copy_output_result.h" +#include "cc/resources/single_release_callback.h" #include "cc/resources/texture_mailbox.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -40,11 +41,13 @@ void CopyOutputRequest::SendBitmapResult(scoped_ptr<SkBitmap> bitmap) { SendResult(CopyOutputResult::CreateBitmapResult(bitmap.Pass()).Pass()); } -void CopyOutputRequest::SendTextureResult(gfx::Size size, - scoped_ptr<TextureMailbox> texture) { - DCHECK(texture->IsTexture()); - SendResult(CopyOutputResult::CreateTextureResult(size, - texture.Pass()).Pass()); +void CopyOutputRequest::SendTextureResult( + gfx::Size size, + const TextureMailbox& texture_mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { + DCHECK(texture_mailbox.IsTexture()); + SendResult(CopyOutputResult::CreateTextureResult( + size, texture_mailbox, release_callback.Pass())); } } // namespace cc diff --git a/chromium/cc/output/copy_output_request.h b/chromium/cc/output/copy_output_request.h index 3f4d925ee16..c4a92481dbc 100644 --- a/chromium/cc/output/copy_output_request.h +++ b/chromium/cc/output/copy_output_request.h @@ -14,6 +14,7 @@ class SkBitmap; namespace cc { class CopyOutputResult; +class SingleReleaseCallback; class TextureMailbox; class CC_EXPORT CopyOutputRequest { @@ -61,7 +62,8 @@ class CC_EXPORT CopyOutputRequest { void SendEmptyResult(); void SendBitmapResult(scoped_ptr<SkBitmap> bitmap); void SendTextureResult(gfx::Size size, - scoped_ptr<TextureMailbox> texture_mailbox); + const TextureMailbox& texture_mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); void SendResult(scoped_ptr<CopyOutputResult> result); diff --git a/chromium/cc/output/copy_output_result.cc b/chromium/cc/output/copy_output_result.cc index 55213cde6b8..1831fba3be1 100644 --- a/chromium/cc/output/copy_output_result.cc +++ b/chromium/cc/output/copy_output_result.cc @@ -5,6 +5,7 @@ #include "cc/output/copy_output_result.h" #include "base/logging.h" +#include "cc/resources/single_release_callback.h" #include "cc/resources/texture_mailbox.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -18,25 +19,32 @@ CopyOutputResult::CopyOutputResult(scoped_ptr<SkBitmap> bitmap) DCHECK(bitmap_); } -CopyOutputResult::CopyOutputResult(gfx::Size size, - scoped_ptr<TextureMailbox> texture_mailbox) +CopyOutputResult::CopyOutputResult( + gfx::Size size, + const TextureMailbox& texture_mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) : size_(size), - texture_mailbox_(texture_mailbox.Pass()) { - DCHECK(texture_mailbox_); - DCHECK(texture_mailbox_->IsTexture()); + texture_mailbox_(texture_mailbox), + release_callback_(release_callback.Pass()) { + DCHECK(texture_mailbox_.IsTexture()); } CopyOutputResult::~CopyOutputResult() { - if (texture_mailbox_) - texture_mailbox_->RunReleaseCallback(0, false); + if (release_callback_) + release_callback_->Run(0, false); } scoped_ptr<SkBitmap> CopyOutputResult::TakeBitmap() { return bitmap_.Pass(); } -scoped_ptr<TextureMailbox> CopyOutputResult::TakeTexture() { - return texture_mailbox_.Pass(); +void CopyOutputResult::TakeTexture( + TextureMailbox* texture_mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback) { + *texture_mailbox = texture_mailbox_; + *release_callback = release_callback_.Pass(); + + texture_mailbox_ = TextureMailbox(); } } // namespace cc diff --git a/chromium/cc/output/copy_output_result.h b/chromium/cc/output/copy_output_result.h index 04cf2c6d489..c2f011d20fc 100644 --- a/chromium/cc/output/copy_output_result.h +++ b/chromium/cc/output/copy_output_result.h @@ -7,11 +7,13 @@ #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" +#include "cc/resources/texture_mailbox.h" #include "ui/gfx/size.h" class SkBitmap; namespace cc { +class SingleReleaseCallback; class TextureMailbox; class CC_EXPORT CopyOutputResult { @@ -25,29 +27,34 @@ class CC_EXPORT CopyOutputResult { } static scoped_ptr<CopyOutputResult> CreateTextureResult( gfx::Size size, - scoped_ptr<TextureMailbox> texture_mailbox) { - return make_scoped_ptr(new CopyOutputResult(size, texture_mailbox.Pass())); + const TextureMailbox& texture_mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { + return make_scoped_ptr( + new CopyOutputResult(size, texture_mailbox, release_callback.Pass())); } ~CopyOutputResult(); bool IsEmpty() const { return !HasBitmap() && !HasTexture(); } bool HasBitmap() const { return !!bitmap_; } - bool HasTexture() const { return !!texture_mailbox_; } + bool HasTexture() const { return texture_mailbox_.IsValid(); } gfx::Size size() const { return size_; } scoped_ptr<SkBitmap> TakeBitmap(); - scoped_ptr<TextureMailbox> TakeTexture(); + void TakeTexture(TextureMailbox* texture_mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback); private: CopyOutputResult(); explicit CopyOutputResult(scoped_ptr<SkBitmap> bitmap); explicit CopyOutputResult(gfx::Size size, - scoped_ptr<TextureMailbox> texture_mailbox); + const TextureMailbox& texture_mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); gfx::Size size_; scoped_ptr<SkBitmap> bitmap_; - scoped_ptr<TextureMailbox> texture_mailbox_; + TextureMailbox texture_mailbox_; + scoped_ptr<SingleReleaseCallback> release_callback_; }; } // namespace cc diff --git a/chromium/cc/output/delegating_renderer.cc b/chromium/cc/output/delegating_renderer.cc index 0c3051cc6dd..1b4021fbbc0 100644 --- a/chromium/cc/output/delegating_renderer.cc +++ b/chromium/cc/output/delegating_renderer.cc @@ -32,20 +32,21 @@ namespace cc { scoped_ptr<DelegatingRenderer> DelegatingRenderer::Create( RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider) { - scoped_ptr<DelegatingRenderer> renderer( - new DelegatingRenderer(client, output_surface, resource_provider)); + scoped_ptr<DelegatingRenderer> renderer(new DelegatingRenderer( + client, settings, output_surface, resource_provider)); if (!renderer->Initialize()) return scoped_ptr<DelegatingRenderer>(); return renderer.Pass(); } -DelegatingRenderer::DelegatingRenderer( - RendererClient* client, - OutputSurface* output_surface, - ResourceProvider* resource_provider) - : Renderer(client), +DelegatingRenderer::DelegatingRenderer(RendererClient* client, + const LayerTreeSettings* settings, + OutputSurface* output_surface, + ResourceProvider* resource_provider) + : Renderer(client, settings), output_surface_(output_surface), resource_provider_(resource_provider), visible_(true) { @@ -59,57 +60,25 @@ bool DelegatingRenderer::Initialize() { capabilities_.allow_partial_texture_updates = false; capabilities_.using_offscreen_context3d = false; - WebGraphicsContext3D* context3d = resource_provider_->GraphicsContext3D(); - - if (!context3d) { - // Software compositing. + if (!output_surface_->context_provider()) { + // TODO(danakj): Make software compositing work. return true; } + WebGraphicsContext3D* context3d = + output_surface_->context_provider()->Context3d(); + if (!context3d->makeContextCurrent()) return false; - std::string unique_context_name = base::StringPrintf( - "%s-%p", - Settings().compositor_name.c_str(), - context3d); - context3d->pushGroupMarkerEXT(unique_context_name.c_str()); - - std::string extensions_string = - UTF16ToASCII(context3d->getString(GL_EXTENSIONS)); - - std::vector<std::string> extensions; - base::SplitString(extensions_string, ' ', &extensions); - - // TODO(danakj): We need non-GPU-specific paths for these things. This - // renderer shouldn't need to use context3d extensions directly. - bool has_set_visibility = false; - bool has_io_surface = false; - bool has_arb_texture_rect = false; - bool has_egl_image = false; - bool has_map_image = false; - for (size_t i = 0; i < extensions.size(); ++i) { - if (extensions[i] == "GL_CHROMIUM_set_visibility") { - has_set_visibility = true; - } else if (extensions[i] == "GL_CHROMIUM_iosurface") { - has_io_surface = true; - } else if (extensions[i] == "GL_ARB_texture_rectangle") { - has_arb_texture_rect = true; - } else if (extensions[i] == "GL_OES_EGL_image_external") { - has_egl_image = true; - } else if (extensions[i] == "GL_CHROMIUM_map_image") { - has_map_image = true; - } - } - - if (has_io_surface) - DCHECK(has_arb_texture_rect); - - capabilities_.using_set_visibility = has_set_visibility; + const ContextProvider::Capabilities& caps = + output_surface_->context_provider()->ContextCapabilities(); - capabilities_.using_egl_image = has_egl_image; + DCHECK(!caps.iosurface || caps.texture_rectangle); - capabilities_.using_map_image = has_map_image; + capabilities_.using_set_visibility = caps.set_visibility; + capabilities_.using_egl_image = caps.egl_image_external; + capabilities_.using_map_image = settings_->use_map_image && caps.map_image; return true; } @@ -129,8 +98,10 @@ static ResourceProvider::ResourceId AppendToArray( return id; } -void DelegatingRenderer::DrawFrame( - RenderPassList* render_passes_in_draw_order) { +void DelegatingRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order, + ContextProvider* offscreen_context_provider, + float device_scale_factor, + bool allow_partial_swap) { TRACE_EVENT0("cc", "DelegatingRenderer::DrawFrame"); DCHECK(!frame_for_swap_buffers_.delegated_frame_data); @@ -168,14 +139,15 @@ void DelegatingRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { void DelegatingRenderer::ReceiveSwapBuffersAck( const CompositorFrameAck& ack) { - resource_provider_->ReceiveFromParent(ack.resources); + resource_provider_->ReceiveReturnsFromParent(ack.resources); } bool DelegatingRenderer::IsContextLost() { - WebGraphicsContext3D* context3d = resource_provider_->GraphicsContext3D(); - if (!context3d) + ContextProvider* context_provider = output_surface_->context_provider(); + if (!context_provider) return false; - return context3d->getGraphicsResetStatusARB() != GL_NO_ERROR; + return context_provider->Context3d()->getGraphicsResetStatusARB() != + GL_NO_ERROR; } void DelegatingRenderer::SetVisible(bool visible) { @@ -183,27 +155,27 @@ void DelegatingRenderer::SetVisible(bool visible) { return; visible_ = visible; - WebGraphicsContext3D* context = resource_provider_->GraphicsContext3D(); + ContextProvider* context_provider = output_surface_->context_provider(); if (!visible_) { TRACE_EVENT0("cc", "DelegatingRenderer::SetVisible dropping resources"); resource_provider_->ReleaseCachedData(); - if (context) - context->flush(); + if (context_provider) + context_provider->Context3d()->flush(); } if (capabilities_.using_set_visibility) { // We loop visibility to the GPU process, since that's what manages memory. // That will allow it to feed us with memory allocations that we can act // upon. - DCHECK(context); - context->setVisibilityCHROMIUM(visible); + DCHECK(context_provider); + context_provider->Context3d()->setVisibilityCHROMIUM(visible); } } void DelegatingRenderer::SendManagedMemoryStats(size_t bytes_visible, size_t bytes_visible_and_nearby, size_t bytes_allocated) { - WebGraphicsContext3D* context = resource_provider_->GraphicsContext3D(); - if (!context) { + ContextProvider* context_provider = output_surface_->context_provider(); + if (!context_provider) { // TODO(piman): software path. NOTIMPLEMENTED(); return; @@ -213,7 +185,7 @@ void DelegatingRenderer::SendManagedMemoryStats(size_t bytes_visible, stats.bytesVisibleAndNearby = bytes_visible_and_nearby; stats.bytesAllocated = bytes_allocated; stats.backbufferRequested = false; - context->sendManagedMemoryStatsCHROMIUM(&stats); + context_provider->Context3d()->sendManagedMemoryStatsCHROMIUM(&stats); } void DelegatingRenderer::SetDiscardBackBufferWhenNotVisible(bool discard) { diff --git a/chromium/cc/output/delegating_renderer.h b/chromium/cc/output/delegating_renderer.h index 11c8c6bd41d..57a2418c452 100644 --- a/chromium/cc/output/delegating_renderer.h +++ b/chromium/cc/output/delegating_renderer.h @@ -19,6 +19,7 @@ class CC_EXPORT DelegatingRenderer : public Renderer { public: static scoped_ptr<DelegatingRenderer> Create( RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider); virtual ~DelegatingRenderer(); @@ -27,7 +28,10 @@ class CC_EXPORT DelegatingRenderer : public Renderer { virtual bool CanReadPixels() const OVERRIDE; - virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) OVERRIDE; + virtual void DrawFrame(RenderPassList* render_passes_in_draw_order, + ContextProvider* offscreen_context_provider, + float device_scale_factor, + bool allow_partial_swap) OVERRIDE; virtual void Finish() OVERRIDE {} @@ -48,6 +52,7 @@ class CC_EXPORT DelegatingRenderer : public Renderer { private: DelegatingRenderer(RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider); bool Initialize(); diff --git a/chromium/cc/output/delegating_renderer_unittest.cc b/chromium/cc/output/delegating_renderer_unittest.cc index 6cb532fe86b..ffa7e4f6e31 100644 --- a/chromium/cc/output/delegating_renderer_unittest.cc +++ b/chromium/cc/output/delegating_renderer_unittest.cc @@ -66,9 +66,8 @@ class DelegatingRendererTestDraw : public DelegatingRendererTest { DelegatedFrameData* last_frame_data = last_frame.delegated_frame_data.get(); ASSERT_TRUE(last_frame.delegated_frame_data); EXPECT_FALSE(last_frame.gl_frame_data); - EXPECT_EQ( - gfx::Rect(host_impl->device_viewport_size()).ToString(), - last_frame_data->render_pass_list.back()->output_rect.ToString()); + EXPECT_EQ(host_impl->DeviceViewport().ToString(), + last_frame_data->render_pass_list.back()->output_rect.ToString()); EXPECT_EQ(0.5f, last_frame.metadata.min_page_scale_factor); EXPECT_EQ(4.f, last_frame.metadata.max_page_scale_factor); @@ -129,11 +128,12 @@ class DelegatingRendererTestResources : public DelegatingRendererTest { EXPECT_EQ(2u, last_frame.delegated_frame_data->render_pass_list.size()); // Each render pass has 10 resources in it. And the root render pass has a - // mask resource used when drawing the child render pass. The number 10 may - // change if AppendOneOfEveryQuadType() is updated, and the value here - // should be updated accordingly. + // mask resource used when drawing the child render pass, as well as its + // replica (it's added twice). The number 10 may change if + // AppendOneOfEveryQuadType() is updated, and the value here should be + // updated accordingly. EXPECT_EQ( - 21u, last_frame.delegated_frame_data->resource_list.size()); + 22u, last_frame.delegated_frame_data->resource_list.size()); EndTest(); } diff --git a/chromium/cc/output/direct_renderer.cc b/chromium/cc/output/direct_renderer.cc index a664d7df822..aad41ed293f 100644 --- a/chromium/cc/output/direct_renderer.cc +++ b/chromium/cc/output/direct_renderer.cc @@ -8,6 +8,7 @@ #include <vector> #include "base/containers/hash_tables.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "base/debug/trace_event.h" #include "base/metrics/histogram.h" #include "cc/base/math_util.h" @@ -58,7 +59,8 @@ namespace cc { DirectRenderer::DrawingFrame::DrawingFrame() : root_render_pass(NULL), current_render_pass(NULL), - current_texture(NULL) {} + current_texture(NULL), + offscreen_context_provider(NULL) {} DirectRenderer::DrawingFrame::~DrawingFrame() {} @@ -125,9 +127,10 @@ gfx::Rect DirectRenderer::MoveFromDrawToWindowSpace( } DirectRenderer::DirectRenderer(RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider) - : Renderer(client), + : Renderer(client, settings), output_surface_(output_surface), resource_provider_(resource_provider) {} @@ -151,7 +154,8 @@ void DirectRenderer::DecideRenderPassAllocationsForFrame( render_passes_in_draw_order[i]->id, render_passes_in_draw_order[i])); std::vector<RenderPass::Id> passes_to_delete; - ScopedPtrHashMap<RenderPass::Id, CachedResource>::const_iterator pass_iter; + base::ScopedPtrHashMap<RenderPass::Id, CachedResource>::const_iterator + pass_iter; for (pass_iter = render_pass_textures_.begin(); pass_iter != render_pass_textures_.end(); ++pass_iter) { @@ -164,12 +168,13 @@ void DirectRenderer::DecideRenderPassAllocationsForFrame( const RenderPass* render_pass_in_frame = it->second; gfx::Size required_size = RenderPassTextureSize(render_pass_in_frame); - GLenum required_format = RenderPassTextureFormat(render_pass_in_frame); + ResourceFormat required_format = + RenderPassTextureFormat(render_pass_in_frame); CachedResource* texture = pass_iter->second; DCHECK(texture); bool size_appropriate = texture->size().width() >= required_size.width() && - texture->size().height() >= required_size.height(); + texture->size().height() >= required_size.height(); if (texture->id() && (!size_appropriate || texture->format() != required_format)) texture->Free(); @@ -190,7 +195,10 @@ void DirectRenderer::DecideRenderPassAllocationsForFrame( } } -void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order) { +void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order, + ContextProvider* offscreen_context_provider, + float device_scale_factor, + bool allow_partial_swap) { TRACE_EVENT0("cc", "DirectRenderer::DrawFrame"); UMA_HISTOGRAM_COUNTS("Renderer4.renderPassCount", render_passes_in_draw_order->size()); @@ -201,9 +209,11 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order) { DrawingFrame frame; frame.root_render_pass = root_render_pass; frame.root_damage_rect = - Capabilities().using_partial_swap && client_->AllowPartialSwap() ? - root_render_pass->damage_rect : root_render_pass->output_rect; + Capabilities().using_partial_swap && allow_partial_swap + ? root_render_pass->damage_rect + : root_render_pass->output_rect; frame.root_damage_rect.Intersect(gfx::Rect(client_->DeviceViewport().size())); + frame.offscreen_context_provider = offscreen_context_provider; EnsureBackbuffer(); @@ -211,12 +221,12 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order) { // can leave the window at the wrong size if we never draw and the proper // viewport size is never set. output_surface_->Reshape(client_->DeviceViewport().size(), - client_->DeviceScaleFactor()); + device_scale_factor); BeginDrawingFrame(&frame); for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) { RenderPass* pass = render_passes_in_draw_order->at(i); - DrawRenderPass(&frame, pass); + DrawRenderPass(&frame, pass, allow_partial_swap); for (ScopedPtrVector<CopyOutputRequest>::iterator it = pass->copy_requests.begin(); @@ -255,14 +265,33 @@ gfx::RectF DirectRenderer::ComputeScissorRectForRenderPass( return render_pass_scissor; } +bool DirectRenderer::NeedDeviceClip(const DrawingFrame* frame) const { + if (frame->current_render_pass != frame->root_render_pass) + return false; + + return !client_->DeviceClip().Contains(client_->DeviceViewport()); +} + +gfx::Rect DirectRenderer::DeviceClipRect(const DrawingFrame* frame) const { + gfx::Rect device_clip_rect = client_->DeviceClip(); + if (FlippedFramebuffer()) + device_clip_rect.set_y(current_surface_size_.height() - + device_clip_rect.bottom()); + return device_clip_rect; +} + void DirectRenderer::SetScissorStateForQuad(const DrawingFrame* frame, const DrawQuad& quad) { if (quad.isClipped()) { - gfx::RectF quad_scissor_rect = quad.clipRect(); - SetScissorTestRect(MoveFromDrawToWindowSpace(quad_scissor_rect)); - } else { - EnsureScissorTestDisabled(); + SetScissorTestRectInDrawSpace(frame, quad.clipRect()); + return; } + if (NeedDeviceClip(frame)) { + SetScissorTestRect(DeviceClipRect(frame)); + return; + } + + EnsureScissorTestDisabled(); } void DirectRenderer::SetScissorStateForQuadWithRenderPassScissor( @@ -281,31 +310,57 @@ void DirectRenderer::SetScissorStateForQuadWithRenderPassScissor( } *should_skip_quad = false; - SetScissorTestRect(MoveFromDrawToWindowSpace(quad_scissor_rect)); + SetScissorTestRectInDrawSpace(frame, quad_scissor_rect); +} + +void DirectRenderer::SetScissorTestRectInDrawSpace(const DrawingFrame* frame, + gfx::RectF draw_space_rect) { + gfx::Rect window_space_rect = MoveFromDrawToWindowSpace(draw_space_rect); + if (NeedDeviceClip(frame)) + window_space_rect.Intersect(DeviceClipRect(frame)); + SetScissorTestRect(window_space_rect); } void DirectRenderer::FinishDrawingQuadList() {} void DirectRenderer::DrawRenderPass(DrawingFrame* frame, - const RenderPass* render_pass) { + const RenderPass* render_pass, + bool allow_partial_swap) { TRACE_EVENT0("cc", "DirectRenderer::DrawRenderPass"); if (!UseRenderPass(frame, render_pass)) return; bool using_scissor_as_optimization = - Capabilities().using_partial_swap && client_->AllowPartialSwap(); + Capabilities().using_partial_swap && allow_partial_swap; gfx::RectF render_pass_scissor; + bool draw_rect_covers_full_surface = true; + if (frame->current_render_pass == frame->root_render_pass && + !client_->DeviceViewport().Contains( + gfx::Rect(output_surface_->SurfaceSize()))) + draw_rect_covers_full_surface = false; if (using_scissor_as_optimization) { render_pass_scissor = ComputeScissorRectForRenderPass(frame); - SetScissorTestRect(MoveFromDrawToWindowSpace(render_pass_scissor)); + SetScissorTestRectInDrawSpace(frame, render_pass_scissor); + if (!render_pass_scissor.Contains(frame->current_render_pass->output_rect)) + draw_rect_covers_full_surface = false; } if (frame->current_render_pass != frame->root_render_pass || - client_->ShouldClearRootRenderPass()) { - if (!using_scissor_as_optimization) + settings_->should_clear_root_render_pass) { + if (NeedDeviceClip(frame)) { + SetScissorTestRect(DeviceClipRect(frame)); + draw_rect_covers_full_surface = false; + } else if (!using_scissor_as_optimization) { EnsureScissorTestDisabled(); - ClearFramebuffer(frame); + } + + bool has_external_stencil_test = + output_surface_->HasExternalStencilTest() && + frame->current_render_pass == frame->root_render_pass; + + DiscardPixels(has_external_stencil_test, draw_rect_covers_full_surface); + ClearFramebuffer(frame, has_external_stencil_test); } const QuadList& quad_list = render_pass->quad_list; @@ -359,8 +414,8 @@ bool DirectRenderer::UseRenderPass(DrawingFrame* frame, enlarge_pass_texture_amount_.y()); if (!texture->id() && !texture->Allocate(size, - RenderPassTextureFormat(render_pass), - ResourceProvider::TextureUsageFramebuffer)) + ResourceProvider::TextureUsageFramebuffer, + RenderPassTextureFormat(render_pass))) return false; return BindFramebufferToTexture(frame, texture, render_pass->output_rect); @@ -368,7 +423,7 @@ bool DirectRenderer::UseRenderPass(DrawingFrame* frame, bool DirectRenderer::HaveCachedResourcesForRenderPassId(RenderPass::Id id) const { - if (!Settings().cache_render_pass_contents) + if (!settings_->cache_render_pass_contents) return false; CachedResource* texture = render_pass_textures_.get(id); @@ -381,8 +436,9 @@ gfx::Size DirectRenderer::RenderPassTextureSize(const RenderPass* render_pass) { } // static -GLenum DirectRenderer::RenderPassTextureFormat(const RenderPass* render_pass) { - return GL_RGBA; +ResourceFormat DirectRenderer::RenderPassTextureFormat( + const RenderPass* render_pass) { + return RGBA_8888; } } // namespace cc diff --git a/chromium/cc/output/direct_renderer.h b/chromium/cc/output/direct_renderer.h index ffc2b67b8a7..e5ee83c87bd 100644 --- a/chromium/cc/output/direct_renderer.h +++ b/chromium/cc/output/direct_renderer.h @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/callback.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "cc/base/cc_export.h" #include "cc/output/renderer.h" #include "cc/resources/resource_provider.h" @@ -30,7 +31,10 @@ class CC_EXPORT DirectRenderer : public Renderer { const RenderPassList& render_passes_in_draw_order) OVERRIDE; virtual bool HaveCachedResourcesForRenderPassId(RenderPass::Id id) const OVERRIDE; - virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) OVERRIDE; + virtual void DrawFrame(RenderPassList* render_passes_in_draw_order, + ContextProvider* offscreen_context_provider, + float device_scale_factor, + bool allow_partial_swap) OVERRIDE; struct CC_EXPORT DrawingFrame { DrawingFrame(); @@ -44,12 +48,15 @@ class CC_EXPORT DirectRenderer : public Renderer { gfx::Transform projection_matrix; gfx::Transform window_matrix; + + ContextProvider* offscreen_context_provider; }; void SetEnlargePassTextureAmountForTesting(gfx::Vector2d amount); protected: DirectRenderer(RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider); @@ -85,6 +92,8 @@ class CC_EXPORT DirectRenderer : public Renderer { gfx::Size surface_size); gfx::Rect MoveFromDrawToWindowSpace(const gfx::RectF& draw_rect) const; + bool NeedDeviceClip(const DrawingFrame* frame) const; + gfx::Rect DeviceClipRect(const DrawingFrame* frame) const; static gfx::RectF ComputeScissorRectForRenderPass(const DrawingFrame* frame); void SetScissorStateForQuad(const DrawingFrame* frame, const DrawQuad& quad); void SetScissorStateForQuadWithRenderPassScissor( @@ -92,11 +101,15 @@ class CC_EXPORT DirectRenderer : public Renderer { const DrawQuad& quad, const gfx::RectF& render_pass_scissor, bool* should_skip_quad); + void SetScissorTestRectInDrawSpace(const DrawingFrame* frame, + gfx::RectF draw_space_rect); static gfx::Size RenderPassTextureSize(const RenderPass* render_pass); - static GLenum RenderPassTextureFormat(const RenderPass* render_pass); + static ResourceFormat RenderPassTextureFormat(const RenderPass* render_pass); - void DrawRenderPass(DrawingFrame* frame, const RenderPass* render_pass); + void DrawRenderPass(DrawingFrame* frame, + const RenderPass* render_pass, + bool allow_partial_swap); bool UseRenderPass(DrawingFrame* frame, const RenderPass* render_pass); virtual void BindFramebufferToOutputSurface(DrawingFrame* frame) = 0; @@ -105,7 +118,10 @@ class CC_EXPORT DirectRenderer : public Renderer { gfx::Rect target_rect) = 0; virtual void SetDrawViewport(gfx::Rect window_space_viewport) = 0; virtual void SetScissorTestRect(gfx::Rect scissor_rect) = 0; - virtual void ClearFramebuffer(DrawingFrame* frame) = 0; + virtual void DiscardPixels(bool has_external_stencil_test, + bool draw_rect_covers_full_surface) = 0; + virtual void ClearFramebuffer(DrawingFrame* frame, + bool has_external_stencil_test) = 0; virtual void DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) = 0; virtual void BeginDrawingFrame(DrawingFrame* frame) = 0; virtual void FinishDrawingFrame(DrawingFrame* frame) = 0; @@ -120,7 +136,7 @@ class CC_EXPORT DirectRenderer : public Renderer { DrawingFrame* frame, scoped_ptr<CopyOutputRequest> request) = 0; - ScopedPtrHashMap<RenderPass::Id, CachedResource> render_pass_textures_; + base::ScopedPtrHashMap<RenderPass::Id, CachedResource> render_pass_textures_; OutputSurface* output_surface_; ResourceProvider* resource_provider_; diff --git a/chromium/cc/output/filter_operation.cc b/chromium/cc/output/filter_operation.cc index 6a778388ae5..f4e3dde0ce5 100644 --- a/chromium/cc/output/filter_operation.cc +++ b/chromium/cc/output/filter_operation.cc @@ -68,7 +68,7 @@ FilterOperation::FilterOperation(FilterType type, float amount, int inset) memset(matrix_, 0, sizeof(matrix_)); } -// TODO(ajuma): Define a version of ui::Tween::ValueBetween for floats, and use +// TODO(ajuma): Define a version of gfx::Tween::ValueBetween for floats, and use // that instead. static float BlendFloats(float from, float to, double progress) { return from * (1.0 - progress) + to * progress; @@ -147,10 +147,9 @@ static FilterOperation CreateNoOpFilter(FilterOperation::FilterType type) { return FilterOperation::CreateZoomFilter(1.f, 0); case FilterOperation::SATURATING_BRIGHTNESS: return FilterOperation::CreateSaturatingBrightnessFilter(0.f); - default: - NOTREACHED(); - return FilterOperation::CreateEmptyFilter(); } + NOTREACHED(); + return FilterOperation::CreateEmptyFilter(); } static float ClampAmountForFilterType(float amount, @@ -173,10 +172,11 @@ static float ClampAmountForFilterType(float amount, case FilterOperation::SATURATING_BRIGHTNESS: return amount; case FilterOperation::COLOR_MATRIX: - default: NOTREACHED(); return amount; } + NOTREACHED(); + return amount; } // static diff --git a/chromium/cc/output/filter_operations.cc b/chromium/cc/output/filter_operations.cc index 24208418026..e526f5c419b 100644 --- a/chromium/cc/output/filter_operations.cc +++ b/chromium/cc/output/filter_operations.cc @@ -86,7 +86,16 @@ bool FilterOperations::HasFilterThatMovesPixels() const { case FilterOperation::DROP_SHADOW: case FilterOperation::ZOOM: return true; - default: + case FilterOperation::OPACITY: + case FilterOperation::COLOR_MATRIX: + case FilterOperation::GRAYSCALE: + case FilterOperation::SEPIA: + case FilterOperation::SATURATE: + case FilterOperation::HUE_ROTATE: + case FilterOperation::INVERT: + case FilterOperation::BRIGHTNESS: + case FilterOperation::CONTRAST: + case FilterOperation::SATURATING_BRIGHTNESS: break; } } @@ -104,10 +113,22 @@ bool FilterOperations::HasFilterThatAffectsOpacity() const { return true; case FilterOperation::COLOR_MATRIX: { const SkScalar* matrix = op.matrix(); - return matrix[15] || matrix[16] || matrix[17] || matrix[18] != 1 || - matrix[19]; + if (matrix[15] || + matrix[16] || + matrix[17] || + matrix[18] != 1 || + matrix[19]) + return true; + break; } - default: + case FilterOperation::GRAYSCALE: + case FilterOperation::SEPIA: + case FilterOperation::SATURATE: + case FilterOperation::HUE_ROTATE: + case FilterOperation::INVERT: + case FilterOperation::BRIGHTNESS: + case FilterOperation::CONTRAST: + case FilterOperation::SATURATING_BRIGHTNESS: break; } } diff --git a/chromium/cc/output/gl_renderer.cc b/chromium/cc/output/gl_renderer.cc index 2dc699104ab..f4b1e575d39 100644 --- a/chromium/cc/output/gl_renderer.cc +++ b/chromium/cc/output/gl_renderer.cc @@ -33,6 +33,7 @@ #include "cc/resources/layer_quad.h" #include "cc/resources/scoped_resource.h" #include "cc/resources/sync_point_helper.h" +#include "cc/resources/texture_mailbox_deleter.h" #include "cc/trees/damage_tracker.h" #include "cc/trees/proxy.h" #include "cc/trees/single_thread_proxy.h" @@ -126,13 +127,20 @@ struct GLRenderer::PendingAsyncReadPixels { DISALLOW_COPY_AND_ASSIGN(PendingAsyncReadPixels); }; -scoped_ptr<GLRenderer> GLRenderer::Create(RendererClient* client, - OutputSurface* output_surface, - ResourceProvider* resource_provider, - int highp_threshold_min, - bool use_skia_gpu_backend) { - scoped_ptr<GLRenderer> renderer(new GLRenderer( - client, output_surface, resource_provider, highp_threshold_min)); +scoped_ptr<GLRenderer> GLRenderer::Create( + RendererClient* client, + const LayerTreeSettings* settings, + OutputSurface* output_surface, + ResourceProvider* resource_provider, + TextureMailboxDeleter* texture_mailbox_deleter, + int highp_threshold_min, + bool use_skia_gpu_backend) { + scoped_ptr<GLRenderer> renderer(new GLRenderer(client, + settings, + output_surface, + resource_provider, + texture_mailbox_deleter, + highp_threshold_min)); if (!renderer->Initialize()) return scoped_ptr<GLRenderer>(); if (use_skia_gpu_backend) { @@ -145,13 +153,16 @@ scoped_ptr<GLRenderer> GLRenderer::Create(RendererClient* client, } GLRenderer::GLRenderer(RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider, + TextureMailboxDeleter* texture_mailbox_deleter, int highp_threshold_min) - : DirectRenderer(client, output_surface, resource_provider), + : DirectRenderer(client, settings, output_surface, resource_provider), offscreen_framebuffer_id_(0), shared_geometry_quad_(gfx::RectF(-0.5f, -0.5f, 1.0f, 1.0f)), - context_(output_surface->context3d()), + context_(output_surface->context_provider()->Context3d()), + texture_mailbox_deleter_(texture_mailbox_deleter), is_backbuffer_discarded_(false), discard_backbuffer_when_not_visible_(false), is_using_bind_uniform_(false), @@ -161,9 +172,7 @@ GLRenderer::GLRenderer(RendererClient* client, blend_shadow_(false), highp_threshold_min_(highp_threshold_min), highp_threshold_cache_(0), - offscreen_context_labelled_(false), - on_demand_tile_raster_resource_id_(0), - weak_factory_(this) { + on_demand_tile_raster_resource_id_(0) { DCHECK(context_); } @@ -171,31 +180,17 @@ bool GLRenderer::Initialize() { if (!context_->makeContextCurrent()) return false; - std::string unique_context_name = base::StringPrintf( - "%s-%p", - Settings().compositor_name.c_str(), - context_); - context_->pushGroupMarkerEXT(unique_context_name.c_str()); - - std::string extensions_string = - UTF16ToASCII(context_->getString(GL_EXTENSIONS)); - std::vector<std::string> extensions_list; - base::SplitString(extensions_string, ' ', &extensions_list); - std::set<std::string> extensions(extensions_list.begin(), - extensions_list.end()); + ContextProvider::Capabilities context_caps = + output_surface_->context_provider()->ContextCapabilities(); capabilities_.using_partial_swap = - Settings().partial_swap_enabled && - extensions.count("GL_CHROMIUM_post_sub_buffer"); + settings_->partial_swap_enabled && context_caps.post_sub_buffer; - capabilities_.using_set_visibility = - extensions.count("GL_CHROMIUM_set_visibility") > 0; + capabilities_.using_set_visibility = context_caps.set_visibility; - if (extensions.count("GL_CHROMIUM_iosurface") > 0) - DCHECK_GT(extensions.count("GL_ARB_texture_rectangle"), 0u); + DCHECK(!context_caps.iosurface || context_caps.texture_rectangle); - capabilities_.using_egl_image = - extensions.count("GL_OES_EGL_image_external") > 0; + capabilities_.using_egl_image = context_caps.egl_image_external; capabilities_.max_texture_size = resource_provider_->max_texture_size(); capabilities_.best_texture_format = resource_provider_->best_texture_format(); @@ -205,17 +200,17 @@ bool GLRenderer::Initialize() { // Check for texture fast paths. Currently we always use MO8 textures, // so we only need to avoid POT textures if we have an NPOT fast-path. - capabilities_.avoid_pow2_textures = - extensions.count("GL_CHROMIUM_fast_NPOT_MO8_textures") > 0; + capabilities_.avoid_pow2_textures = context_caps.fast_npot_mo8_textures; capabilities_.using_offscreen_context3d = true; capabilities_.using_map_image = - extensions.count("GL_CHROMIUM_map_image") > 0 && - Settings().use_map_image; + settings_->use_map_image && context_caps.map_image; + + capabilities_.using_discard_framebuffer = + context_caps.discard_framebuffer; - is_using_bind_uniform_ = - extensions.count("GL_CHROMIUM_bind_uniform_location") > 0; + is_using_bind_uniform_ = context_caps.bind_uniform_location; if (!InitializeSharedObjects()) return false; @@ -244,7 +239,6 @@ GLRenderer::~GLRenderer() { pending_async_read_pixels_.pop_back(); } - context_->setMemoryAllocationChangedCallbackCHROMIUM(NULL); CleanupSharedObjects(); } @@ -296,11 +290,25 @@ void GLRenderer::ViewportChanged() { ReinitializeGrCanvas(); } -void GLRenderer::ClearFramebuffer(DrawingFrame* frame) { +void GLRenderer::DiscardPixels(bool has_external_stencil_test, + bool draw_rect_covers_full_surface) { + if (has_external_stencil_test || !draw_rect_covers_full_surface || + !capabilities_.using_discard_framebuffer) + return; + bool using_default_framebuffer = + !current_framebuffer_lock_ && + output_surface_->capabilities().uses_default_gl_framebuffer; + GLenum attachments[] = {static_cast<GLenum>( + using_default_framebuffer ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0_EXT)}; + context_->discardFramebufferEXT( + GL_FRAMEBUFFER, arraysize(attachments), attachments); +} + +void GLRenderer::ClearFramebuffer(DrawingFrame* frame, + bool has_external_stencil_test) { // It's unsafe to clear when we have a stencil test because glClear ignores // stencil. - if (client_->ExternalStencilTestEnabled() && - frame->current_render_pass == frame->root_render_pass) { + if (has_external_stencil_test) { DCHECK(!frame->current_render_pass->has_transparent_background); return; } @@ -471,13 +479,12 @@ void GLRenderer::DrawDebugBorderQuad(const DrawingFrame* frame, } static inline SkBitmap ApplyFilters(GLRenderer* renderer, + ContextProvider* offscreen_contexts, const FilterOperations& filters, ScopedResource* source_texture_resource) { if (filters.IsEmpty()) return SkBitmap(); - ContextProvider* offscreen_contexts = - renderer->resource_provider()->offscreen_context_provider(); if (!offscreen_contexts || !offscreen_contexts->GrContext()) return SkBitmap(); @@ -492,9 +499,6 @@ static inline SkBitmap ApplyFilters(GLRenderer* renderer, // Make sure skia uses the correct GL context. offscreen_contexts->Context3d()->makeContextCurrent(); - // Lazily label this context. - renderer->LazyLabelOffscreenContext(); - SkBitmap source = RenderSurfaceFilters::Apply(filters, lock.texture_id(), @@ -510,18 +514,18 @@ static inline SkBitmap ApplyFilters(GLRenderer* renderer, offscreen_contexts->Context3d()->flush(); // Use the compositor's GL context again. - renderer->resource_provider()->GraphicsContext3D()->makeContextCurrent(); + renderer->Context()->makeContextCurrent(); return source; } static SkBitmap ApplyImageFilter(GLRenderer* renderer, + ContextProvider* offscreen_contexts, + gfx::Point origin, SkImageFilter* filter, ScopedResource* source_texture_resource) { if (!filter) return SkBitmap(); - ContextProvider* offscreen_contexts = - renderer->resource_provider()->offscreen_context_provider(); if (!offscreen_contexts || !offscreen_contexts->GrContext()) return SkBitmap(); @@ -536,9 +540,6 @@ static SkBitmap ApplyImageFilter(GLRenderer* renderer, // Make sure skia uses the correct GL context. offscreen_contexts->Context3d()->makeContextCurrent(); - // Lazily label this context. - renderer->LazyLabelOffscreenContext(); - // Wrap the source texture in a Ganesh platform texture. GrBackendTextureDesc backend_texture_description; backend_texture_description.fWidth = source_texture_resource->size().width(); @@ -581,6 +582,11 @@ static SkBitmap ApplyImageFilter(GLRenderer* renderer, SkPaint paint; paint.setImageFilter(filter); canvas.clear(SK_ColorTRANSPARENT); + + // TODO(senorblanco): in addition to the origin translation here, the canvas + // should also be scaled to accomodate device pixel ratio and pinch zoom. See + // crbug.com/281516 and crbug.com/281518. + canvas.translate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y())); canvas.drawSprite(source, 0, 0, &paint); // Flush skia context so that all the rendered stuff appears on the @@ -592,7 +598,7 @@ static SkBitmap ApplyImageFilter(GLRenderer* renderer, offscreen_contexts->Context3d()->flush(); // Use the compositor's GL context again. - renderer->resource_provider()->GraphicsContext3D()->makeContextCurrent(); + renderer->Context()->makeContextCurrent(); return device.accessBitmap(false); } @@ -651,8 +657,8 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( scoped_ptr<ScopedResource> device_background_texture = ScopedResource::create(resource_provider_); if (!device_background_texture->Allocate(window_rect.size(), - GL_RGB, - ResourceProvider::TextureUsageAny)) { + ResourceProvider::TextureUsageAny, + RGBA_8888)) { return scoped_ptr<ScopedResource>(); } else { ResourceProvider::ScopedWriteLockGL lock(resource_provider_, @@ -663,7 +669,10 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( } SkBitmap filtered_device_background = - ApplyFilters(this, filters, device_background_texture.get()); + ApplyFilters(this, + frame->offscreen_context_provider, + filters, + device_background_texture.get()); if (!filtered_device_background.getTexture()) return scoped_ptr<ScopedResource>(); @@ -674,8 +683,8 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( scoped_ptr<ScopedResource> background_texture = ScopedResource::create(resource_provider_); if (!background_texture->Allocate(quad->rect.size(), - GL_RGBA, - ResourceProvider::TextureUsageFramebuffer)) + ResourceProvider::TextureUsageFramebuffer, + RGBA_8888)) return scoped_ptr<ScopedResource>(); const RenderPass* target_render_pass = frame->current_render_pass; @@ -777,8 +786,11 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, // in the compositor. use_color_matrix = true; } else { - filter_bitmap = - ApplyImageFilter(this, quad->filter.get(), contents_texture); + filter_bitmap = ApplyImageFilter(this, + frame->offscreen_context_provider, + quad->rect.origin(), + quad->filter.get(), + contents_texture); } } else if (!quad->filters.IsEmpty()) { FilterOperations optimized_filters = @@ -790,7 +802,10 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, color_matrix, optimized_filters.at(0).matrix(), sizeof(color_matrix)); use_color_matrix = true; } else { - filter_bitmap = ApplyFilters(this, optimized_filters, contents_texture); + filter_bitmap = ApplyFilters(this, + frame->offscreen_context_provider, + optimized_filters, + contents_texture); } } @@ -1130,11 +1145,12 @@ static void SolidColorUniformLocation(T program, uniforms->color_location = program->fragment_shader().color_location(); } +// static bool GLRenderer::SetupQuadForAntialiasing( const gfx::Transform& device_transform, const DrawQuad* quad, gfx::QuadF* local_quad, - float edge[24]) const { + float edge[24]) { gfx::Rect tile_rect = quad->visible_rect; bool clipped = false; @@ -1145,11 +1161,8 @@ bool GLRenderer::SetupQuadForAntialiasing( bool is_nearest_rect_within_epsilon = is_axis_aligned_in_target && gfx::IsNearestRectWithinDistance(device_layer_quad.BoundingBox(), kAntiAliasingEpsilon); - - bool use_aa = Settings().allow_antialiasing && - !clipped && // code can't handle clipped quads - !is_nearest_rect_within_epsilon && - quad->IsEdge(); + // AAing clipped quads is not supported by the code yet. + bool use_aa = !clipped && !is_nearest_rect_within_epsilon && quad->IsEdge(); if (!use_aa) return false; @@ -1238,8 +1251,9 @@ void GLRenderer::DrawSolidColorQuad(const DrawingFrame* frame, gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect)); float edge[24]; - bool use_aa = !quad->force_anti_aliasing_off && SetupQuadForAntialiasing( - device_transform, quad, &local_quad, edge); + bool use_aa = + settings_->allow_antialiasing && !quad->force_anti_aliasing_off && + SetupQuadForAntialiasing(device_transform, quad, &local_quad, edge); SolidColorProgramUniforms uniforms; if (use_aa) @@ -1324,20 +1338,11 @@ void GLRenderer::DrawContentQuad(const DrawingFrame* frame, ResourceProvider::ResourceId resource_id) { gfx::Rect tile_rect = quad->visible_rect; - gfx::RectF tex_coord_rect = quad->tex_coord_rect; - float tex_to_geom_scale_x = quad->rect.width() / tex_coord_rect.width(); - float tex_to_geom_scale_y = quad->rect.height() / tex_coord_rect.height(); - - // tex_coord_rect corresponds to quad_rect, but quad_visible_rect may be - // smaller than quad_rect due to occlusion or clipping. Adjust - // tex_coord_rect to match. - gfx::Vector2d top_left_diff = tile_rect.origin() - quad->rect.origin(); - gfx::Vector2d bottom_right_diff = - tile_rect.bottom_right() - quad->rect.bottom_right(); - tex_coord_rect.Inset(top_left_diff.x() / tex_to_geom_scale_x, - top_left_diff.y() / tex_to_geom_scale_y, - -bottom_right_diff.x() / tex_to_geom_scale_x, - -bottom_right_diff.y() / tex_to_geom_scale_y); + gfx::RectF tex_coord_rect = MathUtil::ScaleRectProportional( + quad->tex_coord_rect, quad->rect, tile_rect); + float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width(); + float tex_to_geom_scale_y = + quad->rect.height() / quad->tex_coord_rect.height(); gfx::RectF clamp_geom_rect(tile_rect); gfx::RectF clamp_tex_rect(tex_coord_rect); @@ -1386,7 +1391,7 @@ void GLRenderer::DrawContentQuad(const DrawingFrame* frame, gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect)); float edge[24]; - bool use_aa = SetupQuadForAntialiasing( + bool use_aa = settings_->allow_antialiasing && SetupQuadForAntialiasing( device_transform, quad, &local_quad, edge); TileProgramUniforms uniforms; @@ -1701,20 +1706,37 @@ void GLRenderer::DrawPictureQuad(const DrawingFrame* frame, on_demand_tile_raster_resource_id_ = resource_provider_->CreateGLTexture( quad->texture_size, - GL_RGBA, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + quad->texture_format); } - SkDevice device(on_demand_tile_raster_bitmap_); + SkBitmapDevice device(on_demand_tile_raster_bitmap_); SkCanvas canvas(&device); quad->picture_pile->RasterToBitmap(&canvas, quad->content_rect, quad->contents_scale, NULL); + uint8_t* bitmap_pixels = NULL; + SkBitmap on_demand_tile_raster_bitmap_dest; + SkBitmap::Config config = SkBitmapConfigFromFormat(quad->texture_format); + if (on_demand_tile_raster_bitmap_.getConfig() != config) { + on_demand_tile_raster_bitmap_.copyTo(&on_demand_tile_raster_bitmap_dest, + config); + // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the + // bitmap data. This check will be removed once crbug.com/293728 is fixed. + CHECK_EQ(0u, on_demand_tile_raster_bitmap_dest.rowBytes() % 4); + bitmap_pixels = reinterpret_cast<uint8_t*>( + on_demand_tile_raster_bitmap_dest.getPixels()); + } else { + bitmap_pixels = reinterpret_cast<uint8_t*>( + on_demand_tile_raster_bitmap_.getPixels()); + } + resource_provider_->SetPixels( on_demand_tile_raster_resource_id_, - reinterpret_cast<uint8_t*>(on_demand_tile_raster_bitmap_.getPixels()), + bitmap_pixels, gfx::Rect(quad->texture_size), gfx::Rect(quad->texture_size), gfx::Vector2d()); @@ -1965,11 +1987,8 @@ void GLRenderer::CopyCurrentRenderPassToBitmap( DrawingFrame* frame, scoped_ptr<CopyOutputRequest> request) { gfx::Rect copy_rect = frame->current_render_pass->output_rect; - if (request->has_area()) { - // Intersect with the request's area, positioned with its origin at the - // origin of the full copy_rect. - copy_rect.Intersect(request->area() - copy_rect.OffsetFromOrigin()); - } + if (request->has_area()) + copy_rect.Intersect(request->area()); GetFramebufferPixelsAsync(copy_rect, request.Pass()); } @@ -2095,7 +2114,7 @@ void GLRenderer::SwapBuffers() { compositor_frame.metadata = client_->MakeCompositorFrameMetadata(); compositor_frame.gl_frame_data = make_scoped_ptr(new GLFrameData); compositor_frame.gl_frame_data->size = output_surface_->SurfaceSize(); - if (capabilities_.using_partial_swap && client_->AllowPartialSwap()) { + if (capabilities_.using_partial_swap) { // If supported, we can save significant bandwidth by only swapping the // damaged/scissored region (clamped to the viewport) swap_buffer_rect_.Intersect(client_->DeviceViewport()); @@ -2178,30 +2197,6 @@ void GLRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { AsyncGetFramebufferPixelsCleanupCallback()); } -void GLRenderer::DeleteTextureReleaseCallbackOnImplThread(unsigned texture_id, - unsigned sync_point, - bool lost_resource) { - if (sync_point) - context_->waitSyncPoint(sync_point); - context_->deleteTexture(texture_id); -} - -// static -void GLRenderer::DeleteTextureReleaseCallback( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - base::WeakPtr<GLRenderer> gl_renderer, - unsigned texture_id, - unsigned sync_point, - bool lost_resource) { - task_runner->PostTask( - FROM_HERE, - base::Bind(&GLRenderer::DeleteTextureReleaseCallbackOnImplThread, - gl_renderer, - texture_id, - sync_point, - lost_resource)); -} - void GLRenderer::GetFramebufferPixelsAsync( gfx::Rect rect, scoped_ptr<CopyOutputRequest> request) { DCHECK(!request->IsEmpty()); @@ -2210,10 +2205,6 @@ void GLRenderer::GetFramebufferPixelsAsync( if (rect.IsEmpty()) return; - DCHECK(gfx::Rect(current_surface_size_).Contains(rect)) << - "current_surface_size_: " << current_surface_size_.ToString() << - " rect: " << rect.ToString(); - gfx::Rect window_rect = MoveFromDrawToWindowSpace(rect); if (!request->force_bitmap_result()) { @@ -2227,7 +2218,7 @@ void GLRenderer::GetFramebufferPixelsAsync( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GLC(context_, context_->texParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GetFramebufferTexture(texture_id, GL_RGBA, window_rect); + GetFramebufferTexture(texture_id, RGBA_8888, window_rect); gpu::Mailbox mailbox; unsigned sync_point = 0; @@ -2243,15 +2234,13 @@ void GLRenderer::GetFramebufferPixelsAsync( GL_TEXTURE_2D, mailbox.name)); GLC(context_, context_->bindTexture(GL_TEXTURE_2D, 0)); sync_point = context_->insertSyncPoint(); - scoped_ptr<TextureMailbox> texture_mailbox = make_scoped_ptr( - new TextureMailbox(mailbox, - base::Bind(&GLRenderer::DeleteTextureReleaseCallback, - base::MessageLoopProxy::current(), - weak_factory_.GetWeakPtr(), - texture_id), - GL_TEXTURE_2D, - sync_point)); - request->SendTextureResult(window_rect.size(), texture_mailbox.Pass()); + TextureMailbox texture_mailbox(mailbox, GL_TEXTURE_2D, sync_point); + scoped_ptr<SingleReleaseCallback> release_callback = + texture_mailbox_deleter_->GetReleaseCallback( + output_surface_->context_provider(), texture_id); + request->SendTextureResult(window_rect.size(), + texture_mailbox, + release_callback.Pass()); return; } @@ -2321,7 +2310,7 @@ void GLRenderer::DoGetFramebufferPixels( // Copy the contents of the current (IOSurface-backed) framebuffer into a // temporary texture. GetFramebufferTexture(temporary_texture, - GL_RGBA, + RGBA_8888, gfx::Rect(current_surface_size_)); temporary_fbo = context_->createFramebuffer(); // Attach this texture to an FBO, and perform the readback from that FBO. @@ -2348,7 +2337,7 @@ void GLRenderer::DoGetFramebufferPixels( if (is_async) { query = context_->createQueryEXT(); GLC(context_, context_->beginQueryEXT( - GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM, + GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, query)); } @@ -2389,7 +2378,7 @@ void GLRenderer::DoGetFramebufferPixels( if (is_async) { GLC(context_, context_->endQueryEXT( - GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM)); + GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM)); SyncPointHelper::SignalQuery( context_, query, @@ -2471,9 +2460,8 @@ void GLRenderer::PassOnSkBitmap( request->SendBitmapResult(bitmap.Pass()); } -void GLRenderer::GetFramebufferTexture(unsigned texture_id, - unsigned texture_format, - gfx::Rect window_rect) { +void GLRenderer::GetFramebufferTexture( + unsigned texture_id, ResourceFormat texture_format, gfx::Rect window_rect) { DCHECK(texture_id); DCHECK_GE(window_rect.x(), 0); DCHECK_GE(window_rect.y(), 0); @@ -2482,14 +2470,15 @@ void GLRenderer::GetFramebufferTexture(unsigned texture_id, GLC(context_, context_->bindTexture(GL_TEXTURE_2D, texture_id)); GLC(context_, - context_->copyTexImage2D(GL_TEXTURE_2D, - 0, - texture_format, - window_rect.x(), - window_rect.y(), - window_rect.width(), - window_rect.height(), - 0)); + context_->copyTexImage2D( + GL_TEXTURE_2D, + 0, + ResourceProvider::GetGLDataFormat(texture_format), + window_rect.x(), + window_rect.y(), + window_rect.width(), + window_rect.height(), + 0)); GLC(context_, context_->bindTexture(GL_TEXTURE_2D, 0)); } @@ -2507,7 +2496,7 @@ void GLRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) { current_framebuffer_lock_.reset(); output_surface_->BindFramebuffer(); - if (client_->ExternalStencilTestEnabled()) { + if (output_surface_->HasExternalStencilTest()) { SetStencilEnabled(true); GLC(context_, context_->stencilFunc(GL_EQUAL, 1, 1)); } else { @@ -3114,7 +3103,7 @@ void GLRenderer::ReinitializeGrCanvas() { skia::RefPtr<GrSurface> surface( skia::AdoptRef(gr_context_->wrapBackendRenderTarget(desc))); - skia::RefPtr<SkDevice> device( + skia::RefPtr<SkBaseDevice> device( skia::AdoptRef(SkGpuDevice::Create(surface.get()))); sk_canvas_ = skia::AdoptRef(new SkCanvas(device.get())); } @@ -3149,17 +3138,4 @@ bool GLRenderer::IsContextLost() { return (context_->getGraphicsResetStatusARB() != GL_NO_ERROR); } -void GLRenderer::LazyLabelOffscreenContext() { - if (offscreen_context_labelled_) - return; - offscreen_context_labelled_ = true; - std::string unique_context_name = base::StringPrintf( - "%s-Offscreen-%p", - Settings().compositor_name.c_str(), - context_); - resource_provider()->offscreen_context_provider()->Context3d()-> - pushGroupMarkerEXT(unique_context_name.c_str()); -} - - } // namespace cc diff --git a/chromium/cc/output/gl_renderer.h b/chromium/cc/output/gl_renderer.h index b1a3643b2ba..bc47870063d 100644 --- a/chromium/cc/output/gl_renderer.h +++ b/chromium/cc/output/gl_renderer.h @@ -33,17 +33,21 @@ class PictureDrawQuad; class ScopedResource; class StreamVideoDrawQuad; class TextureDrawQuad; +class TextureMailboxDeleter; class GeometryBinding; class ScopedEnsureFramebufferAllocation; // Class that handles drawing of composited render layers using GL. class CC_EXPORT GLRenderer : public DirectRenderer { public: - static scoped_ptr<GLRenderer> Create(RendererClient* client, - OutputSurface* output_surface, - ResourceProvider* resource_provider, - int highp_threshold_min, - bool use_skia_gpu_backend); + static scoped_ptr<GLRenderer> Create( + RendererClient* client, + const LayerTreeSettings* settings, + OutputSurface* output_surface, + ResourceProvider* resource_provider, + TextureMailboxDeleter* texture_mailbox_deleter, + int highp_threshold_min, + bool use_skia_gpu_backend); virtual ~GLRenderer(); @@ -77,12 +81,13 @@ class CC_EXPORT GLRenderer : public DirectRenderer { int line); bool CanUseSkiaGPUBackend() const; - void LazyLabelOffscreenContext(); protected: GLRenderer(RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider, + TextureMailboxDeleter* texture_mailbox_deleter, int highp_threshold_min); bool IsBackbufferDiscarded() const { return is_backbuffer_discarded_; } @@ -97,7 +102,7 @@ class CC_EXPORT GLRenderer : public DirectRenderer { void GetFramebufferPixelsAsync(gfx::Rect rect, scoped_ptr<CopyOutputRequest> request); void GetFramebufferTexture(unsigned texture_id, - unsigned texture_format, + ResourceFormat texture_format, gfx::Rect device_rect); void ReleaseRenderPassTextures(); @@ -112,7 +117,10 @@ class CC_EXPORT GLRenderer : public DirectRenderer { gfx::Rect target_rect) OVERRIDE; virtual void SetDrawViewport(gfx::Rect window_space_viewport) OVERRIDE; virtual void SetScissorTestRect(gfx::Rect scissor_rect) OVERRIDE; - virtual void ClearFramebuffer(DrawingFrame* frame) OVERRIDE; + virtual void DiscardPixels(bool has_external_stencil_test, + bool draw_rect_covers_full_surface) OVERRIDE; + virtual void ClearFramebuffer(DrawingFrame* frame, + bool has_external_stencil_test) OVERRIDE; virtual void DoDrawQuad(DrawingFrame* frame, const class DrawQuad*) OVERRIDE; virtual void BeginDrawingFrame(DrawingFrame* frame) OVERRIDE; virtual void FinishDrawingFrame(DrawingFrame* frame) OVERRIDE; @@ -124,6 +132,17 @@ class CC_EXPORT GLRenderer : public DirectRenderer { scoped_ptr<CopyOutputRequest> request) OVERRIDE; virtual void FinishDrawingQuadList() OVERRIDE; + // Check if quad needs antialiasing and if so, inflate the quad and + // fill edge array for fragment shader. local_quad is set to + // inflated quad if antialiasing is required, otherwise it is left + // unchanged. edge array is filled with inflated quad's edge data + // if antialiasing is required, otherwise it is left unchanged. + // Returns true if quad requires antialiasing and false otherwise. + static bool SetupQuadForAntialiasing(const gfx::Transform& device_transform, + const DrawQuad* quad, + gfx::QuadF* local_quad, + float edge[24]); + private: friend class GLRendererShaderPixelTest; friend class GLRendererShaderTest; @@ -174,17 +193,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer { const gfx::Transform& draw_matrix, bool flip_vertically); - // Check if quad needs antialiasing and if so, inflate the quad and - // fill edge array for fragment shader. local_quad is set to - // inflated quad if antialiasing is required, otherwise it is left - // unchanged. edge array is filled with inflated quad's edge data - // if antialiasing is required, otherwise it is left unchanged. - // Returns true if quad requires antialiasing and false otherwise. - bool SetupQuadForAntialiasing(const gfx::Transform& device_transform, - const DrawQuad* quad, - gfx::QuadF* local_quad, - float edge[24]) const; - bool UseScopedTexture(DrawingFrame* frame, const ScopedResource* resource, gfx::Rect viewport_rect); @@ -212,16 +220,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer { scoped_ptr<CopyOutputRequest> request, bool success); - static void DeleteTextureReleaseCallback( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - base::WeakPtr<GLRenderer> gl_renderer, - unsigned texture_id, - unsigned sync_point, - bool lost_resource); - void DeleteTextureReleaseCallbackOnImplThread(unsigned texture_id, - unsigned sync_point, - bool lost_resource); - void ReinitializeGrCanvas(); void ReinitializeGLState(); @@ -433,6 +431,8 @@ class CC_EXPORT GLRenderer : public DirectRenderer { skia::RefPtr<GrContext> gr_context_; skia::RefPtr<SkCanvas> sk_canvas_; + TextureMailboxDeleter* texture_mailbox_deleter_; + gfx::Rect swap_buffer_rect_; gfx::Rect scissor_rect_; gfx::Rect viewport_; @@ -447,7 +447,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer { TexturedQuadDrawCache draw_cache_; int highp_threshold_min_; int highp_threshold_cache_; - bool offscreen_context_labelled_; struct PendingAsyncReadPixels; ScopedPtrVector<PendingAsyncReadPixels> pending_async_read_pixels_; @@ -459,8 +458,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer { SkBitmap on_demand_tile_raster_bitmap_; ResourceProvider::ResourceId on_demand_tile_raster_resource_id_; - base::WeakPtrFactory<GLRenderer> weak_factory_; - DISALLOW_COPY_AND_ASSIGN(GLRenderer); }; diff --git a/chromium/cc/output/gl_renderer_unittest.cc b/chromium/cc/output/gl_renderer_unittest.cc index e4213d65842..836287f4448 100644 --- a/chromium/cc/output/gl_renderer_unittest.cc +++ b/chromium/cc/output/gl_renderer_unittest.cc @@ -7,6 +7,7 @@ #include <set> #include "cc/base/math_util.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/output/compositor_frame_metadata.h" #include "cc/resources/prioritized_resource_manager.h" #include "cc/resources/resource_provider.h" @@ -14,11 +15,11 @@ #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/mock_quad_culler.h" #include "cc/test/pixel_test.h" #include "cc/test/render_pass_test_common.h" #include "cc/test/render_pass_test_utils.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -31,7 +32,9 @@ using testing::_; using testing::AnyNumber; +using testing::Args; using testing::AtLeast; +using testing::ElementsAre; using testing::Expectation; using testing::InSequence; using testing::Mock; @@ -116,19 +119,16 @@ TEST_F(GLRendererShaderPixelTest, AllShadersCompile) { TestShaders(); } class FrameCountingContext : public TestWebGraphicsContext3D { public: - FrameCountingContext() : frame_(0) {} + FrameCountingContext() + : frame_(0) { + test_capabilities_.set_visibility = true; + test_capabilities_.discard_backbuffer = true; + } // WebGraphicsContext3D methods. // This method would normally do a glSwapBuffers under the hood. virtual void prepareTexture() { frame_++; } - virtual WebString getString(WebKit::WGC3Denum name) { - if (name == GL_EXTENSIONS) - return WebString( - "GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager " - "GL_CHROMIUM_discard_backbuffer"); - return WebString(); - } // Methods added for test. int frame_count() { return frame_; } @@ -143,9 +143,8 @@ class FakeRendererClient : public RendererClient { : host_impl_(&proxy_), set_full_root_layer_damage_count_(0), root_layer_(LayerImpl::Create(host_impl_.active_tree(), 1)), - viewport_size_(gfx::Size(1, 1)), - scale_factor_(1.f), - external_stencil_test_enabled_(false) { + viewport_(gfx::Rect(0, 0, 1, 1)), + clip_(gfx::Rect(0, 0, 1, 1)) { root_layer_->CreateRenderSurface(); RenderPass::Id render_pass_id = root_layer_->render_surface()->RenderPassId(); @@ -156,45 +155,21 @@ class FakeRendererClient : public RendererClient { } // RendererClient methods. - virtual gfx::Rect DeviceViewport() const OVERRIDE { - static gfx::Size fake_size(1, 1); - return gfx::Rect(fake_size); - } - virtual float DeviceScaleFactor() const OVERRIDE { - return scale_factor_; - } - virtual const LayerTreeSettings& Settings() const OVERRIDE { - static LayerTreeSettings fake_settings; - return fake_settings; - } + virtual gfx::Rect DeviceViewport() const OVERRIDE { return viewport_; } + virtual gfx::Rect DeviceClip() const OVERRIDE { return clip_; } virtual void SetFullRootLayerDamage() OVERRIDE { set_full_root_layer_damage_count_++; } - virtual bool HasImplThread() const OVERRIDE { return false; } - virtual bool ShouldClearRootRenderPass() const OVERRIDE { return true; } virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE { return CompositorFrameMetadata(); } - virtual bool AllowPartialSwap() const OVERRIDE { - return true; - } - virtual bool ExternalStencilTestEnabled() const OVERRIDE { - return external_stencil_test_enabled_; - } - - void EnableExternalStencilTest() { - external_stencil_test_enabled_ = true; - } // Methods added for test. int set_full_root_layer_damage_count() const { return set_full_root_layer_damage_count_; } - void set_viewport_and_scale( - gfx::Size viewport_size, float scale_factor) { - viewport_size_ = viewport_size; - scale_factor_ = scale_factor; - } + void set_viewport(gfx::Rect viewport) { viewport_ = viewport; } + void set_clip(gfx::Rect clip) { clip_ = clip; } RenderPass* root_render_pass() { return render_passes_in_draw_order_.back(); } RenderPassList* render_passes_in_draw_order() { @@ -207,17 +182,22 @@ class FakeRendererClient : public RendererClient { int set_full_root_layer_damage_count_; scoped_ptr<LayerImpl> root_layer_; RenderPassList render_passes_in_draw_order_; - gfx::Size viewport_size_; - float scale_factor_; - bool external_stencil_test_enabled_; + gfx::Rect viewport_; + gfx::Rect clip_; }; class FakeRendererGL : public GLRenderer { public: FakeRendererGL(RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider) - : GLRenderer(client, output_surface, resource_provider, 0) {} + : GLRenderer(client, + settings, + output_surface, + resource_provider, + NULL, + 0) {} // GLRenderer methods. @@ -232,27 +212,33 @@ class FakeRendererGL : public GLRenderer { class GLRendererTest : public testing::Test { protected: - GLRendererTest() - : output_surface_(FakeOutputSurface::Create3d( - scoped_ptr<WebKit::WebGraphicsContext3D>( - new FrameCountingContext()))), - resource_provider_(ResourceProvider::Create(output_surface_.get(), 0)), - renderer_(&mock_client_, - output_surface_.get(), - resource_provider_.get()) {} - - virtual void SetUp() { renderer_.Initialize(); } + GLRendererTest() { + scoped_ptr<FrameCountingContext> context3d(new FrameCountingContext); + context3d_ = context3d.get(); - void SwapBuffers() { renderer_.SwapBuffers(); } + output_surface_ = FakeOutputSurface::Create3d( + context3d.PassAs<TestWebGraphicsContext3D>()).Pass(); + CHECK(output_surface_->BindToClient(&output_surface_client_)); - FrameCountingContext* Context() { - return static_cast<FrameCountingContext*>(output_surface_->context3d()); + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false).Pass(); + renderer_ = make_scoped_ptr(new FakeRendererGL(&renderer_client_, + &settings_, + output_surface_.get(), + resource_provider_.get())); } - scoped_ptr<OutputSurface> output_surface_; - FakeRendererClient mock_client_; + virtual void SetUp() { renderer_->Initialize(); } + + void SwapBuffers() { renderer_->SwapBuffers(); } + + LayerTreeSettings settings_; + FrameCountingContext* context3d_; + FakeOutputSurfaceClient output_surface_client_; + scoped_ptr<FakeOutputSurface> output_surface_; + FakeRendererClient renderer_client_; scoped_ptr<ResourceProvider> resource_provider_; - FakeRendererGL renderer_; + scoped_ptr<FakeRendererGL> renderer_; }; // Closing the namespace here so that GLRendererShaderTest can take advantage @@ -320,15 +306,18 @@ class ShaderCreatorMockGraphicsContext : public TestWebGraphicsContext3D { class GLRendererShaderTest : public testing::Test { protected: - GLRendererShaderTest() - : output_surface_(FakeOutputSurface::Create3d( - scoped_ptr<WebKit::WebGraphicsContext3D>( - new ShaderCreatorMockGraphicsContext()))), - resource_provider_(ResourceProvider::Create(output_surface_.get(), 0)), - renderer_(scoped_ptr<FakeRendererGL>( - new FakeRendererGL(&mock_client_, - output_surface_.get(), - resource_provider_.get()))) { + GLRendererShaderTest() { + output_surface_ = FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>( + new ShaderCreatorMockGraphicsContext())).Pass(); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + + resource_provider_ = ResourceProvider::Create( + output_surface_.get(), 0, false).Pass(); + renderer_.reset(new FakeRendererGL(&renderer_client_, + &settings_, + output_surface_.get(), + resource_provider_.get())); renderer_->Initialize(); } @@ -386,8 +375,10 @@ class GLRendererShaderTest : public testing::Test { renderer_->program_shadow_); } - scoped_ptr<OutputSurface> output_surface_; - FakeRendererClient mock_client_; + LayerTreeSettings settings_; + FakeOutputSurfaceClient output_surface_client_; + scoped_ptr<FakeOutputSurface> output_surface_; + FakeRendererClient renderer_client_; scoped_ptr<ResourceProvider> resource_provider_; scoped_ptr<FakeRendererGL> renderer_; }; @@ -398,12 +389,12 @@ namespace { // Suggest recreating framebuffer when one already exists. // Expected: it does nothing. TEST_F(GLRendererTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing) { - renderer_.SetDiscardBackBufferWhenNotVisible(false); - EXPECT_EQ(0, mock_client_.set_full_root_layer_damage_count()); - EXPECT_FALSE(renderer_.IsBackbufferDiscarded()); + renderer_->SetDiscardBackBufferWhenNotVisible(false); + EXPECT_EQ(0, renderer_client_.set_full_root_layer_damage_count()); + EXPECT_FALSE(renderer_->IsBackbufferDiscarded()); SwapBuffers(); - EXPECT_EQ(1, Context()->frame_count()); + EXPECT_EQ(1, context3d_->frame_count()); } // Test GLRenderer DiscardBackbuffer functionality: @@ -413,76 +404,79 @@ TEST_F(GLRendererTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing) { TEST_F( GLRendererTest, SuggestBackbufferNoShouldDiscardBackbufferAndDamageRootLayerIfNotVisible) { - renderer_.SetVisible(false); - renderer_.SetDiscardBackBufferWhenNotVisible(true); - EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count()); - EXPECT_TRUE(renderer_.IsBackbufferDiscarded()); + renderer_->SetVisible(false); + renderer_->SetDiscardBackBufferWhenNotVisible(true); + EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); + EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); } // Test GLRenderer DiscardBackbuffer functionality: // Suggest discarding framebuffer when one exists and the renderer is visible. // Expected: the allocation is ignored. TEST_F(GLRendererTest, SuggestBackbufferNoDoNothingWhenVisible) { - renderer_.SetVisible(true); - renderer_.SetDiscardBackBufferWhenNotVisible(true); - EXPECT_EQ(0, mock_client_.set_full_root_layer_damage_count()); - EXPECT_FALSE(renderer_.IsBackbufferDiscarded()); + renderer_->SetVisible(true); + renderer_->SetDiscardBackBufferWhenNotVisible(true); + EXPECT_EQ(0, renderer_client_.set_full_root_layer_damage_count()); + EXPECT_FALSE(renderer_->IsBackbufferDiscarded()); } // Test GLRenderer DiscardBackbuffer functionality: // Suggest discarding framebuffer when one does not exist. // Expected: it does nothing. TEST_F(GLRendererTest, SuggestBackbufferNoWhenItDoesntExistShouldDoNothing) { - renderer_.SetVisible(false); - renderer_.SetDiscardBackBufferWhenNotVisible(true); - EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count()); - EXPECT_TRUE(renderer_.IsBackbufferDiscarded()); - - renderer_.SetDiscardBackBufferWhenNotVisible(true); - EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count()); - EXPECT_TRUE(renderer_.IsBackbufferDiscarded()); + renderer_->SetVisible(false); + renderer_->SetDiscardBackBufferWhenNotVisible(true); + EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); + EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); + + renderer_->SetDiscardBackBufferWhenNotVisible(true); + EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); + EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); } // Test GLRenderer DiscardBackbuffer functionality: // Begin drawing a frame while a framebuffer is discarded. // Expected: will recreate framebuffer. TEST_F(GLRendererTest, DiscardedBackbufferIsRecreatedForScopeDuration) { - renderer_.SetVisible(false); - renderer_.SetDiscardBackBufferWhenNotVisible(true); - EXPECT_TRUE(renderer_.IsBackbufferDiscarded()); - EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count()); + renderer_->SetVisible(false); + renderer_->SetDiscardBackBufferWhenNotVisible(true); + EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); + EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); - renderer_.SetVisible(true); - renderer_.DrawFrame(mock_client_.render_passes_in_draw_order()); - EXPECT_FALSE(renderer_.IsBackbufferDiscarded()); + renderer_->SetVisible(true); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_FALSE(renderer_->IsBackbufferDiscarded()); SwapBuffers(); - EXPECT_EQ(1, Context()->frame_count()); + EXPECT_EQ(1, context3d_->frame_count()); } TEST_F(GLRendererTest, FramebufferDiscardedAfterReadbackWhenNotVisible) { - renderer_.SetVisible(false); - renderer_.SetDiscardBackBufferWhenNotVisible(true); - EXPECT_TRUE(renderer_.IsBackbufferDiscarded()); - EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count()); + renderer_->SetVisible(false); + renderer_->SetDiscardBackBufferWhenNotVisible(true); + EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); + EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count()); char pixels[4]; - renderer_.DrawFrame(mock_client_.render_passes_in_draw_order()); - EXPECT_FALSE(renderer_.IsBackbufferDiscarded()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_FALSE(renderer_->IsBackbufferDiscarded()); - renderer_.GetFramebufferPixels(pixels, gfx::Rect(0, 0, 1, 1)); - EXPECT_TRUE(renderer_.IsBackbufferDiscarded()); - EXPECT_EQ(2, mock_client_.set_full_root_layer_damage_count()); + renderer_->GetFramebufferPixels(pixels, gfx::Rect(0, 0, 1, 1)); + EXPECT_TRUE(renderer_->IsBackbufferDiscarded()); + EXPECT_EQ(2, renderer_client_.set_full_root_layer_damage_count()); } TEST_F(GLRendererTest, ExternalStencil) { - EXPECT_FALSE(renderer_.stencil_enabled()); + EXPECT_FALSE(renderer_->stencil_enabled()); - mock_client_.EnableExternalStencilTest(); - mock_client_.root_render_pass()->has_transparent_background = false; + output_surface_->set_has_external_stencil_test(true); + renderer_client_.root_render_pass()->has_transparent_background = false; - renderer_.DrawFrame(mock_client_.render_passes_in_draw_order()); - EXPECT_TRUE(renderer_.stencil_enabled()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_TRUE(renderer_->stencil_enabled()); } class ForbidSynchronousCallContext : public TestWebGraphicsContext3D { @@ -562,11 +556,7 @@ class ForbidSynchronousCallContext : public TestWebGraphicsContext3D { } virtual WebString getString(WGC3Denum name) { - // We allow querying the extension string. - // TODO(enne): It'd be better to check that we only do this before starting - // any other expensive work (like starting a compilation) - if (name != GL_EXTENSIONS) - ADD_FAILURE(); + ADD_FAILURE() << name; return WebString(); } @@ -638,14 +628,20 @@ class ForbidSynchronousCallContext : public TestWebGraphicsContext3D { // This test isn't using the same fixture as GLRendererTest, and you can't mix // TEST() and TEST_F() with the same name, Hence LRC2. TEST(GLRendererTest2, InitializationDoesNotMakeSynchronousCalls) { - FakeRendererClient mock_client; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new ForbidSynchronousCallContext))); + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new ForbidSynchronousCallContext))); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &mock_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); EXPECT_TRUE(renderer.Initialize()); } @@ -677,81 +673,145 @@ class LoseContextOnFirstGetContext : public TestWebGraphicsContext3D { }; TEST(GLRendererTest2, InitializationWithQuicklyLostContextDoesNotAssert) { - FakeRendererClient mock_client; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new LoseContextOnFirstGetContext))); + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new LoseContextOnFirstGetContext))); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &mock_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); renderer.Initialize(); } class ClearCountingContext : public TestWebGraphicsContext3D { public: - ClearCountingContext() : clear_(0) {} - - virtual void clear(WGC3Dbitfield) { clear_++; } - - int clear_count() const { return clear_; } + ClearCountingContext() { + test_capabilities_.discard_framebuffer = true; + } - private: - int clear_; + MOCK_METHOD3(discardFramebufferEXT, + void(WGC3Denum target, + WGC3Dsizei numAttachments, + const WGC3Denum* attachments)); + MOCK_METHOD1(clear, void(WGC3Dbitfield mask)); }; TEST(GLRendererTest2, OpaqueBackground) { - FakeRendererClient mock_client; + scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext); + ClearCountingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( - scoped_ptr<WebKit::WebGraphicsContext3D>(new ClearCountingContext))); - ClearCountingContext* context = - static_cast<ClearCountingContext*>(output_surface->context3d()); + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &mock_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); - mock_client.root_render_pass()->has_transparent_background = false; + LayerTreeSettings settings; + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); - EXPECT_TRUE(renderer.Initialize()); + renderer_client.root_render_pass()->has_transparent_background = false; - renderer.DrawFrame(mock_client.render_passes_in_draw_order()); + EXPECT_TRUE(renderer.Initialize()); -// On DEBUG builds, render passes with opaque background clear to blue to -// easily see regions that were not drawn on the screen. + // On DEBUG builds, render passes with opaque background clear to blue to + // easily see regions that were not drawn on the screen. + EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, _, _)) + .With(Args<2, 1>(ElementsAre(GL_COLOR_EXT))) + .Times(1); #ifdef NDEBUG - EXPECT_EQ(0, context->clear_count()); + EXPECT_CALL(*context, clear(_)).Times(0); #else - EXPECT_EQ(1, context->clear_count()); + EXPECT_CALL(*context, clear(_)).Times(1); #endif + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); + Mock::VerifyAndClearExpectations(context); } TEST(GLRendererTest2, TransparentBackground) { - FakeRendererClient mock_client; + scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext); + ClearCountingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( - scoped_ptr<WebKit::WebGraphicsContext3D>(new ClearCountingContext))); - ClearCountingContext* context = - static_cast<ClearCountingContext*>(output_surface->context3d()); + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &mock_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); - mock_client.root_render_pass()->has_transparent_background = true; + LayerTreeSettings settings; + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); + + renderer_client.root_render_pass()->has_transparent_background = true; EXPECT_TRUE(renderer.Initialize()); - renderer.DrawFrame(mock_client.render_passes_in_draw_order()); + EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, 1, _)) + .Times(1); + EXPECT_CALL(*context, clear(_)).Times(1); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); - EXPECT_EQ(1, context->clear_count()); + Mock::VerifyAndClearExpectations(context); +} + +TEST(GLRendererTest2, OffscreenOutputSurface) { + scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext); + ClearCountingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::CreateOffscreen( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + + scoped_ptr<ResourceProvider> resource_provider( + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); + + EXPECT_TRUE(renderer.Initialize()); + + EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, _, _)) + .With(Args<2, 1>(ElementsAre(GL_COLOR_ATTACHMENT0))) + .Times(1); + EXPECT_CALL(*context, clear(_)).Times(AnyNumber()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); + Mock::VerifyAndClearExpectations(context); } class VisibilityChangeIsLastCallTrackingContext : public TestWebGraphicsContext3D { public: VisibilityChangeIsLastCallTrackingContext() - : last_call_was_set_visibility_(false) {} + : last_call_was_set_visibility_(false) { + test_capabilities_.set_visibility = true; + test_capabilities_.discard_backbuffer = true; + } // WebGraphicsContext3D methods. virtual void setVisibilityCHROMIUM(bool visible) { @@ -780,15 +840,6 @@ class VisibilityChangeIsLastCallTrackingContext last_call_was_set_visibility_ = false; } - // This method would normally do a glSwapBuffers under the hood. - virtual WebString getString(WebKit::WGC3Denum name) { - if (name == GL_EXTENSIONS) - return WebString( - "GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager " - "GL_CHROMIUM_discard_backbuffer"); - return WebString(); - } - // Methods added for test. bool last_call_was_set_visibility() const { return last_call_was_set_visibility_; @@ -799,17 +850,24 @@ class VisibilityChangeIsLastCallTrackingContext }; TEST(GLRendererTest2, VisibilityChangeIsLastCall) { - FakeRendererClient mock_client; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new VisibilityChangeIsLastCallTrackingContext))); - VisibilityChangeIsLastCallTrackingContext* context = - static_cast<VisibilityChangeIsLastCallTrackingContext*>( - output_surface->context3d()); + scoped_ptr<VisibilityChangeIsLastCallTrackingContext> context_owned( + new VisibilityChangeIsLastCallTrackingContext); + VisibilityChangeIsLastCallTrackingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &mock_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); EXPECT_TRUE(renderer.Initialize()); @@ -819,19 +877,17 @@ TEST(GLRendererTest2, VisibilityChangeIsLastCall) { // RenderClient and the Context by giving them both a pointer to a variable on // the stack. renderer.SetVisible(true); - renderer.DrawFrame(mock_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); renderer.SetVisible(false); EXPECT_TRUE(context->last_call_was_set_visibility()); } class TextureStateTrackingContext : public TestWebGraphicsContext3D { public: - TextureStateTrackingContext() : active_texture_(GL_INVALID_ENUM) {} - - virtual WebString getString(WGC3Denum name) { - if (name == GL_EXTENSIONS) - return WebString("GL_OES_EGL_image_external"); - return WebString(); + TextureStateTrackingContext() + : active_texture_(GL_INVALID_ENUM) { + test_capabilities_.egl_image_external = true; } MOCK_METHOD3(texParameteri, @@ -854,16 +910,24 @@ class TextureStateTrackingContext : public TestWebGraphicsContext3D { }; TEST(GLRendererTest2, ActiveTextureState) { - FakeRendererClient fake_client; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new TextureStateTrackingContext))); - TextureStateTrackingContext* context = - static_cast<TextureStateTrackingContext*>(output_surface->context3d()); + scoped_ptr<TextureStateTrackingContext> context_owned( + new TextureStateTrackingContext); + TextureStateTrackingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &fake_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); // During initialization we are allowed to set any texture parameters. EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber()); @@ -922,11 +986,6 @@ TEST(GLRendererTest2, ActiveTextureState) { Mock::VerifyAndClearExpectations(context); } -class NoClearRootRenderPassFakeClient : public FakeRendererClient { - public: - virtual bool ShouldClearRootRenderPass() const OVERRIDE { return false; } -}; - class NoClearRootRenderPassMockContext : public TestWebGraphicsContext3D { public: MOCK_METHOD1(clear, void(WGC3Dbitfield mask)); @@ -938,22 +997,31 @@ class NoClearRootRenderPassMockContext : public TestWebGraphicsContext3D { }; TEST(GLRendererTest2, ShouldClearRootRenderPass) { - NoClearRootRenderPassFakeClient mock_client; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new NoClearRootRenderPassMockContext))); - NoClearRootRenderPassMockContext* mock_context = - static_cast<NoClearRootRenderPassMockContext*>( - output_surface->context3d()); + scoped_ptr<NoClearRootRenderPassMockContext> mock_context_owned( + new NoClearRootRenderPassMockContext); + NoClearRootRenderPassMockContext* mock_context = mock_context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + mock_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &mock_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + settings.should_clear_root_render_pass = false; + + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); EXPECT_TRUE(renderer.Initialize()); - gfx::Rect viewport_rect(mock_client.DeviceViewport()); + gfx::Rect viewport_rect(renderer_client.DeviceViewport()); ScopedPtrVector<RenderPass>& render_passes = - *mock_client.render_passes_in_draw_order(); + *renderer_client.render_passes_in_draw_order(); render_passes.clear(); RenderPass::Id root_pass_id(1, 0); @@ -988,8 +1056,9 @@ TEST(GLRendererTest2, ShouldClearRootRenderPass) { .After(first_render_pass); renderer.DecideRenderPassAllocationsForFrame( - *mock_client.render_passes_in_draw_order()); - renderer.DrawFrame(mock_client.render_passes_in_draw_order()); + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); // In multiple render passes all but the root pass should clear the // framebuffer. @@ -1017,20 +1086,29 @@ class ScissorTestOnClearCheckingContext : public TestWebGraphicsContext3D { }; TEST(GLRendererTest2, ScissorTestWhenClearing) { - FakeRendererClient mock_client; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new ScissorTestOnClearCheckingContext))); + scoped_ptr<ScissorTestOnClearCheckingContext> context_owned( + new ScissorTestOnClearCheckingContext); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &mock_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + FakeRendererClient renderer_client; + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); EXPECT_TRUE(renderer.Initialize()); EXPECT_FALSE(renderer.Capabilities().using_partial_swap); - gfx::Rect viewport_rect(mock_client.DeviceViewport()); + gfx::Rect viewport_rect(renderer_client.DeviceViewport()); ScopedPtrVector<RenderPass>& render_passes = - *mock_client.render_passes_in_draw_order(); + *renderer_client.render_passes_in_draw_order(); render_passes.clear(); gfx::Rect grand_child_rect(25, 25); @@ -1054,25 +1132,190 @@ TEST(GLRendererTest2, ScissorTestWhenClearing) { AddRenderPassQuad(child_pass, grand_child_pass); renderer.DecideRenderPassAllocationsForFrame( - *mock_client.render_passes_in_draw_order()); - renderer.DrawFrame(mock_client.render_passes_in_draw_order()); + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); } -class NonReshapableOutputSurface : public FakeOutputSurface { +class DiscardCheckingContext : public TestWebGraphicsContext3D { public: - explicit NonReshapableOutputSurface( - scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : FakeOutputSurface(context3d.Pass(), false) {} - virtual gfx::Size SurfaceSize() const OVERRIDE { return gfx::Size(500, 500); } + DiscardCheckingContext() : discarded_(0) { + set_have_post_sub_buffer(true); + set_have_discard_framebuffer(true); + } + + virtual void discardFramebufferEXT(WGC3Denum target, + WGC3Dsizei numAttachments, + const WGC3Denum* attachments) { + ++discarded_; + } + + int discarded() const { return discarded_; } + void reset() { discarded_ = 0; } + + private: + int discarded_; }; -class OffsetViewportRendererClient : public FakeRendererClient { +class NonReshapableOutputSurface : public FakeOutputSurface { public: - virtual gfx::Rect DeviceViewport() const OVERRIDE { - return gfx::Rect(10, 10, 100, 100); + explicit NonReshapableOutputSurface( + scoped_ptr<TestWebGraphicsContext3D> context3d) + : FakeOutputSurface(TestContextProvider::Create(context3d.Pass()), + false) { + surface_size_ = gfx::Size(500, 500); } + virtual void Reshape(gfx::Size size, float scale_factor) OVERRIDE {} + void set_fixed_size(gfx::Size size) { surface_size_ = size; } }; +TEST(GLRendererTest2, NoDiscardOnPartialUpdates) { + scoped_ptr<DiscardCheckingContext> context_owned(new DiscardCheckingContext); + DiscardCheckingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<NonReshapableOutputSurface> output_surface( + new NonReshapableOutputSurface( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + output_surface->set_fixed_size(gfx::Size(100, 100)); + + scoped_ptr<ResourceProvider> resource_provider( + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + settings.partial_swap_enabled = true; + FakeRendererClient renderer_client; + renderer_client.set_viewport(gfx::Rect(0, 0, 100, 100)); + renderer_client.set_clip(gfx::Rect(0, 0, 100, 100)); + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); + EXPECT_TRUE(renderer.Initialize()); + EXPECT_TRUE(renderer.Capabilities().using_partial_swap); + + gfx::Rect viewport_rect(renderer_client.DeviceViewport()); + ScopedPtrVector<RenderPass>& render_passes = + *renderer_client.render_passes_in_draw_order(); + render_passes.clear(); + + { + // Partial frame, should not discard. + RenderPass::Id root_pass_id(1, 0); + TestRenderPass* root_pass = AddRenderPass( + &render_passes, root_pass_id, viewport_rect, gfx::Transform()); + AddQuad(root_pass, viewport_rect, SK_ColorGREEN); + root_pass->damage_rect = gfx::RectF(2.f, 2.f, 3.f, 3.f); + + renderer.DecideRenderPassAllocationsForFrame( + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_EQ(0, context->discarded()); + context->reset(); + } + { + // Full frame, should discard. + RenderPass::Id root_pass_id(1, 0); + TestRenderPass* root_pass = AddRenderPass( + &render_passes, root_pass_id, viewport_rect, gfx::Transform()); + AddQuad(root_pass, viewport_rect, SK_ColorGREEN); + root_pass->damage_rect = gfx::RectF(root_pass->output_rect); + + renderer.DecideRenderPassAllocationsForFrame( + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_EQ(1, context->discarded()); + context->reset(); + } + { + // Partial frame, disallow partial swap, should discard. + RenderPass::Id root_pass_id(1, 0); + TestRenderPass* root_pass = AddRenderPass( + &render_passes, root_pass_id, viewport_rect, gfx::Transform()); + AddQuad(root_pass, viewport_rect, SK_ColorGREEN); + root_pass->damage_rect = gfx::RectF(2.f, 2.f, 3.f, 3.f); + + renderer.DecideRenderPassAllocationsForFrame( + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, false); + EXPECT_EQ(1, context->discarded()); + context->reset(); + } + { + // Full frame, external scissor is set, should not discard. + output_surface->set_has_external_stencil_test(true); + RenderPass::Id root_pass_id(1, 0); + TestRenderPass* root_pass = AddRenderPass( + &render_passes, root_pass_id, viewport_rect, gfx::Transform()); + AddQuad(root_pass, viewport_rect, SK_ColorGREEN); + root_pass->damage_rect = gfx::RectF(root_pass->output_rect); + root_pass->has_transparent_background = false; + + renderer.DecideRenderPassAllocationsForFrame( + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_EQ(0, context->discarded()); + context->reset(); + output_surface->set_has_external_stencil_test(false); + } + { + // Full frame, clipped, should not discard. + renderer_client.set_clip(gfx::Rect(10, 10, 10, 10)); + RenderPass::Id root_pass_id(1, 0); + TestRenderPass* root_pass = AddRenderPass( + &render_passes, root_pass_id, viewport_rect, gfx::Transform()); + AddQuad(root_pass, viewport_rect, SK_ColorGREEN); + root_pass->damage_rect = gfx::RectF(root_pass->output_rect); + + renderer.DecideRenderPassAllocationsForFrame( + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_EQ(0, context->discarded()); + context->reset(); + } + { + // Full frame, doesn't cover the surface, should not discard. + renderer_client.set_viewport(gfx::Rect(10, 10, 10, 10)); + viewport_rect = renderer_client.DeviceViewport(); + RenderPass::Id root_pass_id(1, 0); + TestRenderPass* root_pass = AddRenderPass( + &render_passes, root_pass_id, viewport_rect, gfx::Transform()); + AddQuad(root_pass, viewport_rect, SK_ColorGREEN); + root_pass->damage_rect = gfx::RectF(root_pass->output_rect); + + renderer.DecideRenderPassAllocationsForFrame( + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_EQ(0, context->discarded()); + context->reset(); + } + { + // Full frame, doesn't cover the surface (no offset), should not discard. + renderer_client.set_viewport(gfx::Rect(0, 0, 50, 50)); + renderer_client.set_clip(gfx::Rect(0, 0, 100, 100)); + viewport_rect = renderer_client.DeviceViewport(); + RenderPass::Id root_pass_id(1, 0); + TestRenderPass* root_pass = AddRenderPass( + &render_passes, root_pass_id, viewport_rect, gfx::Transform()); + AddQuad(root_pass, viewport_rect, SK_ColorGREEN); + root_pass->damage_rect = gfx::RectF(root_pass->output_rect); + + renderer.DecideRenderPassAllocationsForFrame( + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); + EXPECT_EQ(0, context->discarded()); + context->reset(); + } +} + class FlippedScissorAndViewportContext : public TestWebGraphicsContext3D { public: FlippedScissorAndViewportContext() @@ -1108,21 +1351,32 @@ TEST(GLRendererTest2, ScissorAndViewportWithinNonreshapableSurface) { // and maintains a fixed size. This test verifies that glViewport and // glScissor's Y coordinate is flipped correctly in this environment, and that // the glViewport can be at a nonzero origin within the surface. - OffsetViewportRendererClient mock_client; - scoped_ptr<OutputSurface> output_surface(make_scoped_ptr( - new NonReshapableOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D>( - new FlippedScissorAndViewportContext)))); + scoped_ptr<FlippedScissorAndViewportContext> context_owned( + new FlippedScissorAndViewportContext); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(new NonReshapableOutputSurface( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - FakeRendererGL renderer( - &mock_client, output_surface.get(), resource_provider.get()); + ResourceProvider::Create(output_surface.get(), 0, false)); + + LayerTreeSettings settings; + FakeRendererClient renderer_client; + renderer_client.set_viewport(gfx::Rect(10, 10, 100, 100)); + renderer_client.set_clip(gfx::Rect(10, 10, 100, 100)); + FakeRendererGL renderer(&renderer_client, + &settings, + output_surface.get(), + resource_provider.get()); EXPECT_TRUE(renderer.Initialize()); EXPECT_FALSE(renderer.Capabilities().using_partial_swap); - gfx::Rect viewport_rect(mock_client.DeviceViewport().size()); + gfx::Rect viewport_rect(renderer_client.DeviceViewport().size()); gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20); ScopedPtrVector<RenderPass>& render_passes = - *mock_client.render_passes_in_draw_order(); + *renderer_client.render_passes_in_draw_order(); render_passes.clear(); RenderPass::Id root_pass_id(1, 0); @@ -1131,14 +1385,15 @@ TEST(GLRendererTest2, ScissorAndViewportWithinNonreshapableSurface) { AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN); renderer.DecideRenderPassAllocationsForFrame( - *mock_client.render_passes_in_draw_order()); - renderer.DrawFrame(mock_client.render_passes_in_draw_order()); + *renderer_client.render_passes_in_draw_order()); + renderer.DrawFrame( + renderer_client.render_passes_in_draw_order(), NULL, 1.f, true); } TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { - gfx::Rect viewport_rect(mock_client_.DeviceViewport()); + gfx::Rect viewport_rect(renderer_client_.DeviceViewport()); ScopedPtrVector<RenderPass>* render_passes = - mock_client_.render_passes_in_draw_order(); + renderer_client_.render_passes_in_draw_order(); gfx::Rect child_rect(50, 50); RenderPass::Id child_pass_id(2, 0); @@ -1149,8 +1404,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { cc::ResourceProvider::ResourceId mask = resource_provider_->CreateResource(gfx::Size(20, 12), - resource_provider_->best_texture_format(), - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + resource_provider_->best_texture_format()); resource_provider_->AllocateForTesting(mask); SkScalar matrix[20]; @@ -1193,8 +1449,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { gfx::Transform()); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestRenderPassProgram(); // RenderPassColorMatrixProgram @@ -1209,8 +1466,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { AddRenderPassQuad(root_pass, child_pass, 0, filter, gfx::Transform()); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestRenderPassColorMatrixProgram(); // RenderPassMaskProgram @@ -1229,8 +1487,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { gfx::Transform()); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestRenderPassMaskProgram(); // RenderPassMaskColorMatrixProgram @@ -1245,8 +1504,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { AddRenderPassQuad(root_pass, child_pass, mask, filter, gfx::Transform()); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestRenderPassMaskColorMatrixProgram(); // RenderPassProgramAA @@ -1265,8 +1525,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { transform_causing_aa); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestRenderPassProgramAA(); // RenderPassColorMatrixProgramAA @@ -1281,8 +1542,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { AddRenderPassQuad(root_pass, child_pass, 0, filter, transform_causing_aa); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestRenderPassColorMatrixProgramAA(); // RenderPassMaskProgramAA @@ -1298,8 +1560,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { transform_causing_aa); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestRenderPassMaskProgramAA(); // RenderPassMaskColorMatrixProgramAA @@ -1314,8 +1577,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { AddRenderPassQuad(root_pass, child_pass, mask, filter, transform_causing_aa); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestRenderPassMaskColorMatrixProgramAA(); } @@ -1326,7 +1590,7 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) { RenderPass::Id child_pass_id(2, 0); TestRenderPass* child_pass; - gfx::Rect viewport_rect(mock_client_.DeviceViewport()); + gfx::Rect viewport_rect(renderer_client_.DeviceViewport()); RenderPass::Id root_pass_id(1, 0); TestRenderPass* root_pass; @@ -1345,7 +1609,7 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) { // Set up the render pass quad to be drawn ScopedPtrVector<RenderPass>* render_passes = - mock_client_.render_passes_in_draw_order(); + renderer_client_.render_passes_in_draw_order(); render_passes->clear(); @@ -1362,8 +1626,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) { transform_preventing_aa); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); // If use_aa incorrectly ignores clipping, it will use the // RenderPassProgramAA shader instead of the RenderPassProgram. @@ -1371,9 +1636,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) { } TEST_F(GLRendererShaderTest, DrawSolidColorShader) { - gfx::Rect viewport_rect(mock_client_.DeviceViewport()); + gfx::Rect viewport_rect(renderer_client_.DeviceViewport()); ScopedPtrVector<RenderPass>* render_passes = - mock_client_.render_passes_in_draw_order(); + renderer_client_.render_passes_in_draw_order(); RenderPass::Id root_pass_id(1, 0); TestRenderPass* root_pass; @@ -1392,14 +1657,20 @@ TEST_F(GLRendererShaderTest, DrawSolidColorShader) { pixel_aligned_transform_causing_aa); renderer_->DecideRenderPassAllocationsForFrame( - *mock_client_.render_passes_in_draw_order()); - renderer_->DrawFrame(mock_client_.render_passes_in_draw_order()); + *renderer_client_.render_passes_in_draw_order()); + renderer_->DrawFrame( + renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true); TestSolidColorProgramAA(); } class OutputSurfaceMockContext : public TestWebGraphicsContext3D { public: + OutputSurfaceMockContext() { + test_capabilities_.discard_backbuffer = true; + test_capabilities_.post_sub_buffer = true; + } + // Specifically override methods even if they are unused (used in conjunction // with StrictMock). We need to make sure that GLRenderer does not issue // framebuffer-related GL calls directly. Instead these are supposed to go @@ -1415,20 +1686,14 @@ class OutputSurfaceMockContext : public TestWebGraphicsContext3D { WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset)); - - virtual WebString getString(WebKit::WGC3Denum name) { - if (name == GL_EXTENSIONS) - return WebString( - "GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_discard_backbuffer"); - return WebString(); - } }; class MockOutputSurface : public OutputSurface { public: MockOutputSurface() - : OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D>( - new StrictMock<OutputSurfaceMockContext>)) { + : OutputSurface(TestContextProvider::Create( + scoped_ptr<TestWebGraphicsContext3D>( + new StrictMock<OutputSurfaceMockContext>))) { surface_size_ = gfx::Size(100, 100); } virtual ~MockOutputSurface() {} @@ -1442,15 +1707,21 @@ class MockOutputSurface : public OutputSurface { class MockOutputSurfaceTest : public testing::Test, public FakeRendererClient { protected: - MockOutputSurfaceTest() - : resource_provider_(ResourceProvider::Create(&output_surface_, 0)), - renderer_(this, &output_surface_, resource_provider_.get()) {} + virtual void SetUp() { + FakeOutputSurfaceClient output_surface_client_; + CHECK(output_surface_.BindToClient(&output_surface_client_)); - virtual void SetUp() { EXPECT_TRUE(renderer_.Initialize()); } + resource_provider_ = + ResourceProvider::Create(&output_surface_, 0, false).Pass(); - void SwapBuffers() { renderer_.SwapBuffers(); } + renderer_.reset(new FakeRendererGL( + this, &settings_, &output_surface_, resource_provider_.get())); + EXPECT_TRUE(renderer_->Initialize()); + } + + void SwapBuffers() { renderer_->SwapBuffers(); } - void DrawFrame() { + void DrawFrame(float device_scale_factor) { gfx::Rect viewport_rect(DeviceViewport()); ScopedPtrVector<RenderPass>* render_passes = render_passes_in_draw_order(); render_passes->clear(); @@ -1463,55 +1734,59 @@ class MockOutputSurfaceTest : public testing::Test, public FakeRendererClient { EXPECT_CALL(output_surface_, EnsureBackbuffer()).WillRepeatedly(Return()); EXPECT_CALL(output_surface_, - Reshape(DeviceViewport().size(), DeviceScaleFactor())).Times(1); + Reshape(DeviceViewport().size(), device_scale_factor)).Times(1); EXPECT_CALL(output_surface_, BindFramebuffer()).Times(1); EXPECT_CALL(*Context(), drawElements(_, _, _, _)).Times(1); - renderer_.DecideRenderPassAllocationsForFrame( + renderer_->DecideRenderPassAllocationsForFrame( *render_passes_in_draw_order()); - renderer_.DrawFrame(render_passes_in_draw_order()); + renderer_->DrawFrame( + render_passes_in_draw_order(), NULL, device_scale_factor, true); } OutputSurfaceMockContext* Context() { - return static_cast<OutputSurfaceMockContext*>(output_surface_.context3d()); + return static_cast<OutputSurfaceMockContext*>( + output_surface_.context_provider()->Context3d()); } + LayerTreeSettings settings_; + FakeOutputSurfaceClient output_surface_client_; StrictMock<MockOutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; - FakeRendererGL renderer_; + scoped_ptr<FakeRendererGL> renderer_; }; TEST_F(MockOutputSurfaceTest, DrawFrameAndSwap) { - DrawFrame(); + DrawFrame(1.f); EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); - renderer_.SwapBuffers(); + renderer_->SwapBuffers(); } TEST_F(MockOutputSurfaceTest, DrawFrameAndResizeAndSwap) { - DrawFrame(); + DrawFrame(1.f); EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); - renderer_.SwapBuffers(); + renderer_->SwapBuffers(); - set_viewport_and_scale(gfx::Size(2, 2), 2.f); - renderer_.ViewportChanged(); + set_viewport(gfx::Rect(0, 0, 2, 2)); + renderer_->ViewportChanged(); - DrawFrame(); + DrawFrame(2.f); EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); - renderer_.SwapBuffers(); + renderer_->SwapBuffers(); - DrawFrame(); + DrawFrame(2.f); EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); - renderer_.SwapBuffers(); + renderer_->SwapBuffers(); - set_viewport_and_scale(gfx::Size(1, 1), 1.f); - renderer_.ViewportChanged(); + set_viewport(gfx::Rect(0, 0, 1, 1)); + renderer_->ViewportChanged(); - DrawFrame(); + DrawFrame(1.f); EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1); - renderer_.SwapBuffers(); + renderer_->SwapBuffers(); } class GLRendererTestSyncPoint : public GLRendererPixelTest { @@ -1531,20 +1806,21 @@ class GLRendererTestSyncPoint : public GLRendererPixelTest { TEST_F(GLRendererTestSyncPoint, SignalSyncPointOnLostContext) { int sync_point_callback_count = 0; int other_callback_count = 0; - unsigned sync_point = output_surface_->context3d()->insertSyncPoint(); + unsigned sync_point = + output_surface_->context_provider()->Context3d()->insertSyncPoint(); - output_surface_->context3d()->loseContextCHROMIUM( + output_surface_->context_provider()->Context3d()->loseContextCHROMIUM( GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); SyncPointHelper::SignalSyncPoint( - output_surface_->context3d(), + output_surface_->context_provider()->Context3d(), sync_point, base::Bind(&SyncPointCallback, &sync_point_callback_count)); EXPECT_EQ(0, sync_point_callback_count); EXPECT_EQ(0, other_callback_count); // Make the sync point happen. - output_surface_->context3d()->finish(); + output_surface_->context_provider()->Context3d()->finish(); // Post a task after the sync point. base::MessageLoop::current()->PostTask( FROM_HERE, @@ -1560,17 +1836,18 @@ TEST_F(GLRendererTestSyncPoint, SignalSyncPointOnLostContext) { TEST_F(GLRendererTestSyncPoint, SignalSyncPoint) { int sync_point_callback_count = 0; int other_callback_count = 0; - unsigned sync_point = output_surface_->context3d()->insertSyncPoint(); + unsigned sync_point = + output_surface_->context_provider()->Context3d()->insertSyncPoint(); SyncPointHelper::SignalSyncPoint( - output_surface_->context3d(), + output_surface_->context_provider()->Context3d(), sync_point, base::Bind(&SyncPointCallback, &sync_point_callback_count)); EXPECT_EQ(0, sync_point_callback_count); EXPECT_EQ(0, other_callback_count); // Make the sync point happen. - output_surface_->context3d()->finish(); + output_surface_->context_provider()->Context3d()->finish(); // Post a task after the sync point. base::MessageLoop::current()->PostTask( FROM_HERE, diff --git a/chromium/cc/output/output_surface.cc b/chromium/cc/output/output_surface.cc index be990cc0cb7..555fea27911 100644 --- a/chromium/cc/output/output_surface.cc +++ b/chromium/cc/output/output_surface.cc @@ -4,6 +4,7 @@ #include "cc/output/output_surface.h" +#include <algorithm> #include <set> #include <string> #include <vector> @@ -15,11 +16,11 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "cc/output/compositor_frame.h" +#include "cc/output/compositor_frame_ack.h" #include "cc/output/managed_memory_policy.h" #include "cc/output/output_surface_client.h" #include "cc/scheduler/delay_based_time_source.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" -#include "third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" #include "ui/gfx/rect.h" @@ -30,67 +31,9 @@ using std::string; using std::vector; namespace cc { -namespace { - -ManagedMemoryPolicy::PriorityCutoff ConvertPriorityCutoff( - WebKit::WebGraphicsMemoryAllocation::PriorityCutoff priority_cutoff) { - // This is simple a 1:1 map, the names differ only because the WebKit names - // should be to match the cc names. - switch (priority_cutoff) { - case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing: - return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING; - case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly: - return ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY; - case WebKit::WebGraphicsMemoryAllocation:: - PriorityCutoffAllowVisibleAndNearby: - return ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE; - case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything: - return ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING; - } - NOTREACHED(); - return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING; -} - -} // anonymous namespace - -class OutputSurfaceCallbacks - : public WebKit::WebGraphicsContext3D:: - WebGraphicsSwapBuffersCompleteCallbackCHROMIUM, - public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback, - public WebKit::WebGraphicsContext3D:: - WebGraphicsMemoryAllocationChangedCallbackCHROMIUM { - public: - explicit OutputSurfaceCallbacks(OutputSurface* client) - : client_(client) { - DCHECK(client_); - } - - // WK:WGC3D::WGSwapBuffersCompleteCallbackCHROMIUM implementation. - virtual void onSwapBuffersComplete() { client_->OnSwapBuffersComplete(NULL); } - - // WK:WGC3D::WGContextLostCallback implementation. - virtual void onContextLost() { client_->DidLoseOutputSurface(); } - - // WK:WGC3D::WGMemoryAllocationChangedCallbackCHROMIUM implementation. - virtual void onMemoryAllocationChanged( - WebKit::WebGraphicsMemoryAllocation allocation) { - ManagedMemoryPolicy policy( - allocation.bytesLimitWhenVisible, - ConvertPriorityCutoff(allocation.priorityCutoffWhenVisible), - allocation.bytesLimitWhenNotVisible, - ConvertPriorityCutoff(allocation.priorityCutoffWhenNotVisible), - ManagedMemoryPolicy::kDefaultNumResourcesLimit); - bool discard_backbuffer = !allocation.suggestHaveBackbuffer; - client_->SetMemoryPolicy(policy, discard_backbuffer); - } - - private: - OutputSurface* client_; -}; -OutputSurface::OutputSurface( - scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : context3d_(context3d.Pass()), +OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider) + : context_provider_(context_provider), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), @@ -98,10 +41,10 @@ OutputSurface::OutputSurface( max_frames_pending_(0), pending_swap_buffers_(0), needs_begin_frame_(false), - begin_frame_pending_(false), + client_ready_for_begin_frame_(true), client_(NULL), - check_for_retroactive_begin_frame_pending_(false) { -} + check_for_retroactive_begin_frame_pending_(false), + external_stencil_test_enabled_(false) {} OutputSurface::OutputSurface( scoped_ptr<cc::SoftwareOutputDevice> software_device) @@ -113,15 +56,15 @@ OutputSurface::OutputSurface( max_frames_pending_(0), pending_swap_buffers_(0), needs_begin_frame_(false), - begin_frame_pending_(false), + client_ready_for_begin_frame_(true), client_(NULL), - check_for_retroactive_begin_frame_pending_(false) { -} + check_for_retroactive_begin_frame_pending_(false), + external_stencil_test_enabled_(false) {} OutputSurface::OutputSurface( - scoped_ptr<WebKit::WebGraphicsContext3D> context3d, + scoped_refptr<ContextProvider> context_provider, scoped_ptr<cc::SoftwareOutputDevice> software_device) - : context3d_(context3d.Pass()), + : context_provider_(context_provider), software_device_(software_device.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), @@ -130,10 +73,10 @@ OutputSurface::OutputSurface( max_frames_pending_(0), pending_swap_buffers_(0), needs_begin_frame_(false), - begin_frame_pending_(false), + client_ready_for_begin_frame_(true), client_(NULL), - check_for_retroactive_begin_frame_pending_(false) { -} + check_for_retroactive_begin_frame_pending_(false), + external_stencil_test_enabled_(false) {} void OutputSurface::InitializeBeginFrameEmulation( base::SingleThreadTaskRunner* task_runner, @@ -192,7 +135,7 @@ void OutputSurface::SetNeedsRedrawRect(gfx::Rect damage_rect) { void OutputSurface::SetNeedsBeginFrame(bool enable) { TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginFrame", "enable", enable); needs_begin_frame_ = enable; - begin_frame_pending_ = false; + client_ready_for_begin_frame_ = true; if (frame_rate_controller_) { BeginFrameArgs skipped = frame_rate_controller_->SetActive(enable); if (skipped.IsValid()) @@ -204,14 +147,14 @@ void OutputSurface::SetNeedsBeginFrame(bool enable) { void OutputSurface::BeginFrame(const BeginFrameArgs& args) { TRACE_EVENT2("cc", "OutputSurface::BeginFrame", - "begin_frame_pending_", begin_frame_pending_, + "client_ready_for_begin_frame_", client_ready_for_begin_frame_, "pending_swap_buffers_", pending_swap_buffers_); - if (!needs_begin_frame_ || begin_frame_pending_ || + if (!needs_begin_frame_ || !client_ready_for_begin_frame_ || (pending_swap_buffers_ >= max_frames_pending_ && max_frames_pending_ > 0)) { skipped_begin_frame_args_ = args; } else { - begin_frame_pending_ = true; + client_ready_for_begin_frame_ = false; client_->BeginFrame(args); // args might be an alias for skipped_begin_frame_args_. // Do not reset it before calling BeginFrame! @@ -219,8 +162,13 @@ void OutputSurface::BeginFrame(const BeginFrameArgs& args) { } } -base::TimeDelta OutputSurface::RetroactiveBeginFramePeriod() { - return BeginFrameArgs::DefaultRetroactiveBeginFramePeriod(); +base::TimeTicks OutputSurface::RetroactiveBeginFrameDeadline() { + // TODO(brianderson): Remove the alternative deadline once we have better + // deadline estimations. + base::TimeTicks alternative_deadline = + skipped_begin_frame_args_.frame_time + + BeginFrameArgs::DefaultRetroactiveBeginFramePeriod(); + return std::max(skipped_begin_frame_args_.deadline, alternative_deadline); } void OutputSurface::PostCheckForRetroactiveBeginFrame() { @@ -238,18 +186,11 @@ void OutputSurface::PostCheckForRetroactiveBeginFrame() { void OutputSurface::CheckForRetroactiveBeginFrame() { TRACE_EVENT0("cc", "OutputSurface::CheckForRetroactiveBeginFrame"); check_for_retroactive_begin_frame_pending_ = false; - base::TimeTicks now = base::TimeTicks::Now(); - base::TimeTicks alternative_deadline = - skipped_begin_frame_args_.frame_time + - RetroactiveBeginFramePeriod(); - if (now < skipped_begin_frame_args_.deadline || - now < alternative_deadline) { + if (base::TimeTicks::Now() < RetroactiveBeginFrameDeadline()) BeginFrame(skipped_begin_frame_args_); - } } void OutputSurface::DidSwapBuffers() { - begin_frame_pending_ = false; pending_swap_buffers_++; TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers", "pending_swap_buffers_", pending_swap_buffers_); @@ -258,56 +199,63 @@ void OutputSurface::DidSwapBuffers() { PostCheckForRetroactiveBeginFrame(); } -void OutputSurface::OnSwapBuffersComplete(const CompositorFrameAck* ack) { +void OutputSurface::OnSwapBuffersComplete() { pending_swap_buffers_--; TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete", "pending_swap_buffers_", pending_swap_buffers_); - client_->OnSwapBuffersComplete(ack); + client_->OnSwapBuffersComplete(); if (frame_rate_controller_) frame_rate_controller_->DidSwapBuffersComplete(); PostCheckForRetroactiveBeginFrame(); } +void OutputSurface::ReclaimResources(const CompositorFrameAck* ack) { + client_->ReclaimResources(ack); +} + void OutputSurface::DidLoseOutputSurface() { TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface"); - begin_frame_pending_ = false; + client_ready_for_begin_frame_ = true; pending_swap_buffers_ = 0; + skipped_begin_frame_args_ = BeginFrameArgs(); + if (frame_rate_controller_) + frame_rate_controller_->SetActive(false); client_->DidLoseOutputSurface(); } void OutputSurface::SetExternalStencilTest(bool enabled) { - client_->SetExternalStencilTest(enabled); + external_stencil_test_enabled_ = enabled; } void OutputSurface::SetExternalDrawConstraints(const gfx::Transform& transform, - gfx::Rect viewport) { - client_->SetExternalDrawConstraints(transform, viewport); + gfx::Rect viewport, + gfx::Rect clip, + bool valid_for_tile_management) { + client_->SetExternalDrawConstraints( + transform, viewport, clip, valid_for_tile_management); } OutputSurface::~OutputSurface() { if (frame_rate_controller_) frame_rate_controller_->SetActive(false); - - if (context3d_) { - context3d_->setSwapBuffersCompleteCallbackCHROMIUM(NULL); - context3d_->setContextLostCallback(NULL); - context3d_->setMemoryAllocationChangedCallbackCHROMIUM(NULL); - } + ResetContext3d(); } -bool OutputSurface::ForcedDrawToSoftwareDevice() const { - return false; +bool OutputSurface::HasExternalStencilTest() const { + return external_stencil_test_enabled_; } +bool OutputSurface::ForcedDrawToSoftwareDevice() const { return false; } + bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) { DCHECK(client); client_ = client; bool success = true; - if (context3d_) { - success = context3d_->makeContextCurrent(); + if (context_provider_) { + success = context_provider_->BindToCurrentThread(); if (success) - SetContext3D(context3d_.Pass()); + SetUpContext3d(); } if (!success) @@ -316,71 +264,78 @@ bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) { return success; } -bool OutputSurface::InitializeAndSetContext3D( - scoped_ptr<WebKit::WebGraphicsContext3D> context3d, +bool OutputSurface::InitializeAndSetContext3d( + scoped_refptr<ContextProvider> context_provider, scoped_refptr<ContextProvider> offscreen_context_provider) { - DCHECK(!context3d_); - DCHECK(context3d); + DCHECK(!context_provider_); + DCHECK(context_provider); DCHECK(client_); bool success = false; - if (context3d->makeContextCurrent()) { - SetContext3D(context3d.Pass()); + if (context_provider->BindToCurrentThread()) { + context_provider_ = context_provider; + SetUpContext3d(); if (client_->DeferredInitialize(offscreen_context_provider)) success = true; } if (!success) - ResetContext3D(); + ResetContext3d(); return success; } void OutputSurface::ReleaseGL() { DCHECK(client_); - DCHECK(context3d_); + DCHECK(context_provider_); client_->ReleaseGL(); - ResetContext3D(); + ResetContext3d(); } -void OutputSurface::SetContext3D( - scoped_ptr<WebKit::WebGraphicsContext3D> context3d) { - DCHECK(!context3d_); - DCHECK(context3d); +void OutputSurface::SetUpContext3d() { + DCHECK(context_provider_); DCHECK(client_); - string extensions_string = UTF16ToASCII(context3d->getString(GL_EXTENSIONS)); - vector<string> extensions_list; - base::SplitString(extensions_string, ' ', &extensions_list); - set<string> extensions(extensions_list.begin(), extensions_list.end()); - has_gl_discard_backbuffer_ = - extensions.count("GL_CHROMIUM_discard_backbuffer") > 0; - has_swap_buffers_complete_callback_ = - extensions.count("GL_CHROMIUM_swapbuffers_complete_callback") > 0; - - - context3d_ = context3d.Pass(); - callbacks_.reset(new OutputSurfaceCallbacks(this)); - context3d_->setSwapBuffersCompleteCallbackCHROMIUM(callbacks_.get()); - context3d_->setContextLostCallback(callbacks_.get()); - context3d_->setMemoryAllocationChangedCallbackCHROMIUM(callbacks_.get()); -} - -void OutputSurface::ResetContext3D() { - context3d_.reset(); - callbacks_.reset(); + const ContextProvider::Capabilities& caps = + context_provider_->ContextCapabilities(); + + has_gl_discard_backbuffer_ = caps.discard_backbuffer; + has_swap_buffers_complete_callback_ = caps.swapbuffers_complete_callback; + + context_provider_->SetLostContextCallback( + base::Bind(&OutputSurface::DidLoseOutputSurface, + base::Unretained(this))); + context_provider_->SetSwapBuffersCompleteCallback(base::Bind( + &OutputSurface::OnSwapBuffersComplete, base::Unretained(this))); + context_provider_->SetMemoryPolicyChangedCallback( + base::Bind(&OutputSurface::SetMemoryPolicy, + base::Unretained(this))); +} + +void OutputSurface::ResetContext3d() { + if (context_provider_.get()) { + context_provider_->SetLostContextCallback( + ContextProvider::LostContextCallback()); + context_provider_->SetSwapBuffersCompleteCallback( + ContextProvider::SwapBuffersCompleteCallback()); + context_provider_->SetMemoryPolicyChangedCallback( + ContextProvider::MemoryPolicyChangedCallback()); + } + context_provider_ = NULL; } void OutputSurface::EnsureBackbuffer() { - DCHECK(context3d_); - if (has_gl_discard_backbuffer_) - context3d_->ensureBackbufferCHROMIUM(); + if (context_provider_ && has_gl_discard_backbuffer_) + context_provider_->Context3d()->ensureBackbufferCHROMIUM(); + if (software_device_) + software_device_->EnsureBackbuffer(); } void OutputSurface::DiscardBackbuffer() { - DCHECK(context3d_); - if (has_gl_discard_backbuffer_) - context3d_->discardBackbufferCHROMIUM(); + if (context_provider_ && has_gl_discard_backbuffer_) + context_provider_->Context3d()->discardBackbufferCHROMIUM(); + if (software_device_) + software_device_->DiscardBackbuffer(); } void OutputSurface::Reshape(gfx::Size size, float scale_factor) { @@ -389,8 +344,8 @@ void OutputSurface::Reshape(gfx::Size size, float scale_factor) { surface_size_ = size; device_scale_factor_ = scale_factor; - if (context3d_) { - context3d_->reshapeWithScaleFactor( + if (context_provider_) { + context_provider_->Context3d()->reshapeWithScaleFactor( size.width(), size.height(), scale_factor); } if (software_device_) @@ -402,8 +357,8 @@ gfx::Size OutputSurface::SurfaceSize() const { } void OutputSurface::BindFramebuffer() { - DCHECK(context3d_); - context3d_->bindFramebuffer(GL_FRAMEBUFFER, 0); + DCHECK(context_provider_); + context_provider_->Context3d()->bindFramebuffer(GL_FRAMEBUFFER, 0); } void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) { @@ -413,20 +368,21 @@ void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) { return; } - DCHECK(context3d_); + DCHECK(context_provider_); DCHECK(frame->gl_frame_data); if (frame->gl_frame_data->sub_buffer_rect == gfx::Rect(frame->gl_frame_data->size)) { // Note that currently this has the same effect as SwapBuffers; we should // consider exposing a different entry point on WebGraphicsContext3D. - context3d()->prepareTexture(); + context_provider_->Context3d()->prepareTexture(); } else { gfx::Rect sub_buffer_rect = frame->gl_frame_data->sub_buffer_rect; - context3d()->postSubBufferCHROMIUM(sub_buffer_rect.x(), - sub_buffer_rect.y(), - sub_buffer_rect.width(), - sub_buffer_rect.height()); + context_provider_->Context3d()->postSubBufferCHROMIUM( + sub_buffer_rect.x(), + sub_buffer_rect.y(), + sub_buffer_rect.width(), + sub_buffer_rect.height()); } if (!has_swap_buffers_complete_callback_) @@ -437,10 +393,9 @@ void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) { void OutputSurface::PostSwapBuffersComplete() { base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&OutputSurface::OnSwapBuffersComplete, - weak_ptr_factory_.GetWeakPtr(), - static_cast<CompositorFrameAck*>(NULL))); + FROM_HERE, + base::Bind(&OutputSurface::OnSwapBuffersComplete, + weak_ptr_factory_.GetWeakPtr())); } void OutputSurface::SetMemoryPolicy(const ManagedMemoryPolicy& policy, diff --git a/chromium/cc/output/output_surface.h b/chromium/cc/output/output_surface.h index 86d4dc81dd3..13db5faa06b 100644 --- a/chromium/cc/output/output_surface.h +++ b/chromium/cc/output/output_surface.h @@ -13,7 +13,6 @@ #include "cc/output/context_provider.h" #include "cc/output/software_output_device.h" #include "cc/scheduler/frame_rate_controller.h" -#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" namespace base { class SingleThreadTaskRunner; } @@ -31,7 +30,6 @@ class CompositorFrame; class CompositorFrameAck; struct ManagedMemoryPolicy; class OutputSurfaceClient; -class OutputSurfaceCallbacks; // Represents the output surface for a compositor. The compositor owns // and manages its destruction. Its lifetime is: @@ -46,11 +44,11 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { DEFAULT_MAX_FRAMES_PENDING = 2 }; - explicit OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); + explicit OutputSurface(scoped_refptr<ContextProvider> context_provider); explicit OutputSurface(scoped_ptr<cc::SoftwareOutputDevice> software_device); - OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d, + OutputSurface(scoped_refptr<ContextProvider> context_provider, scoped_ptr<cc::SoftwareOutputDevice> software_device); virtual ~OutputSurface(); @@ -61,7 +59,8 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { max_frames_pending(0), deferred_gl_initialization(false), draw_and_swap_full_viewport_every_frame(false), - adjust_deadline_for_parent(true) {} + adjust_deadline_for_parent(true), + uses_default_gl_framebuffer(true) {} bool delegated_rendering; int max_frames_pending; bool deferred_gl_initialization; @@ -69,20 +68,24 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { // This doesn't handle the <webview> case, but once BeginFrame is // supported natively, we shouldn't need adjust_deadline_for_parent. bool adjust_deadline_for_parent; + // Whether this output surface renders to the default OpenGL zero + // framebuffer or to an offscreen framebuffer. + bool uses_default_gl_framebuffer; }; const Capabilities& capabilities() const { return capabilities_; } + virtual bool HasExternalStencilTest() const; + // Obtain the 3d context or the software device associated with this output // surface. Either of these may return a null pointer, but not both. // In the event of a lost context, the entire output surface should be // recreated. - WebKit::WebGraphicsContext3D* context3d() const { - return context3d_.get(); + scoped_refptr<ContextProvider> context_provider() const { + return context_provider_.get(); } - SoftwareOutputDevice* software_device() const { return software_device_.get(); } @@ -127,21 +130,22 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { // OutputSurfaceClient::BeginFrame until the callback is disabled. virtual void SetNeedsBeginFrame(bool enable); + bool HasClient() { return !!client_; } + protected: // Synchronously initialize context3d and enter hardware mode. // This can only supported in threaded compositing mode. // |offscreen_context_provider| should match what is returned by // LayerTreeClient::OffscreenContextProviderForCompositorThread. - bool InitializeAndSetContext3D( - scoped_ptr<WebKit::WebGraphicsContext3D> context3d, + bool InitializeAndSetContext3d( + scoped_refptr<ContextProvider> context_provider, scoped_refptr<ContextProvider> offscreen_context_provider); void ReleaseGL(); void PostSwapBuffersComplete(); struct cc::OutputSurface::Capabilities capabilities_; - scoped_ptr<OutputSurfaceCallbacks> callbacks_; - scoped_ptr<WebKit::WebGraphicsContext3D> context3d_; + scoped_refptr<ContextProvider> context_provider_; scoped_ptr<cc::SoftwareOutputDevice> software_device_; bool has_gl_discard_backbuffer_; bool has_swap_buffers_complete_callback_; @@ -159,22 +163,28 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { int max_frames_pending_; int pending_swap_buffers_; bool needs_begin_frame_; - bool begin_frame_pending_; + bool client_ready_for_begin_frame_; + + // This stores a BeginFrame that we couldn't process immediately, but might + // process retroactively in the near future. + BeginFrameArgs skipped_begin_frame_args_; // Forwarded to OutputSurfaceClient but threaded through OutputSurface // first so OutputSurface has a chance to update the FrameRateController - bool HasClient() { return !!client_; } void SetNeedsRedrawRect(gfx::Rect damage_rect); void BeginFrame(const BeginFrameArgs& args); void DidSwapBuffers(); - void OnSwapBuffersComplete(const CompositorFrameAck* ack); + void OnSwapBuffersComplete(); + void ReclaimResources(const CompositorFrameAck* ack); void DidLoseOutputSurface(); void SetExternalStencilTest(bool enabled); void SetExternalDrawConstraints(const gfx::Transform& transform, - gfx::Rect viewport); + gfx::Rect viewport, + gfx::Rect clip, + bool valid_for_tile_management); // virtual for testing. - virtual base::TimeDelta RetroactiveBeginFramePeriod(); + virtual base::TimeTicks RetroactiveBeginFrameDeadline(); virtual void PostCheckForRetroactiveBeginFrame(); void CheckForRetroactiveBeginFrame(); @@ -182,19 +192,17 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { OutputSurfaceClient* client_; friend class OutputSurfaceCallbacks; - void SetContext3D(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); - void ResetContext3D(); + void SetUpContext3d(); + void ResetContext3d(); void SetMemoryPolicy(const ManagedMemoryPolicy& policy, bool discard_backbuffer_when_not_visible); - // This stores a BeginFrame that we couldn't process immediately, but might - // process retroactively in the near future. - BeginFrameArgs skipped_begin_frame_args_; - // check_for_retroactive_begin_frame_pending_ is used to avoid posting // redundant checks for a retroactive BeginFrame. bool check_for_retroactive_begin_frame_pending_; + bool external_stencil_test_enabled_; + DISALLOW_COPY_AND_ASSIGN(OutputSurface); }; diff --git a/chromium/cc/output/output_surface_client.h b/chromium/cc/output/output_surface_client.h index 4d1762211bb..c0e2e4554b5 100644 --- a/chromium/cc/output/output_surface_client.h +++ b/chromium/cc/output/output_surface_client.h @@ -32,11 +32,13 @@ class CC_EXPORT OutputSurfaceClient { virtual void ReleaseGL() = 0; virtual void SetNeedsRedrawRect(gfx::Rect damage_rect) = 0; virtual void BeginFrame(const BeginFrameArgs& args) = 0; - virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) = 0; + virtual void OnSwapBuffersComplete() = 0; + virtual void ReclaimResources(const CompositorFrameAck* ack) = 0; virtual void DidLoseOutputSurface() = 0; - virtual void SetExternalStencilTest(bool enabled) = 0; virtual void SetExternalDrawConstraints(const gfx::Transform& transform, - gfx::Rect viewport) = 0; + gfx::Rect viewport, + gfx::Rect clip, + bool valid_for_tile_management) = 0; virtual void SetDiscardBackBufferWhenNotVisible(bool discard) = 0; virtual void SetMemoryPolicy(const ManagedMemoryPolicy& policy) = 0; // If set, |callback| will be called subsequent to each new tree activation, diff --git a/chromium/cc/output/output_surface_unittest.cc b/chromium/cc/output/output_surface_unittest.cc index 276828c04aa..91e7c39d0b1 100644 --- a/chromium/cc/output/output_surface_unittest.cc +++ b/chromium/cc/output/output_surface_unittest.cc @@ -5,47 +5,47 @@ #include "cc/output/output_surface.h" #include "base/test/test_simple_task_runner.h" +#include "cc/debug/test_context_provider.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/output/managed_memory_policy.h" #include "cc/output/output_surface_client.h" #include "cc/output/software_output_device.h" #include "cc/test/fake_output_surface.h" #include "cc/test/fake_output_surface_client.h" #include "cc/test/scheduler_test_common.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h" - -using WebKit::WebGraphicsMemoryAllocation; namespace cc { namespace { class TestOutputSurface : public OutputSurface { public: - explicit TestOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : OutputSurface(context3d.Pass()) {} + explicit TestOutputSurface(scoped_refptr<ContextProvider> context_provider) + : OutputSurface(context_provider), + retroactive_begin_frame_deadline_enabled_(false), + override_retroactive_period_(false) {} explicit TestOutputSurface( scoped_ptr<cc::SoftwareOutputDevice> software_device) - : OutputSurface(software_device.Pass()) {} + : OutputSurface(software_device.Pass()), + retroactive_begin_frame_deadline_enabled_(false), + override_retroactive_period_(false) {} - TestOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d, + TestOutputSurface(scoped_refptr<ContextProvider> context_provider, scoped_ptr<cc::SoftwareOutputDevice> software_device) - : OutputSurface(context3d.Pass(), software_device.Pass()) {} + : OutputSurface(context_provider, software_device.Pass()), + retroactive_begin_frame_deadline_enabled_(false), + override_retroactive_period_(false) {} - bool InitializeNewContext3D( - scoped_ptr<WebKit::WebGraphicsContext3D> new_context3d) { - return InitializeAndSetContext3D(new_context3d.Pass(), + bool InitializeNewContext3d( + scoped_refptr<ContextProvider> new_context_provider) { + return InitializeAndSetContext3d(new_context_provider, scoped_refptr<ContextProvider>()); } using OutputSurface::ReleaseGL; - bool HasClientForTesting() { - return HasClient(); - } - void OnVSyncParametersChangedForTesting(base::TimeTicks timebase, base::TimeDelta interval) { OnVSyncParametersChanged(timebase, interval); @@ -64,11 +64,15 @@ class TestOutputSurface : public OutputSurface { } void OnSwapBuffersCompleteForTesting() { - OnSwapBuffersComplete(NULL); + OnSwapBuffersComplete(); } - void SetRetroactiveBeginFramePeriod(base::TimeDelta period) { - retroactive_begin_frame_period_ = period; + void EnableRetroactiveBeginFrameDeadline(bool enable, + bool override_retroactive_period, + base::TimeDelta period_override) { + retroactive_begin_frame_deadline_enabled_ = enable; + override_retroactive_period_ = override_retroactive_period; + retroactive_period_override_ = period_override; } protected: @@ -77,116 +81,150 @@ class TestOutputSurface : public OutputSurface { CheckForRetroactiveBeginFrame(); } - virtual base::TimeDelta RetroactiveBeginFramePeriod() OVERRIDE { - return retroactive_begin_frame_period_; + virtual base::TimeTicks RetroactiveBeginFrameDeadline() OVERRIDE { + if (retroactive_begin_frame_deadline_enabled_) { + if (override_retroactive_period_) { + return skipped_begin_frame_args_.frame_time + + retroactive_period_override_; + } else { + return OutputSurface::RetroactiveBeginFrameDeadline(); + } + } + return base::TimeTicks(); } - base::TimeDelta retroactive_begin_frame_period_; + bool retroactive_begin_frame_deadline_enabled_; + bool override_retroactive_period_; + base::TimeDelta retroactive_period_override_; }; -TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientSuccess) { - scoped_ptr<TestWebGraphicsContext3D> context3d = - TestWebGraphicsContext3D::Create(); +class TestSoftwareOutputDevice : public SoftwareOutputDevice { + public: + TestSoftwareOutputDevice(); + virtual ~TestSoftwareOutputDevice(); + + // Overriden from cc:SoftwareOutputDevice + virtual void DiscardBackbuffer() OVERRIDE; + virtual void EnsureBackbuffer() OVERRIDE; + + int discard_backbuffer_count() { return discard_backbuffer_count_; } + int ensure_backbuffer_count() { return ensure_backbuffer_count_; } + + private: + int discard_backbuffer_count_; + int ensure_backbuffer_count_; +}; - TestOutputSurface output_surface( - context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_FALSE(output_surface.HasClientForTesting()); +TestSoftwareOutputDevice::TestSoftwareOutputDevice() + : discard_backbuffer_count_(0), ensure_backbuffer_count_(0) {} + +TestSoftwareOutputDevice::~TestSoftwareOutputDevice() {} + +void TestSoftwareOutputDevice::DiscardBackbuffer() { + SoftwareOutputDevice::DiscardBackbuffer(); + discard_backbuffer_count_++; +} + +void TestSoftwareOutputDevice::EnsureBackbuffer() { + SoftwareOutputDevice::EnsureBackbuffer(); + ensure_backbuffer_count_++; +} + +TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientSuccess) { + TestOutputSurface output_surface(TestContextProvider::Create()); + EXPECT_FALSE(output_surface.HasClient()); FakeOutputSurfaceClient client; EXPECT_TRUE(output_surface.BindToClient(&client)); - EXPECT_TRUE(output_surface.HasClientForTesting()); + EXPECT_TRUE(output_surface.HasClient()); EXPECT_FALSE(client.deferred_initialize_called()); // Verify DidLoseOutputSurface callback is hooked up correctly. EXPECT_FALSE(client.did_lose_output_surface_called()); - output_surface.context3d()->loseContextCHROMIUM( + output_surface.context_provider()->Context3d()->loseContextCHROMIUM( GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); EXPECT_TRUE(client.did_lose_output_surface_called()); } TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientFailure) { - scoped_ptr<TestWebGraphicsContext3D> context3d = - TestWebGraphicsContext3D::Create(); + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create(); // Lose the context so BindToClient fails. - context3d->set_times_make_current_succeeds(0); + context_provider->UnboundTestContext3d()->set_times_make_current_succeeds(0); - TestOutputSurface output_surface( - context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_FALSE(output_surface.HasClientForTesting()); + TestOutputSurface output_surface(context_provider); + EXPECT_FALSE(output_surface.HasClient()); FakeOutputSurfaceClient client; EXPECT_FALSE(output_surface.BindToClient(&client)); - EXPECT_FALSE(output_surface.HasClientForTesting()); + EXPECT_FALSE(output_surface.HasClient()); } -class InitializeNewContext3D : public ::testing::Test { +class OutputSurfaceTestInitializeNewContext3d : public ::testing::Test { public: - InitializeNewContext3D() - : context3d_(TestWebGraphicsContext3D::Create()), + OutputSurfaceTestInitializeNewContext3d() + : context_provider_(TestContextProvider::Create()), output_surface_( scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice)) {} protected: void BindOutputSurface() { EXPECT_TRUE(output_surface_.BindToClient(&client_)); - EXPECT_TRUE(output_surface_.HasClientForTesting()); + EXPECT_TRUE(output_surface_.HasClient()); } void InitializeNewContextExpectFail() { - EXPECT_FALSE(output_surface_.InitializeNewContext3D( - context3d_.PassAs<WebKit::WebGraphicsContext3D>())); - EXPECT_TRUE(output_surface_.HasClientForTesting()); + EXPECT_FALSE(output_surface_.InitializeNewContext3d(context_provider_)); + EXPECT_TRUE(output_surface_.HasClient()); - EXPECT_FALSE(output_surface_.context3d()); + EXPECT_FALSE(output_surface_.context_provider()); EXPECT_TRUE(output_surface_.software_device()); } - scoped_ptr<TestWebGraphicsContext3D> context3d_; + scoped_refptr<TestContextProvider> context_provider_; TestOutputSurface output_surface_; FakeOutputSurfaceClient client_; }; -TEST_F(InitializeNewContext3D, Success) { +TEST_F(OutputSurfaceTestInitializeNewContext3d, Success) { BindOutputSurface(); EXPECT_FALSE(client_.deferred_initialize_called()); - EXPECT_TRUE(output_surface_.InitializeNewContext3D( - context3d_.PassAs<WebKit::WebGraphicsContext3D>())); + EXPECT_TRUE(output_surface_.InitializeNewContext3d(context_provider_)); EXPECT_TRUE(client_.deferred_initialize_called()); + EXPECT_EQ(context_provider_, output_surface_.context_provider()); EXPECT_FALSE(client_.did_lose_output_surface_called()); - output_surface_.context3d()->loseContextCHROMIUM( + context_provider_->Context3d()->loseContextCHROMIUM( GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); EXPECT_TRUE(client_.did_lose_output_surface_called()); output_surface_.ReleaseGL(); - EXPECT_FALSE(output_surface_.context3d()); + EXPECT_FALSE(output_surface_.context_provider()); } -TEST_F(InitializeNewContext3D, Context3dMakeCurrentFails) { +TEST_F(OutputSurfaceTestInitializeNewContext3d, Context3dMakeCurrentFails) { BindOutputSurface(); - context3d_->set_times_make_current_succeeds(0); + + context_provider_->UnboundTestContext3d() + ->set_times_make_current_succeeds(0); InitializeNewContextExpectFail(); } -TEST_F(InitializeNewContext3D, ClientDeferredInitializeFails) { +TEST_F(OutputSurfaceTestInitializeNewContext3d, ClientDeferredInitializeFails) { BindOutputSurface(); client_.set_deferred_initialize_result(false); InitializeNewContextExpectFail(); } TEST(OutputSurfaceTest, BeginFrameEmulation) { - scoped_ptr<TestWebGraphicsContext3D> context3d = - TestWebGraphicsContext3D::Create(); - - TestOutputSurface output_surface( - context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_FALSE(output_surface.HasClientForTesting()); + TestOutputSurface output_surface(TestContextProvider::Create()); + EXPECT_FALSE(output_surface.HasClient()); FakeOutputSurfaceClient client; EXPECT_TRUE(output_surface.BindToClient(&client)); - EXPECT_TRUE(output_surface.HasClientForTesting()); + EXPECT_TRUE(output_surface.HasClient()); EXPECT_FALSE(client.deferred_initialize_called()); // Initialize BeginFrame emulation @@ -202,8 +240,8 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) { display_refresh_interval); output_surface.SetMaxFramesPending(2); - output_surface.SetRetroactiveBeginFramePeriod( - base::TimeDelta::FromSeconds(-1)); + output_surface.EnableRetroactiveBeginFrameDeadline( + false, false, base::TimeDelta()); // We should start off with 0 BeginFrames EXPECT_EQ(client.begin_frame_count(), 0); @@ -224,8 +262,10 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) { EXPECT_EQ(client.begin_frame_count(), 1); EXPECT_EQ(output_surface.pending_swap_buffers(), 0); - // DidSwapBuffers should clear the pending BeginFrame. + // SetNeedsBeginFrame should clear the pending BeginFrame after + // a SwapBuffers. output_surface.DidSwapBuffersForTesting(); + output_surface.SetNeedsBeginFrame(true); EXPECT_EQ(client.begin_frame_count(), 1); EXPECT_EQ(output_surface.pending_swap_buffers(), 1); task_runner->RunPendingTasks(); @@ -234,6 +274,7 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) { // BeginFrame should be throttled by pending swap buffers. output_surface.DidSwapBuffersForTesting(); + output_surface.SetNeedsBeginFrame(true); EXPECT_EQ(client.begin_frame_count(), 2); EXPECT_EQ(output_surface.pending_swap_buffers(), 2); task_runner->RunPendingTasks(); @@ -264,23 +305,17 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) { } TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) { - scoped_ptr<TestWebGraphicsContext3D> context3d = - TestWebGraphicsContext3D::Create(); - - TestOutputSurface output_surface( - context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_FALSE(output_surface.HasClientForTesting()); + TestOutputSurface output_surface(TestContextProvider::Create()); + EXPECT_FALSE(output_surface.HasClient()); FakeOutputSurfaceClient client; EXPECT_TRUE(output_surface.BindToClient(&client)); - EXPECT_TRUE(output_surface.HasClientForTesting()); + EXPECT_TRUE(output_surface.HasClient()); EXPECT_FALSE(client.deferred_initialize_called()); output_surface.SetMaxFramesPending(2); - - // Enable retroactive BeginFrames. - output_surface.SetRetroactiveBeginFramePeriod( - base::TimeDelta::FromSeconds(100000)); + output_surface.EnableRetroactiveBeginFrameDeadline( + true, false, base::TimeDelta()); // Optimistically injected BeginFrames should be throttled if // SetNeedsBeginFrame is false... @@ -302,12 +337,14 @@ TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) { output_surface.BeginFrameForTesting(); EXPECT_EQ(client.begin_frame_count(), 2); output_surface.DidSwapBuffersForTesting(); + output_surface.SetNeedsBeginFrame(true); EXPECT_EQ(client.begin_frame_count(), 3); EXPECT_EQ(output_surface.pending_swap_buffers(), 1); // Optimistically injected BeginFrames should be by throttled by pending // swap buffers... output_surface.DidSwapBuffersForTesting(); + output_surface.SetNeedsBeginFrame(true); EXPECT_EQ(client.begin_frame_count(), 3); EXPECT_EQ(output_surface.pending_swap_buffers(), 2); output_surface.BeginFrameForTesting(); @@ -317,28 +354,82 @@ TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) { EXPECT_EQ(client.begin_frame_count(), 4); } +TEST(OutputSurfaceTest, RetroactiveBeginFrameDoesNotDoubleTickWhenEmulating) { + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create(); + + TestOutputSurface output_surface(context_provider); + EXPECT_FALSE(output_surface.HasClient()); + + FakeOutputSurfaceClient client; + EXPECT_TRUE(output_surface.BindToClient(&client)); + EXPECT_TRUE(output_surface.HasClient()); + EXPECT_FALSE(client.deferred_initialize_called()); + + base::TimeDelta big_interval = base::TimeDelta::FromSeconds(10); + + // Initialize BeginFrame emulation + scoped_refptr<base::TestSimpleTaskRunner> task_runner = + new base::TestSimpleTaskRunner; + bool throttle_frame_production = true; + const base::TimeDelta display_refresh_interval = big_interval; + + output_surface.InitializeBeginFrameEmulation( + task_runner.get(), + throttle_frame_production, + display_refresh_interval); + + // We need to subtract an epsilon from Now() because some platforms have + // a slow clock. + output_surface.OnVSyncParametersChangedForTesting( + base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1), big_interval); + + output_surface.SetMaxFramesPending(2); + output_surface.EnableRetroactiveBeginFrameDeadline(true, true, big_interval); + + // We should start off with 0 BeginFrames + EXPECT_EQ(client.begin_frame_count(), 0); + EXPECT_EQ(output_surface.pending_swap_buffers(), 0); + + // The first SetNeedsBeginFrame(true) should start a retroactive BeginFrame. + EXPECT_FALSE(task_runner->HasPendingTask()); + output_surface.SetNeedsBeginFrame(true); + EXPECT_TRUE(task_runner->HasPendingTask()); + EXPECT_GT(task_runner->NextPendingTaskDelay(), big_interval / 2); + EXPECT_EQ(client.begin_frame_count(), 1); + + output_surface.SetNeedsBeginFrame(false); + EXPECT_TRUE(task_runner->HasPendingTask()); + EXPECT_EQ(client.begin_frame_count(), 1); + + // The second SetNeedBeginFrame(true) should not retroactively start a + // BeginFrame if the timestamp would be the same as the previous BeginFrame. + output_surface.SetNeedsBeginFrame(true); + EXPECT_TRUE(task_runner->HasPendingTask()); + EXPECT_EQ(client.begin_frame_count(), 1); +} + TEST(OutputSurfaceTest, MemoryAllocation) { - scoped_ptr<TestWebGraphicsContext3D> scoped_context = - TestWebGraphicsContext3D::Create(); - TestWebGraphicsContext3D* context = scoped_context.get(); + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create(); - TestOutputSurface output_surface( - scoped_context.PassAs<WebKit::WebGraphicsContext3D>()); + TestOutputSurface output_surface(context_provider); FakeOutputSurfaceClient client; EXPECT_TRUE(output_surface.BindToClient(&client)); - WebGraphicsMemoryAllocation allocation; - allocation.suggestHaveBackbuffer = true; - allocation.bytesLimitWhenVisible = 1234; - allocation.priorityCutoffWhenVisible = - WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly; - allocation.bytesLimitWhenNotVisible = 4567; - allocation.priorityCutoffWhenNotVisible = - WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing; + ManagedMemoryPolicy policy(0); + policy.bytes_limit_when_visible = 1234; + policy.priority_cutoff_when_visible = + ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY; + policy.bytes_limit_when_not_visible = 4567; + policy.priority_cutoff_when_not_visible = + ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING; - context->SetMemoryAllocation(allocation); + bool discard_backbuffer_when_not_visible = false; + context_provider->SetMemoryAllocation(policy, + discard_backbuffer_when_not_visible); EXPECT_EQ(1234u, client.memory_policy().bytes_limit_when_visible); EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY, client.memory_policy().priority_cutoff_when_visible); @@ -347,25 +438,49 @@ TEST(OutputSurfaceTest, MemoryAllocation) { client.memory_policy().priority_cutoff_when_not_visible); EXPECT_FALSE(client.discard_backbuffer_when_not_visible()); - allocation.suggestHaveBackbuffer = false; - context->SetMemoryAllocation(allocation); + discard_backbuffer_when_not_visible = true; + context_provider->SetMemoryAllocation(policy, + discard_backbuffer_when_not_visible); EXPECT_TRUE(client.discard_backbuffer_when_not_visible()); - allocation.priorityCutoffWhenVisible = - WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything; - allocation.priorityCutoffWhenNotVisible = - WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleAndNearby; - context->SetMemoryAllocation(allocation); + policy.priority_cutoff_when_visible = + ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING; + policy.priority_cutoff_when_not_visible = + ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE; + context_provider->SetMemoryAllocation(policy, + discard_backbuffer_when_not_visible); EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING, client.memory_policy().priority_cutoff_when_visible); EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE, client.memory_policy().priority_cutoff_when_not_visible); // 0 bytes limit should be ignored. - allocation.bytesLimitWhenVisible = 0; - context->SetMemoryAllocation(allocation); + policy.bytes_limit_when_visible = 0; + context_provider->SetMemoryAllocation(policy, + discard_backbuffer_when_not_visible); EXPECT_EQ(1234u, client.memory_policy().bytes_limit_when_visible); } +TEST(OutputSurfaceTest, SoftwareOutputDeviceBackbufferManagement) { + TestSoftwareOutputDevice* software_output_device = + new TestSoftwareOutputDevice(); + + // TestOutputSurface now owns software_output_device and has responsibility to + // free it. + scoped_ptr<TestSoftwareOutputDevice> p(software_output_device); + TestOutputSurface output_surface(p.PassAs<SoftwareOutputDevice>()); + + EXPECT_EQ(0, software_output_device->ensure_backbuffer_count()); + EXPECT_EQ(0, software_output_device->discard_backbuffer_count()); + + output_surface.EnsureBackbuffer(); + EXPECT_EQ(1, software_output_device->ensure_backbuffer_count()); + EXPECT_EQ(0, software_output_device->discard_backbuffer_count()); + output_surface.DiscardBackbuffer(); + + EXPECT_EQ(1, software_output_device->ensure_backbuffer_count()); + EXPECT_EQ(1, software_output_device->discard_backbuffer_count()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/output/render_surface_filters.cc b/chromium/cc/output/render_surface_filters.cc index fa84e67f4d3..04d8c5ab9f7 100644 --- a/chromium/cc/output/render_surface_filters.cc +++ b/chromium/cc/output/render_surface_filters.cc @@ -226,9 +226,13 @@ bool GetColorMatrix(const FilterOperation& op, SkScalar matrix[20]) { memcpy(matrix, op.matrix(), sizeof(SkScalar[20])); return true; } - default: + case FilterOperation::BLUR: + case FilterOperation::DROP_SHADOW: + case FilterOperation::ZOOM: return false; } + NOTREACHED(); + return false; } class FilterBufferState { diff --git a/chromium/cc/output/renderer.h b/chromium/cc/output/renderer.h index a9a800b6a0e..32a109fac85 100644 --- a/chromium/cc/output/renderer.h +++ b/chromium/cc/output/renderer.h @@ -18,19 +18,13 @@ class ScopedResource; class CC_EXPORT RendererClient { public: - // Draw viewport in non-y-flipped window space. Note that while a draw is in - // progress, this is guaranteed to be contained within the output surface - // size. + // These return the draw viewport and clip in non-y-flipped window space. + // Note that while a draw is in progress, these are guaranteed to be + // contained within the output surface size. virtual gfx::Rect DeviceViewport() const = 0; - - virtual float DeviceScaleFactor() const = 0; - virtual const LayerTreeSettings& Settings() const = 0; + virtual gfx::Rect DeviceClip() const = 0; virtual void SetFullRootLayerDamage() = 0; - virtual bool HasImplThread() const = 0; - virtual bool ShouldClearRootRenderPass() const = 0; virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const = 0; - virtual bool AllowPartialSwap() const = 0; - virtual bool ExternalStencilTestEnabled() const = 0; protected: virtual ~RendererClient() {} @@ -42,8 +36,6 @@ class CC_EXPORT Renderer { virtual const RendererCapabilities& Capabilities() const = 0; - const LayerTreeSettings& Settings() const { return client_->Settings(); } - virtual void ViewportChanged() {} virtual bool CanReadPixels() const = 0; @@ -53,8 +45,12 @@ class CC_EXPORT Renderer { virtual bool HaveCachedResourcesForRenderPassId(RenderPass::Id id) const; // This passes ownership of the render passes to the renderer. It should - // consume them, and empty the list. - virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) = 0; + // consume them, and empty the list. The parameters here may change from frame + // to frame and should not be cached. + virtual void DrawFrame(RenderPassList* render_passes_in_draw_order, + ContextProvider* offscreen_context_provider, + float device_scale_factor, + bool allow_partial_swap) = 0; // Waits for rendering to finish. virtual void Finish() = 0; @@ -78,10 +74,11 @@ class CC_EXPORT Renderer { virtual void SetDiscardBackBufferWhenNotVisible(bool discard) = 0; protected: - explicit Renderer(RendererClient* client) - : client_(client) {} + explicit Renderer(RendererClient* client, const LayerTreeSettings* settings) + : client_(client), settings_(settings) {} RendererClient* client_; + const LayerTreeSettings* settings_; private: DISALLOW_COPY_AND_ASSIGN(Renderer); diff --git a/chromium/cc/output/renderer_pixeltest.cc b/chromium/cc/output/renderer_pixeltest.cc index 9508aa29166..f0187aacdb3 100644 --- a/chromium/cc/output/renderer_pixeltest.cc +++ b/chromium/cc/output/renderer_pixeltest.cc @@ -8,11 +8,11 @@ #include "cc/quads/draw_quad.h" #include "cc/quads/picture_draw_quad.h" #include "cc/quads/texture_draw_quad.h" -#include "cc/resources/platform_color.h" #include "cc/resources/sync_point_helper.h" #include "cc/test/fake_picture_pile_impl.h" #include "cc/test/pixel_test.h" #include "gpu/GLES2/gl2extchromium.h" +#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/effects/SkColorFilterImageFilter.h" @@ -110,8 +110,11 @@ scoped_ptr<TextureDrawQuad> CreateTestTextureDrawQuad( SkColorGetB(texel_color)); std::vector<uint32_t> pixels(rect.size().GetArea(), pixel_color); - ResourceProvider::ResourceId resource = resource_provider->CreateResource( - rect.size(), GL_RGBA, ResourceProvider::TextureUsageAny); + ResourceProvider::ResourceId resource = + resource_provider->CreateResource(rect.size(), + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + RGBA_8888); resource_provider->SetPixels( resource, reinterpret_cast<uint8_t*>(&pixels.front()), @@ -214,6 +217,7 @@ TYPED_TEST(RendererPixelTest, SimpleGreenRect) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green.png")), ExactPixelComparator(true))); } @@ -255,6 +259,7 @@ TYPED_TEST(RendererPixelTest, SimpleGreenRect_NonRootRenderPass) { EXPECT_TRUE(this->RunPixelTestWithReadbackTarget( &pass_list, child_pass_ptr, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green_small.png")), ExactPixelComparator(true))); } @@ -286,6 +291,7 @@ TYPED_TEST(RendererPixelTest, PremultipliedTextureWithoutBackground) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green_alpha.png")), FuzzyPixelOffByOneComparator(true))); } @@ -320,6 +326,7 @@ TYPED_TEST(RendererPixelTest, PremultipliedTextureWithBackground) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green_alpha.png")), FuzzyPixelOffByOneComparator(true))); } @@ -352,6 +359,7 @@ TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithoutBackground) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green_alpha.png")), FuzzyPixelOffByOneComparator(true))); } @@ -387,6 +395,7 @@ TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithBackground) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green_alpha.png")), FuzzyPixelOffByOneComparator(true))); } @@ -401,24 +410,28 @@ class VideoGLRendererPixelTest : public GLRendererPixelTest { ResourceProvider::ResourceId y_resource = resource_provider_->CreateResource( this->device_viewport_size_, - GL_LUMINANCE, - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + LUMINANCE_8); ResourceProvider::ResourceId u_resource = resource_provider_->CreateResource( this->device_viewport_size_, - GL_LUMINANCE, - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + LUMINANCE_8); ResourceProvider::ResourceId v_resource = resource_provider_->CreateResource( this->device_viewport_size_, - GL_LUMINANCE, - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + LUMINANCE_8); ResourceProvider::ResourceId a_resource = 0; if (with_alpha) { a_resource = resource_provider_->CreateResource( this->device_viewport_size_, - GL_LUMINANCE, - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + LUMINANCE_8); } int w = this->device_viewport_size_.width(); @@ -476,6 +489,7 @@ TEST_F(VideoGLRendererPixelTest, SimpleYUVRect) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green.png")), ExactPixelComparator(true))); } @@ -504,6 +518,7 @@ TEST_F(VideoGLRendererPixelTest, SimpleYUVARect) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green_alpha.png")), ExactPixelComparator(true))); } @@ -532,6 +547,7 @@ TEST_F(VideoGLRendererPixelTest, FullyTransparentYUVARect) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("black.png")), ExactPixelComparator(true))); } @@ -631,6 +647,7 @@ TYPED_TEST(RendererPixelTest, FastPassColorFilterAlpha) { // renderer so use a fuzzy comparator. EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha.png")), FuzzyForSoftwareOnlyPixelComparator<TypeParam>(false))); } @@ -733,6 +750,7 @@ TYPED_TEST(RendererPixelTest, FastPassColorFilterAlphaTranslation) { // renderer so use a fuzzy comparator. EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha_translate.png")), FuzzyForSoftwareOnlyPixelComparator<TypeParam>(false))); } @@ -789,6 +807,7 @@ TYPED_TEST(RendererPixelTest, EnlargedRenderPassTexture) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")), ExactPixelComparator(true))); } @@ -857,6 +876,7 @@ TYPED_TEST(RendererPixelTest, EnlargedRenderPassTextureWithAntiAliasing) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("blue_yellow_anti_aliasing.png")), FuzzyPixelOffByOneComparator(true))); } @@ -996,6 +1016,7 @@ TEST_F(GLRendererPixelTestWithBackgroundFilter, InvertFilter) { this->SetUpRenderPassList(); EXPECT_TRUE(this->RunPixelTest( &this->pass_list_, + PixelTest::WithOffscreenContext, base::FilePath(FILE_PATH_LITERAL("background_filter.png")), ExactPixelComparator(true))); } @@ -1003,7 +1024,8 @@ TEST_F(GLRendererPixelTestWithBackgroundFilter, InvertFilter) { class ExternalStencilPixelTest : public GLRendererPixelTest { protected: void ClearBackgroundToGreen() { - WebKit::WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebKit::WebGraphicsContext3D* context3d = + output_surface_->context_provider()->Context3d(); output_surface_->EnsureBackbuffer(); output_surface_->Reshape(device_viewport_size_, 1); context3d->clearColor(0.f, 1.f, 0.f, 1.f); @@ -1012,7 +1034,8 @@ class ExternalStencilPixelTest : public GLRendererPixelTest { void PopulateStencilBuffer() { // Set two quadrants of the stencil buffer to 1. - WebKit::WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebKit::WebGraphicsContext3D* context3d = + output_surface_->context_provider()->Context3d(); ASSERT_TRUE(context3d->getContextAttributes().stencil); output_surface_->EnsureBackbuffer(); output_surface_->Reshape(device_viewport_size_, 1); @@ -1054,6 +1077,7 @@ TEST_F(ExternalStencilPixelTest, StencilTestEnabled) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")), ExactPixelComparator(true))); } @@ -1076,6 +1100,7 @@ TEST_F(ExternalStencilPixelTest, StencilTestDisabled) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green.png")), ExactPixelComparator(true))); } @@ -1125,10 +1150,36 @@ TEST_F(ExternalStencilPixelTest, RenderSurfacesIgnoreStencil) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")), ExactPixelComparator(true))); } +TEST_F(ExternalStencilPixelTest, DeviceClip) { + ClearBackgroundToGreen(); + gfx::Rect clip_rect(gfx::Point(150, 150), gfx::Size(50, 50)); + this->ForceDeviceClip(clip_rect); + + // Draw a blue quad that covers the entire device viewport. It should be + // clipped to the bottom right corner by the device clip. + gfx::Rect rect(this->device_viewport_size_); + RenderPass::Id id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + scoped_ptr<SharedQuadState> blue_shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect); + scoped_ptr<SolidColorDrawQuad> blue = SolidColorDrawQuad::Create(); + blue->SetNew(blue_shared_state.get(), rect, SK_ColorBLUE, false); + pass->quad_list.push_back(blue.PassAs<DrawQuad>()); + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + PixelTest::NoOffscreenContext, + base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")), + ExactPixelComparator(true))); +} + // Software renderer does not support anti-aliased edges. TEST_F(GLRendererPixelTest, AntiAliasing) { gfx::Rect rect(this->device_viewport_size_); @@ -1170,6 +1221,7 @@ TEST_F(GLRendererPixelTest, AntiAliasing) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("anti_aliasing.png")), FuzzyPixelOffByOneComparator(true))); } @@ -1222,6 +1274,7 @@ TEST_F(GLRendererPixelTest, AxisAligned) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("axis_aligned.png")), ExactPixelComparator(true))); } @@ -1263,6 +1316,7 @@ TEST_F(GLRendererPixelTest, ForceAntiAliasingOff) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("force_anti_aliasing_off.png")), ExactPixelComparator(false))); } @@ -1303,6 +1357,7 @@ TEST_F(GLRendererPixelTest, AntiAliasingPerspective) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("anti_aliasing_perspective.png")), FuzzyPixelOffByOneComparator(true))); } @@ -1312,7 +1367,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadIdentityScale) { gfx::Rect viewport(this->device_viewport_size_); bool use_skia_gpu_backend = this->UseSkiaGPUBackend(); // TODO(enne): the renderer should figure this out on its own. - bool contents_swizzled = !PlatformColor::SameComponentOrder(GL_RGBA); + ResourceFormat texture_format = RGBA_8888; RenderPass::Id id(1, 1); gfx::Transform transform_to_root; @@ -1350,7 +1405,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadIdentityScale) { gfx::Rect(), viewport, viewport.size(), - contents_swizzled, + texture_format, viewport, 1.f, use_skia_gpu_backend, @@ -1375,7 +1430,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadIdentityScale) { gfx::Rect(), gfx::RectF(0.f, 0.f, 1.f, 1.f), viewport.size(), - contents_swizzled, + texture_format, viewport, 1.f, use_skia_gpu_backend, @@ -1387,17 +1442,91 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadIdentityScale) { EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")), ExactPixelComparator(true))); } +// Not WithSkiaGPUBackend since that path currently requires tiles for opacity. +TYPED_TEST(RendererPixelTest, PictureDrawQuadOpacity) { + gfx::Size pile_tile_size(1000, 1000); + gfx::Rect viewport(this->device_viewport_size_); + bool use_skia_gpu_backend = this->UseSkiaGPUBackend(); + ResourceFormat texture_format = RGBA_8888; + + RenderPass::Id id(1, 1); + gfx::Transform transform_to_root; + scoped_ptr<RenderPass> pass = + CreateTestRenderPass(id, viewport, transform_to_root); + + // One viewport-filling 0.5-opacity green quad. + scoped_refptr<FakePicturePileImpl> green_pile = + FakePicturePileImpl::CreateFilledPile(pile_tile_size, viewport.size()); + SkPaint green_paint; + green_paint.setColor(SK_ColorGREEN); + green_pile->add_draw_rect_with_paint(viewport, green_paint); + green_pile->RerecordPile(); + + gfx::Transform green_content_to_target_transform; + scoped_ptr<SharedQuadState> green_shared_state = + CreateTestSharedQuadState(green_content_to_target_transform, viewport); + green_shared_state->opacity = 0.5f; + + scoped_ptr<PictureDrawQuad> green_quad = PictureDrawQuad::Create(); + green_quad->SetNew(green_shared_state.get(), + viewport, + gfx::Rect(), + gfx::RectF(0, 0, 1, 1), + viewport.size(), + texture_format, + viewport, + 1.f, + use_skia_gpu_backend, + green_pile); + pass->quad_list.push_back(green_quad.PassAs<DrawQuad>()); + + // One viewport-filling white quad. + scoped_refptr<FakePicturePileImpl> white_pile = + FakePicturePileImpl::CreateFilledPile(pile_tile_size, viewport.size()); + SkPaint white_paint; + white_paint.setColor(SK_ColorWHITE); + white_pile->add_draw_rect_with_paint(viewport, white_paint); + white_pile->RerecordPile(); + + gfx::Transform white_content_to_target_transform; + scoped_ptr<SharedQuadState> white_shared_state = + CreateTestSharedQuadState(white_content_to_target_transform, viewport); + + scoped_ptr<PictureDrawQuad> white_quad = PictureDrawQuad::Create(); + white_quad->SetNew(white_shared_state.get(), + viewport, + gfx::Rect(), + gfx::RectF(0, 0, 1, 1), + viewport.size(), + texture_format, + viewport, + 1.f, + use_skia_gpu_backend, + white_pile); + pass->quad_list.push_back(white_quad.PassAs<DrawQuad>()); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + PixelTest::NoOffscreenContext, + base::FilePath(FILE_PATH_LITERAL("green_alpha.png")), + FuzzyPixelOffByOneComparator(true))); +} + TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadNonIdentityScale) { gfx::Size pile_tile_size(1000, 1000); gfx::Rect viewport(this->device_viewport_size_); bool use_skia_gpu_backend = this->UseSkiaGPUBackend(); // TODO(enne): the renderer should figure this out on its own. - bool contents_swizzled = !PlatformColor::SameComponentOrder(GL_RGBA); + ResourceFormat texture_format = RGBA_8888; RenderPass::Id id(1, 1); gfx::Transform transform_to_root; @@ -1431,7 +1560,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, gfx::Rect(), gfx::RectF(green_rect1.size()), green_rect1.size(), - contents_swizzled, + texture_format, green_rect1, 1.f, use_skia_gpu_backend, @@ -1444,7 +1573,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, gfx::Rect(), gfx::RectF(green_rect2.size()), green_rect2.size(), - contents_swizzled, + texture_format, green_rect2, 1.f, use_skia_gpu_backend, @@ -1516,7 +1645,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, gfx::Rect(), quad_content_rect, content_union_rect.size(), - contents_swizzled, + texture_format, content_union_rect, contents_scale, use_skia_gpu_backend, @@ -1539,6 +1668,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, EXPECT_TRUE(this->RunPixelTest( &pass_list, + PixelTest::NoOffscreenContext, base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")), ExactPixelComparator(true))); } diff --git a/chromium/cc/output/shader.cc b/chromium/cc/output/shader.cc index 8ab2114555f..beabf8db755 100644 --- a/chromium/cc/output/shader.cc +++ b/chromium/cc/output/shader.cc @@ -1597,7 +1597,7 @@ std::string FragmentShaderCheckerboard::GetShaderString( vec2 texCoord = clamp(v_texCoord, 0.0, 1.0) * texTransform.zw + texTransform.xy; vec2 coord = mod(floor(texCoord * frequency * 2.0), 2.0); - float picker = abs(coord.x - coord.y); + float picker = abs(coord.x - coord.y); // NOLINT gl_FragColor = mix(color1, color2, picker) * alpha; } ); // NOLINT(whitespace/parens) diff --git a/chromium/cc/output/software_output_device.cc b/chromium/cc/output/software_output_device.cc index fc77eb4c701..03f977e7426 100644 --- a/chromium/cc/output/software_output_device.cc +++ b/chromium/cc/output/software_output_device.cc @@ -6,8 +6,8 @@ #include "base/logging.h" #include "cc/output/software_frame_data.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkDevice.h" #include "ui/gfx/skia_util.h" namespace cc { @@ -21,7 +21,7 @@ void SoftwareOutputDevice::Resize(gfx::Size viewport_size) { return; viewport_size_ = viewport_size; - device_ = skia::AdoptRef(new SkDevice(SkBitmap::kARGB_8888_Config, + device_ = skia::AdoptRef(new SkBitmapDevice(SkBitmap::kARGB_8888_Config, viewport_size.width(), viewport_size.height(), true)); canvas_ = skia::AdoptRef(new SkCanvas(device_.get())); } diff --git a/chromium/cc/output/software_output_device.h b/chromium/cc/output/software_output_device.h index fbada745a0a..9e2de88b048 100644 --- a/chromium/cc/output/software_output_device.h +++ b/chromium/cc/output/software_output_device.h @@ -8,12 +8,13 @@ #include "base/basictypes.h" #include "cc/base/cc_export.h" #include "skia/ext/refptr.h" +// TODO(robertphillips): change this to "class SkBaseDevice;" +#include "third_party/skia/include/core/SkDevice.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" #include "ui/gfx/vector2d.h" class SkBitmap; -class SkDevice; class SkCanvas; namespace cc { @@ -27,23 +28,47 @@ class CC_EXPORT SoftwareOutputDevice { SoftwareOutputDevice(); virtual ~SoftwareOutputDevice(); - // SoftwareOutputDevice implementation. + // Discards any pre-existing backing buffers and allocates memory for a + // software device of |size|. This must be called before the + // |SoftwareOutputDevice| can be used in other ways. virtual void Resize(gfx::Size size); + // Called on BeginDrawingFrame. The compositor will draw into the returned + // SkCanvas. The |SoftwareOutputDevice| implementation needs to provide a + // valid SkCanvas of at least size |damage_rect|. This class retains ownership + // of the SkCanvas. virtual SkCanvas* BeginPaint(gfx::Rect damage_rect); + + // Called on FinishDrawingFrame. The compositor will no longer mutate the the + // SkCanvas instance returned by |BeginPaint| and should discard any reference + // that it holds to it. virtual void EndPaint(SoftwareFrameData* frame_data); + // Copies pixels inside |rect| from the current software framebuffer to + // |output|. Fails if there is no current softwareframebuffer. virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output); + + // Blit the pixel content of the SoftwareOutputDevice by |delta| with the + // write clipped to |clip_rect|. virtual void Scroll(gfx::Vector2d delta, gfx::Rect clip_rect); + // Discard the backing buffer in the surface provided by this instance. + virtual void DiscardBackbuffer() {} + + // Ensures that there is a backing buffer available on this instance. + virtual void EnsureBackbuffer() {} + // TODO(skaslev) Remove this after UberCompositor lands. + // Called in response to receiving a SwapBuffersAck. At this point, software + // frame identified by id can be reused or discarded as it is no longer being + // displayed. virtual void ReclaimSoftwareFrame(unsigned id); protected: gfx::Size viewport_size_; gfx::Rect damage_rect_; - skia::RefPtr<SkDevice> device_; + skia::RefPtr<SkBaseDevice> device_; skia::RefPtr<SkCanvas> canvas_; private: diff --git a/chromium/cc/output/software_renderer.cc b/chromium/cc/output/software_renderer.cc index 9bf2efef39f..fa8c178df35 100644 --- a/chromium/cc/output/software_renderer.cc +++ b/chromium/cc/output/software_renderer.cc @@ -22,7 +22,7 @@ #include "skia/ext/opacity_draw_filter.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkDevice.h" +#include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/effects/SkLayerRasterizer.h" @@ -52,20 +52,23 @@ bool IsScaleAndIntegerTranslate(const SkMatrix& matrix) { scoped_ptr<SoftwareRenderer> SoftwareRenderer::Create( RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider) { - return make_scoped_ptr( - new SoftwareRenderer(client, output_surface, resource_provider)); + return make_scoped_ptr(new SoftwareRenderer( + client, settings, output_surface, resource_provider)); } SoftwareRenderer::SoftwareRenderer(RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider) - : DirectRenderer(client, output_surface, resource_provider), - visible_(true), - is_scissor_enabled_(false), - output_device_(output_surface->software_device()), - current_canvas_(NULL) { + : DirectRenderer(client, settings, output_surface, resource_provider), + visible_(true), + is_scissor_enabled_(false), + is_backbuffer_discarded_(false), + output_device_(output_surface->software_device()), + current_canvas_(NULL) { if (resource_provider_) { capabilities_.max_texture_size = resource_provider_->max_texture_size(); capabilities_.best_texture_format = @@ -76,7 +79,7 @@ SoftwareRenderer::SoftwareRenderer(RendererClient* client, capabilities_.allow_partial_texture_updates = true; capabilities_.using_partial_swap = true; - capabilities_.using_map_image = Settings().use_map_image; + capabilities_.using_map_image = settings_->use_map_image; capabilities_.using_shared_memory_resources = true; } @@ -128,14 +131,14 @@ void SoftwareRenderer::EnsureScissorTestDisabled() { // clipRect on the current SkCanvas. This is done by setting clipRect to // the viewport's dimensions. is_scissor_enabled_ = false; - SkDevice* device = current_canvas_->getDevice(); + SkBaseDevice* device = current_canvas_->getDevice(); SetClipRect(gfx::Rect(device->width(), device->height())); } void SoftwareRenderer::Finish() {} void SoftwareRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) { - DCHECK(!client_->ExternalStencilTestEnabled()); + DCHECK(!output_surface_->HasExternalStencilTest()); current_framebuffer_lock_.reset(); current_canvas_ = root_canvas_; } @@ -179,7 +182,11 @@ void SoftwareRenderer::ClearCanvas(SkColor color) { current_canvas_->clear(color); } -void SoftwareRenderer::ClearFramebuffer(DrawingFrame* frame) { +void SoftwareRenderer::DiscardPixels(bool has_external_stencil_test, + bool draw_rect_covers_full_surface) {} + +void SoftwareRenderer::ClearFramebuffer(DrawingFrame* frame, + bool has_external_stencil_test) { if (frame->current_render_pass->has_transparent_background) { ClearCanvas(SkColorSetARGB(0, 0, 0, 0)); } else { @@ -228,8 +235,7 @@ void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) { quad->IsLeftEdge() && quad->IsBottomEdge() && quad->IsRightEdge(); - if (Settings().allow_antialiasing && - all_four_edges_are_exterior) + if (settings_->allow_antialiasing && all_four_edges_are_exterior) current_paint_.setAntiAlias(true); current_paint_.setFilterBitmap(true); } @@ -277,9 +283,11 @@ void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) { void SoftwareRenderer::DrawCheckerboardQuad(const DrawingFrame* frame, const CheckerboardDrawQuad* quad) { + gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional( + QuadVertexRect(), quad->rect, quad->visible_rect); current_paint_.setColor(quad->color); current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color)); - current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()), + current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect), current_paint_); } @@ -329,9 +337,11 @@ void SoftwareRenderer::DrawPictureQuad(const DrawingFrame* frame, void SoftwareRenderer::DrawSolidColorQuad(const DrawingFrame* frame, const SolidColorDrawQuad* quad) { + gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional( + QuadVertexRect(), quad->rect, quad->visible_rect); current_paint_.setColor(quad->color); current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color)); - current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()), + current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect), current_paint_); } @@ -350,8 +360,12 @@ void SoftwareRenderer::DrawTextureQuad(const DrawingFrame* frame, quad->uv_bottom_right), bitmap->width(), bitmap->height()); - SkRect sk_uv_rect = gfx::RectFToSkRect(uv_rect); - SkRect quad_rect = gfx::RectFToSkRect(QuadVertexRect()); + gfx::RectF visible_uv_rect = + MathUtil::ScaleRectProportional(uv_rect, quad->rect, quad->visible_rect); + SkRect sk_uv_rect = gfx::RectFToSkRect(visible_uv_rect); + gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional( + QuadVertexRect(), quad->rect, quad->visible_rect); + SkRect quad_rect = gfx::RectFToSkRect(visible_quad_vertex_rect); if (quad->flipped) current_canvas_->scale(1, -1); @@ -384,12 +398,18 @@ void SoftwareRenderer::DrawTileQuad(const DrawingFrame* frame, DCHECK(IsSoftwareResource(quad->resource_id)); ResourceProvider::ScopedReadLockSoftware lock(resource_provider_, quad->resource_id); + gfx::RectF visible_tex_coord_rect = MathUtil::ScaleRectProportional( + quad->tex_coord_rect, quad->rect, quad->visible_rect); + gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional( + QuadVertexRect(), quad->rect, quad->visible_rect); - SkRect uv_rect = gfx::RectFToSkRect(quad->tex_coord_rect); + SkRect uv_rect = gfx::RectFToSkRect(visible_tex_coord_rect); current_paint_.setFilterBitmap(true); - current_canvas_->drawBitmapRectToRect(*lock.sk_bitmap(), &uv_rect, - gfx::RectFToSkRect(QuadVertexRect()), - ¤t_paint_); + current_canvas_->drawBitmapRectToRect( + *lock.sk_bitmap(), + &uv_rect, + gfx::RectFToSkRect(visible_quad_vertex_rect), + ¤t_paint_); } void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame, @@ -404,6 +424,8 @@ void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame, content_texture->id()); SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect()); + SkRect dest_visible_rect = gfx::RectFToSkRect(MathUtil::ScaleRectProportional( + QuadVertexRect(), quad->rect, quad->visible_rect)); SkRect content_rect = SkRect::MakeWH(quad->rect.width(), quad->rect.height()); SkMatrix content_mat; @@ -451,10 +473,10 @@ void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame, mask_rasterizer->addLayer(mask_paint); current_paint_.setRasterizer(mask_rasterizer.get()); - current_canvas_->drawRect(dest_rect, current_paint_); + current_canvas_->drawRect(dest_visible_rect, current_paint_); } else { // TODO(skaslev): Apply background filters and blend with content - current_canvas_->drawRect(dest_rect, current_paint_); + current_canvas_->drawRect(dest_visible_rect, current_paint_); } } @@ -474,11 +496,8 @@ void SoftwareRenderer::CopyCurrentRenderPassToBitmap( DrawingFrame* frame, scoped_ptr<CopyOutputRequest> request) { gfx::Rect copy_rect = frame->current_render_pass->output_rect; - if (request->has_area()) { - // Intersect with the request's area, positioned with its origin at the - // origin of the full copy_rect. - copy_rect.Intersect(request->area() - copy_rect.OffsetFromOrigin()); - } + if (request->has_area()) + copy_rect.Intersect(request->area()); gfx::Rect window_copy_rect = MoveFromDrawToWindowSpace(copy_rect); scoped_ptr<SkBitmap> bitmap(new SkBitmap); @@ -491,6 +510,26 @@ void SoftwareRenderer::CopyCurrentRenderPassToBitmap( request->SendBitmapResult(bitmap.Pass()); } +void SoftwareRenderer::DiscardBackbuffer() { + if (is_backbuffer_discarded_) + return; + + output_surface_->DiscardBackbuffer(); + + is_backbuffer_discarded_ = true; + + // Damage tracker needs a full reset every time framebuffer is discarded. + client_->SetFullRootLayerDamage(); +} + +void SoftwareRenderer::EnsureBackbuffer() { + if (!is_backbuffer_discarded_) + return; + + output_surface_->EnsureBackbuffer(); + is_backbuffer_discarded_ = false; +} + void SoftwareRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { TRACE_EVENT0("cc", "SoftwareRenderer::GetFramebufferPixels"); SkBitmap subset_bitmap; @@ -505,12 +544,15 @@ void SoftwareRenderer::SetVisible(bool visible) { if (visible_ == visible) return; visible_ = visible; + + if (visible_) + EnsureBackbuffer(); + else + DiscardBackbuffer(); } void SoftwareRenderer::SetDiscardBackBufferWhenNotVisible(bool discard) { - // TODO(piman, skaslev): Can we release the backbuffer? We don't currently - // receive memory policy yet anyway. - NOTIMPLEMENTED(); + // The software renderer always discards the backbuffer when not visible. } } // namespace cc diff --git a/chromium/cc/output/software_renderer.h b/chromium/cc/output/software_renderer.h index f810b68274d..5a8780e043c 100644 --- a/chromium/cc/output/software_renderer.h +++ b/chromium/cc/output/software_renderer.h @@ -29,6 +29,7 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer { public: static scoped_ptr<SoftwareRenderer> Create( RendererClient* client, + const LayerTreeSettings* settings, OutputSurface* output_surface, ResourceProvider* resource_provider); @@ -45,6 +46,8 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer { virtual void ReceiveSwapBuffersAck( const CompositorFrameAck& ack) OVERRIDE; virtual void SetDiscardBackBufferWhenNotVisible(bool discard) OVERRIDE; + virtual void DiscardBackbuffer() OVERRIDE; + virtual void EnsureBackbuffer() OVERRIDE; protected: virtual void BindFramebufferToOutputSurface(DrawingFrame* frame) OVERRIDE; @@ -54,7 +57,10 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer { gfx::Rect target_rect) OVERRIDE; virtual void SetDrawViewport(gfx::Rect window_space_viewport) OVERRIDE; virtual void SetScissorTestRect(gfx::Rect scissor_rect) OVERRIDE; - virtual void ClearFramebuffer(DrawingFrame* frame) OVERRIDE; + virtual void DiscardPixels(bool has_external_stencil_test, + bool draw_rect_covers_full_surface) OVERRIDE; + virtual void ClearFramebuffer(DrawingFrame* frame, + bool has_external_stencil_test) OVERRIDE; virtual void DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) OVERRIDE; virtual void BeginDrawingFrame(DrawingFrame* frame) OVERRIDE; virtual void FinishDrawingFrame(DrawingFrame* frame) OVERRIDE; @@ -65,10 +71,10 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer { DrawingFrame* frame, scoped_ptr<CopyOutputRequest> request) OVERRIDE; - SoftwareRenderer( - RendererClient* client, - OutputSurface* output_surface, - ResourceProvider* resource_provider); + SoftwareRenderer(RendererClient* client, + const LayerTreeSettings* settings, + OutputSurface* output_surface, + ResourceProvider* resource_provider); private: void ClearCanvas(SkColor color); @@ -95,6 +101,7 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer { RendererCapabilities capabilities_; bool visible_; bool is_scissor_enabled_; + bool is_backbuffer_discarded_; gfx::Rect scissor_rect_; SoftwareOutputDevice* output_device_; diff --git a/chromium/cc/output/software_renderer_unittest.cc b/chromium/cc/output/software_renderer_unittest.cc index 238d654ac19..03f1635084b 100644 --- a/chromium/cc/output/software_renderer_unittest.cc +++ b/chromium/cc/output/software_renderer_unittest.cc @@ -13,6 +13,7 @@ #include "cc/quads/tile_draw_quad.h" #include "cc/test/animation_test_common.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/render_pass_test_common.h" #include "cc/test/render_pass_test_utils.h" @@ -26,15 +27,16 @@ namespace { class SoftwareRendererTest : public testing::Test, public RendererClient { public: - SoftwareRendererTest() : should_clear_root_render_pass_(true) {} - void InitializeRenderer( scoped_ptr<SoftwareOutputDevice> software_output_device) { output_surface_ = FakeOutputSurface::CreateSoftware( software_output_device.Pass()); - resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false); renderer_ = SoftwareRenderer::Create( - this, output_surface_.get(), resource_provider()); + this, &settings_, output_surface_.get(), resource_provider()); } ResourceProvider* resource_provider() const { @@ -47,40 +49,23 @@ class SoftwareRendererTest : public testing::Test, public RendererClient { viewport_ = viewport; } - void set_should_clear_root_render_pass(bool clear_root_render_pass) { - should_clear_root_render_pass_ = clear_root_render_pass; - } - // RendererClient implementation. virtual gfx::Rect DeviceViewport() const OVERRIDE { return viewport_; } - virtual float DeviceScaleFactor() const OVERRIDE { - return 1.f; - } - virtual const LayerTreeSettings& Settings() const OVERRIDE { - return settings_; - } + virtual gfx::Rect DeviceClip() const OVERRIDE { return DeviceViewport(); } virtual void SetFullRootLayerDamage() OVERRIDE {} - virtual bool HasImplThread() const OVERRIDE { return false; } - virtual bool ShouldClearRootRenderPass() const OVERRIDE { - return should_clear_root_render_pass_; - } virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE { return CompositorFrameMetadata(); } - virtual bool AllowPartialSwap() const OVERRIDE { - return true; - } - virtual bool ExternalStencilTestEnabled() const OVERRIDE { return false; } protected: + LayerTreeSettings settings_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<FakeOutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; scoped_ptr<SoftwareRenderer> renderer_; gfx::Rect viewport_; - LayerTreeSettings settings_; - bool should_clear_root_render_pass_; }; TEST_F(SoftwareRendererTest, SolidColorQuad) { @@ -88,6 +73,7 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) { gfx::Size inner_size(98, 98); gfx::Rect outer_rect(outer_size); gfx::Rect inner_rect(gfx::Point(1, 1), inner_size); + gfx::Rect visible_rect(gfx::Point(1, 2), gfx::Size(98, 97)); set_viewport(gfx::Rect(outer_size)); InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice)); @@ -104,12 +90,15 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) { shared_quad_state.get(), outer_rect, SK_ColorYELLOW, false); scoped_ptr<SolidColorDrawQuad> inner_quad = SolidColorDrawQuad::Create(); inner_quad->SetNew(shared_quad_state.get(), inner_rect, SK_ColorCYAN, false); + inner_quad->visible_rect = visible_rect; root_render_pass->AppendQuad(inner_quad.PassAs<DrawQuad>()); root_render_pass->AppendQuad(outer_quad.PassAs<DrawQuad>()); RenderPassList list; list.push_back(root_render_pass.PassAs<RenderPass>()); - renderer()->DrawFrame(&list); + + float device_scale_factor = 1.f; + renderer()->DrawFrame(&list, NULL, device_scale_factor, true); SkBitmap output; output.setConfig(SkBitmap::kARGB_8888_Config, @@ -121,7 +110,8 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) { EXPECT_EQ(SK_ColorYELLOW, output.getColor(0, 0)); EXPECT_EQ(SK_ColorYELLOW, output.getColor(outer_size.width() - 1, outer_size.height() - 1)); - EXPECT_EQ(SK_ColorCYAN, output.getColor(1, 1)); + EXPECT_EQ(SK_ColorYELLOW, output.getColor(1, 1)); + EXPECT_EQ(SK_ColorCYAN, output.getColor(1, 2)); EXPECT_EQ(SK_ColorCYAN, output.getColor(inner_size.width() - 1, inner_size.height() - 1)); } @@ -135,11 +125,15 @@ TEST_F(SoftwareRendererTest, TileQuad) { InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice)); ResourceProvider::ResourceId resource_yellow = - resource_provider()->CreateResource( - outer_size, GL_RGBA, ResourceProvider::TextureUsageAny); + resource_provider()->CreateResource(outer_size, + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + RGBA_8888); ResourceProvider::ResourceId resource_cyan = - resource_provider()->CreateResource( - inner_size, GL_RGBA, ResourceProvider::TextureUsageAny); + resource_provider()->CreateResource(inner_size, + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + RGBA_8888); SkBitmap yellow_tile; yellow_tile.setConfig( @@ -195,7 +189,9 @@ TEST_F(SoftwareRendererTest, TileQuad) { RenderPassList list; list.push_back(root_render_pass.PassAs<RenderPass>()); - renderer()->DrawFrame(&list); + + float device_scale_factor = 1.f; + renderer()->DrawFrame(&list, NULL, device_scale_factor, true); SkBitmap output; output.setConfig(SkBitmap::kARGB_8888_Config, @@ -212,10 +208,95 @@ TEST_F(SoftwareRendererTest, TileQuad) { output.getColor(inner_size.width() - 1, inner_size.height() - 1)); } +TEST_F(SoftwareRendererTest, TileQuadVisibleRect) { + gfx::Size tile_size(100, 100); + gfx::Rect tile_rect(tile_size); + gfx::Rect visible_rect = tile_rect; + visible_rect.Inset(1, 2, 3, 4); + set_viewport(gfx::Rect(tile_size)); + InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice)); + + ResourceProvider::ResourceId resource_cyan = + resource_provider()->CreateResource(tile_size, + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + RGBA_8888); + + SkBitmap cyan_tile; // The lowest five rows are yellow. + cyan_tile.setConfig( + SkBitmap::kARGB_8888_Config, tile_size.width(), tile_size.height()); + cyan_tile.allocPixels(); + cyan_tile.eraseColor(SK_ColorCYAN); + cyan_tile.eraseArea( + SkIRect::MakeLTRB( + 0, visible_rect.bottom() - 1, tile_rect.width(), tile_rect.bottom()), + SK_ColorYELLOW); + + resource_provider()->SetPixels(resource_cyan, + static_cast<uint8_t*>(cyan_tile.getPixels()), + gfx::Rect(tile_size), + gfx::Rect(tile_size), + gfx::Vector2d()); + + gfx::Rect root_rect = DeviceViewport(); + + scoped_ptr<SharedQuadState> shared_quad_state = SharedQuadState::Create(); + shared_quad_state->SetAll( + gfx::Transform(), tile_size, tile_rect, tile_rect, false, 1.0); + RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1); + scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create(); + root_render_pass->SetNew( + root_render_pass_id, root_rect, root_rect, gfx::Transform()); + scoped_ptr<TileDrawQuad> quad = TileDrawQuad::Create(); + quad->SetNew(shared_quad_state.get(), + tile_rect, + tile_rect, + resource_cyan, + gfx::RectF(tile_size), + tile_size, + false); + quad->visible_rect = visible_rect; + root_render_pass->AppendQuad(quad.PassAs<DrawQuad>()); + + RenderPassList list; + list.push_back(root_render_pass.PassAs<RenderPass>()); + + float device_scale_factor = 1.f; + renderer()->DrawFrame(&list, NULL, device_scale_factor, true); + + SkBitmap output; + output.setConfig(SkBitmap::kARGB_8888_Config, + DeviceViewport().width(), + DeviceViewport().height()); + output.allocPixels(); + renderer()->GetFramebufferPixels(output.getPixels(), tile_rect); + + // Check portion of tile not in visible rect isn't drawn. + const unsigned int kTransparent = SK_ColorTRANSPARENT; + EXPECT_EQ(kTransparent, output.getColor(0, 0)); + EXPECT_EQ(kTransparent, + output.getColor(tile_rect.width() - 1, tile_rect.height() - 1)); + EXPECT_EQ(kTransparent, + output.getColor(visible_rect.x() - 1, visible_rect.y() - 1)); + EXPECT_EQ(kTransparent, + output.getColor(visible_rect.right(), visible_rect.bottom())); + // Ensure visible part is drawn correctly. + EXPECT_EQ(SK_ColorCYAN, output.getColor(visible_rect.x(), visible_rect.y())); + EXPECT_EQ( + SK_ColorCYAN, + output.getColor(visible_rect.right() - 2, visible_rect.bottom() - 2)); + // Ensure last visible line is correct. + EXPECT_EQ( + SK_ColorYELLOW, + output.getColor(visible_rect.right() - 1, visible_rect.bottom() - 1)); +} + TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) { + float device_scale_factor = 1.f; gfx::Rect viewport_rect(0, 0, 100, 100); set_viewport(viewport_rect); - set_should_clear_root_render_pass(false); + + settings_.should_clear_root_render_pass = false; InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice)); RenderPassList list; @@ -233,7 +314,7 @@ TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) { AddQuad(root_clear_pass, viewport_rect, SK_ColorGREEN); renderer()->DecideRenderPassAllocationsForFrame(list); - renderer()->DrawFrame(&list); + renderer()->DrawFrame(&list, NULL, device_scale_factor, true); renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect); EXPECT_EQ(SK_ColorGREEN, output.getColor(0, 0)); @@ -252,7 +333,7 @@ TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) { AddQuad(root_smaller_pass, smaller_rect, SK_ColorMAGENTA); renderer()->DecideRenderPassAllocationsForFrame(list); - renderer()->DrawFrame(&list); + renderer()->DrawFrame(&list, NULL, device_scale_factor, true); renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect); // If we didn't clear, the borders should still be green. @@ -266,5 +347,60 @@ TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) { output.getColor(smaller_rect.right() - 1, smaller_rect.bottom() - 1)); } +TEST_F(SoftwareRendererTest, RenderPassVisibleRect) { + float device_scale_factor = 1.f; + gfx::Rect viewport_rect(0, 0, 100, 100); + set_viewport(viewport_rect); + InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice)); + + RenderPassList list; + + SkBitmap output; + output.setConfig(SkBitmap::kARGB_8888_Config, + viewport_rect.width(), + viewport_rect.height()); + output.allocPixels(); + + // Pass drawn as inner quad is magenta. + gfx::Rect smaller_rect(20, 20, 60, 60); + RenderPass::Id smaller_pass_id(2, 1); + TestRenderPass* smaller_pass = + AddRenderPass(&list, smaller_pass_id, smaller_rect, gfx::Transform()); + AddQuad(smaller_pass, smaller_rect, SK_ColorMAGENTA); + + // Root pass is green. + RenderPass::Id root_clear_pass_id(1, 0); + TestRenderPass* root_clear_pass = + AddRenderPass(&list, root_clear_pass_id, viewport_rect, gfx::Transform()); + AddRenderPassQuad(root_clear_pass, smaller_pass); + AddQuad(root_clear_pass, viewport_rect, SK_ColorGREEN); + + // Interior pass quad has smaller visible rect. + gfx::Rect interior_visible_rect(30, 30, 40, 40); + root_clear_pass->quad_list[0]->visible_rect = interior_visible_rect; + + renderer()->DecideRenderPassAllocationsForFrame(list); + renderer()->DrawFrame(&list, NULL, device_scale_factor, true); + renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect); + + EXPECT_EQ(SK_ColorGREEN, output.getColor(0, 0)); + EXPECT_EQ( + SK_ColorGREEN, + output.getColor(viewport_rect.width() - 1, viewport_rect.height() - 1)); + + // Part outside visible rect should remain green. + EXPECT_EQ(SK_ColorGREEN, output.getColor(smaller_rect.x(), smaller_rect.y())); + EXPECT_EQ( + SK_ColorGREEN, + output.getColor(smaller_rect.right() - 1, smaller_rect.bottom() - 1)); + + EXPECT_EQ( + SK_ColorMAGENTA, + output.getColor(interior_visible_rect.x(), interior_visible_rect.y())); + EXPECT_EQ(SK_ColorMAGENTA, + output.getColor(interior_visible_rect.right() - 1, + interior_visible_rect.bottom() - 1)); +} + } // namespace } // namespace cc diff --git a/chromium/cc/quads/draw_quad_unittest.cc b/chromium/cc/quads/draw_quad_unittest.cc index 0ab0513ef01..5705ce7bf24 100644 --- a/chromium/cc/quads/draw_quad_unittest.cc +++ b/chromium/cc/quads/draw_quad_unittest.cc @@ -665,7 +665,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) { gfx::Rect opaque_rect(33, 44, 22, 33); gfx::RectF tex_coord_rect(31.f, 12.f, 54.f, 20.f); gfx::Size texture_size(85, 32); - bool swizzle_contents = true; + ResourceFormat texture_format = RGBA_8888; gfx::Rect content_rect(30, 40, 20, 30); float contents_scale = 3.141592f; bool can_draw_direct_to_backbuffer = true; @@ -676,7 +676,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) { opaque_rect, tex_coord_rect, texture_size, - swizzle_contents, + texture_format, content_rect, contents_scale, can_draw_direct_to_backbuffer, @@ -685,7 +685,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) { EXPECT_RECT_EQ(opaque_rect, copy_quad->opaque_rect); EXPECT_EQ(tex_coord_rect, copy_quad->tex_coord_rect); EXPECT_EQ(texture_size, copy_quad->texture_size); - EXPECT_EQ(swizzle_contents, copy_quad->swizzle_contents); + EXPECT_EQ(texture_format, copy_quad->texture_format); EXPECT_RECT_EQ(content_rect, copy_quad->content_rect); EXPECT_EQ(contents_scale, copy_quad->contents_scale); EXPECT_EQ(can_draw_direct_to_backbuffer, @@ -695,7 +695,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) { CREATE_QUAD_7_ALL(PictureDrawQuad, tex_coord_rect, texture_size, - swizzle_contents, + texture_format, content_rect, contents_scale, can_draw_direct_to_backbuffer, @@ -703,7 +703,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) { EXPECT_EQ(DrawQuad::PICTURE_CONTENT, copy_quad->material); EXPECT_EQ(tex_coord_rect, copy_quad->tex_coord_rect); EXPECT_EQ(texture_size, copy_quad->texture_size); - EXPECT_EQ(swizzle_contents, copy_quad->swizzle_contents); + EXPECT_EQ(texture_format, copy_quad->texture_format); EXPECT_RECT_EQ(content_rect, copy_quad->content_rect); EXPECT_EQ(contents_scale, copy_quad->contents_scale); EXPECT_EQ(can_draw_direct_to_backbuffer, @@ -893,7 +893,7 @@ TEST_F(DrawQuadIteratorTest, DISABLED_PictureDrawQuad) { gfx::Rect opaque_rect(33, 44, 22, 33); gfx::RectF tex_coord_rect(31.f, 12.f, 54.f, 20.f); gfx::Size texture_size(85, 32); - bool swizzle_contents = true; + ResourceFormat texture_format = RGBA_8888; gfx::Rect content_rect(30, 40, 20, 30); float contents_scale = 3.141592f; bool can_draw_direct_to_backbuffer = true; @@ -904,7 +904,7 @@ TEST_F(DrawQuadIteratorTest, DISABLED_PictureDrawQuad) { opaque_rect, tex_coord_rect, texture_size, - swizzle_contents, + texture_format, content_rect, contents_scale, can_draw_direct_to_backbuffer, diff --git a/chromium/cc/quads/picture_draw_quad.cc b/chromium/cc/quads/picture_draw_quad.cc index 0494764d8aa..e566b247f72 100644 --- a/chromium/cc/quads/picture_draw_quad.cc +++ b/chromium/cc/quads/picture_draw_quad.cc @@ -6,6 +6,7 @@ #include "base/values.h" #include "cc/base/math_util.h" +#include "cc/resources/platform_color.h" namespace cc { @@ -24,18 +25,24 @@ void PictureDrawQuad::SetNew(const SharedQuadState* shared_quad_state, gfx::Rect opaque_rect, const gfx::RectF& tex_coord_rect, gfx::Size texture_size, - bool swizzle_contents, + ResourceFormat texture_format, gfx::Rect content_rect, float contents_scale, bool can_draw_direct_to_backbuffer, scoped_refptr<PicturePileImpl> picture_pile) { - ContentDrawQuadBase::SetNew(shared_quad_state, DrawQuad::PICTURE_CONTENT, - rect, opaque_rect, tex_coord_rect, texture_size, - swizzle_contents); + ContentDrawQuadBase::SetNew(shared_quad_state, + DrawQuad::PICTURE_CONTENT, + rect, + opaque_rect, + tex_coord_rect, + texture_size, + !PlatformColor::SameComponentOrder( + texture_format)); this->content_rect = content_rect; this->contents_scale = contents_scale; this->can_draw_direct_to_backbuffer = can_draw_direct_to_backbuffer; this->picture_pile = picture_pile; + this->texture_format = texture_format; } void PictureDrawQuad::SetAll(const SharedQuadState* shared_quad_state, @@ -45,19 +52,26 @@ void PictureDrawQuad::SetAll(const SharedQuadState* shared_quad_state, bool needs_blending, const gfx::RectF& tex_coord_rect, gfx::Size texture_size, - bool swizzle_contents, + ResourceFormat texture_format, gfx::Rect content_rect, float contents_scale, bool can_draw_direct_to_backbuffer, scoped_refptr<PicturePileImpl> picture_pile) { ContentDrawQuadBase::SetAll(shared_quad_state, - DrawQuad::PICTURE_CONTENT, rect, opaque_rect, - visible_rect, needs_blending, tex_coord_rect, - texture_size, swizzle_contents); + DrawQuad::PICTURE_CONTENT, + rect, + opaque_rect, + visible_rect, + needs_blending, + tex_coord_rect, + texture_size, + !PlatformColor::SameComponentOrder( + texture_format)); this->content_rect = content_rect; this->contents_scale = contents_scale; this->can_draw_direct_to_backbuffer = can_draw_direct_to_backbuffer; this->picture_pile = picture_pile; + this->texture_format = texture_format; } void PictureDrawQuad::IterateResources( @@ -77,6 +91,7 @@ void PictureDrawQuad::ExtendValue(base::DictionaryValue* value) const { value->SetDouble("contents_scale", contents_scale); value->SetBoolean("can_draw_direct_to_backbuffer", can_draw_direct_to_backbuffer); + value->SetInteger("texture_format", texture_format); // TODO(piman): picture_pile? } diff --git a/chromium/cc/quads/picture_draw_quad.h b/chromium/cc/quads/picture_draw_quad.h index beec88ce3f3..756482a610b 100644 --- a/chromium/cc/quads/picture_draw_quad.h +++ b/chromium/cc/quads/picture_draw_quad.h @@ -27,7 +27,7 @@ class CC_EXPORT PictureDrawQuad : public ContentDrawQuadBase { gfx::Rect opaque_rect, const gfx::RectF& tex_coord_rect, gfx::Size texture_size, - bool swizzle_contents, + ResourceFormat texture_format, gfx::Rect content_rect, float contents_scale, bool can_draw_direct_to_backbuffer, @@ -40,7 +40,7 @@ class CC_EXPORT PictureDrawQuad : public ContentDrawQuadBase { bool needs_blending, const gfx::RectF& tex_coord_rect, gfx::Size texture_size, - bool swizzle_contents, + ResourceFormat texture_format, gfx::Rect content_rect, float contents_scale, bool can_draw_direct_to_backbuffer, @@ -50,6 +50,7 @@ class CC_EXPORT PictureDrawQuad : public ContentDrawQuadBase { float contents_scale; bool can_draw_direct_to_backbuffer; scoped_refptr<PicturePileImpl> picture_pile; + ResourceFormat texture_format; virtual void IterateResources(const ResourceIteratorCallback& callback) OVERRIDE; diff --git a/chromium/cc/quads/render_pass.h b/chromium/cc/quads/render_pass.h index 224a0506f03..cf2a02d4a3c 100644 --- a/chromium/cc/quads/render_pass.h +++ b/chromium/cc/quads/render_pass.h @@ -11,11 +11,8 @@ #include "base/callback.h" #include "base/containers/hash_tables.h" #include "cc/base/cc_export.h" -#include "cc/base/scoped_ptr_hash_map.h" #include "cc/base/scoped_ptr_vector.h" #include "skia/ext/refptr.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkImageFilter.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_f.h" #include "ui/gfx/transform.h" diff --git a/chromium/cc/quads/render_pass_draw_quad.cc b/chromium/cc/quads/render_pass_draw_quad.cc index 0528cd5e34f..73b2553e209 100644 --- a/chromium/cc/quads/render_pass_draw_quad.cc +++ b/chromium/cc/quads/render_pass_draw_quad.cc @@ -7,6 +7,7 @@ #include "base/values.h" #include "cc/base/math_util.h" #include "cc/debug/traced_value.h" +#include "third_party/skia/include/core/SkImageFilter.h" namespace cc { diff --git a/chromium/cc/resources/bitmap_content_layer_updater.cc b/chromium/cc/resources/bitmap_content_layer_updater.cc index 3d90eb0a4d5..75b99c54b81 100644 --- a/chromium/cc/resources/bitmap_content_layer_updater.cc +++ b/chromium/cc/resources/bitmap_content_layer_updater.cc @@ -43,8 +43,7 @@ BitmapContentLayerUpdater::BitmapContentLayerUpdater( scoped_ptr<LayerPainter> painter, RenderingStatsInstrumentation* stats_instrumentation, int layer_id) - : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id), - opaque_(false) {} + : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id) {} BitmapContentLayerUpdater::~BitmapContentLayerUpdater() {} @@ -67,13 +66,13 @@ void BitmapContentLayerUpdater::PrepareToUpdate( devtools_instrumentation::kPaintSetup, layer_id_); canvas_size_ = content_rect.size(); canvas_ = skia::AdoptRef(skia::CreateBitmapCanvas( - canvas_size_.width(), canvas_size_.height(), opaque_)); + canvas_size_.width(), canvas_size_.height(), layer_is_opaque_)); } base::TimeTicks start_time = rendering_stats_instrumentation_->StartRecording(); PaintContents(canvas_.get(), - content_rect, + content_rect.origin(), contents_width_scale, contents_height_scale, resulting_opaque_rect); @@ -108,11 +107,12 @@ void BitmapContentLayerUpdater::ReduceMemoryUsage() { } void BitmapContentLayerUpdater::SetOpaque(bool opaque) { - if (opaque != opaque_) { + if (opaque != layer_is_opaque_) { canvas_.clear(); canvas_size_ = gfx::Size(); } - opaque_ = opaque; + + ContentLayerUpdater::SetOpaque(opaque); } } // namespace cc diff --git a/chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc b/chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc index 2c00099de1c..2db3f417a97 100644 --- a/chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc +++ b/chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc @@ -9,8 +9,8 @@ #include "cc/resources/layer_painter.h" #include "cc/resources/prioritized_resource.h" #include "cc/resources/resource_update_queue.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkDevice.h" namespace cc { @@ -28,7 +28,7 @@ void BitmapSkPictureContentLayerUpdater::Resource::Update( SkBitmap::kARGB_8888_Config, source_rect.width(), source_rect.height()); bitmap_.allocPixels(); bitmap_.setIsOpaque(updater_->layer_is_opaque()); - SkDevice device(bitmap_); + SkBitmapDevice device(bitmap_); SkCanvas canvas(&device); updater_->PaintContentsRect(&canvas, source_rect); diff --git a/chromium/cc/resources/content_layer_updater.cc b/chromium/cc/resources/content_layer_updater.cc index c9c2eccf31c..2e73c4bb904 100644 --- a/chromium/cc/resources/content_layer_updater.cc +++ b/chromium/cc/resources/content_layer_updater.cc @@ -9,6 +9,7 @@ #include "cc/debug/rendering_stats_instrumentation.h" #include "cc/resources/layer_painter.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkDevice.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkScalar.h" @@ -23,7 +24,8 @@ ContentLayerUpdater::ContentLayerUpdater( int layer_id) : rendering_stats_instrumentation_(stats_instrumentation), layer_id_(layer_id), - painter_(painter.Pass()) {} + painter_(painter.Pass()), + layer_is_opaque_(false) {} ContentLayerUpdater::~ContentLayerUpdater() {} @@ -33,14 +35,17 @@ void ContentLayerUpdater::set_rendering_stats_instrumentation( } void ContentLayerUpdater::PaintContents(SkCanvas* canvas, - gfx::Rect content_rect, + gfx::Point origin, float contents_width_scale, float contents_height_scale, gfx::Rect* resulting_opaque_rect) { TRACE_EVENT0("cc", "ContentLayerUpdater::PaintContents"); canvas->save(); - canvas->translate(SkFloatToScalar(-content_rect.x()), - SkFloatToScalar(-content_rect.y())); + canvas->translate(SkFloatToScalar(-origin.x()), + SkFloatToScalar(-origin.y())); + + SkBaseDevice* device = canvas->getDevice(); + gfx::Rect content_rect(origin, gfx::Size(device->width(), device->height())); gfx::Rect layer_rect = content_rect; @@ -52,12 +57,14 @@ void ContentLayerUpdater::PaintContents(SkCanvas* canvas, content_rect, 1.f / contents_width_scale, 1.f / contents_height_scale); } - SkPaint paint; - paint.setAntiAlias(false); - paint.setXfermodeMode(SkXfermode::kClear_Mode); SkRect layer_sk_rect = SkRect::MakeXYWH( layer_rect.x(), layer_rect.y(), layer_rect.width(), layer_rect.height()); - canvas->drawRect(layer_sk_rect, paint); + + // If the layer has opaque contents then there is no need to + // clear the canvas before painting. + if (!layer_is_opaque_) + canvas->clear(SK_ColorTRANSPARENT); + canvas->clipRect(layer_sk_rect); gfx::RectF opaque_layer_rect; @@ -71,4 +78,8 @@ void ContentLayerUpdater::PaintContents(SkCanvas* canvas, content_rect_ = content_rect; } +void ContentLayerUpdater::SetOpaque(bool opaque) { + layer_is_opaque_ = opaque; +} + } // namespace cc diff --git a/chromium/cc/resources/content_layer_updater.h b/chromium/cc/resources/content_layer_updater.h index 6c8dee3bba7..6959526ef1c 100644 --- a/chromium/cc/resources/content_layer_updater.h +++ b/chromium/cc/resources/content_layer_updater.h @@ -22,6 +22,7 @@ class RenderingStatsInstrumentation; class CC_EXPORT ContentLayerUpdater : public LayerUpdater { public: void set_rendering_stats_instrumentation(RenderingStatsInstrumentation* rsi); + virtual void SetOpaque(bool) OVERRIDE; protected: ContentLayerUpdater(scoped_ptr<LayerPainter> painter, @@ -30,12 +31,14 @@ class CC_EXPORT ContentLayerUpdater : public LayerUpdater { virtual ~ContentLayerUpdater(); void PaintContents(SkCanvas* canvas, - gfx::Rect content_rect, + gfx::Point origin, float contents_width_scale, float contents_height_scale, gfx::Rect* resulting_opaque_rect); gfx::Rect content_rect() const { return content_rect_; } + bool layer_is_opaque() const { return layer_is_opaque_; } + RenderingStatsInstrumentation* rendering_stats_instrumentation_; int layer_id_; @@ -43,6 +46,10 @@ class CC_EXPORT ContentLayerUpdater : public LayerUpdater { gfx::Rect content_rect_; scoped_ptr<LayerPainter> painter_; + protected: + // True when it is known that all output pixels will be opaque. + bool layer_is_opaque_; + DISALLOW_COPY_AND_ASSIGN(ContentLayerUpdater); }; diff --git a/chromium/cc/resources/image_raster_worker_pool.cc b/chromium/cc/resources/image_raster_worker_pool.cc index 408459c8f87..30add2ad2ea 100644 --- a/chromium/cc/resources/image_raster_worker_pool.cc +++ b/chromium/cc/resources/image_raster_worker_pool.cc @@ -8,7 +8,7 @@ #include "base/values.h" #include "cc/debug/traced_value.h" #include "cc/resources/resource.h" -#include "third_party/skia/include/core/SkDevice.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" namespace cc { @@ -34,14 +34,10 @@ class ImageWorkerPoolTaskImpl : public internal::WorkerPoolTask { if (!buffer_) return; - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, - task_->resource()->size().width(), - task_->resource()->size().height(), - stride_); - bitmap.setPixels(buffer_); - SkDevice device(bitmap); - task_->RunOnWorkerThread(&device, thread_index); + task_->RunOnWorkerThread(thread_index, + buffer_, + task_->resource()->size(), + stride_); } virtual void CompleteOnOriginThread() OVERRIDE { reply_.Run(!HasFinishedRunning()); @@ -104,6 +100,8 @@ void ImageRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) { for (RasterTaskVector::const_iterator it = raster_tasks().begin(); it != raster_tasks().end(); ++it) { internal::RasterWorkerPoolTask* task = it->get(); + DCHECK(!task->HasCompleted()); + DCHECK(!task->WasCanceled()); TaskMap::iterator image_it = image_tasks_.find(task); if (image_it != image_tasks_.end()) { @@ -156,6 +154,11 @@ void ImageRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) { "state", TracedValue::FromValue(StateAsValue().release())); } +ResourceFormat ImageRasterWorkerPool::GetResourceFormat() const { + // Only format supported by CHROMIUM_map_image + return RGBA_8888; +} + void ImageRasterWorkerPool::OnRasterTasksFinished() { DCHECK(raster_tasks_pending_); raster_tasks_pending_ = false; diff --git a/chromium/cc/resources/image_raster_worker_pool.h b/chromium/cc/resources/image_raster_worker_pool.h index d4f4d74d120..63526493350 100644 --- a/chromium/cc/resources/image_raster_worker_pool.h +++ b/chromium/cc/resources/image_raster_worker_pool.h @@ -21,6 +21,7 @@ class CC_EXPORT ImageRasterWorkerPool : public RasterWorkerPool { // Overridden from RasterWorkerPool: virtual void ScheduleTasks(RasterTask::Queue* queue) OVERRIDE; + virtual ResourceFormat GetResourceFormat() const OVERRIDE; virtual void OnRasterTasksFinished() OVERRIDE; virtual void OnRasterTasksRequiredForActivationFinished() OVERRIDE; diff --git a/chromium/cc/resources/layer_tiling_data.h b/chromium/cc/resources/layer_tiling_data.h index 51565eba3b2..c5c9a745871 100644 --- a/chromium/cc/resources/layer_tiling_data.h +++ b/chromium/cc/resources/layer_tiling_data.h @@ -9,10 +9,10 @@ #include "base/basictypes.h" #include "base/containers/hash_tables.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/base/region.h" -#include "cc/base/scoped_ptr_hash_map.h" #include "cc/base/tiling_data.h" #include "ui/gfx/rect.h" @@ -72,7 +72,7 @@ class CC_EXPORT LayerTilingData { DISALLOW_COPY_AND_ASSIGN(Tile); }; typedef std::pair<int, int> TileMapKey; - typedef ScopedPtrHashMap<TileMapKey, Tile> TileMap; + typedef base::ScopedPtrHashMap<TileMapKey, Tile> TileMap; void AddTile(scoped_ptr<Tile> tile, int i, int j); scoped_ptr<Tile> TakeTile(int i, int j); diff --git a/chromium/cc/resources/managed_tile_state.cc b/chromium/cc/resources/managed_tile_state.cc index 6f85331dbb2..8856d4f5168 100644 --- a/chromium/cc/resources/managed_tile_state.cc +++ b/chromium/cc/resources/managed_tile_state.cc @@ -12,63 +12,50 @@ namespace cc { scoped_ptr<base::Value> ManagedTileBinAsValue(ManagedTileBin bin) { switch (bin) { - case NOW_AND_READY_TO_DRAW_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "NOW_AND_READY_TO_DRAW_BIN")); - case NOW_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "NOW_BIN")); - case SOON_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "SOON_BIN")); - case EVENTUALLY_AND_ACTIVE_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "EVENTUALLY_AND_ACTIVE_BIN")); - case EVENTUALLY_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "EVENTUALLY_BIN")); - case NEVER_AND_ACTIVE_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "NEVER_AND_ACTIVE_BIN")); - case NEVER_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "NEVER_BIN")); - default: - DCHECK(false) << "Unrecognized ManagedTileBin value " << bin; - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "<unknown ManagedTileBin value>")); - } -} - -scoped_ptr<base::Value> ManagedTileBinPriorityAsValue( - ManagedTileBinPriority bin_priority) { - switch (bin_priority) { - case HIGH_PRIORITY_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "HIGH_PRIORITY_BIN")); - case LOW_PRIORITY_BIN: - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "LOW_PRIORITY_BIN")); - default: - DCHECK(false) << "Unrecognized ManagedTileBinPriority value"; - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "<unknown ManagedTileBinPriority value>")); + case NOW_AND_READY_TO_DRAW_BIN: + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("NOW_AND_READY_TO_DRAW_BIN")); + case NOW_BIN: + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("NOW_BIN")); + case SOON_BIN: + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("SOON_BIN")); + case EVENTUALLY_AND_ACTIVE_BIN: + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("EVENTUALLY_AND_ACTIVE_BIN")); + case EVENTUALLY_BIN: + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("EVENTUALLY_BIN")); + case AT_LAST_AND_ACTIVE_BIN: + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("AT_LAST_AND_ACTIVE_BIN")); + case AT_LAST_BIN: + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("AT_LAST_BIN")); + case NEVER_BIN: + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("NEVER_BIN")); + case NUM_BINS: + NOTREACHED(); + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("Invalid Bin (NUM_BINS)")); } + return scoped_ptr<base::Value>( + base::Value::CreateStringValue("Invalid Bin (UNKNOWN)")); } ManagedTileState::ManagedTileState() : raster_mode(LOW_QUALITY_RASTER_MODE), - gpu_memmgr_stats_bin(NEVER_BIN), + bin(NEVER_BIN), resolution(NON_IDEAL_RESOLUTION), required_for_activation(false), time_to_needed_in_seconds(std::numeric_limits<float>::infinity()), distance_to_visible_in_pixels(std::numeric_limits<float>::infinity()), visible_and_ready_to_draw(false), scheduled_priority(0) { - for (int i = 0; i < NUM_TREES; ++i) { + for (int i = 0; i < NUM_TREES; ++i) tree_bin[i] = NEVER_BIN; - bin[i] = NEVER_BIN; - } } ManagedTileState::TileVersion::TileVersion() @@ -87,10 +74,9 @@ bool ManagedTileState::TileVersion::IsReadyToDraw() const { case SOLID_COLOR_MODE: case PICTURE_PILE_MODE: return true; - default: - NOTREACHED(); - return false; } + NOTREACHED(); + return false; } size_t ManagedTileState::TileVersion::GPUMemoryUsageInBytes() const { @@ -106,10 +92,10 @@ scoped_ptr<base::Value> ManagedTileState::AsValue() const { scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue()); state->SetBoolean("has_resource", tile_versions[raster_mode].resource_.get() != 0); - state->Set("bin.0", ManagedTileBinAsValue(bin[ACTIVE_TREE]).release()); - state->Set("bin.1", ManagedTileBinAsValue(bin[PENDING_TREE]).release()); - state->Set("gpu_memmgr_stats_bin", - ManagedTileBinAsValue(bin[ACTIVE_TREE]).release()); + state->Set("tree_bin.0", + ManagedTileBinAsValue(tree_bin[ACTIVE_TREE]).release()); + state->Set("tree_bin.1", + ManagedTileBinAsValue(tree_bin[PENDING_TREE]).release()); state->Set("resolution", TileResolutionAsValue(resolution).release()); state->Set("time_to_needed_in_seconds", MathUtil::AsValueSafely(time_to_needed_in_seconds).release()); diff --git a/chromium/cc/resources/managed_tile_state.h b/chromium/cc/resources/managed_tile_state.h index c6065f97895..35e4415dd9e 100644 --- a/chromium/cc/resources/managed_tile_state.h +++ b/chromium/cc/resources/managed_tile_state.h @@ -22,23 +22,16 @@ enum ManagedTileBin { SOON_BIN = 2, // Impl-side version of prepainting. EVENTUALLY_AND_ACTIVE_BIN = 3, // Nice to have, and has a task or resource. EVENTUALLY_BIN = 4, // Nice to have, if we've got memory and time. - NEVER_AND_ACTIVE_BIN = 5, // Dont bother, but has a task or resource. - NEVER_BIN = 6, // Dont bother. - NUM_BINS = 7 + AT_LAST_AND_ACTIVE_BIN = 5, // Only do this after all other bins. + AT_LAST_BIN = 6, // Only do this after all other bins. + NEVER_BIN = 7, // Dont bother. + NUM_BINS = 8 // NOTE: Be sure to update ManagedTileBinAsValue and kBinPolicyMap when adding // or reordering fields. }; scoped_ptr<base::Value> ManagedTileBinAsValue( ManagedTileBin bin); -enum ManagedTileBinPriority { - HIGH_PRIORITY_BIN = 0, - LOW_PRIORITY_BIN = 1, - NUM_BIN_PRIORITIES = 2 -}; -scoped_ptr<base::Value> ManagedTileBinPriorityAsValue( - ManagedTileBinPriority bin); - // This is state that is specific to a tile that is // managed by the TileManager. class CC_EXPORT ManagedTileState { @@ -48,8 +41,7 @@ class CC_EXPORT ManagedTileState { enum Mode { RESOURCE_MODE, SOLID_COLOR_MODE, - PICTURE_PILE_MODE, - NUM_MODES + PICTURE_PILE_MODE }; TileVersion(); @@ -86,12 +78,6 @@ class CC_EXPORT ManagedTileState { size_t GPUMemoryUsageInBytes() const; - void SetResourceForTesting(scoped_ptr<ResourcePool::Resource> resource) { - resource_ = resource.Pass(); - } - const ResourcePool::Resource* GetResourceForTesting() const { - return resource_.get(); - } void SetSolidColorForTesting(SkColor color) { set_solid_color(color); } @@ -138,21 +124,9 @@ class CC_EXPORT ManagedTileState { TileVersion tile_versions[NUM_RASTER_MODES]; RasterMode raster_mode; - // Ephemeral state, valid only during TileManager::ManageTiles. - bool is_in_never_bin_on_both_trees() const { - return (bin[HIGH_PRIORITY_BIN] == NEVER_BIN || - bin[HIGH_PRIORITY_BIN] == NEVER_AND_ACTIVE_BIN) && - (bin[LOW_PRIORITY_BIN] == NEVER_BIN || - bin[LOW_PRIORITY_BIN] == NEVER_AND_ACTIVE_BIN); - } - - ManagedTileBin bin[NUM_BIN_PRIORITIES]; + ManagedTileBin bin; ManagedTileBin tree_bin[NUM_TREES]; - // The bin that the tile would have if the GPU memory manager had - // a maximally permissive policy, send to the GPU memory manager - // to determine policy. - ManagedTileBin gpu_memmgr_stats_bin; TileResolution resolution; bool required_for_activation; float time_to_needed_in_seconds; diff --git a/chromium/cc/resources/picture.cc b/chromium/cc/resources/picture.cc index 8c5d8512ae3..f9afca9ee3d 100644 --- a/chromium/cc/resources/picture.cc +++ b/chromium/cc/resources/picture.cc @@ -93,6 +93,27 @@ Picture::Picture(gfx::Rect layer_rect) // the picture to be recorded in Picture::Record. } +scoped_refptr<Picture> Picture::CreateFromSkpValue(const base::Value* value) { + // Decode the picture from base64. + std::string encoded; + if (!value->GetAsString(&encoded)) + return NULL; + + std::string decoded; + base::Base64Decode(encoded, &decoded); + SkMemoryStream stream(decoded.data(), decoded.size()); + + // Read the picture. This creates an empty picture on failure. + SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap); + if (skpicture == NULL) + return NULL; + + gfx::Rect layer_rect(skpicture->width(), skpicture->height()); + gfx::Rect opaque_rect(skpicture->width(), skpicture->height()); + + return make_scoped_refptr(new Picture(skpicture, layer_rect, opaque_rect)); +} + scoped_refptr<Picture> Picture::CreateFromValue(const base::Value* raw_value) { const base::DictionaryValue* value = NULL; if (!raw_value->GetAsDictionary(&value)) @@ -178,7 +199,7 @@ void Picture::CloneForDrawing(int num_threads) { pixel_refs_)); clones_.push_back(clone); - clone->EmitTraceSnapshot(); + clone->EmitTraceSnapshotAlias(this); } } @@ -348,6 +369,14 @@ void Picture::EmitTraceSnapshot() { "cc::Picture", this, TracedPicture::AsTraceablePicture(this)); } +void Picture::EmitTraceSnapshotAlias(Picture* original) { + TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( + TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "cc::Picture", + this, + TracedPicture::AsTraceablePictureAlias(original)); +} + base::LazyInstance<Picture::PixelRefs> Picture::PixelRefIterator::empty_pixel_refs_; diff --git a/chromium/cc/resources/picture.h b/chromium/cc/resources/picture.h index 15a6c9d0ab4..238647772fc 100644 --- a/chromium/cc/resources/picture.h +++ b/chromium/cc/resources/picture.h @@ -45,6 +45,7 @@ class CC_EXPORT Picture static scoped_refptr<Picture> Create(gfx::Rect layer_rect); static scoped_refptr<Picture> CreateFromValue(const base::Value* value); + static scoped_refptr<Picture> CreateFromSkpValue(const base::Value* value); gfx::Rect LayerRect() const { return layer_rect_; } gfx::Rect OpaqueRect() const { return opaque_rect_; } @@ -115,6 +116,7 @@ class CC_EXPORT Picture }; void EmitTraceSnapshot(); + void EmitTraceSnapshotAlias(Picture* original); private: explicit Picture(gfx::Rect layer_rect); diff --git a/chromium/cc/resources/picture_layer_tiling_perftest.cc b/chromium/cc/resources/picture_layer_tiling_perftest.cc new file mode 100644 index 00000000000..655603d2e85 --- /dev/null +++ b/chromium/cc/resources/picture_layer_tiling_perftest.cc @@ -0,0 +1,172 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/resources/picture_layer_tiling.h" +#include "cc/test/fake_picture_layer_tiling_client.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/perf/perf_test.h" + +namespace cc { + +namespace { + +static const int kTimeLimitMillis = 2000; +static const int kWarmupRuns = 5; +static const int kTimeCheckInterval = 10; + +class PictureLayerTilingPerfTest : public testing::Test { + public: + PictureLayerTilingPerfTest() : num_runs_(0) {} + + virtual void SetUp() OVERRIDE { + picture_layer_tiling_client_.SetTileSize(gfx::Size(256, 256)); + picture_layer_tiling_ = PictureLayerTiling::Create( + 1, gfx::Size(256 * 50, 256 * 50), &picture_layer_tiling_client_); + picture_layer_tiling_->CreateAllTilesForTesting(); + } + + virtual void TearDown() OVERRIDE { + picture_layer_tiling_.reset(NULL); + } + + void EndTest() { + elapsed_ = base::TimeTicks::HighResNow() - start_time_; + } + + bool DidRun() { + ++num_runs_; + if (num_runs_ == kWarmupRuns) + start_time_ = base::TimeTicks::HighResNow(); + + if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) { + base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_; + if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) { + elapsed_ = elapsed; + return false; + } + } + return true; + } + + void RunInvalidateTest(const std::string& test_name, const Region& region) { + start_time_ = base::TimeTicks(); + num_runs_ = 0; + do { + picture_layer_tiling_->Invalidate(region); + } while (DidRun()); + + perf_test::PrintResult("invalidation", "", test_name, + num_runs_ / elapsed_.InSecondsF(), "runs/s", true); + } + + void RunUpdateTilePrioritiesStationaryTest( + const std::string& test_name, + const gfx::Transform& transform) { + start_time_ = base::TimeTicks(); + num_runs_ = 0; + + gfx::Size layer_bounds(50 * 256, 50 * 256); + do { + picture_layer_tiling_->UpdateTilePriorities( + ACTIVE_TREE, + layer_bounds, + gfx::Rect(layer_bounds), + gfx::Rect(layer_bounds), + layer_bounds, + layer_bounds, + 1.f, + 1.f, + transform, + transform, + num_runs_ + 1, + 250); + } while (DidRun()); + + perf_test::PrintResult("update_tile_priorities_stationary", "", test_name, + num_runs_ / elapsed_.InSecondsF(), "runs/s", true); + } + + void RunUpdateTilePrioritiesScrollingTest( + const std::string& test_name, + const gfx::Transform& transform) { + start_time_ = base::TimeTicks(); + num_runs_ = 0; + + gfx::Size layer_bounds(50 * 256, 50 * 256); + gfx::Size viewport_size(1024, 768); + gfx::Rect viewport_rect(viewport_size); + int xoffsets[] = {10, 0, -10, 0}; + int yoffsets[] = {0, 10, 0, -10}; + int offsetIndex = 0; + int offsetCount = 0; + const int maxOffsetCount = 1000; + do { + picture_layer_tiling_->UpdateTilePriorities( + ACTIVE_TREE, + viewport_size, + viewport_rect, + gfx::Rect(layer_bounds), + layer_bounds, + layer_bounds, + 1.f, + 1.f, + transform, + transform, + num_runs_ + 1, + 250); + + viewport_rect = gfx::Rect( + viewport_rect.x() + xoffsets[offsetIndex], + viewport_rect.y() + yoffsets[offsetIndex], + viewport_rect.width(), + viewport_rect.height()); + + if (++offsetCount > maxOffsetCount) { + offsetCount = 0; + offsetIndex = (offsetIndex + 1) % 4; + } + } while (DidRun()); + + perf_test::PrintResult("update_tile_priorities_scrolling", "", test_name, + num_runs_ / elapsed_.InSecondsF(), "runs/s", true); + } + + private: + FakePictureLayerTilingClient picture_layer_tiling_client_; + scoped_ptr<PictureLayerTiling> picture_layer_tiling_; + + base::TimeTicks start_time_; + base::TimeDelta elapsed_; + int num_runs_; +}; + +TEST_F(PictureLayerTilingPerfTest, Invalidate) { + Region one_tile(gfx::Rect(256, 256)); + RunInvalidateTest("1x1", one_tile); + + Region half_region(gfx::Rect(25 * 256, 50 * 256)); + RunInvalidateTest("25x50", half_region); + + Region full_region(gfx::Rect(50 * 256, 50 * 256)); + RunInvalidateTest("50x50", full_region); +} + +TEST_F(PictureLayerTilingPerfTest, UpdateTilePriorities) { + gfx::Transform transform; + RunUpdateTilePrioritiesStationaryTest("no_transform", transform); + RunUpdateTilePrioritiesScrollingTest("no_transform", transform); + + transform.Rotate(10); + RunUpdateTilePrioritiesStationaryTest("rotation", transform); + RunUpdateTilePrioritiesScrollingTest("rotation", transform); + + transform.ApplyPerspectiveDepth(10); + RunUpdateTilePrioritiesStationaryTest("perspective", transform); + RunUpdateTilePrioritiesScrollingTest("perspective", transform); +} + +} // namespace + +} // namespace cc diff --git a/chromium/cc/resources/picture_layer_tiling_set.cc b/chromium/cc/resources/picture_layer_tiling_set.cc index 1b0cb5f7335..14c9cc87a30 100644 --- a/chromium/cc/resources/picture_layer_tiling_set.cc +++ b/chromium/cc/resources/picture_layer_tiling_set.cc @@ -110,6 +110,15 @@ PictureLayerTiling* PictureLayerTilingSet::AddTiling(float contents_scale) { return appended; } +int PictureLayerTilingSet::NumHighResTilings() const { + int num_high_res = 0; + for (size_t i = 0; i < tilings_.size(); ++i) { + if (tilings_[i]->resolution() == HIGH_RESOLUTION) + num_high_res++; + } + return num_high_res; +} + PictureLayerTiling* PictureLayerTilingSet::TilingAtScale(float scale) const { for (size_t i = 0; i < tilings_.size(); ++i) { if (tilings_[i]->contents_scale() == scale) diff --git a/chromium/cc/resources/picture_layer_tiling_set.h b/chromium/cc/resources/picture_layer_tiling_set.h index e498cb964b6..9a3f00db61f 100644 --- a/chromium/cc/resources/picture_layer_tiling_set.h +++ b/chromium/cc/resources/picture_layer_tiling_set.h @@ -37,6 +37,7 @@ class CC_EXPORT PictureLayerTilingSet { PictureLayerTiling* AddTiling(float contents_scale); size_t num_tilings() const { return tilings_.size(); } + int NumHighResTilings() const; PictureLayerTiling* tiling_at(size_t idx) { return tilings_[idx]; } const PictureLayerTiling* tiling_at(size_t idx) const { return tilings_[idx]; diff --git a/chromium/cc/resources/picture_layer_tiling_set_unittest.cc b/chromium/cc/resources/picture_layer_tiling_set_unittest.cc index c47cf4b5a90..231e378fbd0 100644 --- a/chromium/cc/resources/picture_layer_tiling_set_unittest.cc +++ b/chromium/cc/resources/picture_layer_tiling_set_unittest.cc @@ -10,6 +10,7 @@ #include "cc/resources/resource_pool.h" #include "cc/resources/resource_provider.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_picture_layer_tiling_client.h" #include "cc/test/fake_tile_manager_client.h" #include "testing/gtest/include/gtest/gtest.h" @@ -59,10 +60,13 @@ class PictureLayerTilingSetTestWithResources : public testing::Test { float scale_increment, float ideal_contents_scale, float expected_scale) { + FakeOutputSurfaceClient output_surface_client; scoped_ptr<FakeOutputSurface> output_surface = FakeOutputSurface::Create3d(); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider = - ResourceProvider::Create(output_surface.get(), 0); + ResourceProvider::Create(output_surface.get(), 0, false); FakePictureLayerTilingClient client; client.SetTileSize(gfx::Size(256, 256)); @@ -74,17 +78,8 @@ class PictureLayerTilingSetTestWithResources : public testing::Test { PictureLayerTiling* tiling = set.AddTiling(scale); tiling->CreateAllTilesForTesting(); std::vector<Tile*> tiles = tiling->AllTilesForTesting(); - for (size_t i = 0; i < tiles.size(); ++i) { - ManagedTileState::TileVersion& tile_version = - tiles[i]->GetTileVersionForTesting(HIGH_QUALITY_NO_LCD_RASTER_MODE); - EXPECT_FALSE(tile_version.GetResourceForTesting()); - - tile_version.SetResourceForTesting( - make_scoped_ptr(new ResourcePool::Resource( - resource_provider.get(), - gfx::Size(1, 1), - resource_provider->best_texture_format()))); - } + client.tile_manager()->InitializeTilesWithResourcesForTesting( + tiles, resource_provider.get()); } float max_contents_scale = scale; diff --git a/chromium/cc/resources/picture_pile_base.cc b/chromium/cc/resources/picture_pile_base.cc index d982f9fc9b8..0352d30816c 100644 --- a/chromium/cc/resources/picture_pile_base.cc +++ b/chromium/cc/resources/picture_pile_base.cc @@ -152,12 +152,10 @@ void PicturePileBase::Clear() { void PicturePileBase::UpdateRecordedRegion() { recorded_region_.Clear(); - for (int x = 0; x < num_tiles_x(); ++x) { - for (int y = 0; y < num_tiles_y(); ++y) { - if (!HasRecordingAt(x, y)) - continue; - recorded_region_.Union(tile_bounds(x, y)); - } + for (PictureListMap::iterator it = picture_list_map_.begin(); + it != picture_list_map_.end(); ++it) { + const PictureListMapKey& key = it->first; + recorded_region_.Union(tile_bounds(key.first, key.second)); } } diff --git a/chromium/cc/resources/picture_pile_impl.cc b/chromium/cc/resources/picture_pile_impl.cc index 42f3dabd4cf..3a4020e072b 100644 --- a/chromium/cc/resources/picture_pile_impl.cc +++ b/chromium/cc/resources/picture_pile_impl.cc @@ -186,9 +186,13 @@ void PicturePileImpl::RasterCommon( // encompasses all invalidated pixels at any larger scale level. gfx::Rect content_clip = gfx::ScaleToEnclosedRect( (*i)->LayerRect(), contents_scale); + DCHECK(!content_clip.IsEmpty()) << "Layer rect: " << (*i)->LayerRect().ToString() << "Contents scale: " << contents_scale; + + content_clip.Intersect(canvas_rect); + if (!unclipped.Intersects(content_clip)) continue; @@ -215,16 +219,15 @@ void PicturePileImpl::RasterCommon( } if (raster_stats) { - gfx::Rect raster_rect = canvas_rect; - raster_rect.Intersect(content_clip); raster_stats->total_pixels_rasterized += - repeat_count * raster_rect.width() * raster_rect.height(); + repeat_count * content_clip.width() * content_clip.height(); raster_stats->total_rasterize_time += total_duration; raster_stats->best_rasterize_time += best_duration; } if (show_debug_picture_borders_) { - gfx::Rect border = content_clip; + gfx::Rect border = gfx::ScaleToEnclosedRect( + (*i)->LayerRect(), contents_scale); border.Inset(0, 0, 1, 1); SkPaint picture_border_paint; diff --git a/chromium/cc/resources/picture_unittest.cc b/chromium/cc/resources/picture_unittest.cc index ee5e07dbdd6..aa20ebb6381 100644 --- a/chromium/cc/resources/picture_unittest.cc +++ b/chromium/cc/resources/picture_unittest.cc @@ -377,5 +377,58 @@ TEST(PictureTest, PixelRefIteratorOnePixelQuery) { } } } + +TEST(PictureTest, CreateFromSkpValue) { + SkGraphics::Init(); + + gfx::Rect layer_rect(100, 200); + + SkTileGridPicture::TileGridInfo tile_grid_info; + tile_grid_info.fTileInterval = SkISize::Make(100, 200); + tile_grid_info.fMargin.setEmpty(); + tile_grid_info.fOffset.setZero(); + + FakeContentLayerClient content_layer_client; + FakeRenderingStatsInstrumentation stats_instrumentation; + + scoped_ptr<base::Value> tmp; + + SkPaint red_paint; + red_paint.setColor(SkColorSetARGB(255, 255, 0, 0)); + SkPaint green_paint; + green_paint.setColor(SkColorSetARGB(255, 0, 255, 0)); + + // Invalid picture (not a dict). + tmp.reset(new base::StringValue("abc!@#$%")); + scoped_refptr<Picture> invalid_picture = + Picture::CreateFromSkpValue(tmp.get()); + EXPECT_TRUE(!invalid_picture.get()); + + // Single full-size rect picture. + content_layer_client.add_draw_rect(layer_rect, red_paint); + scoped_refptr<Picture> one_rect_picture = Picture::Create(layer_rect); + one_rect_picture->Record(&content_layer_client, + tile_grid_info, + &stats_instrumentation); + scoped_ptr<base::Value> serialized_one_rect( + one_rect_picture->AsValue()); + + const base::DictionaryValue* value = NULL; + EXPECT_TRUE(serialized_one_rect->GetAsDictionary(&value)); + + // Decode the picture from base64. + const base::Value* skp_value; + EXPECT_TRUE(value->Get("skp64", &skp_value)); + + // Reconstruct the picture. + scoped_refptr<Picture> one_rect_picture_check = + Picture::CreateFromSkpValue(skp_value); + EXPECT_TRUE(!!one_rect_picture_check.get()); + + EXPECT_EQ(100, one_rect_picture_check->LayerRect().width()); + EXPECT_EQ(200, one_rect_picture_check->LayerRect().height()); + EXPECT_EQ(100, one_rect_picture_check->OpaqueRect().width()); + EXPECT_EQ(200, one_rect_picture_check->OpaqueRect().height()); +} } // namespace } // namespace cc diff --git a/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc b/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc index a03ed81f4e1..a555e581540 100644 --- a/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc +++ b/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc @@ -9,7 +9,11 @@ #include "base/values.h" #include "cc/debug/traced_value.h" #include "cc/resources/resource.h" -#include "third_party/skia/include/core/SkDevice.h" +#include "third_party/skia/include/core/SkBitmapDevice.h" + +#if defined(OS_ANDROID) +#include "base/android/sys_utils.h" +#endif namespace cc { @@ -37,13 +41,10 @@ class PixelBufferWorkerPoolTaskImpl : public internal::WorkerPoolTask { needs_upload_ = true; return; } - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, - task_->resource()->size().width(), - task_->resource()->size().height()); - bitmap.setPixels(buffer_); - SkDevice device(bitmap); - needs_upload_ = task_->RunOnWorkerThread(&device, thread_index); + needs_upload_ = task_->RunOnWorkerThread(thread_index, + buffer_, + task_->resource()->size(), + 0); } virtual void CompleteOnOriginThread() OVERRIDE { // |needs_upload_| must be be false if task didn't run. @@ -62,22 +63,6 @@ class PixelBufferWorkerPoolTaskImpl : public internal::WorkerPoolTask { DISALLOW_COPY_AND_ASSIGN(PixelBufferWorkerPoolTaskImpl); }; -// If we raster too fast we become upload bound, and pending -// uploads consume memory. For maximum upload throughput, we would -// want to allow for upload_throughput * pipeline_time of pending -// uploads, after which we are just wasting memory. Since we don't -// know our upload throughput yet, this just caps our memory usage. -#if defined(OS_ANDROID) -// For reference Nexus10 can upload 1MB in about 2.5ms. -const size_t kMaxBytesUploadedPerMs = (2 * 1024 * 1024) / 5; -#else -// For reference Chromebook Pixel can upload 1MB in about 0.5ms. -const size_t kMaxBytesUploadedPerMs = 1024 * 1024 * 2; -#endif - -// Assuming a two frame deep pipeline. -const size_t kMaxPendingUploadBytes = 16 * 2 * kMaxBytesUploadedPerMs; - const int kCheckForCompletedRasterTasksDelayMs = 6; const size_t kMaxScheduledRasterTasks = 48; @@ -106,11 +91,13 @@ bool WasCanceled(const internal::RasterWorkerPoolTask* task) { PixelBufferRasterWorkerPool::PixelBufferRasterWorkerPool( ResourceProvider* resource_provider, - size_t num_threads) + size_t num_threads, + size_t max_transfer_buffer_usage_bytes) : RasterWorkerPool(resource_provider, num_threads), shutdown_(false), scheduled_raster_task_count_(0), bytes_pending_upload_(0), + max_bytes_pending_upload_(max_transfer_buffer_usage_bytes), has_performed_uploads_since_last_flush_(false), check_for_completed_raster_tasks_pending_(false), should_notify_client_if_no_tasks_are_pending_(false), @@ -158,6 +145,8 @@ void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) { should_notify_client_if_no_tasks_are_pending_ = true; should_notify_client_if_no_tasks_required_for_activation_are_pending_ = true; + tasks_required_for_activation_.clear(); + // Build new pixel buffer task set. TaskMap new_pixel_buffer_tasks; for (RasterTaskVector::const_iterator it = raster_tasks().begin(); @@ -165,16 +154,13 @@ void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) { internal::RasterWorkerPoolTask* task = it->get(); DCHECK(new_pixel_buffer_tasks.find(task) == new_pixel_buffer_tasks.end()); DCHECK(!task->HasCompleted()); + DCHECK(!task->WasCanceled()); - // Use existing pixel buffer task if available. - TaskMap::iterator pixel_buffer_it = pixel_buffer_tasks_.find(task); - if (pixel_buffer_it == pixel_buffer_tasks_.end()) { - new_pixel_buffer_tasks[task] = NULL; - continue; - } - - new_pixel_buffer_tasks[task] = pixel_buffer_it->second; + new_pixel_buffer_tasks[task] = pixel_buffer_tasks_[task]; pixel_buffer_tasks_.erase(task); + + if (IsRasterTaskRequiredForActivation(task)) + tasks_required_for_activation_.insert(task); } // Transfer remaining pixel buffer tasks to |new_pixel_buffer_tasks| @@ -194,22 +180,17 @@ void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) { completed_tasks_.end(), task) == completed_tasks_.end()); completed_tasks_.push_back(task); - } - } - - tasks_required_for_activation_.clear(); - for (TaskMap::iterator it = new_pixel_buffer_tasks.begin(); - it != new_pixel_buffer_tasks.end(); ++it) { - internal::RasterWorkerPoolTask* task = it->first; - if (IsRasterTaskRequiredForActivation(task)) + } else if (IsRasterTaskRequiredForActivation(task)) { tasks_required_for_activation_.insert(task); + } } // |tasks_required_for_activation_| contains all tasks that need to // complete before we can send a "ready to activate" signal. Tasks // that have already completed should not be part of this set. for (TaskDeque::const_iterator it = completed_tasks_.begin(); - it != completed_tasks_.end(); ++it) { + it != completed_tasks_.end() && !tasks_required_for_activation_.empty(); + ++it) { tasks_required_for_activation_.erase(*it); } @@ -236,6 +217,10 @@ void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) { "state", TracedValue::FromValue(StateAsValue().release())); } +ResourceFormat PixelBufferRasterWorkerPool::GetResourceFormat() const { + return resource_provider()->memory_efficient_texture_format(); +} + void PixelBufferRasterWorkerPool::CheckForCompletedTasks() { TRACE_EVENT0("cc", "PixelBufferRasterWorkerPool::CheckForCompletedTasks"); @@ -470,7 +455,7 @@ void PixelBufferRasterWorkerPool::ScheduleMoreTasks() { // All raster tasks need to be throttled by bytes of pending uploads. size_t new_bytes_pending_upload = bytes_pending_upload; new_bytes_pending_upload += task->resource()->bytes(); - if (new_bytes_pending_upload > kMaxPendingUploadBytes) + if (new_bytes_pending_upload > max_bytes_pending_upload_) break; internal::WorkerPoolTask* pixel_buffer_task = pixel_buffer_it->second.get(); @@ -590,6 +575,11 @@ void PixelBufferRasterWorkerPool::OnRasterTaskCompleted( scoped_refptr<internal::RasterWorkerPoolTask> task, bool was_canceled, bool needs_upload) { + TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc"), + "PixelBufferRasterWorkerPool::OnRasterTaskCompleted", + "was_canceled", was_canceled, + "needs_upload", needs_upload); + DCHECK(pixel_buffer_tasks_.find(task.get()) != pixel_buffer_tasks_.end()); // Balanced with MapPixelBuffer() call in ScheduleMoreTasks(). @@ -673,7 +663,7 @@ scoped_ptr<base::Value> PixelBufferRasterWorkerPool::ThrottleStateAsValue() scoped_ptr<base::DictionaryValue> throttle_state(new base::DictionaryValue); throttle_state->SetInteger("bytes_available_for_upload", - kMaxPendingUploadBytes - bytes_pending_upload_); + max_bytes_pending_upload_ - bytes_pending_upload_); throttle_state->SetInteger("bytes_pending_upload", bytes_pending_upload_); throttle_state->SetInteger("scheduled_raster_task_count", scheduled_raster_task_count_); diff --git a/chromium/cc/resources/pixel_buffer_raster_worker_pool.h b/chromium/cc/resources/pixel_buffer_raster_worker_pool.h index d9613f7634d..a856d8ae5a9 100644 --- a/chromium/cc/resources/pixel_buffer_raster_worker_pool.h +++ b/chromium/cc/resources/pixel_buffer_raster_worker_pool.h @@ -9,6 +9,7 @@ #include <set> #include <vector> +#include "base/containers/hash_tables.h" #include "cc/resources/raster_worker_pool.h" namespace cc { @@ -18,9 +19,13 @@ class CC_EXPORT PixelBufferRasterWorkerPool : public RasterWorkerPool { virtual ~PixelBufferRasterWorkerPool(); static scoped_ptr<RasterWorkerPool> Create( - ResourceProvider* resource_provider, size_t num_threads) { + ResourceProvider* resource_provider, + size_t num_threads, + size_t max_transfer_buffer_usage_bytes) { return make_scoped_ptr<RasterWorkerPool>( - new PixelBufferRasterWorkerPool(resource_provider, num_threads)); + new PixelBufferRasterWorkerPool(resource_provider, + num_threads, + max_transfer_buffer_usage_bytes)); } // Overridden from WorkerPool: @@ -29,12 +34,14 @@ class CC_EXPORT PixelBufferRasterWorkerPool : public RasterWorkerPool { // Overridden from RasterWorkerPool: virtual void ScheduleTasks(RasterTask::Queue* queue) OVERRIDE; + virtual ResourceFormat GetResourceFormat() const OVERRIDE; virtual void OnRasterTasksFinished() OVERRIDE; virtual void OnRasterTasksRequiredForActivationFinished() OVERRIDE; private: PixelBufferRasterWorkerPool(ResourceProvider* resource_provider, - size_t num_threads); + size_t num_threads, + size_t max_transfer_buffer_usage_bytes); void FlushUploads(); void CheckForCompletedUploads(); @@ -62,17 +69,19 @@ class CC_EXPORT PixelBufferRasterWorkerPool : public RasterWorkerPool { TaskDeque tasks_with_pending_upload_; TaskDeque completed_tasks_; - typedef std::set<internal::RasterWorkerPoolTask*> TaskSet; + typedef base::hash_set<internal::RasterWorkerPoolTask*> TaskSet; TaskSet tasks_required_for_activation_; size_t scheduled_raster_task_count_; size_t bytes_pending_upload_; + size_t max_bytes_pending_upload_; bool has_performed_uploads_since_last_flush_; base::CancelableClosure check_for_completed_raster_tasks_callback_; bool check_for_completed_raster_tasks_pending_; bool should_notify_client_if_no_tasks_are_pending_; bool should_notify_client_if_no_tasks_required_for_activation_are_pending_; + ResourceFormat format_; DISALLOW_COPY_AND_ASSIGN(PixelBufferRasterWorkerPool); }; diff --git a/chromium/cc/resources/platform_color.h b/chromium/cc/resources/platform_color.h index 09a606ae5a5..ecdf7c166f2 100644 --- a/chromium/cc/resources/platform_color.h +++ b/chromium/cc/resources/platform_color.h @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "cc/resources/resource_format.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" #include "third_party/skia/include/core/SkTypes.h" @@ -25,34 +26,28 @@ class PlatformColor { } // Returns the most efficient texture format for this platform. - static GLenum BestTextureFormat(bool supports_bgra8888) { - GLenum texture_format = GL_RGBA; + static ResourceFormat BestTextureFormat(bool supports_bgra8888) { switch (Format()) { - case SOURCE_FORMAT_RGBA8: - break; case SOURCE_FORMAT_BGRA8: - if (supports_bgra8888) - texture_format = GL_BGRA_EXT; - break; - default: - NOTREACHED(); - break; + return (supports_bgra8888) ? BGRA_8888 : RGBA_8888; + case SOURCE_FORMAT_RGBA8: + return RGBA_8888; } - return texture_format; + NOTREACHED(); + return RGBA_8888; } // Return true if the given texture format has the same component order // as the color on this platform. - static bool SameComponentOrder(GLenum texture_format) { + static bool SameComponentOrder(ResourceFormat format) { switch (Format()) { case SOURCE_FORMAT_RGBA8: - return texture_format == GL_RGBA; + return format == RGBA_8888 || format == RGBA_4444; case SOURCE_FORMAT_BGRA8: - return texture_format == GL_BGRA_EXT; - default: - NOTREACHED(); - return false; + return format == BGRA_8888; } + NOTREACHED(); + return false; } private: diff --git a/chromium/cc/resources/prioritized_resource.cc b/chromium/cc/resources/prioritized_resource.cc index 78fd90055a9..313b275a3ab 100644 --- a/chromium/cc/resources/prioritized_resource.cc +++ b/chromium/cc/resources/prioritized_resource.cc @@ -15,7 +15,7 @@ namespace cc { PrioritizedResource::PrioritizedResource(PrioritizedResourceManager* manager, gfx::Size size, - GLenum format) + ResourceFormat format) : size_(size), format_(format), bytes_(0), @@ -25,10 +25,7 @@ PrioritizedResource::PrioritizedResource(PrioritizedResourceManager* manager, is_self_managed_(false), backing_(NULL), manager_(NULL) { - // manager_ is set in RegisterTexture() so validity can be checked. - DCHECK(format || size.IsEmpty()); - if (format) - bytes_ = Resource::MemorySizeBytes(size, format); + bytes_ = Resource::MemorySizeBytes(size, format); if (manager) manager->RegisterTexture(this); } @@ -48,7 +45,7 @@ void PrioritizedResource::SetTextureManager( manager->RegisterTexture(this); } -void PrioritizedResource::SetDimensions(gfx::Size size, GLenum format) { +void PrioritizedResource::SetDimensions(gfx::Size size, ResourceFormat format) { if (format_ != format || size_ != size) { is_above_priority_cutoff_ = false; format_ = format; @@ -113,7 +110,7 @@ void PrioritizedResource::Unlink() { } void PrioritizedResource::SetToSelfManagedMemoryPlaceholder(size_t bytes) { - SetDimensions(gfx::Size(), GL_RGBA); + SetDimensions(gfx::Size(), RGBA_8888); set_is_self_managed(true); bytes_ = bytes; } @@ -121,7 +118,7 @@ void PrioritizedResource::SetToSelfManagedMemoryPlaceholder(size_t bytes) { PrioritizedResource::Backing::Backing(unsigned id, ResourceProvider* resource_provider, gfx::Size size, - GLenum format) + ResourceFormat format) : Resource(id, size, format), owner_(NULL), priority_at_last_priority_update_(PriorityCalculator::LowestPriority()), diff --git a/chromium/cc/resources/prioritized_resource.h b/chromium/cc/resources/prioritized_resource.h index 07e07872205..a3d5d89cf64 100644 --- a/chromium/cc/resources/prioritized_resource.h +++ b/chromium/cc/resources/prioritized_resource.h @@ -12,7 +12,6 @@ #include "cc/resources/priority_calculator.h" #include "cc/resources/resource.h" #include "cc/resources/resource_provider.h" -#include "third_party/khronos/GLES2/gl2.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" #include "ui/gfx/vector2d.h" @@ -24,13 +23,16 @@ class Proxy; class CC_EXPORT PrioritizedResource { public: - static scoped_ptr<PrioritizedResource> - Create(PrioritizedResourceManager* manager, gfx::Size size, GLenum format) { + static scoped_ptr<PrioritizedResource> Create( + PrioritizedResourceManager* manager, + gfx::Size size, + ResourceFormat format) { return make_scoped_ptr(new PrioritizedResource(manager, size, format)); } static scoped_ptr<PrioritizedResource> Create( PrioritizedResourceManager* manager) { - return make_scoped_ptr(new PrioritizedResource(manager, gfx::Size(), 0)); + return make_scoped_ptr( + new PrioritizedResource(manager, gfx::Size(), RGBA_8888)); } ~PrioritizedResource(); @@ -38,8 +40,8 @@ class CC_EXPORT PrioritizedResource { // Setting these to the same value is a no-op. void SetTextureManager(PrioritizedResourceManager* manager); PrioritizedResourceManager* resource_manager() { return manager_; } - void SetDimensions(gfx::Size size, GLenum format); - GLenum format() const { return format_; } + void SetDimensions(gfx::Size size, ResourceFormat format); + ResourceFormat format() const { return format_; } gfx::Size size() const { return size_; } size_t bytes() const { return bytes_; } bool contents_swizzled() const { return contents_swizzled_; } @@ -106,7 +108,7 @@ class CC_EXPORT PrioritizedResource { Backing(unsigned id, ResourceProvider* resource_provider, gfx::Size size, - GLenum format); + ResourceFormat format); ~Backing(); void UpdatePriority(); void UpdateInDrawingImplTree(); @@ -146,7 +148,7 @@ class CC_EXPORT PrioritizedResource { PrioritizedResource(PrioritizedResourceManager* resource_manager, gfx::Size size, - GLenum format); + ResourceFormat format); bool is_above_priority_cutoff() { return is_above_priority_cutoff_; } void set_above_priority_cutoff(bool is_above_priority_cutoff) { @@ -161,7 +163,7 @@ class CC_EXPORT PrioritizedResource { void Unlink(); gfx::Size size_; - GLenum format_; + ResourceFormat format_; size_t bytes_; bool contents_swizzled_; diff --git a/chromium/cc/resources/prioritized_resource_manager.cc b/chromium/cc/resources/prioritized_resource_manager.cc index 81188851901..1a6e545a1a5 100644 --- a/chromium/cc/resources/prioritized_resource_manager.cc +++ b/chromium/cc/resources/prioritized_resource_manager.cc @@ -449,13 +449,16 @@ void PrioritizedResourceManager::ReturnBackingTexture( PrioritizedResource::Backing* PrioritizedResourceManager::CreateBacking( gfx::Size size, - GLenum format, + ResourceFormat format, ResourceProvider* resource_provider) { DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); DCHECK(resource_provider); ResourceProvider::ResourceId resource_id = resource_provider->CreateManagedResource( - size, format, ResourceProvider::TextureUsageAny); + size, + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + format); PrioritizedResource::Backing* backing = new PrioritizedResource::Backing( resource_id, resource_provider, size, format); memory_use_bytes_ += backing->bytes(); diff --git a/chromium/cc/resources/prioritized_resource_manager.h b/chromium/cc/resources/prioritized_resource_manager.h index 73967727c2c..07cc7cf39a6 100644 --- a/chromium/cc/resources/prioritized_resource_manager.h +++ b/chromium/cc/resources/prioritized_resource_manager.h @@ -17,7 +17,6 @@ #include "cc/resources/priority_calculator.h" #include "cc/resources/resource.h" #include "cc/trees/proxy.h" -#include "third_party/khronos/GLES2/gl2.h" #include "ui/gfx/size.h" #if defined(COMPILER_GCC) @@ -40,7 +39,8 @@ class CC_EXPORT PrioritizedResourceManager { static scoped_ptr<PrioritizedResourceManager> Create(const Proxy* proxy) { return make_scoped_ptr(new PrioritizedResourceManager(proxy)); } - scoped_ptr<PrioritizedResource> CreateTexture(gfx::Size size, GLenum format) { + scoped_ptr<PrioritizedResource> CreateTexture( + gfx::Size size, ResourceFormat format) { return make_scoped_ptr(new PrioritizedResource(this, size, format)); } ~PrioritizedResourceManager(); @@ -77,6 +77,9 @@ class CC_EXPORT PrioritizedResourceManager { void SetExternalPriorityCutoff(int priority_cutoff) { external_priority_cutoff_ = priority_cutoff; } + int ExternalPriorityCutoff() const { + return external_priority_cutoff_; + } // Return the amount of texture memory required at particular cutoffs. size_t MemoryVisibleBytes() const; @@ -184,7 +187,7 @@ class CC_EXPORT PrioritizedResourceManager { ResourceProvider* resource_provider); PrioritizedResource::Backing* CreateBacking( gfx::Size size, - GLenum format, + ResourceFormat format, ResourceProvider* resource_provider); void EvictFirstBackingResource(ResourceProvider* resource_provider); void SortBackings(); diff --git a/chromium/cc/resources/prioritized_resource_unittest.cc b/chromium/cc/resources/prioritized_resource_unittest.cc index 92ce9ff7239..181498c5a34 100644 --- a/chromium/cc/resources/prioritized_resource_unittest.cc +++ b/chromium/cc/resources/prioritized_resource_unittest.cc @@ -7,6 +7,7 @@ #include "cc/resources/prioritized_resource_manager.h" #include "cc/resources/resource.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_proxy.h" #include "cc/test/tiled_layer_test_common.h" #include "cc/trees/single_thread_proxy.h" // For DebugScopedSetImplThread @@ -18,10 +19,12 @@ class PrioritizedResourceTest : public testing::Test { public: PrioritizedResourceTest() : texture_size_(256, 256), - texture_format_(GL_RGBA), - output_surface_(CreateFakeOutputSurface()) { + texture_format_(RGBA_8888), + output_surface_(FakeOutputSurface::Create3d()) { DebugScopedSetImplThread impl_thread(&proxy_); - resource_provider_ = cc::ResourceProvider::Create(output_surface_.get(), 0); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + resource_provider_ = + cc::ResourceProvider::Create(output_surface_.get(), 0, false); } virtual ~PrioritizedResourceTest() { @@ -91,7 +94,8 @@ class PrioritizedResourceTest : public testing::Test { protected: FakeProxy proxy_; const gfx::Size texture_size_; - const GLenum texture_format_; + const ResourceFormat texture_format_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<OutputSurface> output_surface_; scoped_ptr<cc::ResourceProvider> resource_provider_; }; diff --git a/chromium/cc/resources/prioritized_tile_set.cc b/chromium/cc/resources/prioritized_tile_set.cc index 5b40945e559..6c5c4729d7e 100644 --- a/chromium/cc/resources/prioritized_tile_set.cc +++ b/chromium/cc/resources/prioritized_tile_set.cc @@ -13,14 +13,11 @@ namespace cc { class BinComparator { public: - bool operator()(const scoped_refptr<Tile>& a, - const scoped_refptr<Tile>& b) const { + bool operator()(const Tile* a, + const Tile* b) const { const ManagedTileState& ams = a->managed_state(); const ManagedTileState& bms = b->managed_state(); - if (ams.bin[LOW_PRIORITY_BIN] != bms.bin[LOW_PRIORITY_BIN]) - return ams.bin[LOW_PRIORITY_BIN] < bms.bin[LOW_PRIORITY_BIN]; - if (ams.required_for_activation != bms.required_for_activation) return ams.required_for_activation; @@ -46,18 +43,19 @@ class BinComparator { namespace { -typedef std::vector<scoped_refptr<Tile> > TileVector; +typedef std::vector<Tile*> TileVector; void SortBinTiles(ManagedTileBin bin, TileVector* tiles) { switch (bin) { case NOW_AND_READY_TO_DRAW_BIN: + case NEVER_BIN: break; case NOW_BIN: case SOON_BIN: case EVENTUALLY_AND_ACTIVE_BIN: case EVENTUALLY_BIN: - case NEVER_AND_ACTIVE_BIN: - case NEVER_BIN: + case AT_LAST_AND_ACTIVE_BIN: + case AT_LAST_BIN: std::sort(tiles->begin(), tiles->end(), BinComparator()); break; default: @@ -67,37 +65,52 @@ void SortBinTiles(ManagedTileBin bin, TileVector* tiles) { } // namespace -PrioritizedTileSet::PrioritizedTileSet() {} +PrioritizedTileSet::PrioritizedTileSet() { + for (int bin = 0; bin < NUM_BINS; ++bin) + bin_sorted_[bin] = true; +} PrioritizedTileSet::~PrioritizedTileSet() {} void PrioritizedTileSet::InsertTile(Tile* tile, ManagedTileBin bin) { - tiles_[bin].push_back(make_scoped_refptr(tile)); + tiles_[bin].push_back(tile); + bin_sorted_[bin] = false; } void PrioritizedTileSet::Clear() { - for (int bin = 0; bin < NUM_BINS; ++bin) + for (int bin = 0; bin < NUM_BINS; ++bin) { tiles_[bin].clear(); + bin_sorted_[bin] = true; + } } -void PrioritizedTileSet::Sort() { - for (int bin = 0; bin < NUM_BINS; ++bin) - SortBinTiles(static_cast<ManagedTileBin>(bin), &tiles_[bin]); +void PrioritizedTileSet::SortBinIfNeeded(ManagedTileBin bin) { + if (!bin_sorted_[bin]) { + SortBinTiles(bin, &tiles_[bin]); + bin_sorted_[bin] = true; + } } -PrioritizedTileSet::PriorityIterator::PriorityIterator( - PrioritizedTileSet* tile_set) +PrioritizedTileSet::Iterator::Iterator( + PrioritizedTileSet* tile_set, bool use_priority_ordering) : tile_set_(tile_set), current_bin_(NOW_AND_READY_TO_DRAW_BIN), - iterator_(tile_set->tiles_[current_bin_].begin()) { + use_priority_ordering_(use_priority_ordering) { + if (use_priority_ordering_) + tile_set_->SortBinIfNeeded(current_bin_); + iterator_ = tile_set->tiles_[current_bin_].begin(); if (iterator_ == tile_set_->tiles_[current_bin_].end()) AdvanceList(); } -PrioritizedTileSet::PriorityIterator::~PriorityIterator() {} +PrioritizedTileSet::Iterator::~Iterator() {} + +void PrioritizedTileSet::Iterator::DisablePriorityOrdering() { + use_priority_ordering_ = false; +} -PrioritizedTileSet::PriorityIterator& -PrioritizedTileSet::PriorityIterator::operator++() { +PrioritizedTileSet::Iterator& +PrioritizedTileSet::Iterator::operator++() { // We can't increment past the end of the tiles. DCHECK(iterator_ != tile_set_->tiles_[current_bin_].end()); @@ -107,16 +120,20 @@ PrioritizedTileSet::PriorityIterator::operator++() { return *this; } -Tile* PrioritizedTileSet::PriorityIterator::operator*() { +Tile* PrioritizedTileSet::Iterator::operator*() { DCHECK(iterator_ != tile_set_->tiles_[current_bin_].end()); - return iterator_->get(); + return *iterator_; } -void PrioritizedTileSet::PriorityIterator::AdvanceList() { +void PrioritizedTileSet::Iterator::AdvanceList() { DCHECK(iterator_ == tile_set_->tiles_[current_bin_].end()); while (current_bin_ != NEVER_BIN) { current_bin_ = static_cast<ManagedTileBin>(current_bin_ + 1); + + if (use_priority_ordering_) + tile_set_->SortBinIfNeeded(current_bin_); + iterator_ = tile_set_->tiles_[current_bin_].begin(); if (iterator_ != tile_set_->tiles_[current_bin_].end()) break; diff --git a/chromium/cc/resources/prioritized_tile_set.h b/chromium/cc/resources/prioritized_tile_set.h index fe1b2a0a941..15d0e4f8b48 100644 --- a/chromium/cc/resources/prioritized_tile_set.h +++ b/chromium/cc/resources/prioritized_tile_set.h @@ -7,8 +7,6 @@ #include <vector> -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/resources/managed_tile_state.h" @@ -22,14 +20,16 @@ class CC_EXPORT PrioritizedTileSet { void InsertTile(Tile* tile, ManagedTileBin bin); void Clear(); - void Sort(); - class CC_EXPORT PriorityIterator { + class CC_EXPORT Iterator { public: - explicit PriorityIterator(PrioritizedTileSet* set); - ~PriorityIterator(); + Iterator(PrioritizedTileSet* set, bool use_priority_ordering); - PriorityIterator& operator++(); + ~Iterator(); + + void DisablePriorityOrdering(); + + Iterator& operator++(); Tile* operator->() { return *(*this); } Tile* operator*(); operator bool() const { @@ -41,14 +41,17 @@ class CC_EXPORT PrioritizedTileSet { PrioritizedTileSet* tile_set_; ManagedTileBin current_bin_; - std::vector<scoped_refptr<Tile> >::iterator iterator_; + std::vector<Tile*>::iterator iterator_; + bool use_priority_ordering_; }; private: - friend class PriorityIterator; + friend class Iterator; + + void SortBinIfNeeded(ManagedTileBin bin); - typedef scoped_refptr<Tile> TileRef; - std::vector<TileRef> tiles_[NUM_BINS]; + std::vector<Tile*> tiles_[NUM_BINS]; + bool bin_sorted_[NUM_BINS]; }; } // namespace cc diff --git a/chromium/cc/resources/prioritized_tile_set_unittest.cc b/chromium/cc/resources/prioritized_tile_set_unittest.cc index 645fa04d497..7de1032523c 100644 --- a/chromium/cc/resources/prioritized_tile_set_unittest.cc +++ b/chromium/cc/resources/prioritized_tile_set_unittest.cc @@ -9,6 +9,7 @@ #include "cc/resources/prioritized_tile_set.h" #include "cc/resources/tile.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_picture_pile_impl.h" #include "cc/test/fake_tile_manager.h" #include "cc/test/fake_tile_manager_client.h" @@ -24,9 +25,6 @@ class BinComparator { const ManagedTileState& ams = a->managed_state(); const ManagedTileState& bms = b->managed_state(); - if (ams.bin[LOW_PRIORITY_BIN] != bms.bin[LOW_PRIORITY_BIN]) - return ams.bin[LOW_PRIORITY_BIN] < bms.bin[LOW_PRIORITY_BIN]; - if (ams.required_for_activation != bms.required_for_activation) return ams.required_for_activation; @@ -54,12 +52,16 @@ namespace { class PrioritizedTileSetTest : public testing::Test { public: - PrioritizedTileSetTest() - : output_surface_(FakeOutputSurface::Create3d()), - resource_provider_(ResourceProvider::Create(output_surface_.get(), 0)), - tile_manager_(new FakeTileManager(&tile_manager_client_, - resource_provider_.get())), - picture_pile_(FakePicturePileImpl::CreatePile()) {} + PrioritizedTileSetTest() { + output_surface_ = FakeOutputSurface::Create3d().Pass(); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false).Pass(); + tile_manager_.reset(new FakeTileManager(&tile_manager_client_, + resource_provider_.get())); + picture_pile_ = FakePicturePileImpl::CreatePile(); + } scoped_refptr<Tile> CreateTile() { return make_scoped_refptr(new Tile(tile_manager_.get(), @@ -74,19 +76,22 @@ class PrioritizedTileSetTest : public testing::Test { } private: - FakeTileManagerClient tile_manager_client_; LayerTreeSettings settings_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<FakeOutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; + FakeTileManagerClient tile_manager_client_; scoped_ptr<FakeTileManager> tile_manager_; scoped_refptr<FakePicturePileImpl> picture_pile_; }; TEST_F(PrioritizedTileSetTest, EmptyIterator) { + // Creating an iterator to an empty set should work (but create iterator that + // isn't valid). + PrioritizedTileSet set; - set.Sort(); - PrioritizedTileSet::PriorityIterator it(&set); + PrioritizedTileSet::Iterator it(&set, true); EXPECT_FALSE(it); } @@ -94,9 +99,8 @@ TEST_F(PrioritizedTileSetTest, NonEmptyIterator) { PrioritizedTileSet set; scoped_refptr<Tile> tile = CreateTile(); set.InsertTile(tile, NOW_BIN); - set.Sort(); - PrioritizedTileSet::PriorityIterator it(&set); + PrioritizedTileSet::Iterator it(&set, true); EXPECT_TRUE(it); EXPECT_TRUE(*it == tile.get()); ++it; @@ -104,6 +108,8 @@ TEST_F(PrioritizedTileSetTest, NonEmptyIterator) { } TEST_F(PrioritizedTileSetTest, NowAndReadyToDrawBin) { + // Ensure that tiles in NOW_AND_READY_TO_DRAW_BIN aren't sorted. + PrioritizedTileSet set; TilePriority priorities[4] = { TilePriorityForEventualBin(), @@ -122,11 +128,9 @@ TEST_F(PrioritizedTileSetTest, NowAndReadyToDrawBin) { } } - set.Sort(); - // Tiles should appear in the same order as inserted. int i = 0; - for (PrioritizedTileSet::PriorityIterator it(&set); + for (PrioritizedTileSet::Iterator it(&set, true); it; ++it) { EXPECT_TRUE(*it == tiles[i].get()); @@ -136,6 +140,8 @@ TEST_F(PrioritizedTileSetTest, NowAndReadyToDrawBin) { } TEST_F(PrioritizedTileSetTest, NowBin) { + // Ensure that tiles in NOW_BIN are sorted according to BinComparator. + PrioritizedTileSet set; TilePriority priorities[4] = { TilePriorityForEventualBin(), @@ -154,13 +160,11 @@ TEST_F(PrioritizedTileSetTest, NowBin) { } } - set.Sort(); - // Tiles should appear in BinComparator order. std::sort(tiles.begin(), tiles.end(), BinComparator()); int i = 0; - for (PrioritizedTileSet::PriorityIterator it(&set); + for (PrioritizedTileSet::Iterator it(&set, true); it; ++it) { EXPECT_TRUE(*it == tiles[i].get()); @@ -170,6 +174,8 @@ TEST_F(PrioritizedTileSetTest, NowBin) { } TEST_F(PrioritizedTileSetTest, SoonBin) { + // Ensure that tiles in SOON_BIN are sorted according to BinComparator. + PrioritizedTileSet set; TilePriority priorities[4] = { TilePriorityForEventualBin(), @@ -188,13 +194,43 @@ TEST_F(PrioritizedTileSetTest, SoonBin) { } } - set.Sort(); - // Tiles should appear in BinComparator order. std::sort(tiles.begin(), tiles.end(), BinComparator()); int i = 0; - for (PrioritizedTileSet::PriorityIterator it(&set); + for (PrioritizedTileSet::Iterator it(&set, true); + it; + ++it) { + EXPECT_TRUE(*it == tiles[i].get()); + ++i; + } + EXPECT_EQ(20, i); +} + +TEST_F(PrioritizedTileSetTest, SoonBinNoPriority) { + // Ensure that when not using priority iterator, SOON_BIN tiles + // are not sorted. + + PrioritizedTileSet set; + TilePriority priorities[4] = { + TilePriorityForEventualBin(), + TilePriorityForNowBin(), + TilePriority(), + TilePriorityForSoonBin()}; + + std::vector<scoped_refptr<Tile> > tiles; + for (int priority = 0; priority < 4; ++priority) { + for (int i = 0; i < 5; ++i) { + scoped_refptr<Tile> tile = CreateTile(); + tile->SetPriority(ACTIVE_TREE, priorities[priority]); + tile->SetPriority(PENDING_TREE, priorities[priority]); + tiles.push_back(tile); + set.InsertTile(tile, SOON_BIN); + } + } + + int i = 0; + for (PrioritizedTileSet::Iterator it(&set, false); it; ++it) { EXPECT_TRUE(*it == tiles[i].get()); @@ -204,6 +240,8 @@ TEST_F(PrioritizedTileSetTest, SoonBin) { } TEST_F(PrioritizedTileSetTest, EventuallyAndActiveBin) { + // Ensure that EVENTUALLY_AND_ACTIVE_BIN tiles are sorted. + PrioritizedTileSet set; TilePriority priorities[4] = { TilePriorityForEventualBin(), @@ -222,13 +260,11 @@ TEST_F(PrioritizedTileSetTest, EventuallyAndActiveBin) { } } - set.Sort(); - // Tiles should appear in BinComparator order. std::sort(tiles.begin(), tiles.end(), BinComparator()); int i = 0; - for (PrioritizedTileSet::PriorityIterator it(&set); + for (PrioritizedTileSet::Iterator it(&set, true); it; ++it) { EXPECT_TRUE(*it == tiles[i].get()); @@ -238,6 +274,8 @@ TEST_F(PrioritizedTileSetTest, EventuallyAndActiveBin) { } TEST_F(PrioritizedTileSetTest, EventuallyBin) { + // Ensure that EVENTUALLY_BIN tiles are sorted. + PrioritizedTileSet set; TilePriority priorities[4] = { TilePriorityForEventualBin(), @@ -256,13 +294,11 @@ TEST_F(PrioritizedTileSetTest, EventuallyBin) { } } - set.Sort(); - // Tiles should appear in BinComparator order. std::sort(tiles.begin(), tiles.end(), BinComparator()); int i = 0; - for (PrioritizedTileSet::PriorityIterator it(&set); + for (PrioritizedTileSet::Iterator it(&set, true); it; ++it) { EXPECT_TRUE(*it == tiles[i].get()); @@ -271,7 +307,9 @@ TEST_F(PrioritizedTileSetTest, EventuallyBin) { EXPECT_EQ(20, i); } -TEST_F(PrioritizedTileSetTest, NeverAndActiveBin) { +TEST_F(PrioritizedTileSetTest, AtLastAndActiveBin) { + // Ensure that AT_LAST_AND_ACTIVE_BIN tiles are sorted. + PrioritizedTileSet set; TilePriority priorities[4] = { TilePriorityForEventualBin(), @@ -286,17 +324,15 @@ TEST_F(PrioritizedTileSetTest, NeverAndActiveBin) { tile->SetPriority(ACTIVE_TREE, priorities[priority]); tile->SetPriority(PENDING_TREE, priorities[priority]); tiles.push_back(tile); - set.InsertTile(tile, NEVER_AND_ACTIVE_BIN); + set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN); } } - set.Sort(); - // Tiles should appear in BinComparator order. std::sort(tiles.begin(), tiles.end(), BinComparator()); int i = 0; - for (PrioritizedTileSet::PriorityIterator it(&set); + for (PrioritizedTileSet::Iterator it(&set, true); it; ++it) { EXPECT_TRUE(*it == tiles[i].get()); @@ -305,7 +341,9 @@ TEST_F(PrioritizedTileSetTest, NeverAndActiveBin) { EXPECT_EQ(20, i); } -TEST_F(PrioritizedTileSetTest, NeverBin) { +TEST_F(PrioritizedTileSetTest, AtLastBin) { + // Ensure that AT_LAST_BIN tiles are sorted. + PrioritizedTileSet set; TilePriority priorities[4] = { TilePriorityForEventualBin(), @@ -320,17 +358,15 @@ TEST_F(PrioritizedTileSetTest, NeverBin) { tile->SetPriority(ACTIVE_TREE, priorities[priority]); tile->SetPriority(PENDING_TREE, priorities[priority]); tiles.push_back(tile); - set.InsertTile(tile, NEVER_BIN); + set.InsertTile(tile, AT_LAST_BIN); } } - set.Sort(); - // Tiles should appear in BinComparator order. std::sort(tiles.begin(), tiles.end(), BinComparator()); int i = 0; - for (PrioritizedTileSet::PriorityIterator it(&set); + for (PrioritizedTileSet::Iterator it(&set, true); it; ++it) { EXPECT_TRUE(*it == tiles[i].get()); @@ -340,27 +376,28 @@ TEST_F(PrioritizedTileSetTest, NeverBin) { } TEST_F(PrioritizedTileSetTest, TilesForEachBin) { + // Aggregate test with one tile for each of the bins, which + // should appear in order of the bins. + scoped_refptr<Tile> now_and_ready_to_draw_bin = CreateTile(); scoped_refptr<Tile> now_bin = CreateTile(); scoped_refptr<Tile> soon_bin = CreateTile(); scoped_refptr<Tile> eventually_and_active_bin = CreateTile(); scoped_refptr<Tile> eventually_bin = CreateTile(); - scoped_refptr<Tile> never_bin = CreateTile(); - scoped_refptr<Tile> never_and_active_bin = CreateTile(); + scoped_refptr<Tile> at_last_bin = CreateTile(); + scoped_refptr<Tile> at_last_and_active_bin = CreateTile(); PrioritizedTileSet set; set.InsertTile(soon_bin, SOON_BIN); - set.InsertTile(never_and_active_bin, NEVER_AND_ACTIVE_BIN); + set.InsertTile(at_last_and_active_bin, AT_LAST_AND_ACTIVE_BIN); set.InsertTile(eventually_bin, EVENTUALLY_BIN); set.InsertTile(now_bin, NOW_BIN); set.InsertTile(eventually_and_active_bin, EVENTUALLY_AND_ACTIVE_BIN); - set.InsertTile(never_bin, NEVER_BIN); + set.InsertTile(at_last_bin, AT_LAST_BIN); set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN); - set.Sort(); - // Tiles should appear in order. - PrioritizedTileSet::PriorityIterator it(&set); + PrioritizedTileSet::Iterator it(&set, true); EXPECT_TRUE(*it == now_and_ready_to_draw_bin.get()); ++it; EXPECT_TRUE(*it == now_bin.get()); @@ -371,50 +408,270 @@ TEST_F(PrioritizedTileSetTest, TilesForEachBin) { ++it; EXPECT_TRUE(*it == eventually_bin.get()); ++it; - EXPECT_TRUE(*it == never_and_active_bin.get()); + EXPECT_TRUE(*it == at_last_and_active_bin.get()); ++it; - EXPECT_TRUE(*it == never_bin.get()); + EXPECT_TRUE(*it == at_last_bin.get()); ++it; EXPECT_FALSE(it); } +TEST_F(PrioritizedTileSetTest, ManyTilesForEachBin) { + // Aggregate test with many tiles in each of the bins of various + // priorities. Ensure that they are all returned in a sorted order. + + std::vector<scoped_refptr<Tile> > now_and_ready_to_draw_bins; + std::vector<scoped_refptr<Tile> > now_bins; + std::vector<scoped_refptr<Tile> > soon_bins; + std::vector<scoped_refptr<Tile> > eventually_and_active_bins; + std::vector<scoped_refptr<Tile> > eventually_bins; + std::vector<scoped_refptr<Tile> > at_last_bins; + std::vector<scoped_refptr<Tile> > at_last_and_active_bins; + + TilePriority priorities[4] = { + TilePriorityForEventualBin(), + TilePriorityForNowBin(), + TilePriority(), + TilePriorityForSoonBin()}; + + PrioritizedTileSet set; + for (int priority = 0; priority < 4; ++priority) { + for (int i = 0; i < 5; ++i) { + scoped_refptr<Tile> tile = CreateTile(); + tile->SetPriority(ACTIVE_TREE, priorities[priority]); + tile->SetPriority(PENDING_TREE, priorities[priority]); + + now_and_ready_to_draw_bins.push_back(tile); + now_bins.push_back(tile); + soon_bins.push_back(tile); + eventually_and_active_bins.push_back(tile); + eventually_bins.push_back(tile); + at_last_bins.push_back(tile); + at_last_and_active_bins.push_back(tile); + + set.InsertTile(tile, NOW_AND_READY_TO_DRAW_BIN); + set.InsertTile(tile, NOW_BIN); + set.InsertTile(tile, SOON_BIN); + set.InsertTile(tile, EVENTUALLY_AND_ACTIVE_BIN); + set.InsertTile(tile, EVENTUALLY_BIN); + set.InsertTile(tile, AT_LAST_BIN); + set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN); + } + } + + PrioritizedTileSet::Iterator it(&set, true); + std::vector<scoped_refptr<Tile> >::iterator vector_it; + + // Now and ready are not sorted. + for (vector_it = now_and_ready_to_draw_bins.begin(); + vector_it != now_and_ready_to_draw_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // Now bins are sorted. + std::sort(now_bins.begin(), now_bins.end(), BinComparator()); + for (vector_it = now_bins.begin(); vector_it != now_bins.end(); ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // Soon bins are sorted. + std::sort(soon_bins.begin(), soon_bins.end(), BinComparator()); + for (vector_it = soon_bins.begin(); vector_it != soon_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // Eventually and active bins are sorted. + std::sort(eventually_and_active_bins.begin(), + eventually_and_active_bins.end(), + BinComparator()); + for (vector_it = eventually_and_active_bins.begin(); + vector_it != eventually_and_active_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // Eventually bins are sorted. + std::sort(eventually_bins.begin(), eventually_bins.end(), BinComparator()); + for (vector_it = eventually_bins.begin(); vector_it != eventually_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // At last and active bins are sorted. + std::sort(at_last_and_active_bins.begin(), + at_last_and_active_bins.end(), + BinComparator()); + for (vector_it = at_last_and_active_bins.begin(); + vector_it != at_last_and_active_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // At last bins are sorted. + std::sort(at_last_bins.begin(), at_last_bins.end(), BinComparator()); + for (vector_it = at_last_bins.begin(); vector_it != at_last_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + EXPECT_FALSE(it); +} + +TEST_F(PrioritizedTileSetTest, ManyTilesForEachBinDisablePriority) { + // Aggregate test with many tiles for each of the bins. Tiles should + // appear in order, until DisablePriorityOrdering is called. After that + // tiles should appear in the order they were inserted. + + std::vector<scoped_refptr<Tile> > now_and_ready_to_draw_bins; + std::vector<scoped_refptr<Tile> > now_bins; + std::vector<scoped_refptr<Tile> > soon_bins; + std::vector<scoped_refptr<Tile> > eventually_and_active_bins; + std::vector<scoped_refptr<Tile> > eventually_bins; + std::vector<scoped_refptr<Tile> > at_last_bins; + std::vector<scoped_refptr<Tile> > at_last_and_active_bins; + + TilePriority priorities[4] = { + TilePriorityForEventualBin(), + TilePriorityForNowBin(), + TilePriority(), + TilePriorityForSoonBin()}; + + PrioritizedTileSet set; + for (int priority = 0; priority < 4; ++priority) { + for (int i = 0; i < 5; ++i) { + scoped_refptr<Tile> tile = CreateTile(); + tile->SetPriority(ACTIVE_TREE, priorities[priority]); + tile->SetPriority(PENDING_TREE, priorities[priority]); + + now_and_ready_to_draw_bins.push_back(tile); + now_bins.push_back(tile); + soon_bins.push_back(tile); + eventually_and_active_bins.push_back(tile); + eventually_bins.push_back(tile); + at_last_bins.push_back(tile); + at_last_and_active_bins.push_back(tile); + + set.InsertTile(tile, NOW_AND_READY_TO_DRAW_BIN); + set.InsertTile(tile, NOW_BIN); + set.InsertTile(tile, SOON_BIN); + set.InsertTile(tile, EVENTUALLY_AND_ACTIVE_BIN); + set.InsertTile(tile, EVENTUALLY_BIN); + set.InsertTile(tile, AT_LAST_BIN); + set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN); + } + } + + PrioritizedTileSet::Iterator it(&set, true); + std::vector<scoped_refptr<Tile> >::iterator vector_it; + + // Now and ready are not sorted. + for (vector_it = now_and_ready_to_draw_bins.begin(); + vector_it != now_and_ready_to_draw_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // Now bins are sorted. + std::sort(now_bins.begin(), now_bins.end(), BinComparator()); + for (vector_it = now_bins.begin(); vector_it != now_bins.end(); ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // Soon bins are sorted. + std::sort(soon_bins.begin(), soon_bins.end(), BinComparator()); + for (vector_it = soon_bins.begin(); vector_it != soon_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // After we disable priority ordering, we already have sorted the next vector. + it.DisablePriorityOrdering(); + + // Eventually and active bins are sorted. + std::sort(eventually_and_active_bins.begin(), + eventually_and_active_bins.end(), + BinComparator()); + for (vector_it = eventually_and_active_bins.begin(); + vector_it != eventually_and_active_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // Eventually bins are not sorted. + for (vector_it = eventually_bins.begin(); vector_it != eventually_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // At last and active bins are not sorted. + for (vector_it = at_last_and_active_bins.begin(); + vector_it != at_last_and_active_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + // At last bins are not sorted. + for (vector_it = at_last_bins.begin(); vector_it != at_last_bins.end(); + ++vector_it) { + EXPECT_TRUE(*vector_it == *it); + ++it; + } + + EXPECT_FALSE(it); +} + TEST_F(PrioritizedTileSetTest, TilesForFirstAndLastBins) { + // Make sure that if we have empty lists between two non-empty lists, + // we just get two tiles from the iterator. + scoped_refptr<Tile> now_and_ready_to_draw_bin = CreateTile(); - scoped_refptr<Tile> never_bin = CreateTile(); + scoped_refptr<Tile> at_last_bin = CreateTile(); PrioritizedTileSet set; - set.InsertTile(never_bin, NEVER_BIN); + set.InsertTile(at_last_bin, AT_LAST_BIN); set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN); - set.Sort(); - // Only two tiles should appear and they should appear in order. - PrioritizedTileSet::PriorityIterator it(&set); + PrioritizedTileSet::Iterator it(&set, true); EXPECT_TRUE(*it == now_and_ready_to_draw_bin.get()); ++it; - EXPECT_TRUE(*it == never_bin.get()); + EXPECT_TRUE(*it == at_last_bin.get()); ++it; EXPECT_FALSE(it); } TEST_F(PrioritizedTileSetTest, MultipleIterators) { + // Ensure that multiple iterators don't interfere with each other. + scoped_refptr<Tile> now_and_ready_to_draw_bin = CreateTile(); scoped_refptr<Tile> now_bin = CreateTile(); scoped_refptr<Tile> soon_bin = CreateTile(); scoped_refptr<Tile> eventually_bin = CreateTile(); - scoped_refptr<Tile> never_bin = CreateTile(); + scoped_refptr<Tile> at_last_bin = CreateTile(); PrioritizedTileSet set; set.InsertTile(soon_bin, SOON_BIN); set.InsertTile(eventually_bin, EVENTUALLY_BIN); set.InsertTile(now_bin, NOW_BIN); - set.InsertTile(never_bin, NEVER_BIN); + set.InsertTile(at_last_bin, AT_LAST_BIN); set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN); - set.Sort(); - // Tiles should appear in order. - PrioritizedTileSet::PriorityIterator it(&set); + PrioritizedTileSet::Iterator it(&set, true); EXPECT_TRUE(*it == now_and_ready_to_draw_bin.get()); ++it; EXPECT_TRUE(*it == now_bin.get()); @@ -423,12 +680,12 @@ TEST_F(PrioritizedTileSetTest, MultipleIterators) { ++it; EXPECT_TRUE(*it == eventually_bin.get()); ++it; - EXPECT_TRUE(*it == never_bin.get()); + EXPECT_TRUE(*it == at_last_bin.get()); ++it; EXPECT_FALSE(it); // Creating multiple iterators shouldn't affect old iterators. - PrioritizedTileSet::PriorityIterator second_it(&set); + PrioritizedTileSet::Iterator second_it(&set, true); EXPECT_TRUE(second_it); EXPECT_FALSE(it); @@ -438,7 +695,7 @@ TEST_F(PrioritizedTileSetTest, MultipleIterators) { EXPECT_TRUE(second_it); EXPECT_FALSE(it); - PrioritizedTileSet::PriorityIterator third_it(&set); + PrioritizedTileSet::Iterator third_it(&set, true); EXPECT_TRUE(third_it); ++second_it; ++second_it; @@ -451,7 +708,7 @@ TEST_F(PrioritizedTileSetTest, MultipleIterators) { EXPECT_TRUE(third_it); EXPECT_TRUE(*third_it == soon_bin.get()); EXPECT_TRUE(second_it); - EXPECT_TRUE(*second_it == never_bin.get()); + EXPECT_TRUE(*second_it == at_last_bin.get()); EXPECT_FALSE(it); ++second_it; @@ -461,7 +718,7 @@ TEST_F(PrioritizedTileSetTest, MultipleIterators) { set.Clear(); - PrioritizedTileSet::PriorityIterator empty_it(&set); + PrioritizedTileSet::Iterator empty_it(&set, true); EXPECT_FALSE(empty_it); } diff --git a/chromium/cc/resources/raster_mode.cc b/chromium/cc/resources/raster_mode.cc index b35bc0be42d..47d6344c97d 100644 --- a/chromium/cc/resources/raster_mode.cc +++ b/chromium/cc/resources/raster_mode.cc @@ -21,7 +21,6 @@ scoped_ptr<base::Value> RasterModeAsValue(RasterMode raster_mode) { case LOW_QUALITY_RASTER_MODE: return scoped_ptr<base::Value>( base::Value::CreateStringValue("LOW_QUALITY_RASTER_MODE")); - case NUM_RASTER_MODES: default: NOTREACHED() << "Unrecognized RasterMode value " << raster_mode; return scoped_ptr<base::Value>( diff --git a/chromium/cc/resources/raster_worker_pool.cc b/chromium/cc/resources/raster_worker_pool.cc index aced03fcec6..5aaf0b66540 100644 --- a/chromium/cc/resources/raster_worker_pool.cc +++ b/chromium/cc/resources/raster_worker_pool.cc @@ -13,11 +13,25 @@ #include "cc/resources/picture_pile_impl.h" #include "skia/ext/lazy_pixel_ref.h" #include "skia/ext/paint_simplifier.h" +#include "third_party/skia/include/core/SkBitmap.h" namespace cc { namespace { +// Subclass of Allocator that takes a suitably allocated pointer and uses +// it as the pixel memory for the bitmap. +class IdentityAllocator : public SkBitmap::Allocator { + public: + explicit IdentityAllocator(void* buffer) : buffer_(buffer) {} + virtual bool allocPixelRef(SkBitmap* dst, SkColorTable*) OVERRIDE { + dst->setPixels(buffer_); + return true; + } + private: + void* buffer_; +}; + // Flag to indicate whether we should try and detect that // a tile is of solid color. const bool kUseColorEstimator = true; @@ -89,7 +103,10 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask { analysis_.is_solid_color &= kUseColorEstimator; } - bool RunRasterOnThread(SkDevice* device, unsigned thread_index) { + bool RunRasterOnThread(unsigned thread_index, + void* buffer, + gfx::Size size, + int stride) { TRACE_EVENT2( benchmark_instrumentation::kCategory, benchmark_instrumentation::kRunRasterOnThread, @@ -102,7 +119,7 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask { devtools_instrumentation::kRasterTask, layer_id_); DCHECK(picture_pile_.get()); - DCHECK(device); + DCHECK(buffer); if (analysis_.is_solid_color) return false; @@ -110,8 +127,32 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask { PicturePileImpl* picture_clone = picture_pile_->GetCloneForDrawingOnThread(thread_index); - SkCanvas canvas(device); + SkBitmap bitmap; + switch (resource()->format()) { + case RGBA_4444: + // Use the default stride if we will eventually convert this + // bitmap to 4444. + bitmap.setConfig(SkBitmap::kARGB_8888_Config, + size.width(), + size.height()); + bitmap.allocPixels(); + break; + case RGBA_8888: + case BGRA_8888: + bitmap.setConfig(SkBitmap::kARGB_8888_Config, + size.width(), + size.height(), + stride); + bitmap.setPixels(buffer); + break; + case LUMINANCE_8: + case RGB_565: + NOTREACHED(); + break; + } + SkBitmapDevice device(bitmap); + SkCanvas canvas(&device); skia::RefPtr<SkDrawFilter> draw_filter; switch (raster_mode_) { case LOW_QUALITY_RASTER_MODE: @@ -149,14 +190,20 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask { picture_clone->RasterToBitmap( &canvas, content_rect_, contents_scale_, NULL); } + + ChangeBitmapConfigIfNeeded(bitmap, buffer); + return true; } // Overridden from internal::RasterWorkerPoolTask: - virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index) + virtual bool RunOnWorkerThread(unsigned thread_index, + void* buffer, + gfx::Size size, + int stride) OVERRIDE { RunAnalysisOnThread(thread_index); - return RunRasterOnThread(device, thread_index); + return RunRasterOnThread(thread_index, buffer, size, stride); } virtual void CompleteOnOriginThread() OVERRIDE { reply_.Run(analysis_, !HasFinishedRunning() || WasCanceled()); @@ -177,6 +224,21 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask { return res.PassAs<base::Value>(); } + void ChangeBitmapConfigIfNeeded(const SkBitmap& bitmap, + void* buffer) { + TRACE_EVENT0("cc", "RasterWorkerPoolTaskImpl::ChangeBitmapConfigIfNeeded"); + SkBitmap::Config config = SkBitmapConfigFromFormat( + resource()->format()); + if (bitmap.getConfig() != config) { + SkBitmap bitmap_dest; + IdentityAllocator allocator(buffer); + bitmap.copyTo(&bitmap_dest, config, &allocator); + // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the + // bitmap data. This check will be removed once crbug.com/293728 is fixed. + CHECK_EQ(0u, bitmap_dest.rowBytes() % 4); + } + } + PicturePileImpl::Analysis analysis_; scoped_refptr<PicturePileImpl> picture_pile_; gfx::Rect content_rect_; @@ -199,7 +261,7 @@ class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask { int layer_id, RenderingStatsInstrumentation* rendering_stats, const RasterWorkerPool::Task::Reply& reply) - : pixel_ref_(pixel_ref), + : pixel_ref_(skia::SharePtr(pixel_ref)), layer_id_(layer_id), rendering_stats_(rendering_stats), reply_(reply) {} @@ -207,8 +269,8 @@ class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask { // Overridden from internal::WorkerPoolTask: virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE { TRACE_EVENT0("cc", "ImageDecodeWorkerPoolTaskImpl::RunOnWorkerThread"); - devtools_instrumentation::ScopedLayerTask image_decode_task( - devtools_instrumentation::kImageDecodeTask, layer_id_); + devtools_instrumentation::ScopedImageDecodeTask image_decode_task( + pixel_ref_.get()); base::TimeTicks start_time = rendering_stats_->StartRecording(); pixel_ref_->Decode(); base::TimeDelta duration = rendering_stats_->EndRecording(start_time); @@ -222,7 +284,7 @@ class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask { virtual ~ImageDecodeWorkerPoolTaskImpl() {} private: - skia::LazyPixelRef* pixel_ref_; + skia::RefPtr<skia::LazyPixelRef> pixel_ref_; int layer_id_; RenderingStatsInstrumentation* rendering_stats_; const RasterWorkerPool::Task::Reply reply_; diff --git a/chromium/cc/resources/raster_worker_pool.h b/chromium/cc/resources/raster_worker_pool.h index d0e1e7c0de0..b12a1b43c23 100644 --- a/chromium/cc/resources/raster_worker_pool.h +++ b/chromium/cc/resources/raster_worker_pool.h @@ -11,10 +11,11 @@ #include "cc/debug/rendering_stats_instrumentation.h" #include "cc/resources/picture_pile_impl.h" #include "cc/resources/raster_mode.h" +#include "cc/resources/resource.h" +#include "cc/resources/resource_provider.h" #include "cc/resources/tile_priority.h" #include "cc/resources/worker_pool.h" - -class SkDevice; +#include "third_party/khronos/GLES2/gl2.h" namespace skia { class LazyPixelRef; @@ -23,7 +24,6 @@ class LazyPixelRef; namespace cc { class PicturePileImpl; class PixelBufferRasterWorkerPool; -class Resource; class ResourceProvider; namespace internal { @@ -33,10 +33,13 @@ class CC_EXPORT RasterWorkerPoolTask public: typedef std::vector<scoped_refptr<WorkerPoolTask> > TaskVector; - // Returns true if |device| was written to. False indicate that - // the content of |device| is undefined and the resource doesn't + // Returns true if |buffer| was written to. False indicate that + // the content of |buffer| is undefined and the resource doesn't // need to be initialized. - virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index) = 0; + virtual bool RunOnWorkerThread(unsigned thread_index, + void* buffer, + gfx::Size size, + int stride) = 0; virtual void CompleteOnOriginThread() = 0; void DidRun(bool was_canceled); @@ -183,6 +186,9 @@ class CC_EXPORT RasterWorkerPool : public WorkerPool { // even if they later get canceled by another call to ScheduleTasks(). virtual void ScheduleTasks(RasterTask::Queue* queue) = 0; + // Returns the format that needs to be used for raster task resources. + virtual ResourceFormat GetResourceFormat() const = 0; + // TODO(vmpstr): Figure out an elegant way to not pass this many parameters. static RasterTask CreateRasterTask( const Resource* resource, diff --git a/chromium/cc/resources/raster_worker_pool_perftest.cc b/chromium/cc/resources/raster_worker_pool_perftest.cc index a4a258b16d3..cec9c4820d1 100644 --- a/chromium/cc/resources/raster_worker_pool_perftest.cc +++ b/chromium/cc/resources/raster_worker_pool_perftest.cc @@ -5,7 +5,9 @@ #include "cc/resources/raster_worker_pool.h" #include "base/time/time.h" +#include "cc/test/lap_timer.h" #include "testing/gtest/include/gtest/gtest.h" +#include "testing/perf/perf_test.h" namespace cc { @@ -38,6 +40,10 @@ class PerfRasterWorkerPool : public RasterWorkerPool { virtual void ScheduleTasks(RasterTask::Queue* queue) OVERRIDE { NOTREACHED(); } + virtual ResourceFormat GetResourceFormat() const OVERRIDE { + NOTREACHED(); + return RGBA_8888; + } virtual void OnRasterTasksFinished() OVERRIDE { NOTREACHED(); } @@ -116,7 +122,10 @@ class PerfRasterWorkerPool : public RasterWorkerPool { class RasterWorkerPoolPerfTest : public testing::Test { public: - RasterWorkerPoolPerfTest() : num_runs_(0) {} + RasterWorkerPoolPerfTest() + : timer_(kWarmupRuns, + base::TimeDelta::FromMilliseconds(kTimeLimitMillis), + kTimeCheckInterval) {} // Overridden from testing::Test: virtual void SetUp() OVERRIDE { @@ -126,33 +135,6 @@ class RasterWorkerPoolPerfTest : public testing::Test { raster_worker_pool_->Shutdown(); } - void EndTest() { - elapsed_ = base::TimeTicks::HighResNow() - start_time_; - } - - void AfterTest(const std::string test_name) { - // Format matches chrome/test/perf/perf_test.h:PrintResult - printf("*RESULT %s: %.2f runs/s\n", - test_name.c_str(), - num_runs_ / elapsed_.InSecondsF()); - } - - bool DidRun() { - ++num_runs_; - if (num_runs_ == kWarmupRuns) - start_time_ = base::TimeTicks::HighResNow(); - - if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) { - base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_; - if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) { - elapsed_ = elapsed; - return false; - } - } - - return true; - } - void CreateTasks(RasterWorkerPool::RasterTask::Queue* tasks, unsigned num_raster_tasks, unsigned num_image_decode_tasks) { @@ -194,19 +176,20 @@ class RasterWorkerPoolPerfTest : public testing::Test { } } - void RunBuildTaskGraphTest(const std::string test_name, + void RunBuildTaskGraphTest(const std::string& test_name, unsigned num_raster_tasks, unsigned num_image_decode_tasks) { - start_time_ = base::TimeTicks(); - num_runs_ = 0; + timer_.Reset(); RasterWorkerPool::RasterTask::Queue tasks; CreateTasks(&tasks, num_raster_tasks, num_image_decode_tasks); raster_worker_pool_->SetRasterTasks(&tasks); do { raster_worker_pool_->BuildTaskGraph(); - } while (DidRun()); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); - AfterTest(test_name); + perf_test::PrintResult("build_task_graph", "", test_name, + timer_.LapsPerSecond(), "runs/s", true); } protected: @@ -215,24 +198,22 @@ class RasterWorkerPoolPerfTest : public testing::Test { static void OnImageDecodeTaskCompleted(bool was_canceled) {} scoped_ptr<PerfRasterWorkerPool> raster_worker_pool_; - base::TimeTicks start_time_; - base::TimeDelta elapsed_; - int num_runs_; + LapTimer timer_; }; TEST_F(RasterWorkerPoolPerfTest, BuildTaskGraph) { - RunBuildTaskGraphTest("build_task_graph_10_0", 10, 0); - RunBuildTaskGraphTest("build_task_graph_100_0", 100, 0); - RunBuildTaskGraphTest("build_task_graph_1000_0", 1000, 0); - RunBuildTaskGraphTest("build_task_graph_10_1", 10, 1); - RunBuildTaskGraphTest("build_task_graph_100_1", 100, 1); - RunBuildTaskGraphTest("build_task_graph_1000_1", 1000, 1); - RunBuildTaskGraphTest("build_task_graph_10_4", 10, 4); - RunBuildTaskGraphTest("build_task_graph_100_4", 100, 4); - RunBuildTaskGraphTest("build_task_graph_1000_4", 1000, 4); - RunBuildTaskGraphTest("build_task_graph_10_16", 10, 16); - RunBuildTaskGraphTest("build_task_graph_100_16", 100, 16); - RunBuildTaskGraphTest("build_task_graph_1000_16", 1000, 16); + RunBuildTaskGraphTest("10_0", 10, 0); + RunBuildTaskGraphTest("100_0", 100, 0); + RunBuildTaskGraphTest("1000_0", 1000, 0); + RunBuildTaskGraphTest("10_1", 10, 1); + RunBuildTaskGraphTest("100_1", 100, 1); + RunBuildTaskGraphTest("1000_1", 1000, 1); + RunBuildTaskGraphTest("10_4", 10, 4); + RunBuildTaskGraphTest("100_4", 100, 4); + RunBuildTaskGraphTest("1000_4", 1000, 4); + RunBuildTaskGraphTest("10_16", 10, 16); + RunBuildTaskGraphTest("100_16", 100, 16); + RunBuildTaskGraphTest("1000_16", 1000, 16); } } // namespace diff --git a/chromium/cc/resources/raster_worker_pool_unittest.cc b/chromium/cc/resources/raster_worker_pool_unittest.cc index 3d27c41b353..61cb324f772 100644 --- a/chromium/cc/resources/raster_worker_pool_unittest.cc +++ b/chromium/cc/resources/raster_worker_pool_unittest.cc @@ -4,8 +4,10 @@ #include "cc/resources/raster_worker_pool.h" +#include <limits> #include <vector> +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/resources/image_raster_worker_pool.h" #include "cc/resources/picture_pile.h" #include "cc/resources/picture_pile_impl.h" @@ -13,6 +15,7 @@ #include "cc/resources/resource_provider.h" #include "cc/resources/scoped_resource.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -32,8 +35,10 @@ class TestRasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask { did_raster_(false) {} // Overridden from internal::WorkerPoolTask: - virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index) - OVERRIDE { + virtual bool RunOnWorkerThread(unsigned thread_index, + void* buffer, + gfx::Size size, + int stride) OVERRIDE { did_raster_ = true; return true; } @@ -55,12 +60,15 @@ class RasterWorkerPoolTest : public testing::Test, public RasterWorkerPoolClient { public: RasterWorkerPoolTest() - : output_surface_(FakeOutputSurface::Create3d()), - resource_provider_( - ResourceProvider::Create(output_surface_.get(), 0)), + : context_provider_(TestContextProvider::Create()), check_interval_milliseconds_(1), timeout_seconds_(5), timed_out_(false) { + output_surface_ = FakeOutputSurface::Create3d(context_provider_).Pass(); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false).Pass(); } virtual ~RasterWorkerPoolTest() { resource_provider_.reset(); @@ -98,8 +106,11 @@ class RasterWorkerPoolTest : public testing::Test, raster_worker_pool_ = ImageRasterWorkerPool::Create( resource_provider(), 1); } else { - raster_worker_pool_ = PixelBufferRasterWorkerPool::Create( - resource_provider(), 1); + raster_worker_pool_ = + PixelBufferRasterWorkerPool::Create( + resource_provider(), + 1, + std::numeric_limits<size_t>::max()); } raster_worker_pool_->SetClient(this); @@ -149,7 +160,7 @@ class RasterWorkerPoolTest : public testing::Test, scoped_ptr<ScopedResource> resource( ScopedResource::create(resource_provider())); - resource->Allocate(size, GL_RGBA, ResourceProvider::TextureUsageAny); + resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888); const Resource* const_resource = resource.get(); RasterWorkerPool::Task::Set empty; @@ -190,6 +201,8 @@ class RasterWorkerPoolTest : public testing::Test, } protected: + scoped_refptr<TestContextProvider> context_provider_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<FakeOutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; scoped_ptr<RasterWorkerPool> raster_worker_pool_; @@ -259,8 +272,7 @@ class RasterWorkerPoolTestFailedMapResource : public RasterWorkerPoolTest { // Overridden from RasterWorkerPoolTest: virtual void BeginTest() OVERRIDE { - TestWebGraphicsContext3D* context3d = - static_cast<TestWebGraphicsContext3D*>(output_surface_->context3d()); + TestWebGraphicsContext3D* context3d = context_provider_->TestContext3d(); context3d->set_times_map_image_chromium_succeeds(0); context3d->set_times_map_buffer_chromium_succeeds(0); AppendTask(0u); diff --git a/chromium/cc/resources/release_callback.h b/chromium/cc/resources/release_callback.h new file mode 100644 index 00000000000..9433f7f6eb9 --- /dev/null +++ b/chromium/cc/resources/release_callback.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RESOURCES_RELEASE_CALLBACK_H_ +#define CC_RESOURCES_RELEASE_CALLBACK_H_ + +#include "base/callback.h" + +namespace cc { + +typedef base::Callback<void(unsigned sync_point, bool is_lost)> + ReleaseCallback; + +} // namespace cc + +#endif // CC_RESOURCES_RELEASE_CALLBACK_H_ diff --git a/chromium/cc/resources/resource.cc b/chromium/cc/resources/resource.cc index 192eaebddc6..c97db7fb872 100644 --- a/chromium/cc/resources/resource.cc +++ b/chromium/cc/resources/resource.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "cc/resources/resource.h" -#include "third_party/khronos/GLES2/gl2ext.h" namespace cc { @@ -14,26 +13,8 @@ size_t Resource::bytes() const { return MemorySizeBytes(size_, format_); } -size_t Resource::BytesPerPixel(GLenum format) { - size_t components_per_pixel = 0; - size_t bytes_per_component = 1; - switch (format) { - case GL_RGBA: - case GL_BGRA_EXT: - components_per_pixel = 4; - break; - case GL_LUMINANCE: - components_per_pixel = 1; - break; - default: - NOTREACHED(); - } - return components_per_pixel * bytes_per_component; +size_t Resource::MemorySizeBytes(gfx::Size size, ResourceFormat format) { + return ResourceProvider::BytesPerPixel(format) * size.width() * size.height(); } -size_t Resource::MemorySizeBytes(gfx::Size size, GLenum format) { - return BytesPerPixel(format) * size.width() * size.height(); -} - - } // namespace cc diff --git a/chromium/cc/resources/resource.h b/chromium/cc/resources/resource.h index 1c658223649..d2d104c1370 100644 --- a/chromium/cc/resources/resource.h +++ b/chromium/cc/resources/resource.h @@ -15,23 +15,21 @@ namespace cc { class CC_EXPORT Resource { public: Resource() : id_(0) {} - Resource(unsigned id, gfx::Size size, GLenum format) + Resource(unsigned id, gfx::Size size, ResourceFormat format) : id_(id), size_(size), format_(format) {} ResourceProvider::ResourceId id() const { return id_; } gfx::Size size() const { return size_; } - GLenum format() const { return format_; } - + ResourceFormat format() const { return format_; } size_t bytes() const; - static size_t BytesPerPixel(GLenum format); - static size_t MemorySizeBytes(gfx::Size size, GLenum format); + static size_t MemorySizeBytes(gfx::Size size, ResourceFormat format); protected: void set_id(ResourceProvider::ResourceId id) { id_ = id; } - void set_dimensions(gfx::Size size, GLenum format) { + void set_dimensions(gfx::Size size, ResourceFormat format) { size_ = size; format_ = format; } @@ -39,7 +37,7 @@ class CC_EXPORT Resource { private: ResourceProvider::ResourceId id_; gfx::Size size_; - GLenum format_; + ResourceFormat format_; DISALLOW_COPY_AND_ASSIGN(Resource); }; diff --git a/chromium/cc/resources/resource_format.cc b/chromium/cc/resources/resource_format.cc new file mode 100644 index 00000000000..5ea3df12598 --- /dev/null +++ b/chromium/cc/resources/resource_format.cc @@ -0,0 +1,25 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/resources/resource_format.h" + +namespace cc { + +SkBitmap::Config SkBitmapConfigFromFormat(ResourceFormat format) { + switch (format) { + case RGBA_4444: + return SkBitmap::kARGB_4444_Config; + case RGBA_8888: + case BGRA_8888: + return SkBitmap::kARGB_8888_Config; + case LUMINANCE_8: + case RGB_565: + NOTREACHED(); + break; + } + NOTREACHED(); + return SkBitmap::kARGB_8888_Config; +} + +} // namespace cc diff --git a/chromium/cc/resources/resource_format.h b/chromium/cc/resources/resource_format.h new file mode 100644 index 00000000000..ef83cc02eb7 --- /dev/null +++ b/chromium/cc/resources/resource_format.h @@ -0,0 +1,26 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RESOURCES_RESOURCE_FORMAT_H_ +#define CC_RESOURCES_RESOURCE_FORMAT_H_ + +#include "base/logging.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace cc { + +enum ResourceFormat { + RGBA_8888, + RGBA_4444, + BGRA_8888, + LUMINANCE_8, + RGB_565, + RESOURCE_FORMAT_MAX = RGB_565, +}; + +SkBitmap::Config SkBitmapConfigFromFormat(ResourceFormat format); + +} // namespace cc + +#endif // CC_RESOURCES_RESOURCE_FORMAT_H_ diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc index 06bc1fbe8fd..cef86ded178 100644 --- a/chromium/cc/resources/resource_pool.cc +++ b/chromium/cc/resources/resource_pool.cc @@ -10,11 +10,12 @@ namespace cc { ResourcePool::Resource::Resource(cc::ResourceProvider* resource_provider, gfx::Size size, - GLenum format) + ResourceFormat format) : cc::Resource(resource_provider->CreateManagedResource( size, - format, - ResourceProvider::TextureUsageAny), + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + format), size, format), resource_provider_(resource_provider) { @@ -42,17 +43,13 @@ ResourcePool::~ResourcePool() { } scoped_ptr<ResourcePool::Resource> ResourcePool::AcquireResource( - gfx::Size size, GLenum format) { + gfx::Size size, ResourceFormat format) { for (ResourceList::iterator it = unused_resources_.begin(); it != unused_resources_.end(); ++it) { Resource* resource = *it; - // TODO(epenner): It would be nice to DCHECK that this - // doesn't happen two frames in a row for any resource - // in this pool. if (!resource_provider_->CanLockForWrite(resource->id())) continue; - if (resource->size() != size) continue; if (resource->format() != format) @@ -104,10 +101,15 @@ void ResourcePool::ReduceResourceUsage() { if (!ResourceUsageTooHigh()) break; - // MRU eviction pattern as least recently used is less likely to - // be blocked by read lock fence. - Resource* resource = unused_resources_.back(); - unused_resources_.pop_back(); + // LRU eviction pattern. Most recently used might be blocked by + // a read lock fence but it's still better to evict the least + // recently used as it prevents a resource that is hard to reuse + // because of unique size from being kept around. Resources that + // can't be locked for write might also not be truly free-able. + // We can free the resource here but it doesn't mean that the + // memory is necessarily returned to the OS. + Resource* resource = unused_resources_.front(); + unused_resources_.pop_front(); memory_usage_bytes_ -= resource->bytes(); unused_memory_usage_bytes_ -= resource->bytes(); --resource_count_; diff --git a/chromium/cc/resources/resource_pool.h b/chromium/cc/resources/resource_pool.h index f6bb8652476..21bbb0a70d0 100644 --- a/chromium/cc/resources/resource_pool.h +++ b/chromium/cc/resources/resource_pool.h @@ -11,9 +11,9 @@ #include "cc/base/cc_export.h" #include "cc/output/renderer.h" #include "cc/resources/resource.h" +#include "cc/resources/resource_format.h" namespace cc { -class ResourceProvider; class CC_EXPORT ResourcePool { public: @@ -21,7 +21,7 @@ class CC_EXPORT ResourcePool { public: Resource(ResourceProvider* resource_provider, gfx::Size size, - GLenum format); + ResourceFormat format); ~Resource(); private: @@ -36,8 +36,8 @@ class CC_EXPORT ResourcePool { virtual ~ResourcePool(); - scoped_ptr<ResourcePool::Resource> AcquireResource(gfx::Size size, - GLenum format); + scoped_ptr<ResourcePool::Resource> AcquireResource( + gfx::Size size, ResourceFormat format); void ReleaseResource(scoped_ptr<ResourcePool::Resource>); void SetResourceUsageLimits(size_t max_memory_usage_bytes, @@ -46,6 +46,9 @@ class CC_EXPORT ResourcePool { void ReduceResourceUsage(); + size_t total_memory_usage_bytes() const { + return memory_usage_bytes_; + } size_t acquired_memory_usage_bytes() const { return memory_usage_bytes_ - unused_memory_usage_bytes_; } diff --git a/chromium/cc/resources/resource_provider.cc b/chromium/cc/resources/resource_provider.cc index 11eaab07a28..ee761d17564 100644 --- a/chromium/cc/resources/resource_provider.cc +++ b/chromium/cc/resources/resource_provider.cc @@ -8,12 +8,13 @@ #include <limits> #include "base/containers/hash_tables.h" -#include "base/debug/alias.h" #include "base/stl_util.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "cc/base/util.h" #include "cc/output/gl_renderer.h" // For the GLC() macro. #include "cc/resources/platform_color.h" +#include "cc/resources/returned_resource.h" #include "cc/resources/transferable_resource.h" #include "cc/scheduler/texture_uploader.h" #include "gpu/GLES2/gl2extchromium.h" @@ -29,15 +30,21 @@ namespace cc { namespace { -GLenum TextureToStorageFormat(GLenum texture_format) { +// Measured in seconds. +const double kSoftwareUploadTickRate = 0.000250; +const double kTextureUploadTickRate = 0.004; + +GLenum TextureToStorageFormat(ResourceFormat format) { GLenum storage_format = GL_RGBA8_OES; - switch (texture_format) { - case GL_RGBA: + switch (format) { + case RGBA_8888: break; - case GL_BGRA_EXT: + case BGRA_8888: storage_format = GL_BGRA8_EXT; break; - default: + case RGBA_4444: + case LUMINANCE_8: + case RGB_565: NOTREACHED(); break; } @@ -45,37 +52,54 @@ GLenum TextureToStorageFormat(GLenum texture_format) { return storage_format; } -bool IsTextureFormatSupportedForStorage(GLenum format) { - return (format == GL_RGBA || format == GL_BGRA_EXT); +bool IsFormatSupportedForStorage(ResourceFormat format) { + switch (format) { + case RGBA_8888: + case BGRA_8888: + return true; + case RGBA_4444: + case LUMINANCE_8: + case RGB_565: + return false; + } + return false; } -unsigned CreateTextureId(WebGraphicsContext3D* context3d) { - unsigned texture_id = 0; - GLC(context3d, texture_id = context3d->createTexture()); - GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, texture_id)); - GLC(context3d, context3d->texParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GLC(context3d, context3d->texParameteri( - GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GLC(context3d, context3d->texParameteri( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLC(context3d, context3d->texParameteri( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - return texture_id; -} +class ScopedSetActiveTexture { + public: + ScopedSetActiveTexture(WebGraphicsContext3D* context3d, GLenum unit) + : context3d_(context3d), unit_(unit) { + DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(context3d_)); + + if (unit_ != GL_TEXTURE0) + GLC(context3d_, context3d_->activeTexture(unit_)); + } + + ~ScopedSetActiveTexture() { + // Active unit being GL_TEXTURE0 is effectively the ground state. + if (unit_ != GL_TEXTURE0) + GLC(context3d_, context3d_->activeTexture(GL_TEXTURE0)); + } + + private: + WebGraphicsContext3D* context3d_; + GLenum unit_; +}; } // namespace ResourceProvider::Resource::Resource() - : gl_id(0), + : child_id(0), + gl_id(0), gl_pixel_buffer_id(0), gl_upload_query_id(0), pixels(NULL), pixel_buffer(NULL), lock_for_read_count(0), + imported_count(0), + exported_count(0), locked_for_write(false), external(false), - exported(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), @@ -83,31 +107,37 @@ ResourceProvider::Resource::Resource() enable_read_lock_fences(false), read_lock_fence(NULL), size(), - format(0), + original_filter(0), filter(0), + target(0), image_id(0), texture_pool(0), + wrap_mode(0), + lost(false), hint(TextureUsageAny), - type(static_cast<ResourceType>(0)) {} + type(static_cast<ResourceType>(0)), + format(RGBA_8888) {} ResourceProvider::Resource::~Resource() {} -ResourceProvider::Resource::Resource( - unsigned texture_id, - gfx::Size size, - GLenum format, - GLenum filter, - GLenum texture_pool, - TextureUsageHint hint) - : gl_id(texture_id), +ResourceProvider::Resource::Resource(unsigned texture_id, + gfx::Size size, + GLenum filter, + GLenum texture_pool, + GLint wrap_mode, + TextureUsageHint hint, + ResourceFormat format) + : child_id(0), + gl_id(texture_id), gl_pixel_buffer_id(0), gl_upload_query_id(0), pixels(NULL), pixel_buffer(NULL), lock_for_read_count(0), + imported_count(0), + exported_count(0), locked_for_write(false), external(false), - exported(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), @@ -115,24 +145,34 @@ ResourceProvider::Resource::Resource( enable_read_lock_fences(false), read_lock_fence(NULL), size(size), - format(format), + original_filter(filter), filter(filter), + target(0), image_id(0), texture_pool(texture_pool), + wrap_mode(wrap_mode), + lost(false), hint(hint), - type(GLTexture) {} + type(GLTexture), + format(format) { + DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT); +} -ResourceProvider::Resource::Resource( - uint8_t* pixels, gfx::Size size, GLenum format, GLenum filter) - : gl_id(0), +ResourceProvider::Resource::Resource(uint8_t* pixels, + gfx::Size size, + GLenum filter, + GLint wrap_mode) + : child_id(0), + gl_id(0), gl_pixel_buffer_id(0), gl_upload_query_id(0), pixels(pixels), pixel_buffer(NULL), lock_for_read_count(0), + imported_count(0), + exported_count(0), locked_for_write(false), external(false), - exported(false), marked_for_deletion(false), pending_set_pixels(false), set_pixels_completion_forced(false), @@ -140,12 +180,18 @@ ResourceProvider::Resource::Resource( enable_read_lock_fences(false), read_lock_fence(NULL), size(size), - format(format), + original_filter(filter), filter(filter), + target(0), image_id(0), texture_pool(0), + wrap_mode(wrap_mode), + lost(false), hint(TextureUsageAny), - type(Bitmap) {} + type(Bitmap), + format(RGBA_8888) { + DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT); +} ResourceProvider::Child::Child() {} @@ -153,12 +199,15 @@ ResourceProvider::Child::~Child() {} scoped_ptr<ResourceProvider> ResourceProvider::Create( OutputSurface* output_surface, - int highp_threshold_min) { + int highp_threshold_min, + bool use_rgba_4444_texture_format) { scoped_ptr<ResourceProvider> resource_provider( - new ResourceProvider(output_surface, highp_threshold_min)); + new ResourceProvider(output_surface, + highp_threshold_min, + use_rgba_4444_texture_format)); bool success = false; - if (output_surface->context3d()) { + if (resource_provider->Context3d()) { success = resource_provider->InitializeGL(); } else { resource_provider->InitializeSoftware(); @@ -173,34 +222,40 @@ scoped_ptr<ResourceProvider> ResourceProvider::Create( } ResourceProvider::~ResourceProvider() { + while (!children_.empty()) + DestroyChild(children_.begin()->first); while (!resources_.empty()) DeleteResourceInternal(resources_.begin(), ForShutdown); CleanUpGLIfNeeded(); } -WebGraphicsContext3D* ResourceProvider::GraphicsContext3D() { - DCHECK(thread_checker_.CalledOnValidThread()); - return output_surface_->context3d(); +bool ResourceProvider::InUseByConsumer(ResourceId id) { + Resource* resource = GetResource(id); + return resource->lock_for_read_count > 0 || resource->exported_count > 0 || + resource->lost; } -bool ResourceProvider::InUseByConsumer(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - return !!resource->lock_for_read_count || resource->exported; +bool ResourceProvider::IsLost(ResourceId id) { + Resource* resource = GetResource(id); + return resource->lost; } ResourceProvider::ResourceId ResourceProvider::CreateResource( - gfx::Size size, GLenum format, TextureUsageHint hint) { + gfx::Size size, + GLint wrap_mode, + TextureUsageHint hint, + ResourceFormat format) { DCHECK(!size.IsEmpty()); switch (default_resource_type_) { case GLTexture: - return CreateGLTexture( - size, format, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, hint); + return CreateGLTexture(size, + GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, + wrap_mode, + hint, + format); case Bitmap: - DCHECK(format == GL_RGBA); + DCHECK_EQ(RGBA_8888, format); return CreateBitmap(size); case InvalidType: break; @@ -211,14 +266,20 @@ ResourceProvider::ResourceId ResourceProvider::CreateResource( } ResourceProvider::ResourceId ResourceProvider::CreateManagedResource( - gfx::Size size, GLenum format, TextureUsageHint hint) { + gfx::Size size, + GLint wrap_mode, + TextureUsageHint hint, + ResourceFormat format) { DCHECK(!size.IsEmpty()); switch (default_resource_type_) { case GLTexture: - return CreateGLTexture( - size, format, GL_TEXTURE_POOL_MANAGED_CHROMIUM, hint); + return CreateGLTexture(size, + GL_TEXTURE_POOL_MANAGED_CHROMIUM, + wrap_mode, + hint, + format); case Bitmap: - DCHECK(format == GL_RGBA); + DCHECK_EQ(RGBA_8888, format); return CreateBitmap(size); case InvalidType: break; @@ -229,13 +290,17 @@ ResourceProvider::ResourceId ResourceProvider::CreateManagedResource( } ResourceProvider::ResourceId ResourceProvider::CreateGLTexture( - gfx::Size size, GLenum format, GLenum texture_pool, TextureUsageHint hint) { + gfx::Size size, + GLenum texture_pool, + GLint wrap_mode, + TextureUsageHint hint, + ResourceFormat format) { DCHECK_LE(size.width(), max_texture_size_); DCHECK_LE(size.height(), max_texture_size_); DCHECK(thread_checker_.CalledOnValidThread()); ResourceId id = next_id_++; - Resource resource(0, size, format, GL_LINEAR, texture_pool, hint); + Resource resource(0, size, GL_LINEAR, texture_pool, wrap_mode, hint, format); resource.allocated = false; resources_[id] = resource; return id; @@ -247,7 +312,7 @@ ResourceProvider::ResourceId ResourceProvider::CreateBitmap(gfx::Size size) { uint8_t* pixels = new uint8_t[4 * size.GetArea()]; ResourceId id = next_id_++; - Resource resource(pixels, size, GL_RGBA, GL_LINEAR); + Resource resource(pixels, size, GL_LINEAR, GL_CLAMP_TO_EDGE); resource.allocated = true; resources_[id] = resource; return id; @@ -259,7 +324,7 @@ ResourceProvider::CreateResourceFromExternalTexture( unsigned texture_id) { DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->bindTexture(texture_target, texture_id)); GLC(context3d, context3d->texParameteri( @@ -272,7 +337,13 @@ ResourceProvider::CreateResourceFromExternalTexture( texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); ResourceId id = next_id_++; - Resource resource(texture_id, gfx::Size(), 0, GL_LINEAR, 0, TextureUsageAny); + Resource resource(texture_id, + gfx::Size(), + GL_LINEAR, + 0, + GL_CLAMP_TO_EDGE, + TextureUsageAny, + RGBA_8888); resource.external = true; resource.allocated = true; resources_[id] = resource; @@ -280,25 +351,35 @@ ResourceProvider::CreateResourceFromExternalTexture( } ResourceProvider::ResourceId ResourceProvider::CreateResourceFromTextureMailbox( - const TextureMailbox& mailbox) { + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback) { DCHECK(thread_checker_.CalledOnValidThread()); // Just store the information. Mailbox will be consumed in LockForRead(). ResourceId id = next_id_++; DCHECK(mailbox.IsValid()); Resource& resource = resources_[id]; if (mailbox.IsTexture()) { - resource = Resource(0, gfx::Size(), 0, GL_LINEAR, 0, TextureUsageAny); + resource = Resource(0, + gfx::Size(), + GL_LINEAR, + 0, + GL_CLAMP_TO_EDGE, + TextureUsageAny, + RGBA_8888); } else { DCHECK(mailbox.IsSharedMemory()); base::SharedMemory* shared_memory = mailbox.shared_memory(); DCHECK(shared_memory->memory()); uint8_t* pixels = reinterpret_cast<uint8_t*>(shared_memory->memory()); - resource = Resource(pixels, mailbox.shared_memory_size(), - GL_RGBA, GL_LINEAR); + resource = Resource( + pixels, mailbox.shared_memory_size(), GL_LINEAR, GL_CLAMP_TO_EDGE); } resource.external = true; resource.allocated = true; resource.mailbox = mailbox; + resource.release_callback = + base::Bind(&SingleReleaseCallback::Run, + base::Owned(release_callback.release())); return id; } @@ -309,9 +390,10 @@ void ResourceProvider::DeleteResource(ResourceId id) { Resource* resource = &it->second; DCHECK(!resource->lock_for_read_count); DCHECK(!resource->marked_for_deletion); + DCHECK_EQ(resource->imported_count, 0); DCHECK(resource->pending_set_pixels || !resource->locked_for_write); - if (resource->exported) { + if (resource->exported_count > 0) { resource->marked_for_deletion = true; return; } else { @@ -322,37 +404,37 @@ void ResourceProvider::DeleteResource(ResourceId id) { void ResourceProvider::DeleteResourceInternal(ResourceMap::iterator it, DeleteStyle style) { Resource* resource = &it->second; - bool lost_resource = lost_output_surface_; + bool lost_resource = lost_output_surface_ || resource->lost; - DCHECK(!resource->exported || style != Normal); - if (style == ForShutdown && resource->exported) + DCHECK(resource->exported_count == 0 || style != Normal); + if (style == ForShutdown && resource->exported_count > 0) lost_resource = true; if (resource->image_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->destroyImageCHROMIUM(resource->image_id)); } if (resource->gl_id && !resource->external) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->deleteTexture(resource->gl_id)); } if (resource->gl_upload_query_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->deleteQueryEXT(resource->gl_upload_query_id)); } if (resource->gl_pixel_buffer_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); GLC(context3d, context3d->deleteBuffer(resource->gl_pixel_buffer_id)); } if (resource->mailbox.IsValid() && resource->external) { unsigned sync_point = resource->mailbox.sync_point(); if (resource->mailbox.IsTexture()) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); if (resource->gl_id) GLC(context3d, context3d->deleteTexture(resource->gl_id)); @@ -366,7 +448,7 @@ void ResourceProvider::DeleteResourceInternal(ResourceMap::iterator it, resource->pixels = NULL; } } - resource->mailbox.RunReleaseCallback(sync_point, lost_resource); + resource->release_callback.Run(sync_point, lost_resource); } if (resource->pixels) delete[] resource->pixels; @@ -378,10 +460,7 @@ void ResourceProvider::DeleteResourceInternal(ResourceMap::iterator it, ResourceProvider::ResourceType ResourceProvider::GetResourceType( ResourceId id) { - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - return resource->type; + return GetResource(id)->type; } void ResourceProvider::SetPixels(ResourceId id, @@ -389,20 +468,17 @@ void ResourceProvider::SetPixels(ResourceId id, gfx::Rect image_rect, gfx::Rect source_rect, gfx::Vector2d dest_offset) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(!resource->locked_for_write); DCHECK(!resource->lock_for_read_count); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); DCHECK(ReadLockFenceHasPassed(resource)); LazyAllocate(resource); if (resource->gl_id) { DCHECK(!resource->pending_set_pixels); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(texture_uploader_.get()); context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id); @@ -416,7 +492,7 @@ void ResourceProvider::SetPixels(ResourceId id, if (resource->pixels) { DCHECK(resource->allocated); - DCHECK(resource->format == GL_RGBA); + DCHECK_EQ(RGBA_8888, resource->format); SkBitmap src_full; src_full.setConfig( SkBitmap::kARGB_8888_Config, image_rect.width(), image_rect.height()); @@ -470,23 +546,32 @@ void ResourceProvider::ReleaseCachedData() { texture_uploader_->ReleaseCachedQueries(); } +base::TimeDelta ResourceProvider::TextureUpdateTickRate() { + // Software resource uploads happen on impl thread, so don't bother batching + // them up and trying to wait for them to complete. + double rate = + texture_uploader_ ? kTextureUploadTickRate : kSoftwareUploadTickRate; + return base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond * + rate); +} + void ResourceProvider::Flush() { DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); if (context3d) context3d->flush(); } void ResourceProvider::Finish() { DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); if (context3d) context3d->finish(); } bool ResourceProvider::ShallowFlushIfSupported() { DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); if (!context3d || !use_shallow_flush_) return false; @@ -494,16 +579,20 @@ bool ResourceProvider::ShallowFlushIfSupported() { return true; } -const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) { +ResourceProvider::Resource* ResourceProvider::GetResource(ResourceId id) { DCHECK(thread_checker_.CalledOnValidThread()); ResourceMap::iterator it = resources_.find(id); CHECK(it != resources_.end()); - Resource* resource = &it->second; + return &it->second; +} + +const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) { + Resource* resource = GetResource(id); DCHECK(!resource->locked_for_write || resource->set_pixels_completion_forced) << "locked for write: " << resource->locked_for_write << " pixels completion forced: " << resource->set_pixels_completion_forced; - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); // Uninitialized! Call SetPixels or LockForWrite first. DCHECK(resource->allocated); @@ -511,7 +600,7 @@ const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) { if (resource->external) { if (!resource->gl_id && resource->mailbox.IsTexture()) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); if (resource->mailbox.sync_point()) { GLC(context3d, @@ -534,25 +623,20 @@ const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) { } void ResourceProvider::UnlockForRead(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK_GT(resource->lock_for_read_count, 0); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); resource->lock_for_read_count--; } const ResourceProvider::Resource* ResourceProvider::LockForWrite( ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(!resource->locked_for_write); DCHECK(!resource->lock_for_read_count); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->external); + DCHECK(!resource->lost); DCHECK(ReadLockFenceHasPassed(resource)); LazyAllocate(resource); @@ -561,24 +645,16 @@ const ResourceProvider::Resource* ResourceProvider::LockForWrite( } bool ResourceProvider::CanLockForWrite(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - return !resource->locked_for_write && - !resource->lock_for_read_count && - !resource->exported && - !resource->external && - ReadLockFenceHasPassed(resource); + Resource* resource = GetResource(id); + return !resource->locked_for_write && !resource->lock_for_read_count && + !resource->exported_count && !resource->external && !resource->lost && + ReadLockFenceHasPassed(resource); } void ResourceProvider::UnlockForWrite(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(resource->locked_for_write); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->external); resource->locked_for_write = false; } @@ -639,7 +715,7 @@ ResourceProvider::ScopedWriteLockGL::~ScopedWriteLockGL() { void ResourceProvider::PopulateSkBitmapWithResource( SkBitmap* sk_bitmap, const Resource* resource) { DCHECK(resource->pixels); - DCHECK(resource->format == GL_RGBA); + DCHECK_EQ(RGBA_8888, resource->format); sk_bitmap->setConfig(SkBitmap::kARGB_8888_Config, resource->size.width(), resource->size.height()); @@ -674,7 +750,8 @@ ResourceProvider::ScopedWriteLockSoftware::~ScopedWriteLockSoftware() { } ResourceProvider::ResourceProvider(OutputSurface* output_surface, - int highp_threshold_min) + int highp_threshold_min, + bool use_rgba_4444_texture_format) : output_surface_(output_surface), lost_output_surface_(false), highp_threshold_min_(highp_threshold_min), @@ -685,7 +762,10 @@ ResourceProvider::ResourceProvider(OutputSurface* output_surface, use_texture_usage_hint_(false), use_shallow_flush_(false), max_texture_size_(0), - best_texture_format_(0) {} + best_texture_format_(RGBA_8888), + use_rgba_4444_texture_format_(use_rgba_4444_texture_format) { + DCHECK(output_surface_->HasClient()); +} void ResourceProvider::InitializeSoftware() { DCHECK(thread_checker_.CalledOnValidThread()); @@ -695,7 +775,7 @@ void ResourceProvider::InitializeSoftware() { default_resource_type_ = Bitmap; max_texture_size_ = INT_MAX / 2; - best_texture_format_ = GL_RGBA; + best_texture_format_ = RGBA_8888; } bool ResourceProvider::InitializeGL() { @@ -703,7 +783,7 @@ bool ResourceProvider::InitializeGL() { DCHECK(!texture_uploader_); DCHECK_NE(GLTexture, default_resource_type_); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); if (!context3d->makeContextCurrent()) @@ -711,24 +791,14 @@ bool ResourceProvider::InitializeGL() { default_resource_type_ = GLTexture; - std::string extensions_string = - UTF16ToASCII(context3d->getString(GL_EXTENSIONS)); - std::vector<std::string> extensions; - base::SplitString(extensions_string, ' ', &extensions); - bool use_map_sub = false; - bool use_bgra = false; - for (size_t i = 0; i < extensions.size(); ++i) { - if (extensions[i] == "GL_EXT_texture_storage") - use_texture_storage_ext_ = true; - else if (extensions[i] == "GL_ANGLE_texture_usage") - use_texture_usage_hint_ = true; - else if (extensions[i] == "GL_CHROMIUM_map_sub") - use_map_sub = true; - else if (extensions[i] == "GL_CHROMIUM_shallow_flush") - use_shallow_flush_ = true; - else if (extensions[i] == "GL_EXT_texture_format_BGRA8888") - use_bgra = true; - } + const ContextProvider::Capabilities& caps = + output_surface_->context_provider()->ContextCapabilities(); + + bool use_map_sub = caps.map_sub; + bool use_bgra = caps.texture_format_bgra8888; + use_texture_storage_ext_ = caps.texture_storage; + use_shallow_flush_ = caps.shallow_flush; + use_texture_usage_hint_ = caps.texture_usage; texture_uploader_ = TextureUploader::Create(context3d, use_map_sub, use_shallow_flush_); @@ -740,7 +810,7 @@ bool ResourceProvider::InitializeGL() { } void ResourceProvider::CleanUpGLIfNeeded() { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); if (default_resource_type_ != GLTexture) { // We are not in GL mode, but double check before returning. DCHECK(!context3d); @@ -754,9 +824,12 @@ void ResourceProvider::CleanUpGLIfNeeded() { Finish(); } -int ResourceProvider::CreateChild() { +int ResourceProvider::CreateChild(const ReturnCallback& return_callback) { DCHECK(thread_checker_.CalledOnValidThread()); + Child child_info; + child_info.return_callback = return_callback; + int child = next_child_++; children_[child] = child_info; return child; @@ -764,13 +837,26 @@ int ResourceProvider::CreateChild() { void ResourceProvider::DestroyChild(int child_id) { DCHECK(thread_checker_.CalledOnValidThread()); + ChildMap::iterator it = children_.find(child_id); DCHECK(it != children_.end()); Child& child = it->second; + + ResourceIdArray resources_for_child; + for (ResourceIdMap::iterator child_it = child.child_to_parent_map.begin(); child_it != child.child_to_parent_map.end(); - ++child_it) - DeleteResource(child_it->second); + ++child_it) { + ResourceId id = child_it->second; + resources_for_child.push_back(id); + } + + // If the child is going away, don't consider any resources in use. + child.in_use_resources.clear(); + + DeleteAndReturnUnusedResourcesToChild( + &child, ForShutdown, resources_for_child); + children_.erase(it); } @@ -785,60 +871,21 @@ const ResourceProvider::ResourceIdMap& ResourceProvider::GetChildToParentMap( void ResourceProvider::PrepareSendToParent(const ResourceIdArray& resources, TransferableResourceArray* list) { DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); - if (!context3d || !context3d->makeContextCurrent()) { - // TODO(skaslev): Implement this path for software compositing. - return; - } - bool need_sync_point = false; - for (ResourceIdArray::const_iterator it = resources.begin(); - it != resources.end(); - ++it) { - TransferableResource resource; - if (TransferResource(context3d, *it, &resource)) { - if (!resource.sync_point) - need_sync_point = true; - resources_.find(*it)->second.exported = true; - list->push_back(resource); - } - } - if (need_sync_point) { - unsigned int sync_point = context3d->insertSyncPoint(); - for (TransferableResourceArray::iterator it = list->begin(); - it != list->end(); - ++it) { - if (!it->sync_point) - it->sync_point = sync_point; - } - } -} - -void ResourceProvider::PrepareSendToChild(int child, - const ResourceIdArray& resources, - TransferableResourceArray* list) { - DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); if (!context3d || !context3d->makeContextCurrent()) { // TODO(skaslev): Implement this path for software compositing. return; } - Child& child_info = children_.find(child)->second; bool need_sync_point = false; for (ResourceIdArray::const_iterator it = resources.begin(); it != resources.end(); ++it) { TransferableResource resource; - if (!TransferResource(context3d, *it, &resource)) - NOTREACHED(); + TransferResource(context3d, *it, &resource); if (!resource.sync_point) need_sync_point = true; - DCHECK(child_info.parent_to_child_map.find(*it) != - child_info.parent_to_child_map.end()); - resource.id = child_info.parent_to_child_map[*it]; - child_info.parent_to_child_map.erase(*it); - child_info.child_to_parent_map.erase(resource.id); + ++resources_.find(*it)->second.exported_count; list->push_back(resource); - DeleteResource(*it); } if (need_sync_point) { unsigned int sync_point = context3d->insertSyncPoint(); @@ -854,7 +901,7 @@ void ResourceProvider::PrepareSendToChild(int child, void ResourceProvider::ReceiveFromChild( int child, const TransferableResourceArray& resources) { DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); if (!context3d || !context3d->makeContextCurrent()) { // TODO(skaslev): Implement this path for software compositing. return; @@ -863,6 +910,12 @@ void ResourceProvider::ReceiveFromChild( for (TransferableResourceArray::const_iterator it = resources.begin(); it != resources.end(); ++it) { + ResourceIdMap::iterator resource_in_map_it = + child_info.child_to_parent_map.find(it->id); + if (resource_in_map_it != child_info.child_to_parent_map.end()) { + resources_[resource_in_map_it->second].imported_count++; + continue; + } unsigned texture_id; // NOTE: If the parent is a browser and the child a renderer, the parent // is not supposed to have its context wait, because that could induce @@ -876,62 +929,162 @@ void ResourceProvider::ReceiveFromChild( GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, texture_id)); GLC(context3d, context3d->consumeTextureCHROMIUM(GL_TEXTURE_2D, it->mailbox.name)); - ResourceId id = next_id_++; - Resource resource( - texture_id, it->size, it->format, it->filter, 0, TextureUsageAny); + ResourceId local_id = next_id_++; + Resource resource(texture_id, + it->size, + it->filter, + 0, + GL_CLAMP_TO_EDGE, + TextureUsageAny, + it->format); resource.mailbox.SetName(it->mailbox); + resource.child_id = child; // Don't allocate a texture for a child. resource.allocated = true; - resources_[id] = resource; - child_info.parent_to_child_map[id] = it->id; - child_info.child_to_parent_map[it->id] = id; + resource.imported_count = 1; + resources_[local_id] = resource; + child_info.parent_to_child_map[local_id] = it->id; + child_info.child_to_parent_map[it->id] = local_id; } } -void ResourceProvider::ReceiveFromParent( - const TransferableResourceArray& resources) { +void ResourceProvider::DeclareUsedResourcesFromChild( + int child, + const ResourceIdArray& resources_from_child) { DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + + Child& child_info = children_.find(child)->second; + child_info.in_use_resources.clear(); + + for (size_t i = 0; i < resources_from_child.size(); ++i) { + ResourceIdMap::iterator it = + child_info.child_to_parent_map.find(resources_from_child[i]); + DCHECK(it != child_info.child_to_parent_map.end()); + + ResourceId local_id = it->second; + child_info.in_use_resources.insert(local_id); + } + + ResourceIdArray unused; + for (ResourceIdMap::iterator it = child_info.child_to_parent_map.begin(); + it != child_info.child_to_parent_map.end(); + ++it) { + ResourceId local_id = it->second; + bool resource_is_in_use = child_info.in_use_resources.count(local_id) > 0; + if (!resource_is_in_use) + unused.push_back(local_id); + } + DeleteAndReturnUnusedResourcesToChild(&child_info, Normal, unused); +} + +// static +bool ResourceProvider::CompareResourceMapIteratorsByChildId( + const std::pair<ReturnedResource, ResourceMap::iterator>& a, + const std::pair<ReturnedResource, ResourceMap::iterator>& b) { + const ResourceMap::iterator& a_it = a.second; + const ResourceMap::iterator& b_it = b.second; + const Resource& a_resource = a_it->second; + const Resource& b_resource = b_it->second; + return a_resource.child_id < b_resource.child_id; +} + +void ResourceProvider::ReceiveReturnsFromParent( + const ReturnedResourceArray& resources) { + DCHECK(thread_checker_.CalledOnValidThread()); + WebGraphicsContext3D* context3d = Context3d(); if (!context3d || !context3d->makeContextCurrent()) { // TODO(skaslev): Implement this path for software compositing. return; } - for (TransferableResourceArray::const_iterator it = resources.begin(); + + int child_id = 0; + Child* child_info = NULL; + ResourceIdArray resources_for_child; + + std::vector<std::pair<ReturnedResource, ResourceMap::iterator> > + sorted_resources; + + for (ReturnedResourceArray::const_iterator it = resources.begin(); it != resources.end(); ++it) { - ResourceMap::iterator map_iterator = resources_.find(it->id); - DCHECK(map_iterator != resources_.end()); + ResourceId local_id = it->id; + ResourceMap::iterator map_iterator = resources_.find(local_id); + + // Resource was already lost (e.g. it belonged to a child that was + // destroyed). + if (map_iterator == resources_.end()) + continue; + + sorted_resources.push_back( + std::pair<ReturnedResource, ResourceMap::iterator>(*it, map_iterator)); + } + + std::sort(sorted_resources.begin(), + sorted_resources.end(), + CompareResourceMapIteratorsByChildId); + + for (size_t i = 0; i < sorted_resources.size(); ++i) { + ReturnedResource& returned = sorted_resources[i].first; + ResourceMap::iterator& map_iterator = sorted_resources[i].second; + ResourceId local_id = map_iterator->first; Resource* resource = &map_iterator->second; - DCHECK(resource->exported); - resource->exported = false; - resource->filter = it->filter; - DCHECK(resource->mailbox.ContainsMailbox(it->mailbox)); + + CHECK_GE(resource->exported_count, returned.count); + resource->exported_count -= returned.count; + resource->lost |= returned.lost; + if (resource->exported_count) + continue; + if (resource->gl_id) { - if (it->sync_point) - GLC(context3d, context3d->waitSyncPoint(it->sync_point)); + if (returned.sync_point) + GLC(context3d, context3d->waitSyncPoint(returned.sync_point)); } else { - resource->mailbox = TextureMailbox(resource->mailbox.name(), - resource->mailbox.callback(), - it->sync_point); + resource->mailbox = + TextureMailbox(resource->mailbox.name(), returned.sync_point); } - if (resource->marked_for_deletion) + + if (!resource->marked_for_deletion) + continue; + + if (!resource->child_id) { + // The resource belongs to this ResourceProvider, so it can be destroyed. DeleteResourceInternal(map_iterator, Normal); + continue; + } + + // Delete the resource and return it to the child it came from one. + if (resource->child_id != child_id) { + ChildMap::iterator child_it = children_.find(resource->child_id); + DCHECK(child_it != children_.end()); + + if (child_id) { + DCHECK_NE(resources_for_child.size(), 0u); + DeleteAndReturnUnusedResourcesToChild( + child_info, Normal, resources_for_child); + resources_for_child.clear(); + } + + child_info = &child_it->second; + child_id = resource->child_id; + } + resources_for_child.push_back(local_id); + } + + if (child_id) { + DCHECK_NE(resources_for_child.size(), 0u); + DeleteAndReturnUnusedResourcesToChild( + child_info, Normal, resources_for_child); } } -bool ResourceProvider::TransferResource(WebGraphicsContext3D* context, +void ResourceProvider::TransferResource(WebGraphicsContext3D* context, ResourceId id, TransferableResource* resource) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* source = &it->second; + Resource* source = GetResource(id); DCHECK(!source->locked_for_write); DCHECK(!source->lock_for_read_count); DCHECK(!source->external || (source->external && source->mailbox.IsValid())); DCHECK(source->allocated); - if (source->exported) - return false; resource->id = id; resource->format = source->format; resource->filter = source->filter; @@ -956,30 +1109,114 @@ bool ResourceProvider::TransferResource(WebGraphicsContext3D* context, resource->sync_point = source->mailbox.sync_point(); source->mailbox.ResetSyncPoint(); } +} - return true; +void ResourceProvider::DeleteAndReturnUnusedResourcesToChild( + Child* child_info, + DeleteStyle style, + const ResourceIdArray& unused) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(child_info); + + if (unused.empty()) + return; + + WebGraphicsContext3D* context3d = Context3d(); + if (!context3d || !context3d->makeContextCurrent()) { + // TODO(skaslev): Implement this path for software compositing. + return; + } + + ReturnedResourceArray to_return; + + bool need_sync_point = false; + for (size_t i = 0; i < unused.size(); ++i) { + ResourceId local_id = unused[i]; + + ResourceMap::iterator it = resources_.find(local_id); + CHECK(it != resources_.end()); + Resource& resource = it->second; + + DCHECK(!resource.locked_for_write); + DCHECK(!resource.lock_for_read_count); + DCHECK_EQ(0u, child_info->in_use_resources.count(local_id)); + DCHECK(child_info->parent_to_child_map.count(local_id)); + + ResourceId child_id = child_info->parent_to_child_map[local_id]; + DCHECK(child_info->child_to_parent_map.count(child_id)); + + bool is_lost = resource.lost || lost_output_surface_; + if (resource.exported_count > 0) { + if (style != ForShutdown) { + // Defer this until we receive the resource back from the parent. + resource.marked_for_deletion = true; + continue; + } + + // We still have an exported_count, so we'll have to lose it. + is_lost = true; + } + + if (resource.filter != resource.original_filter) { + DCHECK(resource.target); + DCHECK(resource.gl_id); + + GLC(context3d, context3d->bindTexture(resource.target, resource.gl_id)); + GLC(context3d, + context3d->texParameteri(resource.target, + GL_TEXTURE_MIN_FILTER, + resource.original_filter)); + GLC(context3d, + context3d->texParameteri(resource.target, + GL_TEXTURE_MAG_FILTER, + resource.original_filter)); + } + + ReturnedResource returned; + returned.id = child_id; + returned.sync_point = resource.mailbox.sync_point(); + if (!returned.sync_point) + need_sync_point = true; + returned.count = resource.imported_count; + returned.lost = is_lost; + to_return.push_back(returned); + + child_info->parent_to_child_map.erase(local_id); + child_info->child_to_parent_map.erase(child_id); + resource.imported_count = 0; + DeleteResourceInternal(it, style); + } + if (need_sync_point) { + unsigned int sync_point = context3d->insertSyncPoint(); + for (size_t i = 0; i < to_return.size(); ++i) { + if (!to_return[i].sync_point) + to_return[i].sync_point = sync_point; + } + } + + if (!to_return.empty()) + child_info->return_callback.Run(to_return); } void ResourceProvider::AcquirePixelBuffer(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); if (resource->type == GLTexture) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); if (!resource->gl_pixel_buffer_id) resource->gl_pixel_buffer_id = context3d->createBuffer(); context3d->bindBuffer( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, resource->gl_pixel_buffer_id); + unsigned bytes_per_pixel = BytesPerPixel(resource->format); context3d->bufferData( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, - 4 * resource->size.GetArea(), + resource->size.height() * RoundUp(bytes_per_pixel + * resource->size.width(), 4u), NULL, GL_DYNAMIC_DRAW); context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); @@ -994,12 +1231,9 @@ void ResourceProvider::AcquirePixelBuffer(ResourceId id) { } void ResourceProvider::ReleasePixelBuffer(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); // The pixel buffer can be released while there is a pending "set pixels" @@ -1017,7 +1251,7 @@ void ResourceProvider::ReleasePixelBuffer(ResourceId id) { if (resource->type == GLTexture) { if (!resource->gl_pixel_buffer_id) return; - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); context3d->bindBuffer( GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, @@ -1039,16 +1273,13 @@ void ResourceProvider::ReleasePixelBuffer(ResourceId id) { } uint8_t* ResourceProvider::MapPixelBuffer(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); if (resource->type == GLTexture) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(resource->gl_pixel_buffer_id); context3d->bindBuffer( @@ -1070,16 +1301,13 @@ uint8_t* ResourceProvider::MapPixelBuffer(ResourceId id) { } void ResourceProvider::UnmapPixelBuffer(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); DCHECK(!resource->image_id); if (resource->type == GLTexture) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(resource->gl_pixel_buffer_id); context3d->bindBuffer( @@ -1095,16 +1323,14 @@ void ResourceProvider::BindForSampling(ResourceProvider::ResourceId resource_id, GLenum unit, GLenum filter) { DCHECK(thread_checker_.CalledOnValidThread()); - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); ResourceMap::iterator it = resources_.find(resource_id); DCHECK(it != resources_.end()); Resource* resource = &it->second; DCHECK(resource->lock_for_read_count); DCHECK(!resource->locked_for_write || resource->set_pixels_completion_forced); - DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(context3d)); - if (unit != GL_TEXTURE0) - GLC(context3d, context3d->activeTexture(unit)); + ScopedSetActiveTexture scoped_active_tex(context3d, unit); GLC(context3d, context3d->bindTexture(target, resource->gl_id)); if (filter != resource->filter) { GLC(context3d, context3d->texParameteri(target, @@ -1114,14 +1340,14 @@ void ResourceProvider::BindForSampling(ResourceProvider::ResourceId resource_id, GL_TEXTURE_MAG_FILTER, filter)); resource->filter = filter; + if (resource->target == 0) + resource->target = target; + else + DCHECK_EQ(resource->target, target); } if (resource->image_id) context3d->bindTexImage2DCHROMIUM(target, resource->image_id); - - // Active unit being GL_TEXTURE0 is effectively the ground state. - if (unit != GL_TEXTURE0) - GLC(context3d, context3d->activeTexture(GL_TEXTURE0)); } void ResourceProvider::UnbindForSampling( @@ -1134,21 +1360,13 @@ void ResourceProvider::UnbindForSampling( if (!resource->image_id) return; - WebGraphicsContext3D* context3d = output_surface_->context3d(); - DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(context3d)); - if (unit != GL_TEXTURE0) - GLC(context3d, context3d->activeTexture(unit)); + WebGraphicsContext3D* context3d = Context3d(); + ScopedSetActiveTexture scoped_active_tex(context3d, unit); context3d->releaseTexImage2DCHROMIUM(target, resource->image_id); - // Active unit being GL_TEXTURE0 is effectively the ground state. - if (unit != GL_TEXTURE0) - GLC(context3d, context3d->activeTexture(GL_TEXTURE0)); } void ResourceProvider::BeginSetPixels(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(!resource->pending_set_pixels); LazyCreate(resource); @@ -1161,7 +1379,7 @@ void ResourceProvider::BeginSetPixels(ResourceId id) { LockForWrite(id); if (resource->gl_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(resource->gl_pixel_buffer_id); context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id); @@ -1171,37 +1389,39 @@ void ResourceProvider::BeginSetPixels(ResourceId id) { if (!resource->gl_upload_query_id) resource->gl_upload_query_id = context3d->createQueryEXT(); context3d->beginQueryEXT( - GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM, + GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM, resource->gl_upload_query_id); if (allocate) { - context3d->asyncTexImage2DCHROMIUM(GL_TEXTURE_2D, - 0, /* level */ - resource->format, - resource->size.width(), - resource->size.height(), - 0, /* border */ - resource->format, - GL_UNSIGNED_BYTE, - NULL); + context3d->asyncTexImage2DCHROMIUM( + GL_TEXTURE_2D, + 0, /* level */ + GetGLInternalFormat(resource->format), + resource->size.width(), + resource->size.height(), + 0, /* border */ + GetGLDataFormat(resource->format), + GetGLDataType(resource->format), + NULL); } else { - context3d->asyncTexSubImage2DCHROMIUM(GL_TEXTURE_2D, - 0, /* level */ - 0, /* x */ - 0, /* y */ - resource->size.width(), - resource->size.height(), - resource->format, - GL_UNSIGNED_BYTE, - NULL); + context3d->asyncTexSubImage2DCHROMIUM( + GL_TEXTURE_2D, + 0, /* level */ + 0, /* x */ + 0, /* y */ + resource->size.width(), + resource->size.height(), + GetGLDataFormat(resource->format), + GetGLDataType(resource->format), + NULL); } - context3d->endQueryEXT(GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM); + context3d->endQueryEXT(GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM); context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0); } if (resource->pixels) { DCHECK(!resource->mailbox.IsValid()); DCHECK(resource->pixel_buffer); - DCHECK(resource->format == GL_RGBA); + DCHECK_EQ(RGBA_8888, resource->format); std::swap(resource->pixels, resource->pixel_buffer); delete[] resource->pixel_buffer; @@ -1213,16 +1433,13 @@ void ResourceProvider::BeginSetPixels(ResourceId id) { } void ResourceProvider::ForceSetPixelsToComplete(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(resource->locked_for_write); DCHECK(resource->pending_set_pixels); DCHECK(!resource->set_pixels_completion_forced); if (resource->gl_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id)); GLC(context3d, context3d->waitAsyncTexImage2DCHROMIUM(GL_TEXTURE_2D)); GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, 0)); @@ -1232,15 +1449,12 @@ void ResourceProvider::ForceSetPixelsToComplete(ResourceId id) { } bool ResourceProvider::DidSetPixelsComplete(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); DCHECK(resource->locked_for_write); DCHECK(resource->pending_set_pixels); if (resource->gl_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); DCHECK(resource->gl_upload_query_id); unsigned complete = 1; @@ -1259,10 +1473,12 @@ bool ResourceProvider::DidSetPixelsComplete(ResourceId id) { } void ResourceProvider::CreateForTesting(ResourceId id) { - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - LazyCreate(resource); + LazyCreate(GetResource(id)); +} + +GLint ResourceProvider::WrapModeForTesting(ResourceId id) { + Resource* resource = GetResource(id); + return resource->wrap_mode; } void ResourceProvider::LazyCreate(Resource* resource) { @@ -1273,10 +1489,20 @@ void ResourceProvider::LazyCreate(Resource* resource) { if (resource->texture_pool == 0) return; - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); + // Create and set texture properties. Allocation is delayed until needed. - resource->gl_id = CreateTextureId(context3d); + GLC(context3d, resource->gl_id = context3d->createTexture()); + GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id)); + GLC(context3d, context3d->texParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GLC(context3d, context3d->texParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GLC(context3d, context3d->texParameteri( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, resource->wrap_mode)); + GLC(context3d, context3d->texParameteri( + GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, resource->wrap_mode)); GLC(context3d, context3d->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_POOL_CHROMIUM, resource->texture_pool)); @@ -1288,10 +1514,7 @@ void ResourceProvider::LazyCreate(Resource* resource) { } void ResourceProvider::AllocateForTesting(ResourceId id) { - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - LazyAllocate(resource); + LazyAllocate(GetResource(id)); } void ResourceProvider::LazyAllocate(Resource* resource) { @@ -1302,11 +1525,11 @@ void ResourceProvider::LazyAllocate(Resource* resource) { if (resource->allocated || !resource->gl_id) return; resource->allocated = true; - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); gfx::Size& size = resource->size; - GLenum format = resource->format; + ResourceFormat format = resource->format; GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id)); - if (use_texture_storage_ext_ && IsTextureFormatSupportedForStorage(format)) { + if (use_texture_storage_ext_ && IsFormatSupportedForStorage(format)) { GLenum storage_format = TextureToStorageFormat(format); GLC(context3d, context3d->texStorage2DEXT(GL_TEXTURE_2D, 1, @@ -1316,33 +1539,26 @@ void ResourceProvider::LazyAllocate(Resource* resource) { } else { GLC(context3d, context3d->texImage2D(GL_TEXTURE_2D, 0, - format, + GetGLInternalFormat(format), size.width(), size.height(), 0, - format, - GL_UNSIGNED_BYTE, + GetGLDataFormat(format), + GetGLDataType(format), NULL)); } } void ResourceProvider::EnableReadLockFences(ResourceProvider::ResourceId id, bool enable) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; + Resource* resource = GetResource(id); resource->enable_read_lock_fences = enable; } void ResourceProvider::AcquireImage(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - + Resource* resource = GetResource(id); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); if (resource->type != GLTexture) return; @@ -1351,26 +1567,23 @@ void ResourceProvider::AcquireImage(ResourceId id) { return; resource->allocated = true; - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); + DCHECK_EQ(RGBA_8888, resource->format); resource->image_id = context3d->createImageCHROMIUM( resource->size.width(), resource->size.height(), GL_RGBA8_OES); DCHECK(resource->image_id); } void ResourceProvider::ReleaseImage(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - + Resource* resource = GetResource(id); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); if (!resource->image_id) return; - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); context3d->destroyImageCHROMIUM(resource->image_id); resource->image_id = 0; @@ -1378,17 +1591,13 @@ void ResourceProvider::ReleaseImage(ResourceId id) { } uint8_t* ResourceProvider::MapImage(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - + Resource* resource = GetResource(id); DCHECK(ReadLockFenceHasPassed(resource)); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); if (resource->image_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); return static_cast<uint8_t*>( context3d->mapImageCHROMIUM(resource->image_id, GL_READ_WRITE)); @@ -1401,34 +1610,26 @@ uint8_t* ResourceProvider::MapImage(ResourceId id) { } void ResourceProvider::UnmapImage(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - + Resource* resource = GetResource(id); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); if (resource->image_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); context3d->unmapImageCHROMIUM(resource->image_id); } } int ResourceProvider::GetImageStride(ResourceId id) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(id); - CHECK(it != resources_.end()); - Resource* resource = &it->second; - + Resource* resource = GetResource(id); DCHECK(!resource->external); - DCHECK(!resource->exported); + DCHECK_EQ(resource->exported_count, 0); int stride = 0; if (resource->image_id) { - WebGraphicsContext3D* context3d = output_surface_->context3d(); + WebGraphicsContext3D* context3d = Context3d(); DCHECK(context3d); context3d->getImageParameterivCHROMIUM( resource->image_id, GL_IMAGE_ROWBYTES_CHROMIUM, &stride); @@ -1443,4 +1644,59 @@ GLint ResourceProvider::GetActiveTextureUnit(WebGraphicsContext3D* context) { return active_unit; } +WebKit::WebGraphicsContext3D* ResourceProvider::Context3d() const { + ContextProvider* context_provider = output_surface_->context_provider(); + return context_provider ? context_provider->Context3d() : NULL; +} + +size_t ResourceProvider::BytesPerPixel(ResourceFormat format) { + switch (format) { + case RGBA_8888: + case BGRA_8888: + return 4; + case RGBA_4444: + case RGB_565: + return 2; + case LUMINANCE_8: + return 1; + } + NOTREACHED(); + return 4; +} + +GLenum ResourceProvider::GetGLDataType(ResourceFormat format) { + switch (format) { + case RGBA_4444: + return GL_UNSIGNED_SHORT_4_4_4_4; + case RGBA_8888: + case BGRA_8888: + case LUMINANCE_8: + return GL_UNSIGNED_BYTE; + case RGB_565: + return GL_UNSIGNED_SHORT_5_6_5; + } + NOTREACHED(); + return GL_UNSIGNED_BYTE; +} + +GLenum ResourceProvider::GetGLDataFormat(ResourceFormat format) { + switch (format) { + case RGBA_8888: + case RGBA_4444: + return GL_RGBA; + case BGRA_8888: + return GL_BGRA_EXT; + case LUMINANCE_8: + return GL_LUMINANCE; + case RGB_565: + return GL_RGB; + } + NOTREACHED(); + return GL_RGBA; +} + +GLenum ResourceProvider::GetGLInternalFormat(ResourceFormat format) { + return GetGLDataFormat(format); +} + } // namespace cc diff --git a/chromium/cc/resources/resource_provider.h b/chromium/cc/resources/resource_provider.h index 1d51a2748e7..928d4f9aa2a 100644 --- a/chromium/cc/resources/resource_provider.h +++ b/chromium/cc/resources/resource_provider.h @@ -8,6 +8,7 @@ #include <deque> #include <set> #include <string> +#include <utility> #include <vector> #include "base/basictypes.h" @@ -18,6 +19,10 @@ #include "cc/base/cc_export.h" #include "cc/output/context_provider.h" #include "cc/output/output_surface.h" +#include "cc/resources/release_callback.h" +#include "cc/resources/resource_format.h" +#include "cc/resources/return_callback.h" +#include "cc/resources/single_release_callback.h" #include "cc/resources/texture_mailbox.h" #include "cc/resources/transferable_resource.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -57,8 +62,8 @@ class CC_EXPORT ResourceProvider { }; static scoped_ptr<ResourceProvider> Create(OutputSurface* output_surface, - int highp_threshold_min); - + int highp_threshold_min, + bool use_rgba_4444_texture_format); virtual ~ResourceProvider(); void InitializeSoftware(); @@ -66,14 +71,17 @@ class CC_EXPORT ResourceProvider { void DidLoseOutputSurface() { lost_output_surface_ = true; } - WebKit::WebGraphicsContext3D* GraphicsContext3D(); int max_texture_size() const { return max_texture_size_; } - GLenum best_texture_format() const { return best_texture_format_; } + ResourceFormat memory_efficient_texture_format() const { + return use_rgba_4444_texture_format_ ? RGBA_4444 : best_texture_format_; + } + ResourceFormat best_texture_format() const { return best_texture_format_; } size_t num_resources() const { return resources_.size(); } // Checks whether a resource is in use by a consumer. bool InUseByConsumer(ResourceId id); + bool IsLost(ResourceId id); // Producer interface. @@ -82,20 +90,23 @@ class CC_EXPORT ResourceProvider { // Creates a resource of the default resource type. ResourceId CreateResource(gfx::Size size, - GLenum format, - TextureUsageHint hint); + GLint wrap_mode, + TextureUsageHint hint, + ResourceFormat format); // Creates a resource which is tagged as being managed for GPU memory // accounting purposes. ResourceId CreateManagedResource(gfx::Size size, - GLenum format, - TextureUsageHint hint); + GLint wrap_mode, + TextureUsageHint hint, + ResourceFormat format); // You can also explicitly create a specific resource type. ResourceId CreateGLTexture(gfx::Size size, - GLenum format, GLenum texture_pool, - TextureUsageHint hint); + GLint wrap_mode, + TextureUsageHint hint, + ResourceFormat format); ResourceId CreateBitmap(gfx::Size size); // Wraps an external texture into a GL resource. @@ -104,7 +115,9 @@ class CC_EXPORT ResourceProvider { unsigned texture_id); // Wraps an external texture mailbox into a GL resource. - ResourceId CreateResourceFromTextureMailbox(const TextureMailbox& mailbox); + ResourceId CreateResourceFromTextureMailbox( + const TextureMailbox& mailbox, + scoped_ptr<SingleReleaseCallback> release_callback); void DeleteResource(ResourceId id); @@ -122,6 +135,7 @@ class CC_EXPORT ResourceProvider { double EstimatedUploadsPerSecond(); void FlushUploads(); void ReleaseCachedData(); + base::TimeDelta TextureUpdateTickRate(); // Flush all context operations, kicking uploads and ensuring ordering with // respect to other contexts. @@ -136,7 +150,7 @@ class CC_EXPORT ResourceProvider { bool ShallowFlushIfSupported(); // Creates accounting for a child. Returns a child ID. - int CreateChild(); + int CreateChild(const ReturnCallback& return_callback); // Destroys accounting for the child, deleting all accounted resources. void DestroyChild(int child); @@ -151,29 +165,31 @@ class CC_EXPORT ResourceProvider { void PrepareSendToParent(const ResourceIdArray& resources, TransferableResourceArray* transferable_resources); - // Prepares resources to be transfered back to the child, moving them to - // mailboxes and serializing meta-data into TransferableResources. - // Resources are removed from the ResourceProvider. Note: the resource IDs - // passed are in the parent namespace and will be translated to the child - // namespace when returned. - void PrepareSendToChild(int child, - const ResourceIdArray& resources, - TransferableResourceArray* transferable_resources); - // Receives resources from a child, moving them from mailboxes. Resource IDs // passed are in the child namespace, and will be translated to the parent // namespace, added to the child->parent map. + // This adds the resources to the working set in the ResourceProvider without + // declaring which resources are in use. Use DeclareUsedResourcesFromChild + // after calling this method to do that. All calls to ReceiveFromChild should + // be followed by a DeclareUsedResourcesFromChild. // NOTE: if the sync_point is set on any TransferableResource, this will // wait on it. void ReceiveFromChild( int child, const TransferableResourceArray& transferable_resources); + // Once a set of resources have been received, they may or may not be used. + // This declares what set of resources are currently in use from the child, + // releasing any other resources back to the child. + void DeclareUsedResourcesFromChild( + int child, + const ResourceIdArray& resources_from_child); + // Receives resources from the parent, moving them from mailboxes. Resource // IDs passed are in the child namespace. // NOTE: if the sync_point is set on any TransferableResource, this will // wait on it. - void ReceiveFromParent( - const TransferableResourceArray& transferable_resources); + void ReceiveReturnsFromParent( + const ReturnedResourceArray& transferable_resources); // The following lock classes are part of the ResourceProvider API and are // needed to read and write the resource contents. The user must ensure @@ -313,6 +329,8 @@ class CC_EXPORT ResourceProvider { // For tests only! void CreateForTesting(ResourceId id); + GLint WrapModeForTesting(ResourceId id); + // Sets the current read fence. If a resource is locked for read // and has read fences enabled, the resource will not allow writes // until this fence has passed. @@ -327,14 +345,11 @@ class CC_EXPORT ResourceProvider { // Indicates if we can currently lock this resource for write. bool CanLockForWrite(ResourceId id); - cc::ContextProvider* offscreen_context_provider() { - return offscreen_context_provider_.get(); - } - void set_offscreen_context_provider( - scoped_refptr<cc::ContextProvider> offscreen_context_provider) { - offscreen_context_provider_ = offscreen_context_provider; - } static GLint GetActiveTextureUnit(WebKit::WebGraphicsContext3D* context); + static size_t BytesPerPixel(ResourceFormat format); + static GLenum GetGLDataType(ResourceFormat format); + static GLenum GetGLDataFormat(ResourceFormat format); + static GLenum GetGLInternalFormat(ResourceFormat format); private: struct Resource { @@ -342,24 +357,31 @@ class CC_EXPORT ResourceProvider { ~Resource(); Resource(unsigned texture_id, gfx::Size size, - GLenum format, GLenum filter, GLenum texture_pool, - TextureUsageHint hint); - Resource(uint8_t* pixels, gfx::Size size, GLenum format, GLenum filter); + GLint wrap_mode, + TextureUsageHint hint, + ResourceFormat format); + Resource(uint8_t* pixels, + gfx::Size size, + GLenum filter, + GLint wrap_mode); + int child_id; unsigned gl_id; // Pixel buffer used for set pixels without unnecessary copying. unsigned gl_pixel_buffer_id; // Query used to determine when asynchronous set pixels complete. unsigned gl_upload_query_id; TextureMailbox mailbox; + ReleaseCallback release_callback; uint8_t* pixels; uint8_t* pixel_buffer; int lock_for_read_count; + int imported_count; + int exported_count; bool locked_for_write; bool external; - bool exported; bool marked_for_deletion; bool pending_set_pixels; bool set_pixels_completion_forced; @@ -367,21 +389,32 @@ class CC_EXPORT ResourceProvider { bool enable_read_lock_fences; scoped_refptr<Fence> read_lock_fence; gfx::Size size; - GLenum format; // TODO(skyostil): Use a separate sampler object for filter state. + GLenum original_filter; GLenum filter; + GLenum target; unsigned image_id; GLenum texture_pool; + GLint wrap_mode; + bool lost; TextureUsageHint hint; ResourceType type; + ResourceFormat format; }; typedef base::hash_map<ResourceId, Resource> ResourceMap; + + static bool CompareResourceMapIteratorsByChildId( + const std::pair<ReturnedResource, ResourceMap::iterator>& a, + const std::pair<ReturnedResource, ResourceMap::iterator>& b); + struct Child { Child(); ~Child(); ResourceIdMap child_to_parent_map; ResourceIdMap parent_to_child_map; + ReturnCallback return_callback; + ResourceIdSet in_use_resources; }; typedef base::hash_map<int, Child> ChildMap; @@ -390,11 +423,13 @@ class CC_EXPORT ResourceProvider { resource->read_lock_fence->HasPassed(); } - explicit ResourceProvider(OutputSurface* output_surface, - int highp_threshold_min); + ResourceProvider(OutputSurface* output_surface, + int highp_threshold_min, + bool use_rgba_4444_texture_format); void CleanUpGLIfNeeded(); + Resource* GetResource(ResourceId id); const Resource* LockForRead(ResourceId id); void UnlockForRead(ResourceId id); const Resource* LockForWrite(ResourceId id); @@ -402,7 +437,7 @@ class CC_EXPORT ResourceProvider { static void PopulateSkBitmapWithResource(SkBitmap* sk_bitmap, const Resource* resource); - bool TransferResource(WebKit::WebGraphicsContext3D* context, + void TransferResource(WebKit::WebGraphicsContext3D* context, ResourceId id, TransferableResource* resource); enum DeleteStyle { @@ -410,6 +445,9 @@ class CC_EXPORT ResourceProvider { ForShutdown, }; void DeleteResourceInternal(ResourceMap::iterator it, DeleteStyle style); + void DeleteAndReturnUnusedResourcesToChild(Child* child_info, + DeleteStyle style, + const ResourceIdArray& unused); void LazyCreate(Resource* resource); void LazyAllocate(Resource* resource); @@ -424,6 +462,9 @@ class CC_EXPORT ResourceProvider { GLenum target, GLenum unit); + // Returns NULL if the output_surface_ does not have a ContextProvider. + WebKit::WebGraphicsContext3D* Context3d() const; + OutputSurface* output_surface_; bool lost_output_surface_; int highp_threshold_min_; @@ -438,13 +479,12 @@ class CC_EXPORT ResourceProvider { bool use_shallow_flush_; scoped_ptr<TextureUploader> texture_uploader_; int max_texture_size_; - GLenum best_texture_format_; - - scoped_refptr<cc::ContextProvider> offscreen_context_provider_; + ResourceFormat best_texture_format_; base::ThreadChecker thread_checker_; scoped_refptr<Fence> current_read_lock_fence_; + bool use_rgba_4444_texture_format_; DISALLOW_COPY_AND_ASSIGN(ResourceProvider); }; diff --git a/chromium/cc/resources/resource_provider_unittest.cc b/chromium/cc/resources/resource_provider_unittest.cc index a23af0b577c..fed3016e2e5 100644 --- a/chromium/cc/resources/resource_provider_unittest.cc +++ b/chromium/cc/resources/resource_provider_unittest.cc @@ -11,10 +11,12 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "cc/base/scoped_ptr_deque.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/output/output_surface.h" +#include "cc/resources/returned_resource.h" +#include "cc/resources/single_release_callback.h" #include "cc/test/fake_output_surface.h" #include "cc/test/fake_output_surface_client.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -39,24 +41,41 @@ using WebKit::WebGLId; namespace cc { namespace { -size_t TextureSize(gfx::Size size, WGC3Denum format) { +size_t TextureSize(gfx::Size size, ResourceFormat format) { unsigned int components_per_pixel = 4; unsigned int bytes_per_component = 1; return size.width() * size.height() * components_per_pixel * bytes_per_component; } +class TextureStateTrackingContext : public TestWebGraphicsContext3D { + public: + MOCK_METHOD2(bindTexture, void(WGC3Denum target, WebGLId texture)); + MOCK_METHOD3(texParameteri, + void(WGC3Denum target, WGC3Denum pname, WGC3Dint param)); + MOCK_METHOD1(waitSyncPoint, void(unsigned sync_point)); + MOCK_METHOD0(insertSyncPoint, unsigned(void)); + MOCK_METHOD2(produceTextureCHROMIUM, void(WGC3Denum target, + const WGC3Dbyte* mailbox)); + MOCK_METHOD2(consumeTextureCHROMIUM, void(WGC3Denum target, + const WGC3Dbyte* mailbox)); + + // Force all textures to be "1" so we can test for them. + virtual WebKit::WebGLId NextTextureId() OVERRIDE { return 1; } +}; + struct Texture : public base::RefCounted<Texture> { - Texture() : format(0), filter(GL_NEAREST_MIPMAP_LINEAR) {} + Texture() : format(RGBA_8888), + filter(GL_NEAREST_MIPMAP_LINEAR) {} - void Reallocate(gfx::Size size, WGC3Denum format) { + void Reallocate(gfx::Size size, ResourceFormat format) { this->size = size; this->format = format; this->data.reset(new uint8_t[TextureSize(size, format)]); } gfx::Size size; - WGC3Denum format; + ResourceFormat format; WGC3Denum filter; scoped_ptr<uint8_t[]> data; @@ -219,7 +238,9 @@ class ResourceProviderContext : public TestWebGraphicsContext3D { ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target); ASSERT_FALSE(level); ASSERT_TRUE(textures_[current_texture_].get()); - ASSERT_EQ(textures_[current_texture_]->format, format); + ASSERT_EQ( + ResourceProvider::GetGLDataFormat(textures_[current_texture_]->format), + format); ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type); ASSERT_TRUE(pixels); SetPixels(xoffset, yoffset, width, height, pixels); @@ -262,7 +283,7 @@ class ResourceProviderContext : public TestWebGraphicsContext3D { mailbox, last_waited_sync_point_); } - void GetPixels(gfx::Size size, WGC3Denum format, uint8_t* pixels) { + void GetPixels(gfx::Size size, ResourceFormat format, uint8_t* pixels) { ASSERT_TRUE(current_texture_); scoped_refptr<Texture> texture = textures_[current_texture_]; ASSERT_TRUE(texture.get()); @@ -293,7 +314,16 @@ class ResourceProviderContext : public TestWebGraphicsContext3D { ASSERT_TRUE(current_texture_); scoped_refptr<Texture> texture = textures_[current_texture_]; ASSERT_TRUE(texture.get()); - texture->Reallocate(size, format); + ResourceFormat texture_format = RGBA_8888; + switch (format) { + case GL_RGBA: + texture_format = RGBA_8888; + break; + case GL_BGRA_EXT: + texture_format = BGRA_8888; + break; + } + texture->Reallocate(size, texture_format); } void SetPixels(int xoffset, @@ -338,7 +368,7 @@ void GetResourcePixels(ResourceProvider* resource_provider, ResourceProviderContext* context, ResourceProvider::ResourceId id, gfx::Size size, - WGC3Denum format, + ResourceFormat format, uint8_t* pixels) { switch (resource_provider->default_resource_type()) { case ResourceProvider::GLTexture: { @@ -366,13 +396,21 @@ class ResourceProviderTest : public testing::TestWithParam<ResourceProvider::ResourceType> { public: ResourceProviderTest() - : shared_data_(ContextSharedData::Create()) { + : shared_data_(ContextSharedData::Create()), + context3d_(NULL) { switch (GetParam()) { - case ResourceProvider::GLTexture: - output_surface_ = - FakeOutputSurface::Create3d(ResourceProviderContext::Create( - shared_data_.get()).PassAs<WebKit::WebGraphicsContext3D>()); + case ResourceProvider::GLTexture: { + scoped_ptr<ResourceProviderContext> context3d( + ResourceProviderContext::Create(shared_data_.get())); + context3d_ = context3d.get(); + + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create( + context3d.PassAs<TestWebGraphicsContext3D>()); + + output_surface_ = FakeOutputSurface::Create3d(context_provider); break; + } case ResourceProvider::Bitmap: output_surface_ = FakeOutputSurface::CreateSoftware( make_scoped_ptr(new SoftwareOutputDevice)); @@ -381,33 +419,33 @@ class ResourceProviderTest NOTREACHED(); break; } - resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + resource_provider_ = ResourceProvider::Create( + output_surface_.get(), 0, false); } - ResourceProviderContext* context() { - return static_cast<ResourceProviderContext*>(output_surface_->context3d()); + static void CollectResources(ReturnedResourceArray* array, + const ReturnedResourceArray& returned) { + array->insert(array->end(), returned.begin(), returned.end()); } - void SetResourceFilter(ResourceProvider* resource_provider, - ResourceProvider::ResourceId id, - WGC3Denum filter) { + static ReturnCallback GetReturnCallback(ReturnedResourceArray* array) { + return base::Bind(&ResourceProviderTest::CollectResources, array); + } + + static void SetResourceFilter(ResourceProvider* resource_provider, + ResourceProvider::ResourceId id, + WGC3Denum filter) { ResourceProvider::ScopedSamplerGL sampler( resource_provider, id, GL_TEXTURE_2D, filter); } - WGC3Denum GetResourceFilter(ResourceProvider* resource_provider, - ResourceProvider::ResourceId id) { - DCHECK_EQ(GetParam(), ResourceProvider::GLTexture); - ResourceProvider::ScopedReadLockGL lock_gl(resource_provider, id); - EXPECT_NE(0u, lock_gl.texture_id()); - ResourceProviderContext* context = static_cast<ResourceProviderContext*>( - resource_provider->GraphicsContext3D()); - context->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id()); - return context->GetTextureFilter(); - } + ResourceProviderContext* context() { return context3d_; } protected: scoped_ptr<ContextSharedData> shared_data_; + ResourceProviderContext* context3d_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<OutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; }; @@ -418,12 +456,12 @@ void CheckCreateResource(ResourceProvider::ResourceType expected_default_type, DCHECK_EQ(expected_default_type, resource_provider->default_resource_type()); gfx::Size size(1, 1); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; size_t pixel_size = TextureSize(size, format); ASSERT_EQ(4U, pixel_size); ResourceProvider::ResourceId id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); EXPECT_EQ(1, static_cast<int>(resource_provider->num_resources())); if (expected_default_type == ResourceProvider::GLTexture) EXPECT_EQ(0, context->texture_count()); @@ -450,12 +488,12 @@ TEST_P(ResourceProviderTest, Basic) { TEST_P(ResourceProviderTest, Upload) { gfx::Size size(2, 2); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; size_t pixel_size = TextureSize(size, format); ASSERT_EQ(16U, pixel_size); ResourceProvider::ResourceId id = resource_provider_->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); uint8_t image[16] = { 0 }; gfx::Rect image_rect(size); @@ -520,29 +558,38 @@ TEST_P(ResourceProviderTest, TransferResources) { if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d( - ResourceProviderContext::Create(shared_data_.get()) - .PassAs<WebKit::WebGraphicsContext3D>())); + scoped_ptr<ResourceProviderContext> child_context_owned( + ResourceProviderContext::Create(shared_data_.get())); + ResourceProviderContext* child_context = child_context_owned.get(); + + FakeOutputSurfaceClient child_output_surface_client; + scoped_ptr<OutputSurface> child_output_surface( + FakeOutputSurface::Create3d( + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + scoped_ptr<ResourceProvider> child_resource_provider( - ResourceProvider::Create(child_output_surface.get(), 0)); + ResourceProvider::Create(child_output_surface.get(), 0, false)); gfx::Size size(1, 1); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; size_t pixel_size = TextureSize(size, format); ASSERT_EQ(4U, pixel_size); ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); uint8_t data1[4] = { 1, 2, 3, 4 }; gfx::Rect rect(size); child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d()); ResourceProvider::ResourceId id2 = child_resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); uint8_t data2[4] = { 5, 5, 5, 5 }; child_resource_provider->SetPixels(id2, data2, rect, rect, gfx::Vector2d()); - int child_id = resource_provider_->CreateChild(); + ReturnedResourceArray returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); { // Transfer some resources to the parent. ResourceProvider::ResourceIdArray resource_ids_to_transfer; @@ -557,6 +604,8 @@ TEST_P(ResourceProviderTest, TransferResources) { EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1)); EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2)); resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); } EXPECT_EQ(2u, resource_provider_->num_resources()); @@ -579,32 +628,40 @@ TEST_P(ResourceProviderTest, TransferResources) { EXPECT_EQ(0, memcmp(data2, result, pixel_size)); { // Check that transfering again the same resource from the child to the - // parent is a noop. + // parent works. ResourceProvider::ResourceIdArray resource_ids_to_transfer; resource_ids_to_transfer.push_back(id1); TransferableResourceArray list; child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list); - EXPECT_EQ(0u, list.size()); + EXPECT_EQ(1u, list.size()); + EXPECT_EQ(id1, list[0].id); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + child_resource_provider->ReceiveReturnsFromParent(returned); + // id1 was exported twice, we returned it only once, it should still be + // in-use. + EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1)); } { - // Transfer resources back from the parent to the child. - ResourceProvider::ResourceIdArray resource_ids_to_transfer; - resource_ids_to_transfer.push_back(mapped_id1); - resource_ids_to_transfer.push_back(mapped_id2); - TransferableResourceArray list; - resource_provider_->PrepareSendToChild( - child_id, resource_ids_to_transfer, &list); - ASSERT_EQ(2u, list.size()); - EXPECT_NE(0u, list[0].sync_point); - EXPECT_NE(0u, list[1].sync_point); - child_resource_provider->ReceiveFromParent(list); + EXPECT_EQ(0u, returned_to_child.size()); + + // Transfer resources back from the parent to the child. Set no resources as + // being in use. + ResourceProvider::ResourceIdArray no_resources; + resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources); + + ASSERT_EQ(2u, returned_to_child.size()); + EXPECT_NE(0u, returned_to_child[0].sync_point); + EXPECT_NE(0u, returned_to_child[1].sync_point); + EXPECT_FALSE(returned_to_child[0].lost); + EXPECT_FALSE(returned_to_child[1].lost); + child_resource_provider->ReceiveReturnsFromParent(returned_to_child); + returned_to_child.clear(); } EXPECT_FALSE(child_resource_provider->InUseByConsumer(id1)); EXPECT_FALSE(child_resource_provider->InUseByConsumer(id2)); - ResourceProviderContext* child_context = - static_cast<ResourceProviderContext*>(child_output_surface->context3d()); { ResourceProvider::ScopedReadLockGL lock(child_resource_provider.get(), id1); ASSERT_NE(0U, lock.texture_id()); @@ -633,98 +690,267 @@ TEST_P(ResourceProviderTest, TransferResources) { EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1)); EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2)); resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); } + EXPECT_EQ(0u, returned_to_child.size()); + EXPECT_EQ(2u, resource_provider_->num_resources()); resource_provider_->DestroyChild(child_id); EXPECT_EQ(0u, resource_provider_->num_resources()); + + ASSERT_EQ(2u, returned_to_child.size()); + EXPECT_NE(0u, returned_to_child[0].sync_point); + EXPECT_NE(0u, returned_to_child[1].sync_point); + EXPECT_FALSE(returned_to_child[0].lost); + EXPECT_FALSE(returned_to_child[1].lost); } -TEST_P(ResourceProviderTest, DeleteTransferredResources) { +TEST_P(ResourceProviderTest, DeleteExportedResources) { // Resource transfer is only supported with GL textures for now. if (GetParam() != ResourceProvider::GLTexture) return; + scoped_ptr<ResourceProviderContext> child_context_owned( + ResourceProviderContext::Create(shared_data_.get())); + + FakeOutputSurfaceClient child_output_surface_client; scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d( - ResourceProviderContext::Create(shared_data_.get()) - .PassAs<WebKit::WebGraphicsContext3D>())); + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + scoped_ptr<ResourceProvider> child_resource_provider( - ResourceProvider::Create(child_output_surface.get(), 0)); + ResourceProvider::Create(child_output_surface.get(), 0, false)); gfx::Size size(1, 1); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; size_t pixel_size = TextureSize(size, format); ASSERT_EQ(4U, pixel_size); - ResourceProvider::ResourceId id = child_resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); - uint8_t data[4] = { 1, 2, 3, 4 }; + ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource( + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); + uint8_t data1[4] = {1, 2, 3, 4}; gfx::Rect rect(size); - child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d()); + child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d()); + + ResourceProvider::ResourceId id2 = child_resource_provider->CreateResource( + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); + uint8_t data2[4] = {5, 5, 5, 5}; + child_resource_provider->SetPixels(id2, data2, rect, rect, gfx::Vector2d()); - int child_id = resource_provider_->CreateChild(); + ReturnedResourceArray returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); { - // Transfer some resource to the parent. + // Transfer some resources to the parent. ResourceProvider::ResourceIdArray resource_ids_to_transfer; - resource_ids_to_transfer.push_back(id); + resource_ids_to_transfer.push_back(id1); + resource_ids_to_transfer.push_back(id2); TransferableResourceArray list; child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list); - ASSERT_EQ(1u, list.size()); + ASSERT_EQ(2u, list.size()); EXPECT_NE(0u, list[0].sync_point); - EXPECT_TRUE(child_resource_provider->InUseByConsumer(id)); + EXPECT_NE(0u, list[1].sync_point); + EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1)); + EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2)); resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); } - // Delete textures in the child, while they are transfered. - child_resource_provider->DeleteResource(id); - EXPECT_EQ(1u, child_resource_provider->num_resources()); + EXPECT_EQ(2u, resource_provider_->num_resources()); + ResourceProvider::ResourceIdMap resource_map = + resource_provider_->GetChildToParentMap(child_id); + ResourceProvider::ResourceId mapped_id1 = resource_map[id1]; + ResourceProvider::ResourceId mapped_id2 = resource_map[id2]; + EXPECT_NE(0u, mapped_id1); + EXPECT_NE(0u, mapped_id2); + EXPECT_FALSE(resource_provider_->InUseByConsumer(id1)); + EXPECT_FALSE(resource_provider_->InUseByConsumer(id2)); + { - // Transfer resources back from the parent to the child. - ResourceProvider::ResourceIdMap resource_map = - resource_provider_->GetChildToParentMap(child_id); - ResourceProvider::ResourceId mapped_id = resource_map[id]; - EXPECT_NE(0u, mapped_id); + // The parent transfers the resources to the grandparent. ResourceProvider::ResourceIdArray resource_ids_to_transfer; - resource_ids_to_transfer.push_back(mapped_id); + resource_ids_to_transfer.push_back(mapped_id1); + resource_ids_to_transfer.push_back(mapped_id2); TransferableResourceArray list; - resource_provider_->PrepareSendToChild( - child_id, resource_ids_to_transfer, &list); - ASSERT_EQ(1u, list.size()); + resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list); + + ASSERT_EQ(2u, list.size()); EXPECT_NE(0u, list[0].sync_point); - child_resource_provider->ReceiveFromParent(list); + EXPECT_NE(0u, list[1].sync_point); + EXPECT_TRUE(resource_provider_->InUseByConsumer(id1)); + EXPECT_TRUE(resource_provider_->InUseByConsumer(id2)); + + // Release the resource in the parent. Set no resources as being in use. The + // resources are exported so that can't be transferred back yet. + ResourceProvider::ResourceIdArray no_resources; + resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources); + + EXPECT_EQ(0u, returned_to_child.size()); + EXPECT_EQ(2u, resource_provider_->num_resources()); + + // Return the resources from the grandparent to the parent. They should be + // returned to the child then. + EXPECT_EQ(2u, list.size()); + EXPECT_EQ(mapped_id1, list[0].id); + EXPECT_EQ(mapped_id2, list[1].id); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + resource_provider_->ReceiveReturnsFromParent(returned); + + EXPECT_EQ(0u, resource_provider_->num_resources()); + ASSERT_EQ(2u, returned_to_child.size()); + EXPECT_NE(0u, returned_to_child[0].sync_point); + EXPECT_NE(0u, returned_to_child[1].sync_point); + EXPECT_FALSE(returned_to_child[0].lost); + EXPECT_FALSE(returned_to_child[1].lost); } - EXPECT_EQ(0u, child_resource_provider->num_resources()); } -TEST_P(ResourceProviderTest, TextureFilters) { +TEST_P(ResourceProviderTest, DestroyChildWithExportedResources) { // Resource transfer is only supported with GL textures for now. if (GetParam() != ResourceProvider::GLTexture) return; + scoped_ptr<ResourceProviderContext> child_context_owned( + ResourceProviderContext::Create(shared_data_.get())); + + FakeOutputSurfaceClient child_output_surface_client; scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d( - ResourceProviderContext::Create(shared_data_.get()) - .PassAs<WebKit::WebGraphicsContext3D>())); + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + scoped_ptr<ResourceProvider> child_resource_provider( - ResourceProvider::Create(child_output_surface.get(), 0)); + ResourceProvider::Create(child_output_surface.get(), 0, false)); gfx::Size size(1, 1); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; + size_t pixel_size = TextureSize(size, format); + ASSERT_EQ(4U, pixel_size); + + ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource( + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); + uint8_t data1[4] = {1, 2, 3, 4}; + gfx::Rect rect(size); + child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d()); + + ResourceProvider::ResourceId id2 = child_resource_provider->CreateResource( + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); + uint8_t data2[4] = {5, 5, 5, 5}; + child_resource_provider->SetPixels(id2, data2, rect, rect, gfx::Vector2d()); + + ReturnedResourceArray returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); + { + // Transfer some resources to the parent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(id1); + resource_ids_to_transfer.push_back(id2); + TransferableResourceArray list; + child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, + &list); + ASSERT_EQ(2u, list.size()); + EXPECT_NE(0u, list[0].sync_point); + EXPECT_NE(0u, list[1].sync_point); + EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1)); + EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2)); + resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); + } + + EXPECT_EQ(2u, resource_provider_->num_resources()); + ResourceProvider::ResourceIdMap resource_map = + resource_provider_->GetChildToParentMap(child_id); + ResourceProvider::ResourceId mapped_id1 = resource_map[id1]; + ResourceProvider::ResourceId mapped_id2 = resource_map[id2]; + EXPECT_NE(0u, mapped_id1); + EXPECT_NE(0u, mapped_id2); + EXPECT_FALSE(resource_provider_->InUseByConsumer(id1)); + EXPECT_FALSE(resource_provider_->InUseByConsumer(id2)); + + { + // The parent transfers the resources to the grandparent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(mapped_id1); + resource_ids_to_transfer.push_back(mapped_id2); + TransferableResourceArray list; + resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list); + + ASSERT_EQ(2u, list.size()); + EXPECT_NE(0u, list[0].sync_point); + EXPECT_NE(0u, list[1].sync_point); + EXPECT_TRUE(resource_provider_->InUseByConsumer(id1)); + EXPECT_TRUE(resource_provider_->InUseByConsumer(id2)); + + // Release the resource in the parent. Set no resources as being in use. The + // resources are exported so that can't be transferred back yet. + ResourceProvider::ResourceIdArray no_resources; + resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources); + + // Destroy the child, the resources should be returned immediately from the + // parent and marked as lost. + EXPECT_EQ(0u, returned_to_child.size()); + EXPECT_EQ(2u, resource_provider_->num_resources()); + + resource_provider_->DestroyChild(child_id); + + EXPECT_EQ(0u, resource_provider_->num_resources()); + ASSERT_EQ(2u, returned_to_child.size()); + EXPECT_NE(0u, returned_to_child[0].sync_point); + EXPECT_NE(0u, returned_to_child[1].sync_point); + EXPECT_TRUE(returned_to_child[0].lost); + EXPECT_TRUE(returned_to_child[1].lost); + returned_to_child.clear(); + + // Return the resources from the grandparent to the parent. They should be + // dropped on the floor since they were already returned to the child. + EXPECT_EQ(2u, list.size()); + EXPECT_EQ(mapped_id1, list[0].id); + EXPECT_EQ(mapped_id2, list[1].id); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + resource_provider_->ReceiveReturnsFromParent(returned); + + EXPECT_EQ(0u, returned_to_child.size()); + } +} + +TEST_P(ResourceProviderTest, DeleteTransferredResources) { + // Resource transfer is only supported with GL textures for now. + if (GetParam() != ResourceProvider::GLTexture) + return; + + scoped_ptr<ResourceProviderContext> child_context_owned( + ResourceProviderContext::Create(shared_data_.get())); + + FakeOutputSurfaceClient child_output_surface_client; + scoped_ptr<OutputSurface> child_output_surface( + FakeOutputSurface::Create3d( + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + + scoped_ptr<ResourceProvider> child_resource_provider( + ResourceProvider::Create(child_output_surface.get(), 0, false)); + + gfx::Size size(1, 1); + ResourceFormat format = RGBA_8888; size_t pixel_size = TextureSize(size, format); ASSERT_EQ(4U, pixel_size); ResourceProvider::ResourceId id = child_resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); uint8_t data[4] = { 1, 2, 3, 4 }; gfx::Rect rect(size); child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d()); - EXPECT_EQ(static_cast<unsigned>(GL_LINEAR), - GetResourceFilter(child_resource_provider.get(), id)); - SetResourceFilter(child_resource_provider.get(), id, GL_NEAREST); - EXPECT_EQ(static_cast<unsigned>(GL_NEAREST), - GetResourceFilter(child_resource_provider.get(), id)); - int child_id = resource_provider_->CreateChild(); + ReturnedResourceArray returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); { // Transfer some resource to the parent. ResourceProvider::ResourceIdArray resource_ids_to_transfer; @@ -733,34 +959,196 @@ TEST_P(ResourceProviderTest, TextureFilters) { child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list); ASSERT_EQ(1u, list.size()); - EXPECT_EQ(static_cast<unsigned>(GL_NEAREST), list[0].filter); + EXPECT_NE(0u, list[0].sync_point); + EXPECT_TRUE(child_resource_provider->InUseByConsumer(id)); resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); } - ResourceProvider::ResourceIdMap resource_map = - resource_provider_->GetChildToParentMap(child_id); - ResourceProvider::ResourceId mapped_id = resource_map[id]; - EXPECT_NE(0u, mapped_id); - EXPECT_EQ(static_cast<unsigned>(GL_NEAREST), - GetResourceFilter(resource_provider_.get(), mapped_id)); - SetResourceFilter(resource_provider_.get(), mapped_id, GL_LINEAR); - EXPECT_EQ(static_cast<unsigned>(GL_LINEAR), - GetResourceFilter(resource_provider_.get(), mapped_id)); + + // Delete textures in the child, while they are transfered. + child_resource_provider->DeleteResource(id); + EXPECT_EQ(1u, child_resource_provider->num_resources()); { - // Transfer resources back from the parent to the child. - ResourceProvider::ResourceIdArray resource_ids_to_transfer; - resource_ids_to_transfer.push_back(mapped_id); - TransferableResourceArray list; - resource_provider_->PrepareSendToChild( - child_id, resource_ids_to_transfer, &list); - ASSERT_EQ(1u, list.size()); - EXPECT_EQ(static_cast<unsigned>(GL_LINEAR), list[0].filter); - child_resource_provider->ReceiveFromParent(list); - } - EXPECT_EQ(static_cast<unsigned>(GL_LINEAR), - GetResourceFilter(child_resource_provider.get(), id)); - SetResourceFilter(child_resource_provider.get(), id, GL_NEAREST); - EXPECT_EQ(static_cast<unsigned>(GL_NEAREST), - GetResourceFilter(child_resource_provider.get(), id)); + EXPECT_EQ(0u, returned_to_child.size()); + + // Transfer resources back from the parent to the child. Set no resources as + // being in use. + ResourceProvider::ResourceIdArray no_resources; + resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources); + + ASSERT_EQ(1u, returned_to_child.size()); + EXPECT_NE(0u, returned_to_child[0].sync_point); + child_resource_provider->ReceiveReturnsFromParent(returned_to_child); + } + EXPECT_EQ(0u, child_resource_provider->num_resources()); +} + +class ResourceProviderTestTextureFilters : public ResourceProviderTest { + public: + static void RunTest(GLenum child_filter, GLenum parent_filter) { + scoped_ptr<TextureStateTrackingContext> child_context_owned( + new TextureStateTrackingContext); + TextureStateTrackingContext* child_context = child_context_owned.get(); + + FakeOutputSurfaceClient child_output_surface_client; + scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d( + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + + scoped_ptr<ResourceProvider> child_resource_provider( + ResourceProvider::Create(child_output_surface.get(), 0, false)); + + scoped_ptr<TextureStateTrackingContext> parent_context_owned( + new TextureStateTrackingContext); + TextureStateTrackingContext* parent_context = parent_context_owned.get(); + + FakeOutputSurfaceClient parent_output_surface_client; + scoped_ptr<OutputSurface> parent_output_surface(FakeOutputSurface::Create3d( + parent_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(parent_output_surface->BindToClient(&parent_output_surface_client)); + + scoped_ptr<ResourceProvider> parent_resource_provider( + ResourceProvider::Create(parent_output_surface.get(), 0, false)); + + gfx::Size size(1, 1); + ResourceFormat format = RGBA_8888; + int texture_id = 1; + + size_t pixel_size = TextureSize(size, format); + ASSERT_EQ(4U, pixel_size); + + ResourceProvider::ResourceId id = child_resource_provider->CreateResource( + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); + + // The new texture is created with GL_LINEAR. + EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id)) + .Times(2); // Once to create and once to allocate. + EXPECT_CALL(*child_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + EXPECT_CALL(*child_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + EXPECT_CALL( + *child_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + EXPECT_CALL( + *child_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + EXPECT_CALL(*child_context, + texParameteri(GL_TEXTURE_2D, + GL_TEXTURE_POOL_CHROMIUM, + GL_TEXTURE_POOL_UNMANAGED_CHROMIUM)); + child_resource_provider->AllocateForTesting(id); + Mock::VerifyAndClearExpectations(child_context); + + uint8_t data[4] = { 1, 2, 3, 4 }; + gfx::Rect rect(size); + + EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id)); + child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d()); + Mock::VerifyAndClearExpectations(child_context); + + // The texture is set to |child_filter| in the child. + EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id)); + if (child_filter != GL_LINEAR) { + EXPECT_CALL( + *child_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, child_filter)); + EXPECT_CALL( + *child_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, child_filter)); + } + SetResourceFilter(child_resource_provider.get(), id, child_filter); + Mock::VerifyAndClearExpectations(child_context); + + ReturnedResourceArray returned_to_child; + int child_id = parent_resource_provider->CreateChild( + GetReturnCallback(&returned_to_child)); + { + // Transfer some resource to the parent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(id); + TransferableResourceArray list; + + EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id)); + EXPECT_CALL(*child_context, + produceTextureCHROMIUM(GL_TEXTURE_2D, _)); + EXPECT_CALL(*child_context, insertSyncPoint()); + child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, + &list); + Mock::VerifyAndClearExpectations(child_context); + + ASSERT_EQ(1u, list.size()); + EXPECT_EQ(static_cast<unsigned>(child_filter), list[0].filter); + + EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, texture_id)); + EXPECT_CALL(*parent_context, + consumeTextureCHROMIUM(GL_TEXTURE_2D, _)); + parent_resource_provider->ReceiveFromChild(child_id, list); + Mock::VerifyAndClearExpectations(parent_context); + + parent_resource_provider->DeclareUsedResourcesFromChild( + child_id, resource_ids_to_transfer); + Mock::VerifyAndClearExpectations(parent_context); + } + ResourceProvider::ResourceIdMap resource_map = + parent_resource_provider->GetChildToParentMap(child_id); + ResourceProvider::ResourceId mapped_id = resource_map[id]; + EXPECT_NE(0u, mapped_id); + + // The texture is set to |parent_filter| in the parent. + EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, texture_id)); + EXPECT_CALL( + *parent_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, parent_filter)); + EXPECT_CALL( + *parent_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, parent_filter)); + SetResourceFilter(parent_resource_provider.get(), mapped_id, parent_filter); + Mock::VerifyAndClearExpectations(parent_context); + + // The texture should be reset to |child_filter| in the parent when it is + // returned, since that is how it was received. + EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, texture_id)); + EXPECT_CALL( + *parent_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, child_filter)); + EXPECT_CALL( + *parent_context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, child_filter)); + + { + EXPECT_EQ(0u, returned_to_child.size()); + + // Transfer resources back from the parent to the child. Set no resources + // as being in use. + ResourceProvider::ResourceIdArray no_resources; + EXPECT_CALL(*parent_context, insertSyncPoint()); + parent_resource_provider->DeclareUsedResourcesFromChild(child_id, + no_resources); + Mock::VerifyAndClearExpectations(parent_context); + + ASSERT_EQ(1u, returned_to_child.size()); + child_resource_provider->ReceiveReturnsFromParent(returned_to_child); + } + + // The child remembers the texture filter is set to |child_filter|. + EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id)); + SetResourceFilter(child_resource_provider.get(), id, child_filter); + Mock::VerifyAndClearExpectations(child_context); + } +}; + +TEST_P(ResourceProviderTest, TextureFilters_ChildNearestParentLinear) { + if (GetParam() != ResourceProvider::GLTexture) + return; + ResourceProviderTestTextureFilters::RunTest(GL_NEAREST, GL_LINEAR); +} + +TEST_P(ResourceProviderTest, TextureFilters_ChildLinearParentNearest) { + if (GetParam() != ResourceProvider::GLTexture) + return; + ResourceProviderTestTextureFilters::RunTest(GL_LINEAR, GL_NEAREST); } void ReleaseTextureMailbox(unsigned* release_sync_point, @@ -790,11 +1178,12 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) { unsigned release_sync_point = 0; bool lost_resource = false; - TextureMailbox::ReleaseCallback callback = + ReleaseCallback callback = base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource); ResourceProvider::ResourceId resource = resource_provider_->CreateResourceFromTextureMailbox( - TextureMailbox(mailbox, callback, sync_point)); + TextureMailbox(mailbox, sync_point), + SingleReleaseCallback::Create(callback)); EXPECT_EQ(1, context()->texture_count()); EXPECT_EQ(0u, release_sync_point); { @@ -814,7 +1203,8 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) { context()->bindTexture(GL_TEXTURE_2D, other_texture); context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); uint8_t test_data[4] = { 0 }; - context()->GetPixels(gfx::Size(1, 1), GL_RGBA, test_data); + context()->GetPixels( + gfx::Size(1, 1), RGBA_8888, test_data); EXPECT_EQ(0, memcmp(data, test_data, sizeof(data))); context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); context()->deleteTexture(other_texture); @@ -823,7 +1213,9 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) { // Receive the resource, then delete it, expect the sync points to be // consistent. - resource_provider_->ReceiveFromParent(list); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + resource_provider_->ReceiveReturnsFromParent(returned); EXPECT_EQ(1, context()->texture_count()); EXPECT_EQ(0u, release_sync_point); @@ -838,7 +1230,8 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) { EXPECT_LT(0u, sync_point); release_sync_point = 0; resource = resource_provider_->CreateResourceFromTextureMailbox( - TextureMailbox(mailbox, callback, sync_point)); + TextureMailbox(mailbox, sync_point), + SingleReleaseCallback::Create(callback)); EXPECT_EQ(1, context()->texture_count()); EXPECT_EQ(0u, release_sync_point); { @@ -858,7 +1251,8 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) { context()->bindTexture(GL_TEXTURE_2D, other_texture); context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); uint8_t test_data[4] = { 0 }; - context()->GetPixels(gfx::Size(1, 1), GL_RGBA, test_data); + context()->GetPixels( + gfx::Size(1, 1), RGBA_8888, test_data); EXPECT_EQ(0, memcmp(data, test_data, sizeof(data))); context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); context()->deleteTexture(other_texture); @@ -872,7 +1266,9 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) { // Then receive the resource which should release the mailbox, expect the // sync points to be consistent. - resource_provider_->ReceiveFromParent(list); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + resource_provider_->ReceiveReturnsFromParent(returned); EXPECT_LE(list[0].sync_point, release_sync_point); EXPECT_FALSE(lost_resource); } @@ -883,6 +1279,330 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) { context()->deleteTexture(texture); } +TEST_P(ResourceProviderTest, LostResourceInParent) { + // Resource transfer is only supported with GL textures for now. + if (GetParam() != ResourceProvider::GLTexture) + return; + + scoped_ptr<ResourceProviderContext> child_context_owned( + ResourceProviderContext::Create(shared_data_.get())); + + FakeOutputSurfaceClient child_output_surface_client; + scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d( + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + + scoped_ptr<ResourceProvider> child_resource_provider( + ResourceProvider::Create(child_output_surface.get(), 0, false)); + + gfx::Size size(1, 1); + ResourceFormat format = RGBA_8888; + ResourceProvider::ResourceId resource = + child_resource_provider->CreateResource( + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); + child_resource_provider->AllocateForTesting(resource); + + ReturnedResourceArray returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); + { + // Transfer the resource to the parent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(resource); + TransferableResourceArray list; + child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, + &list); + EXPECT_EQ(1u, list.size()); + + resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); + } + + // Lose the output surface in the parent. + resource_provider_->DidLoseOutputSurface(); + + { + EXPECT_EQ(0u, returned_to_child.size()); + + // Transfer resources back from the parent to the child. Set no resources as + // being in use. + ResourceProvider::ResourceIdArray no_resources; + resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources); + + // Expect the resource to be lost. + ASSERT_EQ(1u, returned_to_child.size()); + EXPECT_TRUE(returned_to_child[0].lost); + child_resource_provider->ReceiveReturnsFromParent(returned_to_child); + returned_to_child.clear(); + } + + // The resource should be lost. + EXPECT_TRUE(child_resource_provider->IsLost(resource)); + + // Lost resources stay in use in the parent forever. + EXPECT_TRUE(child_resource_provider->InUseByConsumer(resource)); +} + +TEST_P(ResourceProviderTest, LostResourceInGrandParent) { + // Resource transfer is only supported with GL textures for now. + if (GetParam() != ResourceProvider::GLTexture) + return; + + scoped_ptr<ResourceProviderContext> child_context_owned( + ResourceProviderContext::Create(shared_data_.get())); + + FakeOutputSurfaceClient child_output_surface_client; + scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d( + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + + scoped_ptr<ResourceProvider> child_resource_provider( + ResourceProvider::Create(child_output_surface.get(), 0, false)); + + gfx::Size size(1, 1); + ResourceFormat format = RGBA_8888; + ResourceProvider::ResourceId resource = + child_resource_provider->CreateResource( + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); + child_resource_provider->AllocateForTesting(resource); + + ReturnedResourceArray returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); + { + // Transfer the resource to the parent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(resource); + TransferableResourceArray list; + child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, + &list); + EXPECT_EQ(1u, list.size()); + + resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); + } + + { + ResourceProvider::ResourceIdMap resource_map = + resource_provider_->GetChildToParentMap(child_id); + ResourceProvider::ResourceId parent_resource = resource_map[resource]; + EXPECT_NE(0u, parent_resource); + + // Transfer to a grandparent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(parent_resource); + TransferableResourceArray list; + resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list); + + // Receive back a lost resource from the grandparent. + EXPECT_EQ(1u, list.size()); + EXPECT_EQ(parent_resource, list[0].id); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + EXPECT_EQ(1u, returned.size()); + EXPECT_EQ(parent_resource, returned[0].id); + returned[0].lost = true; + resource_provider_->ReceiveReturnsFromParent(returned); + + // The resource should be lost. + EXPECT_TRUE(resource_provider_->IsLost(parent_resource)); + + // Lost resources stay in use in the parent forever. + EXPECT_TRUE(resource_provider_->InUseByConsumer(parent_resource)); + } + + { + EXPECT_EQ(0u, returned_to_child.size()); + + // Transfer resources back from the parent to the child. Set no resources as + // being in use. + ResourceProvider::ResourceIdArray no_resources; + resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources); + + // Expect the resource to be lost. + ASSERT_EQ(1u, returned_to_child.size()); + EXPECT_TRUE(returned_to_child[0].lost); + child_resource_provider->ReceiveReturnsFromParent(returned_to_child); + returned_to_child.clear(); + } + + // The resource should be lost. + EXPECT_TRUE(child_resource_provider->IsLost(resource)); + + // Lost resources stay in use in the parent forever. + EXPECT_TRUE(child_resource_provider->InUseByConsumer(resource)); +} + +TEST_P(ResourceProviderTest, LostMailboxInParent) { + // Resource transfer is only supported with GL textures for now. + if (GetParam() != ResourceProvider::GLTexture) + return; + + scoped_ptr<ResourceProviderContext> child_context_owned( + ResourceProviderContext::Create(shared_data_.get())); + ResourceProviderContext* child_context = child_context_owned.get(); + + FakeOutputSurfaceClient child_output_surface_client; + scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d( + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + + scoped_ptr<ResourceProvider> child_resource_provider( + ResourceProvider::Create(child_output_surface.get(), 0, false)); + + unsigned texture = child_context->createTexture(); + gpu::Mailbox gpu_mailbox; + child_context->bindTexture(GL_TEXTURE_2D, texture); + child_context->genMailboxCHROMIUM(gpu_mailbox.name); + child_context->produceTextureCHROMIUM(GL_TEXTURE_2D, gpu_mailbox.name); + + unsigned release_sync_point = 0; + bool lost_resource = false; + ReleaseCallback callback = + base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource); + ResourceProvider::ResourceId resource = + child_resource_provider->CreateResourceFromTextureMailbox( + TextureMailbox(gpu_mailbox), SingleReleaseCallback::Create(callback)); + + ReturnedResourceArray returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); + { + // Transfer the resource to the parent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(resource); + TransferableResourceArray list; + child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, + &list); + EXPECT_EQ(1u, list.size()); + + resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); + } + + // Lose the output surface in the parent. + resource_provider_->DidLoseOutputSurface(); + + { + EXPECT_EQ(0u, returned_to_child.size()); + + // Transfer resources back from the parent to the child. Set no resources as + // being in use. + ResourceProvider::ResourceIdArray no_resources; + resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources); + + // Expect the resource to be lost. + ASSERT_EQ(1u, returned_to_child.size()); + EXPECT_TRUE(returned_to_child[0].lost); + child_resource_provider->ReceiveReturnsFromParent(returned_to_child); + returned_to_child.clear(); + } + + // Delete the resource in the child. Expect the resource to be lost. + child_resource_provider->DeleteResource(resource); + EXPECT_TRUE(lost_resource); + + child_context->waitSyncPoint(release_sync_point); + child_context->deleteTexture(texture); +} + +TEST_P(ResourceProviderTest, LostMailboxInGrandParent) { + // Resource transfer is only supported with GL textures for now. + if (GetParam() != ResourceProvider::GLTexture) + return; + + scoped_ptr<ResourceProviderContext> child_context_owned( + ResourceProviderContext::Create(shared_data_.get())); + ResourceProviderContext* child_context = child_context_owned.get(); + + FakeOutputSurfaceClient child_output_surface_client; + scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d( + child_context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(child_output_surface->BindToClient(&child_output_surface_client)); + + scoped_ptr<ResourceProvider> child_resource_provider( + ResourceProvider::Create(child_output_surface.get(), 0, false)); + + unsigned texture = child_context->createTexture(); + gpu::Mailbox gpu_mailbox; + child_context->bindTexture(GL_TEXTURE_2D, texture); + child_context->genMailboxCHROMIUM(gpu_mailbox.name); + child_context->produceTextureCHROMIUM(GL_TEXTURE_2D, gpu_mailbox.name); + + unsigned release_sync_point = 0; + bool lost_resource = false; + ReleaseCallback callback = + base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource); + ResourceProvider::ResourceId resource = + child_resource_provider->CreateResourceFromTextureMailbox( + TextureMailbox(gpu_mailbox), SingleReleaseCallback::Create(callback)); + + ReturnedResourceArray returned_to_child; + int child_id = + resource_provider_->CreateChild(GetReturnCallback(&returned_to_child)); + { + // Transfer the resource to the parent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(resource); + TransferableResourceArray list; + child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, + &list); + EXPECT_EQ(1u, list.size()); + + resource_provider_->ReceiveFromChild(child_id, list); + resource_provider_->DeclareUsedResourcesFromChild(child_id, + resource_ids_to_transfer); + } + + { + ResourceProvider::ResourceIdMap resource_map = + resource_provider_->GetChildToParentMap(child_id); + ResourceProvider::ResourceId parent_resource = resource_map[resource]; + EXPECT_NE(0u, parent_resource); + + // Transfer to a grandparent. + ResourceProvider::ResourceIdArray resource_ids_to_transfer; + resource_ids_to_transfer.push_back(parent_resource); + TransferableResourceArray list; + resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list); + + // Receive back a lost resource from the grandparent. + EXPECT_EQ(1u, list.size()); + EXPECT_EQ(parent_resource, list[0].id); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + EXPECT_EQ(1u, returned.size()); + EXPECT_EQ(parent_resource, returned[0].id); + returned[0].lost = true; + resource_provider_->ReceiveReturnsFromParent(returned); + } + + { + EXPECT_EQ(0u, returned_to_child.size()); + + // Transfer resources back from the parent to the child. Set no resources as + // being in use. + ResourceProvider::ResourceIdArray no_resources; + resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources); + + // Expect the resource to be lost. + ASSERT_EQ(1u, returned_to_child.size()); + EXPECT_TRUE(returned_to_child[0].lost); + child_resource_provider->ReceiveReturnsFromParent(returned_to_child); + returned_to_child.clear(); + } + + // Delete the resource in the child. Expect the resource to be lost. + child_resource_provider->DeleteResource(resource); + EXPECT_TRUE(lost_resource); + + child_context->waitSyncPoint(release_sync_point); + child_context->deleteTexture(texture); +} + TEST_P(ResourceProviderTest, Shutdown) { // TextureMailbox callbacks only exist for GL textures for now. if (GetParam() != ResourceProvider::GLTexture) @@ -898,10 +1618,11 @@ TEST_P(ResourceProviderTest, Shutdown) { unsigned release_sync_point = 0; bool lost_resource = false; - TextureMailbox::ReleaseCallback callback = - base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource)); resource_provider_->CreateResourceFromTextureMailbox( - TextureMailbox(mailbox, callback, sync_point)); + TextureMailbox(mailbox, sync_point), + callback.Pass()); EXPECT_EQ(0u, release_sync_point); EXPECT_FALSE(lost_resource); @@ -937,10 +1658,11 @@ TEST_P(ResourceProviderTest, ShutdownSharedMemory) { CreateAndFillSharedMemory(size, 0)); bool release_called = false; - TextureMailbox::ReleaseCallback callback = - base::Bind(ReleaseSharedMemoryCallback, &release_called); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind(ReleaseSharedMemoryCallback, &release_called)); resource_provider_->CreateResourceFromTextureMailbox( - TextureMailbox(shared_memory.get(), size, callback)); + TextureMailbox(shared_memory.get(), size), + callback.Pass()); resource_provider_.reset(); @@ -962,11 +1684,12 @@ TEST_P(ResourceProviderTest, ShutdownWithExportedResource) { unsigned release_sync_point = 0; bool lost_resource = false; - TextureMailbox::ReleaseCallback callback = - base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource)); ResourceProvider::ResourceId resource = resource_provider_->CreateResourceFromTextureMailbox( - TextureMailbox(mailbox, callback, sync_point)); + TextureMailbox(mailbox, sync_point), + callback.Pass()); // Transfer the resource, so we can't release it properly on shutdown. ResourceProvider::ResourceIdArray resource_ids_to_transfer; @@ -999,10 +1722,11 @@ TEST_P(ResourceProviderTest, LostContext) { unsigned release_sync_point = 0; bool lost_resource = false; - TextureMailbox::ReleaseCallback callback = - base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource)); resource_provider_->CreateResourceFromTextureMailbox( - TextureMailbox(mailbox, callback, sync_point)); + TextureMailbox(mailbox, sync_point), + callback.Pass()); EXPECT_EQ(0u, release_sync_point); EXPECT_FALSE(lost_resource); @@ -1014,39 +1738,30 @@ TEST_P(ResourceProviderTest, LostContext) { EXPECT_TRUE(lost_resource); } -class TextureStateTrackingContext : public TestWebGraphicsContext3D { - public: - MOCK_METHOD2(bindTexture, void(WGC3Denum target, WebGLId texture)); - MOCK_METHOD3(texParameteri, - void(WGC3Denum target, WGC3Denum pname, WGC3Dint param)); - MOCK_METHOD1(waitSyncPoint, void(unsigned sync_point)); - MOCK_METHOD0(insertSyncPoint, unsigned(void)); - MOCK_METHOD2(produceTextureCHROMIUM, void(WGC3Denum target, - const WGC3Dbyte* mailbox)); - MOCK_METHOD2(consumeTextureCHROMIUM, void(WGC3Denum target, - const WGC3Dbyte* mailbox)); - - // Force all textures to be "1" so we can test for them. - virtual WebKit::WebGLId NextTextureId() OVERRIDE { return 1; } -}; - TEST_P(ResourceProviderTest, ScopedSampler) { // Sampling is only supported for GL textures. if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new TextureStateTrackingContext))); - TextureStateTrackingContext* context = - static_cast<TextureStateTrackingContext*>(output_surface->context3d()); + scoped_ptr<TextureStateTrackingContext> context_owned( + new TextureStateTrackingContext); + TextureStateTrackingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); gfx::Size size(1, 1); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; int texture_id = 1; + ResourceProvider::ResourceId id = resource_provider->CreateResource( + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); + // Check that the texture gets created with the right sampler settings. EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)) .Times(2); // Once to create and once to allocate. @@ -1064,9 +1779,9 @@ TEST_P(ResourceProviderTest, ScopedSampler) { texParameteri(GL_TEXTURE_2D, GL_TEXTURE_POOL_CHROMIUM, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM)); - ResourceProvider::ResourceId id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + resource_provider->AllocateForTesting(id); + Mock::VerifyAndClearExpectations(context); // Creating a sampler with the default filter should not change any texture // parameters. @@ -1074,6 +1789,7 @@ TEST_P(ResourceProviderTest, ScopedSampler) { EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)); ResourceProvider::ScopedSamplerGL sampler( resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR); + Mock::VerifyAndClearExpectations(context); } // Using a different filter should be reflected in the texture parameters. @@ -1087,6 +1803,7 @@ TEST_P(ResourceProviderTest, ScopedSampler) { texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); ResourceProvider::ScopedSamplerGL sampler( resource_provider.get(), id, GL_TEXTURE_2D, GL_NEAREST); + Mock::VerifyAndClearExpectations(context); } // Test resetting to the default filter. @@ -1098,9 +1815,8 @@ TEST_P(ResourceProviderTest, ScopedSampler) { texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); ResourceProvider::ScopedSamplerGL sampler( resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR); + Mock::VerifyAndClearExpectations(context); } - - Mock::VerifyAndClearExpectations(context); } TEST_P(ResourceProviderTest, ManagedResource) { @@ -1108,21 +1824,25 @@ TEST_P(ResourceProviderTest, ManagedResource) { if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new TextureStateTrackingContext))); - TextureStateTrackingContext* context = - static_cast<TextureStateTrackingContext*>(output_surface->context3d()); + scoped_ptr<TextureStateTrackingContext> context_owned( + new TextureStateTrackingContext); + TextureStateTrackingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); gfx::Size size(1, 1); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; int texture_id = 1; // Check that the texture gets created with the right sampler settings. ResourceProvider::ResourceId id = resource_provider->CreateManagedResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)); EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); @@ -1144,6 +1864,59 @@ TEST_P(ResourceProviderTest, ManagedResource) { Mock::VerifyAndClearExpectations(context); } +TEST_P(ResourceProviderTest, TextureWrapMode) { + // Sampling is only supported for GL textures. + if (GetParam() != ResourceProvider::GLTexture) + return; + + scoped_ptr<TextureStateTrackingContext> context_owned( + new TextureStateTrackingContext); + TextureStateTrackingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + + scoped_ptr<ResourceProvider> resource_provider( + ResourceProvider::Create(output_surface.get(), 0, false)); + + gfx::Size size(1, 1); + ResourceFormat format = RGBA_8888; + int texture_id = 1; + GLenum texture_pool = GL_TEXTURE_POOL_UNMANAGED_CHROMIUM; + + for (int i = 0; i < 2; ++i) { + GLint wrap_mode = i ? GL_CLAMP_TO_EDGE : GL_REPEAT; + // Check that the texture gets created with the right sampler settings. + ResourceProvider::ResourceId id = + resource_provider->CreateGLTexture(size, + texture_pool, + wrap_mode, + ResourceProvider::TextureUsageAny, + format); + EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)); + EXPECT_CALL(*context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + EXPECT_CALL(*context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + EXPECT_CALL( + *context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode)); + EXPECT_CALL( + *context, + texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode)); + EXPECT_CALL(*context, + texParameteri(GL_TEXTURE_2D, + GL_TEXTURE_POOL_CHROMIUM, + GL_TEXTURE_POOL_UNMANAGED_CHROMIUM)); + resource_provider->CreateForTesting(id); + EXPECT_NE(0u, id); + + Mock::VerifyAndClearExpectations(context); + } +} + static void EmptyReleaseCallback(unsigned sync_point, bool lost_resource) {} TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory) { @@ -1155,17 +1928,22 @@ TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory) { scoped_ptr<base::SharedMemory> shared_memory( CreateAndFillSharedMemory(size, kBadBeef)); + FakeOutputSurfaceClient output_surface_client; scoped_ptr<OutputSurface> output_surface( FakeOutputSurface::CreateSoftware(make_scoped_ptr( new SoftwareOutputDevice))); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); - TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback); - TextureMailbox mailbox(shared_memory.get(), size, callback); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind(&EmptyReleaseCallback)); + TextureMailbox mailbox(shared_memory.get(), size); ResourceProvider::ResourceId id = - resource_provider->CreateResourceFromTextureMailbox(mailbox); + resource_provider->CreateResourceFromTextureMailbox( + mailbox, callback.Pass()); EXPECT_NE(0u, id); { @@ -1182,13 +1960,17 @@ TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D) { if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new TextureStateTrackingContext))); - TextureStateTrackingContext* context = - static_cast<TextureStateTrackingContext*>(output_surface->context3d()); + scoped_ptr<TextureStateTrackingContext> context_owned( + new TextureStateTrackingContext); + TextureStateTrackingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); unsigned texture_id = 1; unsigned sync_point = 30; @@ -1202,14 +1984,14 @@ TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D) { gpu::Mailbox gpu_mailbox; memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1); - TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind(&EmptyReleaseCallback)); - TextureMailbox mailbox(gpu_mailbox, - callback, - sync_point); + TextureMailbox mailbox(gpu_mailbox, sync_point); ResourceProvider::ResourceId id = - resource_provider->CreateResourceFromTextureMailbox(mailbox); + resource_provider->CreateResourceFromTextureMailbox( + mailbox, callback.Pass()); EXPECT_NE(0u, id); Mock::VerifyAndClearExpectations(context); @@ -1242,13 +2024,17 @@ TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES) { if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new TextureStateTrackingContext))); - TextureStateTrackingContext* context = - static_cast<TextureStateTrackingContext*>(output_surface->context3d()); + scoped_ptr<TextureStateTrackingContext> context_owned( + new TextureStateTrackingContext); + TextureStateTrackingContext* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); unsigned texture_id = 1; unsigned sync_point = 30; @@ -1262,15 +2048,14 @@ TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES) { gpu::Mailbox gpu_mailbox; memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1); - TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind(&EmptyReleaseCallback)); - TextureMailbox mailbox(gpu_mailbox, - callback, - target, - sync_point); + TextureMailbox mailbox(gpu_mailbox, target, sync_point); ResourceProvider::ResourceId id = - resource_provider->CreateResourceFromTextureMailbox(mailbox); + resource_provider->CreateResourceFromTextureMailbox( + mailbox, callback.Pass()); EXPECT_NE(0u, id); Mock::VerifyAndClearExpectations(context); @@ -1359,28 +2144,29 @@ TEST_P(ResourceProviderTest, TextureAllocation) { // Only for GL textures. if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<WebKit::WebGraphicsContext3D> mock_context( - static_cast<WebKit::WebGraphicsContext3D*>( - new StrictMock<AllocationTrackingContext3D>)); - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(mock_context.Pass())); + scoped_ptr<AllocationTrackingContext3D> context_owned( + new StrictMock<AllocationTrackingContext3D>); + AllocationTrackingContext3D* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); + + scoped_ptr<ResourceProvider> resource_provider( + ResourceProvider::Create(output_surface.get(), 0, false)); gfx::Size size(2, 2); gfx::Vector2d offset(0, 0); gfx::Rect rect(0, 0, 2, 2); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; ResourceProvider::ResourceId id = 0; uint8_t pixels[16] = { 0 }; int texture_id = 123; - AllocationTrackingContext3D* context = - static_cast<AllocationTrackingContext3D*>(output_surface->context3d()); - scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); - // Lazy allocation. Don't allocate when creating the resource. id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id)); EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1); @@ -1393,7 +2179,7 @@ TEST_P(ResourceProviderTest, TextureAllocation) { // Do allocate when we set the pixels. id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id)); EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3); @@ -1408,7 +2194,7 @@ TEST_P(ResourceProviderTest, TextureAllocation) { // Same for async version. id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); resource_provider->AcquirePixelBuffer(id); EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id)); @@ -1429,24 +2215,25 @@ TEST_P(ResourceProviderTest, TextureAllocation) { TEST_P(ResourceProviderTest, PixelBuffer_GLTexture) { if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<WebKit::WebGraphicsContext3D> mock_context( - static_cast<WebKit::WebGraphicsContext3D*>( - new StrictMock<AllocationTrackingContext3D>)); - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(mock_context.Pass())); + scoped_ptr<AllocationTrackingContext3D> context_owned( + new StrictMock<AllocationTrackingContext3D>); + AllocationTrackingContext3D* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); gfx::Size size(2, 2); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; ResourceProvider::ResourceId id = 0; int texture_id = 123; - AllocationTrackingContext3D* context = - static_cast<AllocationTrackingContext3D*>(output_surface->context3d()); scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); resource_provider->AcquirePixelBuffer(id); EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id)); @@ -1468,20 +2255,22 @@ TEST_P(ResourceProviderTest, PixelBuffer_GLTexture) { TEST_P(ResourceProviderTest, PixelBuffer_Bitmap) { if (GetParam() != ResourceProvider::Bitmap) return; + FakeOutputSurfaceClient output_surface_client; scoped_ptr<OutputSurface> output_surface( FakeOutputSurface::CreateSoftware(make_scoped_ptr( new SoftwareOutputDevice))); + CHECK(output_surface->BindToClient(&output_surface_client)); gfx::Size size(1, 1); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; ResourceProvider::ResourceId id = 0; const uint32_t kBadBeef = 0xbadbeef; scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); resource_provider->AcquirePixelBuffer(id); void* data = resource_provider->MapPixelBuffer(id); @@ -1509,24 +2298,25 @@ TEST_P(ResourceProviderTest, ForcingAsyncUploadToComplete) { // Only for GL textures. if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<WebKit::WebGraphicsContext3D> mock_context( - static_cast<WebKit::WebGraphicsContext3D*>( - new StrictMock<AllocationTrackingContext3D>)); - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(mock_context.Pass())); + scoped_ptr<AllocationTrackingContext3D> context_owned( + new StrictMock<AllocationTrackingContext3D>); + AllocationTrackingContext3D* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); gfx::Size size(2, 2); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; ResourceProvider::ResourceId id = 0; int texture_id = 123; - AllocationTrackingContext3D* context = - static_cast<AllocationTrackingContext3D*>(output_surface->context3d()); scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); resource_provider->AcquirePixelBuffer(id); EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id)); @@ -1549,26 +2339,27 @@ TEST_P(ResourceProviderTest, ForcingAsyncUploadToComplete) { } TEST_P(ResourceProviderTest, PixelBufferLostContext) { - scoped_ptr<WebKit::WebGraphicsContext3D> mock_context( - static_cast<WebKit::WebGraphicsContext3D*>( - new NiceMock<AllocationTrackingContext3D>)); - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(mock_context.Pass())); + scoped_ptr<AllocationTrackingContext3D> context_owned( + new NiceMock<AllocationTrackingContext3D>); + AllocationTrackingContext3D* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); gfx::Size size(2, 2); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; ResourceProvider::ResourceId id = 0; int texture_id = 123; - AllocationTrackingContext3D* context = - static_cast<AllocationTrackingContext3D*>(output_surface->context3d()); scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); EXPECT_CALL(*context, createTexture()).WillRepeatedly(Return(texture_id)); id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); context->loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); resource_provider->AcquirePixelBuffer(id); @@ -1583,27 +2374,28 @@ TEST_P(ResourceProviderTest, Image_GLTexture) { // Only for GL textures. if (GetParam() != ResourceProvider::GLTexture) return; - scoped_ptr<WebKit::WebGraphicsContext3D> mock_context( - static_cast<WebKit::WebGraphicsContext3D*>( - new StrictMock<AllocationTrackingContext3D>)); - scoped_ptr<OutputSurface> output_surface( - FakeOutputSurface::Create3d(mock_context.Pass())); + scoped_ptr<AllocationTrackingContext3D> context_owned( + new StrictMock<AllocationTrackingContext3D>); + AllocationTrackingContext3D* context = context_owned.get(); + + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); + CHECK(output_surface->BindToClient(&output_surface_client)); const int kWidth = 2; const int kHeight = 2; gfx::Size size(kWidth, kHeight); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; ResourceProvider::ResourceId id = 0; const unsigned kTextureId = 123u; const unsigned kImageId = 234u; - AllocationTrackingContext3D* context = - static_cast<AllocationTrackingContext3D*>(output_surface->context3d()); scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); EXPECT_CALL(*context, createImageCHROMIUM(kWidth, kHeight, GL_RGBA8_OES)) .WillOnce(Return(kImageId)) .RetiresOnSaturation(); @@ -1659,20 +2451,22 @@ TEST_P(ResourceProviderTest, Image_GLTexture) { TEST_P(ResourceProviderTest, Image_Bitmap) { if (GetParam() != ResourceProvider::Bitmap) return; + FakeOutputSurfaceClient output_surface_client; scoped_ptr<OutputSurface> output_surface( FakeOutputSurface::CreateSoftware(make_scoped_ptr( new SoftwareOutputDevice))); + CHECK(output_surface->BindToClient(&output_surface_client)); gfx::Size size(1, 1); - WGC3Denum format = GL_RGBA; + ResourceFormat format = RGBA_8888; ResourceProvider::ResourceId id = 0; const uint32_t kBadBeef = 0xbadbeef; scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); id = resource_provider->CreateResource( - size, format, ResourceProvider::TextureUsageAny); + size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format); resource_provider->AcquireImage(id); const int kStride = 0; @@ -1699,15 +2493,17 @@ TEST_P(ResourceProviderTest, Image_Bitmap) { void InitializeGLAndCheck(ContextSharedData* shared_data, ResourceProvider* resource_provider, FakeOutputSurface* output_surface) { - scoped_ptr<ResourceProviderContext> context = + scoped_ptr<ResourceProviderContext> context_owned = ResourceProviderContext::Create(shared_data); - output_surface->SetAndInitializeContext3D( - context.PassAs<WebKit::WebGraphicsContext3D>()); + ResourceProviderContext* context = context_owned.get(); + + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create( + context_owned.PassAs<TestWebGraphicsContext3D>()); + output_surface->InitializeAndSetContext3d(context_provider, NULL); EXPECT_TRUE(resource_provider->InitializeGL()); - CheckCreateResource( - ResourceProvider::GLTexture, - resource_provider, - static_cast<ResourceProviderContext*>(output_surface->context3d())); + + CheckCreateResource(ResourceProvider::GLTexture, resource_provider, context); } TEST(ResourceProviderTest, BasicInitializeGLSoftware) { @@ -1718,7 +2514,7 @@ TEST(ResourceProviderTest, BasicInitializeGLSoftware) { scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice))); EXPECT_TRUE(output_surface->BindToClient(&client)); scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(output_surface.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL); @@ -1727,6 +2523,7 @@ TEST(ResourceProviderTest, BasicInitializeGLSoftware) { output_surface.get()); resource_provider->InitializeSoftware(); + output_surface->ReleaseGL(); CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL); InitializeGLAndCheck(shared_data.get(), diff --git a/chromium/cc/resources/resource_update_controller.cc b/chromium/cc/resources/resource_update_controller.cc index a46a7d1a36e..3c8b1e709ff 100644 --- a/chromium/cc/resources/resource_update_controller.cc +++ b/chromium/cc/resources/resource_update_controller.cc @@ -16,9 +16,6 @@ namespace { const size_t kPartialTextureUpdatesMax = 12; // Measured in seconds. -const double kTextureUpdateTickRate = 0.004; - -// Measured in seconds. const double kUploaderBusyTickRate = 0.001; // Number of blocking update intervals to allow. @@ -36,7 +33,8 @@ size_t ResourceUpdateController::MaxFullUpdatesPerTick( ResourceProvider* resource_provider) { double textures_per_second = resource_provider->EstimatedUploadsPerSecond(); size_t textures_per_tick = - floor(kTextureUpdateTickRate * textures_per_second); + floor(resource_provider->TextureUpdateTickRate().InSecondsF() * + textures_per_second); return textures_per_tick ? textures_per_tick : 1; } @@ -119,7 +117,7 @@ base::TimeTicks ResourceUpdateController::Now() const { } base::TimeDelta ResourceUpdateController::UpdateMoreTexturesTime() const { - return base::TimeDelta::FromMilliseconds(kTextureUpdateTickRate * 1000); + return resource_provider_->TextureUpdateTickRate(); } size_t ResourceUpdateController::UpdateMoreTexturesSize() const { diff --git a/chromium/cc/resources/resource_update_controller_unittest.cc b/chromium/cc/resources/resource_update_controller_unittest.cc index 1060801934b..31b5d71662a 100644 --- a/chromium/cc/resources/resource_update_controller_unittest.cc +++ b/chromium/cc/resources/resource_update_controller_unittest.cc @@ -5,11 +5,12 @@ #include "cc/resources/resource_update_controller.h" #include "base/test/test_simple_task_runner.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/resources/prioritized_resource_manager.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_proxy.h" #include "cc/test/scheduler_test_common.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "cc/test/tiled_layer_test_common.h" #include "cc/trees/single_thread_proxy.h" // For DebugScopedSetImplThread #include "testing/gtest/include/gtest/gtest.h" @@ -34,8 +35,9 @@ class ResourceUpdateControllerTest; class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D { public: explicit WebGraphicsContext3DForUploadTest(ResourceUpdateControllerTest* test) - : test_(test), - support_shallow_flush_(true) {} + : test_(test) { + test_capabilities_.shallow_flush = true; + } virtual void flush(void) OVERRIDE; virtual void shallowFlushCHROMIUM(void) OVERRIDE; @@ -51,12 +53,6 @@ class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D { const void* pixels) OVERRIDE; virtual GrGLInterface* onCreateGrGLInterface() OVERRIDE { return NULL; } - virtual WebString getString(WGC3Denum name) OVERRIDE { - if (support_shallow_flush_) - return WebString("GL_CHROMIUM_shallow_flush"); - return WebString(""); - } - virtual void getQueryObjectuivEXT( WebGLId id, WGC3Denum pname, @@ -64,7 +60,6 @@ class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D { private: ResourceUpdateControllerTest* test_; - bool support_shallow_flush_; }; class ResourceUpdateControllerTest : public Test { @@ -124,21 +119,25 @@ class ResourceUpdateControllerTest : public Test { protected: virtual void SetUp() { - output_surface_ = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new WebGraphicsContext3DForUploadTest(this))); bitmap_.setConfig(SkBitmap::kARGB_8888_Config, 300, 150); bitmap_.allocPixels(); for (int i = 0; i < 4; i++) { textures_[i] = PrioritizedResource::Create(resource_manager_.get(), - gfx::Size(300, 150), GL_RGBA); + gfx::Size(300, 150), + RGBA_8888); textures_[i]-> set_request_priority(PriorityCalculator::VisiblePriority(true)); } resource_manager_->PrioritizeTextures(); - resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0); + output_surface_ = FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>( + new WebGraphicsContext3DForUploadTest(this))); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false); } void AppendFullUploadsOfIndexedTextureToUpdateQueue(int count, @@ -193,6 +192,7 @@ class ResourceUpdateControllerTest : public Test { protected: // Classes required to interact and test the ResourceUpdateController FakeProxy proxy_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<OutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; scoped_ptr<ResourceUpdateQueue> queue_; diff --git a/chromium/cc/resources/return_callback.h b/chromium/cc/resources/return_callback.h new file mode 100644 index 00000000000..d12254bd662 --- /dev/null +++ b/chromium/cc/resources/return_callback.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RESOURCES_RETURN_CALLBACK_H_ +#define CC_RESOURCES_RETURN_CALLBACK_H_ + +#include "base/callback.h" +#include "cc/resources/returned_resource.h" + +namespace cc { + +typedef base::Callback<void(const ReturnedResourceArray&)> ReturnCallback; + +} // namespace cc + +#endif // CC_RESOURCES_RETURN_CALLBACK_H_ diff --git a/chromium/cc/resources/returned_resource.h b/chromium/cc/resources/returned_resource.h new file mode 100644 index 00000000000..e2008b415a3 --- /dev/null +++ b/chromium/cc/resources/returned_resource.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RESOURCES_RETURNED_RESOURCE_H_ +#define CC_RESOURCES_RETURNED_RESOURCE_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "cc/base/cc_export.h" + +namespace cc { + +struct CC_EXPORT ReturnedResource { + ReturnedResource() : id(0), sync_point(0), count(0), lost(false) {} + unsigned id; + unsigned sync_point; + int count; + bool lost; +}; + +typedef std::vector<ReturnedResource> ReturnedResourceArray; + +} // namespace cc + +#endif // CC_RESOURCES_RETURNED_RESOURCE_H_ diff --git a/chromium/cc/resources/scoped_resource.cc b/chromium/cc/resources/scoped_resource.cc index e444eee571f..5fcaaca7acc 100644 --- a/chromium/cc/resources/scoped_resource.cc +++ b/chromium/cc/resources/scoped_resource.cc @@ -16,13 +16,14 @@ ScopedResource::~ScopedResource() { } bool ScopedResource::Allocate(gfx::Size size, - GLenum format, - ResourceProvider::TextureUsageHint hint) { + ResourceProvider::TextureUsageHint hint, + ResourceFormat format) { DCHECK(!id()); DCHECK(!size.IsEmpty()); set_dimensions(size, format); - set_id(resource_provider_->CreateResource(size, format, hint)); + set_id(resource_provider_->CreateResource( + size, GL_CLAMP_TO_EDGE, hint, format)); #ifndef NDEBUG allocate_thread_id_ = base::PlatformThread::CurrentId(); diff --git a/chromium/cc/resources/scoped_resource.h b/chromium/cc/resources/scoped_resource.h index 6a2c8f1e727..8e316eb76db 100644 --- a/chromium/cc/resources/scoped_resource.h +++ b/chromium/cc/resources/scoped_resource.h @@ -26,8 +26,8 @@ class CC_EXPORT ScopedResource : public Resource { virtual ~ScopedResource(); bool Allocate(gfx::Size size, - GLenum format, - ResourceProvider::TextureUsageHint hint); + ResourceProvider::TextureUsageHint hint, + ResourceFormat texture_format); void Free(); void Leak(); diff --git a/chromium/cc/resources/scoped_resource_unittest.cc b/chromium/cc/resources/scoped_resource_unittest.cc index b08c1e21daf..8b59995f289 100644 --- a/chromium/cc/resources/scoped_resource_unittest.cc +++ b/chromium/cc/resources/scoped_resource_unittest.cc @@ -6,17 +6,20 @@ #include "cc/output/renderer.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/tiled_layer_test_common.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/khronos/GLES2/gl2.h" namespace cc { namespace { TEST(ScopedResourceTest, NewScopedResource) { - scoped_ptr<OutputSurface> context(CreateFakeOutputSurface()); + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d()); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(context.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); scoped_ptr<ScopedResource> texture = ScopedResource::create(resource_provider.get()); @@ -29,34 +32,42 @@ TEST(ScopedResourceTest, NewScopedResource) { } TEST(ScopedResourceTest, CreateScopedResource) { - scoped_ptr<OutputSurface> context(CreateFakeOutputSurface()); + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d()); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(context.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); scoped_ptr<ScopedResource> texture = ScopedResource::create(resource_provider.get()); - texture->Allocate( - gfx::Size(30, 30), GL_RGBA, ResourceProvider::TextureUsageAny); + texture->Allocate(gfx::Size(30, 30), + ResourceProvider::TextureUsageAny, + RGBA_8888); // The texture has an allocated byte-size now. size_t expected_bytes = 30 * 30 * 4; EXPECT_EQ(expected_bytes, texture->bytes()); EXPECT_LT(0u, texture->id()); - EXPECT_EQ(static_cast<unsigned>(GL_RGBA), texture->format()); + EXPECT_EQ(static_cast<unsigned>(RGBA_8888), texture->format()); EXPECT_EQ(gfx::Size(30, 30), texture->size()); } TEST(ScopedResourceTest, ScopedResourceIsDeleted) { - scoped_ptr<OutputSurface> context(CreateFakeOutputSurface()); + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d()); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(context.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); { scoped_ptr<ScopedResource> texture = ScopedResource::create(resource_provider.get()); EXPECT_EQ(0u, resource_provider->num_resources()); - texture->Allocate( - gfx::Size(30, 30), GL_RGBA, ResourceProvider::TextureUsageAny); + texture->Allocate(gfx::Size(30, 30), + ResourceProvider::TextureUsageAny, + RGBA_8888); EXPECT_LT(0u, texture->id()); EXPECT_EQ(1u, resource_provider->num_resources()); } @@ -66,8 +77,9 @@ TEST(ScopedResourceTest, ScopedResourceIsDeleted) { scoped_ptr<ScopedResource> texture = ScopedResource::create(resource_provider.get()); EXPECT_EQ(0u, resource_provider->num_resources()); - texture->Allocate( - gfx::Size(30, 30), GL_RGBA, ResourceProvider::TextureUsageAny); + texture->Allocate(gfx::Size(30, 30), + ResourceProvider::TextureUsageAny, + RGBA_8888); EXPECT_LT(0u, texture->id()); EXPECT_EQ(1u, resource_provider->num_resources()); texture->Free(); @@ -76,16 +88,20 @@ TEST(ScopedResourceTest, ScopedResourceIsDeleted) { } TEST(ScopedResourceTest, LeakScopedResource) { - scoped_ptr<OutputSurface> context(CreateFakeOutputSurface()); + FakeOutputSurfaceClient output_surface_client; + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d()); + CHECK(output_surface->BindToClient(&output_surface_client)); + scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(context.get(), 0)); + ResourceProvider::Create(output_surface.get(), 0, false)); { scoped_ptr<ScopedResource> texture = ScopedResource::create(resource_provider.get()); EXPECT_EQ(0u, resource_provider->num_resources()); - texture->Allocate( - gfx::Size(30, 30), GL_RGBA, ResourceProvider::TextureUsageAny); + texture->Allocate(gfx::Size(30, 30), + ResourceProvider::TextureUsageAny, + RGBA_8888); EXPECT_LT(0u, texture->id()); EXPECT_EQ(1u, resource_provider->num_resources()); diff --git a/chromium/cc/resources/scoped_ui_resource.cc b/chromium/cc/resources/scoped_ui_resource.cc index 16225528cf7..e69b4bc9368 100644 --- a/chromium/cc/resources/scoped_ui_resource.cc +++ b/chromium/cc/resources/scoped_ui_resource.cc @@ -12,12 +12,12 @@ namespace cc { scoped_ptr<ScopedUIResource> ScopedUIResource::Create( LayerTreeHost* host, - scoped_refptr<UIResourceBitmap> bitmap) { + const UIResourceBitmap& bitmap) { return make_scoped_ptr(new ScopedUIResource(host, bitmap)); } ScopedUIResource::ScopedUIResource(LayerTreeHost* host, - scoped_refptr<UIResourceBitmap> bitmap) + const UIResourceBitmap& bitmap) : bitmap_(bitmap), host_(host) { DCHECK(host_); id_ = host_->CreateUIResource(this); @@ -32,12 +32,9 @@ ScopedUIResource::~ScopedUIResource() { } } -scoped_refptr<UIResourceBitmap> ScopedUIResource::GetBitmap( - UIResourceId uid, - bool resource_lost) { +UIResourceBitmap ScopedUIResource::GetBitmap(UIResourceId uid, + bool resource_lost) { return bitmap_; } -ScopedUIResource::ScopedUIResource() {} - } // namespace cc diff --git a/chromium/cc/resources/scoped_ui_resource.h b/chromium/cc/resources/scoped_ui_resource.h index 628d372c552..c257e1e25dc 100644 --- a/chromium/cc/resources/scoped_ui_resource.h +++ b/chromium/cc/resources/scoped_ui_resource.h @@ -15,25 +15,27 @@ namespace cc { class LayerTreeHost; +// ScopedUIResource creates an UIResource from a bitmap and a LayerTreeHost. +// This class holds a pointer to the host so that when the instance goes out of +// scope, the created resource is deleted. On a GetBitmap call from the +// UIResource manager, ScopeUIResource always returns the reference to the +// initially given bitmap regardless of whether the request was due to lost +// resource or not. class CC_EXPORT ScopedUIResource : public UIResourceClient { public: - static scoped_ptr<ScopedUIResource> Create( - LayerTreeHost* host, - scoped_refptr<UIResourceBitmap> bitmap); + static scoped_ptr<ScopedUIResource> Create(LayerTreeHost* host, + const UIResourceBitmap& bitmap); virtual ~ScopedUIResource(); - virtual scoped_refptr<UIResourceBitmap> GetBitmap( - UIResourceId uid, - bool resource_lost) OVERRIDE; + // UIResourceClient implementation. + virtual UIResourceBitmap GetBitmap(UIResourceId uid, + bool resource_lost) OVERRIDE; UIResourceId id() { return id_; } protected: - ScopedUIResource(LayerTreeHost* host, scoped_refptr<UIResourceBitmap> bitmap); + ScopedUIResource(LayerTreeHost* host, const UIResourceBitmap& bitmap); - // An empty default contructor for testing. - ScopedUIResource(); - - scoped_refptr<UIResourceBitmap> bitmap_; + UIResourceBitmap bitmap_; LayerTreeHost* host_; UIResourceId id_; diff --git a/chromium/cc/resources/single_release_callback.cc b/chromium/cc/resources/single_release_callback.cc new file mode 100644 index 00000000000..9c6b9de5f07 --- /dev/null +++ b/chromium/cc/resources/single_release_callback.cc @@ -0,0 +1,29 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/resources/single_release_callback.h" + +#include "base/callback_helpers.h" +#include "base/logging.h" + +namespace cc { + +SingleReleaseCallback::SingleReleaseCallback(const ReleaseCallback& callback) + : has_been_run_(false), callback_(callback) { + DCHECK(!callback_.is_null()) + << "Use a NULL SingleReleaseCallback for an empty callback."; +} + +SingleReleaseCallback::~SingleReleaseCallback() { + DCHECK(callback_.is_null() || has_been_run_) + << "SingleReleaseCallback was never run."; +} + +void SingleReleaseCallback::Run(unsigned sync_point, bool is_lost) { + DCHECK(!has_been_run_) << "SingleReleaseCallback was run more than once."; + has_been_run_ = true; + callback_.Run(sync_point, is_lost); +} + +} // namespace cc diff --git a/chromium/cc/resources/single_release_callback.h b/chromium/cc/resources/single_release_callback.h new file mode 100644 index 00000000000..fe1a3ba92b4 --- /dev/null +++ b/chromium/cc/resources/single_release_callback.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RESOURCES_SINGLE_RELEASE_CALLBACK_H_ +#define CC_RESOURCES_SINGLE_RELEASE_CALLBACK_H_ + +#include "base/memory/scoped_ptr.h" +#include "cc/base/cc_export.h" +#include "cc/resources/release_callback.h" + +namespace cc { + +class CC_EXPORT SingleReleaseCallback { + public: + static scoped_ptr<SingleReleaseCallback> Create(const ReleaseCallback& cb) { + return make_scoped_ptr(new SingleReleaseCallback(cb)); + } + + ~SingleReleaseCallback(); + + void Run(unsigned sync_point, bool is_lost); + + private: + explicit SingleReleaseCallback(const ReleaseCallback& callback); + + bool has_been_run_; + ReleaseCallback callback_; +}; + +} // namespace cc + +#endif // CC_RESOURCES_SINGLE_RELEASE_CALLBACK_H_ diff --git a/chromium/cc/resources/skpicture_content_layer_updater.cc b/chromium/cc/resources/skpicture_content_layer_updater.cc index e44dbc25205..79769bc847c 100644 --- a/chromium/cc/resources/skpicture_content_layer_updater.cc +++ b/chromium/cc/resources/skpicture_content_layer_updater.cc @@ -17,8 +17,7 @@ SkPictureContentLayerUpdater::SkPictureContentLayerUpdater( scoped_ptr<LayerPainter> painter, RenderingStatsInstrumentation* stats_instrumentation, int layer_id) - : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id), - layer_is_opaque_(false) {} + : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id) {} SkPictureContentLayerUpdater::~SkPictureContentLayerUpdater() {} @@ -33,7 +32,7 @@ void SkPictureContentLayerUpdater::PrepareToUpdate( base::TimeTicks start_time = rendering_stats_instrumentation_->StartRecording(); PaintContents(canvas, - content_rect, + content_rect.origin(), contents_width_scale, contents_height_scale, resulting_opaque_rect); @@ -49,8 +48,4 @@ void SkPictureContentLayerUpdater::DrawPicture(SkCanvas* canvas) { canvas->drawPicture(picture_); } -void SkPictureContentLayerUpdater::SetOpaque(bool opaque) { - layer_is_opaque_ = opaque; -} - } // namespace cc diff --git a/chromium/cc/resources/skpicture_content_layer_updater.h b/chromium/cc/resources/skpicture_content_layer_updater.h index 41a9f01e14e..511d8ee9bfd 100644 --- a/chromium/cc/resources/skpicture_content_layer_updater.h +++ b/chromium/cc/resources/skpicture_content_layer_updater.h @@ -20,9 +20,6 @@ class LayerPainter; // FrameBufferSkPictureContentLayerUpdater are two examples of such // implementations. class SkPictureContentLayerUpdater : public ContentLayerUpdater { - public: - virtual void SetOpaque(bool opaque) OVERRIDE; - protected: SkPictureContentLayerUpdater( scoped_ptr<LayerPainter> painter, @@ -37,13 +34,9 @@ class SkPictureContentLayerUpdater : public ContentLayerUpdater { gfx::Rect* resulting_opaque_rect) OVERRIDE; void DrawPicture(SkCanvas* canvas); - bool layer_is_opaque() const { return layer_is_opaque_; } - private: // Recording canvas. SkPicture picture_; - // True when it is known that all output pixels will be opaque. - bool layer_is_opaque_; DISALLOW_COPY_AND_ASSIGN(SkPictureContentLayerUpdater); }; diff --git a/chromium/cc/resources/texture_mailbox.cc b/chromium/cc/resources/texture_mailbox.cc index 0d4787438f4..149d56c8d1a 100644 --- a/chromium/cc/resources/texture_mailbox.cc +++ b/chromium/cc/resources/texture_mailbox.cc @@ -15,69 +15,48 @@ TextureMailbox::TextureMailbox() shared_memory_(NULL) { } -TextureMailbox::TextureMailbox( - const std::string& mailbox_name, - const ReleaseCallback& callback) - : callback_(callback), - target_(GL_TEXTURE_2D), +TextureMailbox::TextureMailbox(const std::string& mailbox_name) + : target_(GL_TEXTURE_2D), sync_point_(0), shared_memory_(NULL) { - DCHECK(mailbox_name.empty() == callback.is_null()); if (!mailbox_name.empty()) { CHECK(mailbox_name.size() == sizeof(name_.name)); name_.SetName(reinterpret_cast<const int8*>(mailbox_name.data())); } } -TextureMailbox::TextureMailbox( - const gpu::Mailbox& mailbox_name, - const ReleaseCallback& callback) - : callback_(callback), - target_(GL_TEXTURE_2D), +TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox_name) + : target_(GL_TEXTURE_2D), sync_point_(0), shared_memory_(NULL) { - DCHECK(mailbox_name.IsZero() == callback.is_null()); name_.SetName(mailbox_name.name); } -TextureMailbox::TextureMailbox( - const gpu::Mailbox& mailbox_name, - const ReleaseCallback& callback, - unsigned sync_point) - : callback_(callback), - target_(GL_TEXTURE_2D), +TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox_name, + unsigned sync_point) + : target_(GL_TEXTURE_2D), sync_point_(sync_point), shared_memory_(NULL) { - DCHECK(mailbox_name.IsZero() == callback.is_null()); name_.SetName(mailbox_name.name); } -TextureMailbox::TextureMailbox( - const gpu::Mailbox& mailbox_name, - const ReleaseCallback& callback, - unsigned texture_target, - unsigned sync_point) - : callback_(callback), - target_(texture_target), +TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox_name, + unsigned texture_target, + unsigned sync_point) + : target_(texture_target), sync_point_(sync_point), shared_memory_(NULL) { - DCHECK(mailbox_name.IsZero() == callback.is_null()); name_.SetName(mailbox_name.name); } -TextureMailbox::TextureMailbox( - base::SharedMemory* shared_memory, - gfx::Size size, - const ReleaseCallback& callback) - : callback_(callback), - target_(GL_TEXTURE_2D), +TextureMailbox::TextureMailbox(base::SharedMemory* shared_memory, + gfx::Size size) + : target_(GL_TEXTURE_2D), sync_point_(0), shared_memory_(shared_memory), - shared_memory_size_(size) { -} + shared_memory_size_(size) {} -TextureMailbox::~TextureMailbox() { -} +TextureMailbox::~TextureMailbox() {} bool TextureMailbox::Equals(const TextureMailbox& other) const { if (other.IsTexture()) @@ -97,22 +76,9 @@ bool TextureMailbox::ContainsHandle(base::SharedMemoryHandle handle) const { return shared_memory_ && shared_memory_->handle() == handle; } -void TextureMailbox::RunReleaseCallback(unsigned sync_point, - bool lost_resource) const { - if (!callback_.is_null()) - callback_.Run(sync_point, lost_resource); -} - -void TextureMailbox::SetName(const gpu::Mailbox& other) { +void TextureMailbox::SetName(const gpu::Mailbox& name) { DCHECK(shared_memory_ == NULL); - name_.SetName(other.name); -} - -TextureMailbox TextureMailbox::CopyWithNewCallback( - const ReleaseCallback& callback) const { - TextureMailbox result(*this); - result.callback_ = callback; - return result; + name_ = name; } size_t TextureMailbox::shared_memory_size_in_bytes() const { diff --git a/chromium/cc/resources/texture_mailbox.h b/chromium/cc/resources/texture_mailbox.h index 855287df2d1..a9b021b2390 100644 --- a/chromium/cc/resources/texture_mailbox.h +++ b/chromium/cc/resources/texture_mailbox.h @@ -19,23 +19,16 @@ namespace cc { // can hold a shared memory resource as well as a texture mailbox. class CC_EXPORT TextureMailbox { public: - typedef base::Callback<void(unsigned sync_point, - bool lost_resource)> ReleaseCallback; TextureMailbox(); - TextureMailbox(const std::string& mailbox_name, - const ReleaseCallback& callback); + explicit TextureMailbox(const std::string& mailbox_name); + explicit TextureMailbox(const gpu::Mailbox& mailbox_name); TextureMailbox(const gpu::Mailbox& mailbox_name, - const ReleaseCallback& callback); - TextureMailbox(const gpu::Mailbox& mailbox_name, - const ReleaseCallback& callback, unsigned sync_point); TextureMailbox(const gpu::Mailbox& mailbox_name, - const ReleaseCallback& callback, unsigned texture_target, unsigned sync_point); TextureMailbox(base::SharedMemory* shared_memory, - gfx::Size size, - const ReleaseCallback& callback); + gfx::Size size); ~TextureMailbox(); @@ -47,12 +40,9 @@ class CC_EXPORT TextureMailbox { bool ContainsMailbox(const gpu::Mailbox&) const; bool ContainsHandle(base::SharedMemoryHandle handle) const; - const ReleaseCallback& callback() const { return callback_; } const int8* data() const { return name_.name; } const gpu::Mailbox& name() const { return name_; } void ResetSyncPoint() { sync_point_ = 0; } - void RunReleaseCallback(unsigned sync_point, bool lost_resource) const; - void SetName(const gpu::Mailbox&); unsigned target() const { return target_; } unsigned sync_point() const { return sync_point_; } @@ -60,11 +50,12 @@ class CC_EXPORT TextureMailbox { gfx::Size shared_memory_size() const { return shared_memory_size_; } size_t shared_memory_size_in_bytes() const; - TextureMailbox CopyWithNewCallback(const ReleaseCallback& callback) const; + // TODO(danakj): ReleaseCallback should be separate from this class, and stop + // storing a TextureMailbox in ResourceProvider. Then we can remove this. + void SetName(const gpu::Mailbox& name); private: gpu::Mailbox name_; - ReleaseCallback callback_; unsigned target_; unsigned sync_point_; base::SharedMemory* shared_memory_; diff --git a/chromium/cc/resources/texture_mailbox_deleter.cc b/chromium/cc/resources/texture_mailbox_deleter.cc new file mode 100644 index 00000000000..ae6df17691d --- /dev/null +++ b/chromium/cc/resources/texture_mailbox_deleter.cc @@ -0,0 +1,92 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/resources/texture_mailbox_deleter.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop_proxy.h" +#include "cc/output/context_provider.h" +#include "cc/resources/single_release_callback.h" +#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" + +namespace cc { + +static void DeleteTextureOnImplThread( + const scoped_refptr<ContextProvider>& context_provider, + unsigned texture_id, + unsigned sync_point, + bool is_lost) { + if (sync_point) + context_provider->Context3d()->waitSyncPoint(sync_point); + context_provider->Context3d()->deleteTexture(texture_id); +} + +static void PostTaskFromMainToImplThread( + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + ReleaseCallback run_impl_callback, + unsigned sync_point, + bool is_lost) { + // This posts the task to RunDeleteTextureOnImplThread(). + impl_task_runner->PostTask( + FROM_HERE, base::Bind(run_impl_callback, sync_point, is_lost)); +} + +TextureMailboxDeleter::TextureMailboxDeleter() : weak_ptr_factory_(this) {} + +TextureMailboxDeleter::~TextureMailboxDeleter() { + for (size_t i = 0; i < impl_callbacks_.size(); ++i) + impl_callbacks_.at(i)->Run(0, true); +} + +scoped_ptr<SingleReleaseCallback> TextureMailboxDeleter::GetReleaseCallback( + const scoped_refptr<ContextProvider>& context_provider, + unsigned texture_id) { + // This callback owns a reference on the |context_provider|. It must be + // destroyed on the impl thread. Upon destruction of this class, the + // callback must immediately be destroyed. + scoped_ptr<SingleReleaseCallback> impl_callback = + SingleReleaseCallback::Create(base::Bind(&DeleteTextureOnImplThread, + context_provider, + texture_id)); + + impl_callbacks_.push_back(impl_callback.Pass()); + + // The raw pointer to the impl-side callback is valid as long as this + // class is alive. So we guard it with a WeakPtr. + ReleaseCallback run_impl_callback( + base::Bind(&TextureMailboxDeleter::RunDeleteTextureOnImplThread, + weak_ptr_factory_.GetWeakPtr(), + impl_callbacks_.back())); + + // Provide a callback for the main thread that posts back to the impl + // thread. + scoped_ptr<SingleReleaseCallback> main_callback = + SingleReleaseCallback::Create(base::Bind( + &PostTaskFromMainToImplThread, + base::MessageLoopProxy::current(), + run_impl_callback)); + + return main_callback.Pass(); +} + +void TextureMailboxDeleter::RunDeleteTextureOnImplThread( + SingleReleaseCallback* impl_callback, + unsigned sync_point, + bool is_lost) { + for (size_t i = 0; i < impl_callbacks_.size(); ++i) { + if (impl_callbacks_.at(i) == impl_callback) { + // Run the callback, then destroy it here on the impl thread. + impl_callbacks_.at(i)->Run(sync_point, is_lost); + impl_callbacks_.erase(impl_callbacks_.begin() + i); + return; + } + } + + NOTREACHED() << "The Callback returned by GetDeleteCallback() was called " + << "more than once."; +} + +} // namespace cc diff --git a/chromium/cc/resources/texture_mailbox_deleter.h b/chromium/cc/resources/texture_mailbox_deleter.h new file mode 100644 index 00000000000..072dcab375b --- /dev/null +++ b/chromium/cc/resources/texture_mailbox_deleter.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_RESOURCES_TEXTURE_MAILBOX_DELETER_H_ +#define CC_RESOURCES_TEXTURE_MAILBOX_DELETER_H_ + +#include "base/memory/weak_ptr.h" +#include "cc/base/cc_export.h" +#include "cc/base/scoped_ptr_vector.h" + +namespace cc { +class ContextProvider; +class SingleReleaseCallback; + +class CC_EXPORT TextureMailboxDeleter { + public: + TextureMailboxDeleter(); + ~TextureMailboxDeleter(); + + // Returns a Callback that can be used as the ReleaseCallback for a + // TextureMailbox attached to the |texture_id|. The ReleaseCallback can + // be passed to other threads and will destroy the texture, once it is + // run, on the impl thread. If the TextureMailboxDeleter is destroyed + // due to the compositor shutting down, then the ReleaseCallback will + // become a no-op and the texture will be deleted immediately on the + // impl thread, along with dropping the reference to the ContextProvider. + scoped_ptr<SingleReleaseCallback> GetReleaseCallback( + const scoped_refptr<ContextProvider>& context_provider, + unsigned texture_id); + + private: + // Runs the |impl_callback| to delete the texture and removes the callback + // from the |impl_callbacks_| list. + void RunDeleteTextureOnImplThread( + SingleReleaseCallback* impl_callback, + unsigned sync_point, + bool is_lost); + + base::WeakPtrFactory<TextureMailboxDeleter> weak_ptr_factory_; + ScopedPtrVector<SingleReleaseCallback> impl_callbacks_; +}; + +} // namespace cc + +#endif // CC_RESOURCES_TEXTURE_MAILBOX_DELETER_H_ diff --git a/chromium/cc/resources/texture_mailbox_deleter_unittest.cc b/chromium/cc/resources/texture_mailbox_deleter_unittest.cc new file mode 100644 index 00000000000..a6ec48ed2ed --- /dev/null +++ b/chromium/cc/resources/texture_mailbox_deleter_unittest.cc @@ -0,0 +1,44 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/resources/texture_mailbox_deleter.h" + +#include "cc/debug/test_context_provider.h" +#include "cc/debug/test_web_graphics_context_3d.h" +#include "cc/resources/single_release_callback.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +TEST(TextureMailboxDeleterTest, Destroy) { + scoped_ptr<TextureMailboxDeleter> deleter(new TextureMailboxDeleter); + + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create(); + context_provider->BindToCurrentThread(); + + unsigned texture_id = context_provider->Context3d()->createTexture(); + + EXPECT_TRUE(context_provider->HasOneRef()); + EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures()); + + scoped_ptr<SingleReleaseCallback> cb = + deleter->GetReleaseCallback(context_provider, texture_id).Pass(); + EXPECT_FALSE(context_provider->HasOneRef()); + EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures()); + + // When the deleter is destroyed, it immediately drops its ref on the + // ContextProvider, and deletes the texture. + deleter.reset(); + EXPECT_TRUE(context_provider->HasOneRef()); + EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures()); + + // Run the scoped release callback before destroying it, but it won't do + // anything. + cb->Run(0, false); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/resources/tile.cc b/chromium/cc/resources/tile.cc index ab4fe7af549..d7e1d2c2da2 100644 --- a/chromium/cc/resources/tile.cc +++ b/chromium/cc/resources/tile.cc @@ -37,10 +37,28 @@ Tile::Tile(TileManager* tile_manager, Tile::~Tile() { TRACE_EVENT_OBJECT_DELETED_WITH_ID( - TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Tile", this); + TRACE_DISABLED_BY_DEFAULT("cc.debug") "," + TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), + "cc::Tile", this); tile_manager_->UnregisterTile(this); } +void Tile::SetPriority(WhichTree tree, const TilePriority& priority) { + if (priority == priority_[tree]) + return; + + priority_[tree] = priority; + tile_manager_->DidChangeTilePriority(this); +} + +void Tile::MarkRequiredForActivation() { + if (priority_[PENDING_TREE].required_for_activation) + return; + + priority_[PENDING_TREE].required_for_activation = true; + tile_manager_->DidChangeTilePriority(this); +} + scoped_ptr<base::Value> Tile::AsValue() const { scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue()); TracedValue::MakeDictIntoImplicitSnapshot(res.get(), "cc::Tile", this); diff --git a/chromium/cc/resources/tile.h b/chromium/cc/resources/tile.h index be9b19a4f1b..3caa407ae9c 100644 --- a/chromium/cc/resources/tile.h +++ b/chromium/cc/resources/tile.h @@ -53,13 +53,9 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> { priority_[PENDING_TREE]); } - void SetPriority(WhichTree tree, const TilePriority& priority) { - priority_[tree] = priority; - } + void SetPriority(WhichTree tree, const TilePriority& priority); - void mark_required_for_activation() { - priority_[PENDING_TREE].required_for_activation = true; - } + void MarkRequiredForActivation(); bool required_for_activation() const { return priority_[PENDING_TREE].required_for_activation; @@ -75,7 +71,7 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> { scoped_ptr<base::Value> AsValue() const; - bool IsReadyToDraw() const { + inline bool IsReadyToDraw() const { for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) { if (managed_state_.tile_versions[mode].IsReadyToDraw()) return true; @@ -117,6 +113,8 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> { return managed_state_.tile_versions[mode]; } + gfx::Size size() const { return tile_size_.size(); } + private: // Methods called by by tile manager. friend class TileManager; @@ -126,10 +124,6 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> { ManagedTileState& managed_state() { return managed_state_; } const ManagedTileState& managed_state() const { return managed_state_; } - inline size_t bytes_consumed_if_allocated() const { - return 4 * tile_size_.width() * tile_size_.height(); - } - // Normal private methods. friend class base::RefCounted<Tile>; ~Tile(); @@ -141,7 +135,7 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> { float contents_scale_; gfx::Rect opaque_rect_; - TilePriority priority_[NUM_BIN_PRIORITIES]; + TilePriority priority_[NUM_TREES]; ManagedTileState managed_state_; int layer_id_; int source_frame_number_; diff --git a/chromium/cc/resources/tile_manager.cc b/chromium/cc/resources/tile_manager.cc index e6850bcfd84..b8cbdca1fa7 100644 --- a/chromium/cc/resources/tile_manager.cc +++ b/chromium/cc/resources/tile_manager.cc @@ -32,7 +32,8 @@ const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = { NEVER_BIN, // [SOON_BIN] NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN] NEVER_BIN, // [EVENTUALLY_BIN] - NEVER_BIN, // [NEVER_AND_ACTIVE_BIN] + NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN] + NEVER_BIN, // [AT_LAST_BIN] NEVER_BIN // [NEVER_BIN] }, { // [ALLOW_ABSOLUTE_MINIMUM] NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN] @@ -40,7 +41,8 @@ const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = { NEVER_BIN, // [SOON_BIN] NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN] NEVER_BIN, // [EVENTUALLY_BIN] - NEVER_BIN, // [NEVER_AND_ACTIVE_BIN] + NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN] + NEVER_BIN, // [AT_LAST_BIN] NEVER_BIN // [NEVER_BIN] }, { // [ALLOW_PREPAINT_ONLY] NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN] @@ -48,7 +50,8 @@ const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = { SOON_BIN, // [SOON_BIN] NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN] NEVER_BIN, // [EVENTUALLY_BIN] - NEVER_BIN, // [NEVER_AND_ACTIVE_BIN] + NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN] + NEVER_BIN, // [AT_LAST_BIN] NEVER_BIN // [NEVER_BIN] }, { // [ALLOW_ANYTHING] NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN] @@ -56,7 +59,8 @@ const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = { SOON_BIN, // [SOON_BIN] EVENTUALLY_AND_ACTIVE_BIN, // [EVENTUALLY_AND_ACTIVE_BIN] EVENTUALLY_BIN, // [EVENTUALLY_BIN] - NEVER_AND_ACTIVE_BIN, // [NEVER_AND_ACTIVE_BIN] + AT_LAST_AND_ACTIVE_BIN, // [AT_LAST_AND_ACTIVE_BIN] + AT_LAST_BIN, // [AT_LAST_BIN] NEVER_BIN // [NEVER_BIN] } }; @@ -78,7 +82,7 @@ inline ManagedTileBin BinFromTilePriority(const TilePriority& prio, if (prio.distance_to_visible_in_pixels == std::numeric_limits<float>::infinity()) - return is_active ? NEVER_AND_ACTIVE_BIN : NEVER_BIN; + return NEVER_BIN; if (can_be_in_now_bin && prio.time_to_visible_in_seconds == 0) return is_ready_to_draw ? NOW_AND_READY_TO_DRAW_BIN : NOW_BIN; @@ -120,7 +124,8 @@ scoped_ptr<TileManager> TileManager::Create( ResourceProvider* resource_provider, size_t num_raster_threads, RenderingStatsInstrumentation* rendering_stats_instrumentation, - bool use_map_image) { + bool use_map_image, + size_t max_transfer_buffer_usage_bytes) { return make_scoped_ptr( new TileManager(client, resource_provider, @@ -128,10 +133,11 @@ scoped_ptr<TileManager> TileManager::Create( ImageRasterWorkerPool::Create( resource_provider, num_raster_threads) : PixelBufferRasterWorkerPool::Create( - resource_provider, num_raster_threads), + resource_provider, + num_raster_threads, + max_transfer_buffer_usage_bytes), num_raster_threads, - rendering_stats_instrumentation, - resource_provider->best_texture_format())); + rendering_stats_instrumentation)); } TileManager::TileManager( @@ -139,18 +145,21 @@ TileManager::TileManager( ResourceProvider* resource_provider, scoped_ptr<RasterWorkerPool> raster_worker_pool, size_t num_raster_threads, - RenderingStatsInstrumentation* rendering_stats_instrumentation, - GLenum texture_format) + RenderingStatsInstrumentation* rendering_stats_instrumentation) : client_(client), resource_pool_(ResourcePool::Create(resource_provider)), raster_worker_pool_(raster_worker_pool.Pass()), + prioritized_tiles_dirty_(false), all_tiles_that_need_to_be_rasterized_have_memory_(true), all_tiles_required_for_activation_have_memory_(true), - all_tiles_required_for_activation_have_been_initialized_(true), + memory_required_bytes_(0), + memory_nice_to_have_bytes_(0), + bytes_releasable_(0), + resources_releasable_(0), ever_exceeded_memory_budget_(false), rendering_stats_instrumentation_(rendering_stats_instrumentation), did_initialize_visible_tile_(false), - texture_format_(texture_format) { + did_check_for_completed_tasks_since_last_schedule_tasks_(true) { raster_worker_pool_->SetClient(this); } @@ -159,17 +168,18 @@ TileManager::~TileManager() { // our memory usage to drop to zero. global_state_ = GlobalStateThatImpactsTilePriority(); - // Clear |prioritized_tiles_| so that tiles kept alive by it can be freed. - prioritized_tiles_.Clear(); DCHECK_EQ(0u, tiles_.size()); - TileVector empty; - ScheduleTasks(empty); + RasterWorkerPool::RasterTask::Queue empty; + raster_worker_pool_->ScheduleTasks(&empty); // This should finish all pending tasks and release any uninitialized // resources. raster_worker_pool_->Shutdown(); raster_worker_pool_->CheckForCompletedTasks(); + + DCHECK_EQ(0u, bytes_releasable_); + DCHECK_EQ(0u, resources_releasable_); } void TileManager::SetGlobalState( @@ -186,6 +196,8 @@ void TileManager::RegisterTile(Tile* tile) { DCHECK(tiles_.find(tile->id()) == tiles_.end()); tiles_[tile->id()] = tile; + used_layer_counts_[tile->layer_id()]++; + prioritized_tiles_dirty_ = true; } void TileManager::UnregisterTile(Tile* tile) { @@ -193,12 +205,35 @@ void TileManager::UnregisterTile(Tile* tile) { DCHECK(tiles_.find(tile->id()) != tiles_.end()); tiles_.erase(tile->id()); + + LayerCountMap::iterator layer_it = used_layer_counts_.find(tile->layer_id()); + DCHECK_GT(layer_it->second, 0); + if (--layer_it->second == 0) { + used_layer_counts_.erase(layer_it); + image_decode_tasks_.erase(tile->layer_id()); + } + + prioritized_tiles_dirty_ = true; +} + +void TileManager::DidChangeTilePriority(Tile* tile) { + prioritized_tiles_dirty_ = true; } bool TileManager::ShouldForceTasksRequiredForActivationToComplete() const { return GlobalState().tree_priority != SMOOTHNESS_TAKES_PRIORITY; } +PrioritizedTileSet* TileManager::GetPrioritizedTileSet() { + if (!prioritized_tiles_dirty_) + return &prioritized_tiles_; + + prioritized_tiles_.Clear(); + GetTilesWithAssignedBins(&prioritized_tiles_); + prioritized_tiles_dirty_ = false; + return &prioritized_tiles_; +} + void TileManager::DidFinishRunningTasks() { TRACE_EVENT0("cc", "TileManager::DidFinishRunningTasks"); @@ -208,9 +243,10 @@ void TileManager::DidFinishRunningTasks() { return; raster_worker_pool_->CheckForCompletedTasks(); + did_check_for_completed_tasks_since_last_schedule_tasks_ = true; TileVector tiles_that_need_to_be_rasterized; - AssignGpuMemoryToTiles(&prioritized_tiles_, + AssignGpuMemoryToTiles(GetPrioritizedTileSet(), &tiles_that_need_to_be_rasterized); // |tiles_that_need_to_be_rasterized| will be empty when we reach a @@ -220,6 +256,12 @@ void TileManager::DidFinishRunningTasks() { return; } + // We don't reserve memory for required-for-activation tiles during + // accelerated gestures, so we just postpone activation when we don't + // have these tiles, and activate after the accelerated gesture. + bool allow_rasterize_on_demand = + global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY; + // Use on-demand raster for any required-for-activation tiles that have not // been been assigned memory after reaching a steady memory state. This // ensures that we activate even when OOM. @@ -229,8 +271,12 @@ void TileManager::DidFinishRunningTasks() { ManagedTileState::TileVersion& tile_version = mts.tile_versions[mts.raster_mode]; - if (tile->required_for_activation() && !tile_version.IsReadyToDraw()) + if (tile->required_for_activation() && !tile_version.IsReadyToDraw()) { + // If we can't raster on demand, give up early (and don't activate). + if (!allow_rasterize_on_demand) + return; tile_version.set_rasterize_on_demand(); + } } client_->NotifyReadyToActivate(); @@ -251,6 +297,10 @@ void TileManager::DidFinishRunningTasksRequiredForActivation() { void TileManager::GetTilesWithAssignedBins(PrioritizedTileSet* tiles) { TRACE_EVENT0("cc", "TileManager::GetTilesWithAssignedBins"); + // Compute new stats to be return by GetMemoryStats(). + memory_required_bytes_ = 0; + memory_nice_to_have_bytes_ = 0; + const TileMemoryLimitPolicy memory_policy = global_state_.memory_limit_policy; const TreePriority tree_priority = global_state_.tree_priority; @@ -259,72 +309,85 @@ void TileManager::GetTilesWithAssignedBins(PrioritizedTileSet* tiles) { Tile* tile = it->second; ManagedTileState& mts = tile->managed_state(); - TilePriority prio[NUM_BIN_PRIORITIES]; + const ManagedTileState::TileVersion& tile_version = + tile->GetTileVersionForDrawing(); + bool tile_is_ready_to_draw = tile_version.IsReadyToDraw(); + bool tile_is_active = + tile_is_ready_to_draw || + !mts.tile_versions[mts.raster_mode].raster_task_.is_null(); + + // Get the active priority and bin. + TilePriority active_priority = tile->priority(ACTIVE_TREE); + ManagedTileBin active_bin = BinFromTilePriority( + active_priority, tree_priority, tile_is_ready_to_draw, tile_is_active); + mts.tree_bin[ACTIVE_TREE] = kBinPolicyMap[memory_policy][active_bin]; + + // Get the pending priority and bin. + TilePriority pending_priority = tile->priority(PENDING_TREE); + ManagedTileBin pending_bin = BinFromTilePriority( + pending_priority, tree_priority, tile_is_ready_to_draw, tile_is_active); + mts.tree_bin[PENDING_TREE] = kBinPolicyMap[memory_policy][pending_bin]; + + // Get the combined priority and bin. + TilePriority combined_priority = tile->combined_priority(); + ManagedTileBin combined_bin = BinFromTilePriority(combined_priority, + tree_priority, + tile_is_ready_to_draw, + tile_is_active); + + // The bin that the tile would have if the GPU memory manager had + // a maximally permissive policy, send to the GPU memory manager + // to determine policy. + ManagedTileBin gpu_memmgr_stats_bin = NEVER_BIN; + + TilePriority* high_priority = NULL; switch (tree_priority) { case SAME_PRIORITY_FOR_BOTH_TREES: - prio[HIGH_PRIORITY_BIN] = prio[LOW_PRIORITY_BIN] = - tile->combined_priority(); + mts.bin = kBinPolicyMap[memory_policy][combined_bin]; + gpu_memmgr_stats_bin = combined_bin; + high_priority = &combined_priority; break; case SMOOTHNESS_TAKES_PRIORITY: - prio[HIGH_PRIORITY_BIN] = tile->priority(ACTIVE_TREE); - prio[LOW_PRIORITY_BIN] = tile->priority(PENDING_TREE); + mts.bin = mts.tree_bin[ACTIVE_TREE]; + gpu_memmgr_stats_bin = active_bin; + high_priority = &active_priority; break; case NEW_CONTENT_TAKES_PRIORITY: - prio[HIGH_PRIORITY_BIN] = tile->priority(PENDING_TREE); - prio[LOW_PRIORITY_BIN] = tile->priority(ACTIVE_TREE); + mts.bin = mts.tree_bin[PENDING_TREE]; + gpu_memmgr_stats_bin = pending_bin; + high_priority = &pending_priority; break; } - bool tile_is_ready_to_draw = tile->IsReadyToDraw(); - bool tile_is_active = - tile_is_ready_to_draw || - !mts.tile_versions[mts.raster_mode].raster_task_.is_null(); + if (!tile_is_ready_to_draw || tile_version.requires_resource()) { + if ((gpu_memmgr_stats_bin == NOW_BIN) || + (gpu_memmgr_stats_bin == NOW_AND_READY_TO_DRAW_BIN)) + memory_required_bytes_ += BytesConsumedIfAllocated(tile); + if (gpu_memmgr_stats_bin != NEVER_BIN) + memory_nice_to_have_bytes_ += BytesConsumedIfAllocated(tile); + } - mts.resolution = prio[HIGH_PRIORITY_BIN].resolution; - mts.time_to_needed_in_seconds = - prio[HIGH_PRIORITY_BIN].time_to_visible_in_seconds; - mts.distance_to_visible_in_pixels = - prio[HIGH_PRIORITY_BIN].distance_to_visible_in_pixels; - mts.required_for_activation = - prio[HIGH_PRIORITY_BIN].required_for_activation; - - mts.bin[HIGH_PRIORITY_BIN] = - BinFromTilePriority(prio[HIGH_PRIORITY_BIN], - tree_priority, - tile_is_ready_to_draw, - tile_is_active); - mts.bin[LOW_PRIORITY_BIN] = - BinFromTilePriority(prio[LOW_PRIORITY_BIN], - tree_priority, - tile_is_ready_to_draw, - tile_is_active); - mts.gpu_memmgr_stats_bin = - BinFromTilePriority(tile->combined_priority(), - tree_priority, - tile_is_ready_to_draw, - tile_is_active); - - ManagedTileBin active_bin = - BinFromTilePriority(tile->priority(ACTIVE_TREE), - tree_priority, - tile_is_ready_to_draw, - tile_is_active); - mts.tree_bin[ACTIVE_TREE] = kBinPolicyMap[memory_policy][active_bin]; + // Bump up the priority if we determined it's NEVER_BIN on one tree, + // but is still required on the other tree. + bool is_in_never_bin_on_both_trees = + mts.tree_bin[ACTIVE_TREE] == NEVER_BIN && + mts.tree_bin[PENDING_TREE] == NEVER_BIN; - ManagedTileBin pending_bin = - BinFromTilePriority(tile->priority(PENDING_TREE), - tree_priority, - tile_is_ready_to_draw, - tile_is_active); - mts.tree_bin[PENDING_TREE] = kBinPolicyMap[memory_policy][pending_bin]; + if (mts.bin == NEVER_BIN && !is_in_never_bin_on_both_trees) + mts.bin = tile_is_active ? AT_LAST_AND_ACTIVE_BIN : AT_LAST_BIN; - for (int i = 0; i < NUM_BIN_PRIORITIES; ++i) - mts.bin[i] = kBinPolicyMap[memory_policy][mts.bin[i]]; + DCHECK(high_priority != NULL); + + mts.resolution = high_priority->resolution; + mts.time_to_needed_in_seconds = high_priority->time_to_visible_in_seconds; + mts.distance_to_visible_in_pixels = + high_priority->distance_to_visible_in_pixels; + mts.required_for_activation = high_priority->required_for_activation; mts.visible_and_ready_to_draw = mts.tree_bin[ACTIVE_TREE] == NOW_AND_READY_TO_DRAW_BIN; - if (mts.is_in_never_bin_on_both_trees()) { + if (mts.bin == NEVER_BIN) { FreeResourcesForTile(tile); continue; } @@ -336,45 +399,44 @@ void TileManager::GetTilesWithAssignedBins(PrioritizedTileSet* tiles) { // priority. ManagedTileBin priority_bin = mts.visible_and_ready_to_draw ? NOW_AND_READY_TO_DRAW_BIN - : mts.bin[HIGH_PRIORITY_BIN]; + : mts.bin; // Insert the tile into a priority set. tiles->InsertTile(tile, priority_bin); } } -void TileManager::GetPrioritizedTileSet(PrioritizedTileSet* tiles) { - TRACE_EVENT0("cc", "TileManager::GetPrioritizedTileSet"); - - GetTilesWithAssignedBins(tiles); - tiles->Sort(); -} - void TileManager::ManageTiles() { TRACE_EVENT0("cc", "TileManager::ManageTiles"); - // Clear |prioritized_tiles_| so that tiles kept alive by it can be freed. - prioritized_tiles_.Clear(); - - GetPrioritizedTileSet(&prioritized_tiles_); + // We need to call CheckForCompletedTasks() once in-between each call + // to ScheduleTasks() to prevent canceled tasks from being scheduled. + if (!did_check_for_completed_tasks_since_last_schedule_tasks_) { + raster_worker_pool_->CheckForCompletedTasks(); + did_check_for_completed_tasks_since_last_schedule_tasks_ = true; + } TileVector tiles_that_need_to_be_rasterized; - AssignGpuMemoryToTiles(&prioritized_tiles_, + AssignGpuMemoryToTiles(GetPrioritizedTileSet(), &tiles_that_need_to_be_rasterized); - CleanUpUnusedImageDecodeTasks(); + + // Finally, schedule rasterizer tasks. + ScheduleTasks(tiles_that_need_to_be_rasterized); TRACE_EVENT_INSTANT1( "cc", "DidManage", TRACE_EVENT_SCOPE_THREAD, "state", TracedValue::FromValue(BasicStateAsValue().release())); - // Finally, schedule rasterizer tasks. - ScheduleTasks(tiles_that_need_to_be_rasterized); + TRACE_COUNTER_ID1("cc", "unused_memory_bytes", this, + resource_pool_->total_memory_usage_bytes() - + resource_pool_->acquired_memory_usage_bytes()); } bool TileManager::UpdateVisibleTiles() { TRACE_EVENT0("cc", "TileManager::UpdateVisibleTiles"); raster_worker_pool_->CheckForCompletedTasks(); + did_check_for_completed_tasks_since_last_schedule_tasks_ = true; TRACE_EVENT_INSTANT1( "cc", "DidUpdateVisibleTiles", TRACE_EVENT_SCOPE_THREAD, @@ -391,29 +453,12 @@ bool TileManager::UpdateVisibleTiles() { void TileManager::GetMemoryStats( size_t* memory_required_bytes, size_t* memory_nice_to_have_bytes, + size_t* memory_allocated_bytes, size_t* memory_used_bytes) const { - *memory_required_bytes = 0; - *memory_nice_to_have_bytes = 0; + *memory_required_bytes = memory_required_bytes_; + *memory_nice_to_have_bytes = memory_nice_to_have_bytes_; + *memory_allocated_bytes = resource_pool_->total_memory_usage_bytes(); *memory_used_bytes = resource_pool_->acquired_memory_usage_bytes(); - for (TileMap::const_iterator it = tiles_.begin(); - it != tiles_.end(); - ++it) { - const Tile* tile = it->second; - const ManagedTileState& mts = tile->managed_state(); - - const ManagedTileState::TileVersion& tile_version = - tile->GetTileVersionForDrawing(); - if (tile_version.IsReadyToDraw() && - !tile_version.requires_resource()) - continue; - - size_t tile_bytes = tile->bytes_consumed_if_allocated(); - if ((mts.gpu_memmgr_stats_bin == NOW_BIN) || - (mts.gpu_memmgr_stats_bin == NOW_AND_READY_TO_DRAW_BIN)) - *memory_required_bytes += tile_bytes; - if (mts.gpu_memmgr_stats_bin != NEVER_BIN) - *memory_nice_to_have_bytes += tile_bytes; - } } scoped_ptr<base::Value> TileManager::BasicStateAsValue() const { @@ -440,13 +485,16 @@ scoped_ptr<base::Value> TileManager::GetMemoryRequirementsAsValue() const { size_t memory_required_bytes; size_t memory_nice_to_have_bytes; + size_t memory_allocated_bytes; size_t memory_used_bytes; GetMemoryStats(&memory_required_bytes, &memory_nice_to_have_bytes, + &memory_allocated_bytes, &memory_used_bytes); requirements->SetInteger("memory_required_bytes", memory_required_bytes); requirements->SetInteger("memory_nice_to_have_bytes", memory_nice_to_have_bytes); + requirements->SetInteger("memory_allocated_bytes", memory_allocated_bytes); requirements->SetInteger("memory_used_bytes", memory_used_bytes); return requirements.PassAs<base::Value>(); } @@ -477,32 +525,16 @@ void TileManager::AssignGpuMemoryToTiles( // Now give memory out to the tiles until we're out, and build // the needs-to-be-rasterized queue. - size_t bytes_releasable = 0; - size_t resources_releasable = 0; - for (PrioritizedTileSet::PriorityIterator it(tiles); - it; - ++it) { - const Tile* tile = *it; - const ManagedTileState& mts = tile->managed_state(); - for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) { - if (mts.tile_versions[mode].resource_) { - bytes_releasable += tile->bytes_consumed_if_allocated(); - resources_releasable++; - } - } - } - all_tiles_that_need_to_be_rasterized_have_memory_ = true; all_tiles_required_for_activation_have_memory_ = true; - all_tiles_required_for_activation_have_been_initialized_ = true; // Cast to prevent overflow. int64 bytes_available = - static_cast<int64>(bytes_releasable) + + static_cast<int64>(bytes_releasable_) + static_cast<int64>(global_state_.memory_limit_in_bytes) - static_cast<int64>(resource_pool_->acquired_memory_usage_bytes()); int resources_available = - resources_releasable + + resources_releasable_ + global_state_.num_resources_limit - resource_pool_->acquired_resource_count(); @@ -516,7 +548,7 @@ void TileManager::AssignGpuMemoryToTiles( bool oomed = false; unsigned schedule_priority = 1u; - for (PrioritizedTileSet::PriorityIterator it(tiles); + for (PrioritizedTileSet::Iterator it(tiles, true); it; ++it) { Tile* tile = *it; @@ -534,7 +566,7 @@ void TileManager::AssignGpuMemoryToTiles( continue; // If the tile is not needed, free it up. - if (mts.is_in_never_bin_on_both_trees()) { + if (mts.bin == NEVER_BIN) { FreeResourcesForTile(tile); continue; } @@ -545,7 +577,7 @@ void TileManager::AssignGpuMemoryToTiles( // It costs to maintain a resource. for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) { if (mts.tile_versions[mode].resource_) { - tile_bytes += tile->bytes_consumed_if_allocated(); + tile_bytes += BytesConsumedIfAllocated(tile); tile_resources++; } } @@ -557,7 +589,7 @@ void TileManager::AssignGpuMemoryToTiles( // If we don't have the required version, and it's not in flight // then we'll have to pay to create a new task. if (!tile_version.resource_ && tile_version.raster_task_.is_null()) { - tile_bytes += tile->bytes_consumed_if_allocated(); + tile_bytes += BytesConsumedIfAllocated(tile); tile_resources++; } } @@ -584,9 +616,6 @@ void TileManager::AssignGpuMemoryToTiles( DCHECK(!tile_version.resource_); - if (tile->required_for_activation()) - all_tiles_required_for_activation_have_been_initialized_ = false; - // Tile shouldn't be rasterized if |tiles_that_need_to_be_rasterized| // has reached it's limit or we've failed to assign gpu memory to this // or any higher priority tile. Preventing tiles that fit into memory @@ -599,6 +628,7 @@ void TileManager::AssignGpuMemoryToTiles( all_tiles_that_need_to_be_rasterized_have_memory_ = false; if (tile->required_for_activation()) all_tiles_required_for_activation_have_memory_ = false; + it.DisablePriorityOrdering(); continue; } @@ -616,40 +646,22 @@ void TileManager::AssignGpuMemoryToTiles( memory_stats_from_last_assign_.bytes_allocated = bytes_allocatable - bytes_left; memory_stats_from_last_assign_.bytes_unreleasable = - bytes_allocatable - bytes_releasable; + bytes_allocatable - bytes_releasable_; memory_stats_from_last_assign_.bytes_over = bytes_that_exceeded_memory_budget; } -void TileManager::CleanUpUnusedImageDecodeTasks() { - // Calculate a set of layers that are used by at least one tile. - base::hash_set<int> used_layers; - for (TileMap::iterator it = tiles_.begin(); it != tiles_.end(); ++it) - used_layers.insert(it->second->layer_id()); - - // Now calculate the set of layers in |image_decode_tasks_| that are not used - // by any tile. - std::vector<int> unused_layers; - for (LayerPixelRefTaskMap::iterator it = image_decode_tasks_.begin(); - it != image_decode_tasks_.end(); - ++it) { - if (used_layers.find(it->first) == used_layers.end()) - unused_layers.push_back(it->first); - } - - // Erase unused layers from |image_decode_tasks_|. - for (std::vector<int>::iterator it = unused_layers.begin(); - it != unused_layers.end(); - ++it) { - image_decode_tasks_.erase(*it); - } -} - void TileManager::FreeResourceForTile(Tile* tile, RasterMode mode) { ManagedTileState& mts = tile->managed_state(); if (mts.tile_versions[mode].resource_) { resource_pool_->ReleaseResource( mts.tile_versions[mode].resource_.Pass()); + + DCHECK_GE(bytes_releasable_, BytesConsumedIfAllocated(tile)); + DCHECK_GE(resources_releasable_, 1u); + + bytes_releasable_ -= BytesConsumedIfAllocated(tile); + --resources_releasable_; } } @@ -682,6 +694,8 @@ void TileManager::ScheduleTasks( "count", tiles_that_need_to_be_rasterized.size()); RasterWorkerPool::RasterTask::Queue tasks; + DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_); + // Build a new task queue containing all task currently needed. Tasks // are added in order of priority, highest priority task first. for (TileVector::const_iterator it = tiles_that_need_to_be_rasterized.begin(); @@ -709,6 +723,8 @@ void TileManager::ScheduleTasks( // scheduled tasks and effectively cancels all tasks not present // in |tasks|. raster_worker_pool_->ScheduleTasks(&tasks); + + did_check_for_completed_tasks_since_last_schedule_tasks_ = false; } RasterWorkerPool::Task TileManager::CreateImageDecodeTask( @@ -727,8 +743,9 @@ RasterWorkerPool::RasterTask TileManager::CreateRasterTask(Tile* tile) { ManagedTileState& mts = tile->managed_state(); scoped_ptr<ResourcePool::Resource> resource = - resource_pool_->AcquireResource(tile->tile_size_.size(), - texture_format_); + resource_pool_->AcquireResource( + tile->tile_size_.size(), + raster_worker_pool_->GetResourceFormat()); const Resource* const_resource = resource.get(); // Create and queue all image decode tasks that this tile depends on. @@ -833,6 +850,9 @@ void TileManager::OnRasterTaskCompleted( } else { tile_version.set_use_resource(); tile_version.resource_ = resource.Pass(); + + bytes_releasable_ += BytesConsumedIfAllocated(tile); + ++resources_releasable_; } FreeUnusedResourcesForTile(tile); diff --git a/chromium/cc/resources/tile_manager.h b/chromium/cc/resources/tile_manager.h index 2e226c9999d..3f262bcf28d 100644 --- a/chromium/cc/resources/tile_manager.h +++ b/chromium/cc/resources/tile_manager.h @@ -52,7 +52,8 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient { ResourceProvider* resource_provider, size_t num_raster_threads, RenderingStatsInstrumentation* rendering_stats_instrumentation, - bool use_map_image); + bool use_map_image, + size_t max_transfer_buffer_usage_bytes); virtual ~TileManager(); const GlobalStateThatImpactsTilePriority& GlobalState() const { @@ -69,14 +70,32 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient { scoped_ptr<base::Value> AllTilesAsValue() const; void GetMemoryStats(size_t* memory_required_bytes, size_t* memory_nice_to_have_bytes, + size_t* memory_allocated_bytes, size_t* memory_used_bytes) const; const MemoryHistory::Entry& memory_stats_from_last_assign() const { return memory_stats_from_last_assign_; } - bool AreTilesRequiredForActivationReady() const { - return all_tiles_required_for_activation_have_been_initialized_; + void InitializeTilesWithResourcesForTesting( + const std::vector<Tile*>& tiles, + ResourceProvider* resource_provider) { + for (size_t i = 0; i < tiles.size(); ++i) { + ManagedTileState& mts = tiles[i]->managed_state(); + ManagedTileState::TileVersion& tile_version = + mts.tile_versions[HIGH_QUALITY_NO_LCD_RASTER_MODE]; + + tile_version.resource_ = make_scoped_ptr( + new ResourcePool::Resource(resource_provider, + gfx::Size(1, 1), + resource_provider->best_texture_format())); + + bytes_releasable_ += BytesConsumedIfAllocated(tiles[i]); + ++resources_releasable_; + } + } + RasterWorkerPool* RasterWorkerPoolForTesting() { + return raster_worker_pool_.get(); } protected: @@ -84,13 +103,13 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient { ResourceProvider* resource_provider, scoped_ptr<RasterWorkerPool> raster_worker_pool, size_t num_raster_threads, - RenderingStatsInstrumentation* rendering_stats_instrumentation, - GLenum texture_format); + RenderingStatsInstrumentation* rendering_stats_instrumentation); // Methods called by Tile friend class Tile; void RegisterTile(Tile* tile); void UnregisterTile(Tile* tile); + void DidChangeTilePriority(Tile* tile); // Overriden from RasterWorkerPoolClient: virtual bool ShouldForceTasksRequiredForActivationToComplete() const @@ -109,7 +128,6 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient { PrioritizedTileSet* tiles, TileVector* tiles_that_need_to_be_rasterized); void GetTilesWithAssignedBins(PrioritizedTileSet* tiles); - void GetPrioritizedTileSet(PrioritizedTileSet* tiles); private: void OnImageDecodeTaskCompleted( @@ -123,8 +141,12 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient { const PicturePileImpl::Analysis& analysis, bool was_canceled); + inline size_t BytesConsumedIfAllocated(const Tile* tile) const { + return Resource::MemorySizeBytes(tile->size(), + raster_worker_pool_->GetResourceFormat()); + } + RasterMode DetermineRasterMode(const Tile* tile) const; - void CleanUpUnusedImageDecodeTasks(); void FreeResourceForTile(Tile* tile, RasterMode mode); void FreeResourcesForTile(Tile* tile); void FreeUnusedResourcesForTile(Tile* tile); @@ -132,6 +154,7 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient { Tile* tile, skia::LazyPixelRef* pixel_ref); RasterWorkerPool::RasterTask CreateRasterTask(Tile* tile); scoped_ptr<base::Value> GetMemoryRequirementsAsValue() const; + PrioritizedTileSet* GetPrioritizedTileSet(); TileManagerClient* client_; scoped_ptr<ResourcePool> resource_pool_; @@ -142,10 +165,16 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient { TileMap tiles_; PrioritizedTileSet prioritized_tiles_; + bool prioritized_tiles_dirty_; bool all_tiles_that_need_to_be_rasterized_have_memory_; bool all_tiles_required_for_activation_have_memory_; - bool all_tiles_required_for_activation_have_been_initialized_; + + size_t memory_required_bytes_; + size_t memory_nice_to_have_bytes_; + + size_t bytes_releasable_; + size_t resources_releasable_; bool ever_exceeded_memory_budget_; MemoryHistory::Entry memory_stats_from_last_assign_; @@ -153,13 +182,15 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient { RenderingStatsInstrumentation* rendering_stats_instrumentation_; bool did_initialize_visible_tile_; - - GLenum texture_format_; + bool did_check_for_completed_tasks_since_last_schedule_tasks_; typedef base::hash_map<uint32_t, RasterWorkerPool::Task> PixelRefTaskMap; typedef base::hash_map<int, PixelRefTaskMap> LayerPixelRefTaskMap; LayerPixelRefTaskMap image_decode_tasks_; + typedef base::hash_map<int, int> LayerCountMap; + LayerCountMap used_layer_counts_; + RasterTaskCompletionStats update_visible_tiles_stats_; DISALLOW_COPY_AND_ASSIGN(TileManager); diff --git a/chromium/cc/resources/tile_manager_perftest.cc b/chromium/cc/resources/tile_manager_perftest.cc index ca6f9971c60..99d16a187e7 100644 --- a/chromium/cc/resources/tile_manager_perftest.cc +++ b/chromium/cc/resources/tile_manager_perftest.cc @@ -6,12 +6,15 @@ #include "cc/resources/tile.h" #include "cc/resources/tile_priority.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_picture_pile_impl.h" #include "cc/test/fake_tile_manager.h" #include "cc/test/fake_tile_manager_client.h" +#include "cc/test/lap_timer.h" #include "cc/test/test_tile_priorities.h" #include "testing/gtest/include/gtest/gtest.h" +#include "testing/perf/perf_test.h" namespace cc { @@ -23,21 +26,30 @@ static const int kTimeCheckInterval = 10; class TileManagerPerfTest : public testing::Test { public: - typedef std::vector<scoped_refptr<Tile> > TileVector; + typedef std::vector<std::pair<scoped_refptr<Tile>, ManagedTileBin> > + TileBinVector; - TileManagerPerfTest() : num_runs_(0) {} + TileManagerPerfTest() + : timer_(kWarmupRuns, + base::TimeDelta::FromMilliseconds(kTimeLimitMillis), + kTimeCheckInterval) {} // Overridden from testing::Test: virtual void SetUp() OVERRIDE { output_surface_ = FakeOutputSurface::Create3d(); - resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false); tile_manager_ = make_scoped_ptr( new FakeTileManager(&tile_manager_client_, resource_provider_.get())); GlobalStateThatImpactsTilePriority state; gfx::Size tile_size = settings_.default_tile_size; state.memory_limit_in_bytes = - 10000 * 4 * tile_size.width() * tile_size.height(); + 10000u * 4u * + static_cast<size_t>(tile_size.width() * tile_size.height()); + state.num_resources_limit = 10000; state.memory_limit_policy = ALLOW_ANYTHING; state.tree_priority = SMOOTHNESS_TAKES_PRIORITY; @@ -50,34 +62,47 @@ class TileManagerPerfTest : public testing::Test { picture_pile_ = NULL; } - void EndTest() { - elapsed_ = base::TimeTicks::HighResNow() - start_time_; - } - - void AfterTest(const std::string test_name) { - // Format matches chrome/test/perf/perf_test.h:PrintResult - printf("*RESULT %s: %.2f runs/s\n", - test_name.c_str(), - num_runs_ / elapsed_.InSecondsF()); + TilePriority GetTilePriorityFromBin(ManagedTileBin bin) { + switch (bin) { + case NOW_AND_READY_TO_DRAW_BIN: + case NOW_BIN: + return TilePriorityForNowBin(); + case SOON_BIN: + return TilePriorityForSoonBin(); + case EVENTUALLY_AND_ACTIVE_BIN: + case EVENTUALLY_BIN: + return TilePriorityForEventualBin(); + case AT_LAST_BIN: + case AT_LAST_AND_ACTIVE_BIN: + case NEVER_BIN: + return TilePriority(); + default: + NOTREACHED(); + return TilePriority(); + } } - bool DidRun() { - ++num_runs_; - if (num_runs_ == kWarmupRuns) - start_time_ = base::TimeTicks::HighResNow(); - - if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) { - base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_; - if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) { - elapsed_ = elapsed; - return false; - } + ManagedTileBin GetNextBin(ManagedTileBin bin) { + switch (bin) { + case NOW_AND_READY_TO_DRAW_BIN: + case NOW_BIN: + return SOON_BIN; + case SOON_BIN: + return EVENTUALLY_BIN; + case EVENTUALLY_AND_ACTIVE_BIN: + case EVENTUALLY_BIN: + return NEVER_BIN; + case AT_LAST_BIN: + case AT_LAST_AND_ACTIVE_BIN: + case NEVER_BIN: + return NOW_BIN; + default: + NOTREACHED(); + return NEVER_BIN; } - - return true; } - void CreateBinTiles(int count, TilePriority priority, TileVector* tiles) { + void CreateBinTiles(int count, ManagedTileBin bin, TileBinVector* tiles) { for (int i = 0; i < count; ++i) { scoped_refptr<Tile> tile = make_scoped_refptr(new Tile(tile_manager_.get(), @@ -89,32 +114,50 @@ class TileManagerPerfTest : public testing::Test { 0, 0, true)); - tile->SetPriority(ACTIVE_TREE, priority); - tile->SetPriority(PENDING_TREE, priority); - tiles->push_back(tile); + tile->SetPriority(ACTIVE_TREE, GetTilePriorityFromBin(bin)); + tile->SetPriority(PENDING_TREE, GetTilePriorityFromBin(bin)); + tiles->push_back(std::make_pair(tile, bin)); } } - void CreateTiles(int count, TileVector* tiles) { + void CreateTiles(int count, TileBinVector* tiles) { // Roughly an equal amount of all bins. int count_per_bin = count / NUM_BINS; - CreateBinTiles(count_per_bin, TilePriorityForNowBin(), tiles); - CreateBinTiles(count_per_bin, TilePriorityForSoonBin(), tiles); - CreateBinTiles(count_per_bin, TilePriorityForEventualBin(), tiles); - CreateBinTiles(count - 3 * count_per_bin, TilePriority(), tiles); + CreateBinTiles(count_per_bin, NOW_BIN, tiles); + CreateBinTiles(count_per_bin, SOON_BIN, tiles); + CreateBinTiles(count_per_bin, EVENTUALLY_BIN, tiles); + CreateBinTiles(count - 3 * count_per_bin, NEVER_BIN, tiles); } - void RunManageTilesTest(const std::string test_name, - unsigned tile_count) { - start_time_ = base::TimeTicks(); - num_runs_ = 0; - TileVector tiles; + void RunManageTilesTest(const std::string& test_name, + unsigned tile_count, + int priority_change_percent) { + DCHECK_GE(tile_count, 100u); + DCHECK_GE(priority_change_percent, 0); + DCHECK_LE(priority_change_percent, 100); + TileBinVector tiles; CreateTiles(tile_count, &tiles); + timer_.Reset(); do { + if (priority_change_percent > 0) { + for (unsigned i = 0; + i < tile_count; + i += 100 / priority_change_percent) { + Tile* tile = tiles[i].first.get(); + ManagedTileBin bin = GetNextBin(tiles[i].second); + tile->SetPriority(ACTIVE_TREE, GetTilePriorityFromBin(bin)); + tile->SetPriority(PENDING_TREE, GetTilePriorityFromBin(bin)); + tiles[i].second = bin; + } + } + tile_manager_->ManageTiles(); - } while (DidRun()); + tile_manager_->CheckForCompletedTasks(); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); - AfterTest(test_name); + perf_test::PrintResult("manage_tiles", "", test_name, + timer_.LapsPerSecond(), "runs/s", true); } private: @@ -122,18 +165,22 @@ class TileManagerPerfTest : public testing::Test { LayerTreeSettings settings_; scoped_ptr<FakeTileManager> tile_manager_; scoped_refptr<FakePicturePileImpl> picture_pile_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<FakeOutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; - - base::TimeTicks start_time_; - base::TimeDelta elapsed_; - int num_runs_; + LapTimer timer_; }; TEST_F(TileManagerPerfTest, ManageTiles) { - RunManageTilesTest("manage_tiles_100", 100); - RunManageTilesTest("manage_tiles_1000", 1000); - RunManageTilesTest("manage_tiles_10000", 10000); + RunManageTilesTest("100_0", 100, 0); + RunManageTilesTest("1000_0", 1000, 0); + RunManageTilesTest("10000_0", 10000, 0); + RunManageTilesTest("100_10", 100, 10); + RunManageTilesTest("1000_10", 1000, 10); + RunManageTilesTest("10000_10", 10000, 10); + RunManageTilesTest("100_100", 100, 100); + RunManageTilesTest("1000_100", 1000, 100); + RunManageTilesTest("10000_100", 10000, 100); } } // namespace diff --git a/chromium/cc/resources/tile_manager_unittest.cc b/chromium/cc/resources/tile_manager_unittest.cc index 6fb06908568..0274914a324 100644 --- a/chromium/cc/resources/tile_manager_unittest.cc +++ b/chromium/cc/resources/tile_manager_unittest.cc @@ -5,6 +5,7 @@ #include "cc/resources/tile.h" #include "cc/resources/tile_priority.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" #include "cc/test/fake_picture_pile_impl.h" #include "cc/test/fake_tile_manager.h" #include "cc/test/fake_tile_manager_client.h" @@ -22,7 +23,10 @@ class TileManagerTest : public testing::TestWithParam<bool> { TileMemoryLimitPolicy memory_limit_policy, TreePriority tree_priority) { output_surface_ = FakeOutputSurface::Create3d(); - resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0); + CHECK(output_surface_->BindToClient(&output_surface_client_)); + + resource_provider_ = + ResourceProvider::Create(output_surface_.get(), 0, false); tile_manager_ = make_scoped_ptr( new FakeTileManager(&tile_manager_client_, resource_provider_.get())); @@ -41,6 +45,7 @@ class TileManagerTest : public testing::TestWithParam<bool> { state.memory_limit_in_bytes = 100 * 1000 * 1000; state.num_resources_limit = max_tiles; } + state.unused_memory_limit_in_bytes = state.memory_limit_in_bytes; state.memory_limit_policy = memory_limit_policy; state.tree_priority = tree_priority; @@ -53,6 +58,7 @@ class TileManagerTest : public testing::TestWithParam<bool> { gfx::Size tile_size = settings_.default_tile_size; state.memory_limit_in_bytes = max_memory_tiles_ * 4 * tile_size.width() * tile_size.height(); + state.unused_memory_limit_in_bytes = state.memory_limit_in_bytes; state.memory_limit_policy = memory_limit_policy_; state.num_resources_limit = 100; state.tree_priority = tree_priority; @@ -66,15 +72,16 @@ class TileManagerTest : public testing::TestWithParam<bool> { testing::Test::TearDown(); } - TileVector CreateTiles(int count, - TilePriority active_priority, - TilePriority pending_priority) { + TileVector CreateTilesWithSize(int count, + TilePriority active_priority, + TilePriority pending_priority, + gfx::Size tile_size) { TileVector tiles; for (int i = 0; i < count; ++i) { scoped_refptr<Tile> tile = make_scoped_refptr(new Tile(tile_manager_.get(), picture_pile_.get(), - settings_.default_tile_size, + tile_size, gfx::Rect(), gfx::Rect(), 1.0, @@ -88,6 +95,15 @@ class TileManagerTest : public testing::TestWithParam<bool> { return tiles; } + TileVector CreateTiles(int count, + TilePriority active_priority, + TilePriority pending_priority) { + return CreateTilesWithSize(count, + active_priority, + pending_priority, + settings_.default_tile_size); + } + FakeTileManager* tile_manager() { return tile_manager_.get(); } @@ -119,6 +135,7 @@ class TileManagerTest : public testing::TestWithParam<bool> { LayerTreeSettings settings_; scoped_ptr<FakeTileManager> tile_manager_; scoped_refptr<FakePicturePileImpl> picture_pile_; + FakeOutputSurfaceClient output_surface_client_; scoped_ptr<FakeOutputSurface> output_surface_; scoped_ptr<ResourceProvider> resource_provider_; TileMemoryLimitPolicy memory_limit_policy_; @@ -461,6 +478,48 @@ TEST_P(TileManagerTest, TextReRasterAsNoLCD) { EXPECT_EQ(0, TilesWithLCDCount(pending_tree_tiles)); } +TEST_P(TileManagerTest, RespectMemoryLimit) { + Initialize(5, ALLOW_ANYTHING, SMOOTHNESS_TAKES_PRIORITY); + TileVector large_tiles = CreateTiles( + 5, TilePriorityForNowBin(), TilePriority()); + + size_t memory_required_bytes; + size_t memory_nice_to_have_bytes; + size_t memory_allocated_bytes; + size_t memory_used_bytes; + + tile_manager()->ManageTiles(); + tile_manager()->GetMemoryStats(&memory_required_bytes, + &memory_nice_to_have_bytes, + &memory_allocated_bytes, + &memory_used_bytes); + // Allocated bytes should never be more than the memory limit. + EXPECT_LE(memory_allocated_bytes, + tile_manager()->GlobalState().memory_limit_in_bytes); + + // Finish raster of large tiles. + tile_manager()->UpdateVisibleTiles(); + + // Remove all large tiles. This will leave the memory currently + // used by these tiles as unused when ManageTiles() is called. + large_tiles.clear(); + + // Create a new set of tiles using a different size. These tiles + // can use the memory currently assigned to the lerge tiles but + // they can't use the same resources as the size doesn't match. + TileVector small_tiles = CreateTilesWithSize( + 5, TilePriorityForNowBin(), TilePriority(), gfx::Size(128, 128)); + + tile_manager()->ManageTiles(); + tile_manager()->GetMemoryStats(&memory_required_bytes, + &memory_nice_to_have_bytes, + &memory_allocated_bytes, + &memory_used_bytes); + // Allocated bytes should never be more than the memory limit. + EXPECT_LE(memory_allocated_bytes, + tile_manager()->GlobalState().memory_limit_in_bytes); +} + // If true, the max tile limit should be applied as bytes; if false, // as num_resources_limit. INSTANTIATE_TEST_CASE_P(TileManagerTests, diff --git a/chromium/cc/resources/tile_priority.cc b/chromium/cc/resources/tile_priority.cc index 970ea1d81a5..eeb87cdcbbc 100644 --- a/chromium/cc/resources/tile_priority.cc +++ b/chromium/cc/resources/tile_priority.cc @@ -87,11 +87,10 @@ scoped_ptr<base::Value> TileResolutionAsValue( case NON_IDEAL_RESOLUTION: return scoped_ptr<base::Value>(base::Value::CreateStringValue( "NON_IDEAL_RESOLUTION")); - default: - DCHECK(false) << "Unrecognized TileResolution value " << resolution; - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "<unknown TileResolution value>")); } + DCHECK(false) << "Unrecognized TileResolution value " << resolution; + return scoped_ptr<base::Value>(base::Value::CreateStringValue( + "<unknown TileResolution value>")); } scoped_ptr<base::Value> TilePriority::AsValue() const { @@ -176,11 +175,10 @@ scoped_ptr<base::Value> TreePriorityAsValue(TreePriority prio) { case NEW_CONTENT_TAKES_PRIORITY: return scoped_ptr<base::Value>(base::Value::CreateStringValue( "NEW_CONTENT_TAKES_PRIORITY")); - default: - DCHECK(false) << "Unrecognized priority value " << prio; - return scoped_ptr<base::Value>(base::Value::CreateStringValue( - "<unknown>")); } + DCHECK(false) << "Unrecognized priority value " << prio; + return scoped_ptr<base::Value>(base::Value::CreateStringValue( + "<unknown>")); } scoped_ptr<base::Value> GlobalStateThatImpactsTilePriority::AsValue() const { diff --git a/chromium/cc/resources/tile_priority.h b/chromium/cc/resources/tile_priority.h index 5c92d2f178e..e613dd77b04 100644 --- a/chromium/cc/resources/tile_priority.h +++ b/chromium/cc/resources/tile_priority.h @@ -106,7 +106,8 @@ struct CC_EXPORT TilePriority { bool operator ==(const TilePriority& other) const { return resolution == other.resolution && time_to_visible_in_seconds == other.time_to_visible_in_seconds && - distance_to_visible_in_pixels == other.distance_to_visible_in_pixels; + distance_to_visible_in_pixels == other.distance_to_visible_in_pixels && + required_for_activation == other.required_for_activation; // No need to compare current_screen_quad which is for debug only and // never changes by itself. } diff --git a/chromium/cc/resources/transferable_resource.cc b/chromium/cc/resources/transferable_resource.cc index be2be1a448c..1b8930f34ad 100644 --- a/chromium/cc/resources/transferable_resource.cc +++ b/chromium/cc/resources/transferable_resource.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/logging.h" +#include "cc/resources/returned_resource.h" #include "cc/resources/transferable_resource.h" namespace cc { @@ -10,11 +11,28 @@ namespace cc { TransferableResource::TransferableResource() : id(0), sync_point(0), - format(0), + format(RGBA_8888), filter(0) { } TransferableResource::~TransferableResource() { } +ReturnedResource TransferableResource::ToReturnedResource() const { + ReturnedResource returned; + returned.id = id; + returned.sync_point = sync_point; + returned.count = 1; + return returned; +} + +// static +void TransferableResource::ReturnResources( + const TransferableResourceArray& input, + ReturnedResourceArray* output) { + for (TransferableResourceArray::const_iterator it = input.begin(); + it != input.end(); ++it) + output->push_back(it->ToReturnedResource()); +} + } // namespace cc diff --git a/chromium/cc/resources/transferable_resource.h b/chromium/cc/resources/transferable_resource.h index 5c979433c90..0ea62436991 100644 --- a/chromium/cc/resources/transferable_resource.h +++ b/chromium/cc/resources/transferable_resource.h @@ -9,25 +9,33 @@ #include "base/basictypes.h" #include "cc/base/cc_export.h" +#include "cc/resources/resource_format.h" #include "gpu/command_buffer/common/mailbox.h" #include "ui/gfx/size.h" namespace cc { +struct ReturnedResource; +typedef std::vector<ReturnedResource> ReturnedResourceArray; +struct TransferableResource; +typedef std::vector<TransferableResource> TransferableResourceArray; + struct CC_EXPORT TransferableResource { TransferableResource(); ~TransferableResource(); + ReturnedResource ToReturnedResource() const; + static void ReturnResources(const TransferableResourceArray& input, + ReturnedResourceArray* output); + unsigned id; unsigned sync_point; - uint32 format; + ResourceFormat format; uint32 filter; gfx::Size size; gpu::Mailbox mailbox; }; -typedef std::vector<TransferableResource> TransferableResourceArray; - } // namespace cc #endif // CC_RESOURCES_TRANSFERABLE_RESOURCE_H_ diff --git a/chromium/cc/resources/ui_resource_bitmap.cc b/chromium/cc/resources/ui_resource_bitmap.cc index 8bbfb37deee..86acfa5185c 100644 --- a/chromium/cc/resources/ui_resource_bitmap.cc +++ b/chromium/cc/resources/ui_resource_bitmap.cc @@ -4,23 +4,52 @@ #include "cc/resources/ui_resource_bitmap.h" +#include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "third_party/skia/include/core/SkBitmap.h" namespace cc { -scoped_refptr<UIResourceBitmap> -UIResourceBitmap::Create(uint8_t* pixels, - UIResourceFormat format, - gfx::Size size) { - scoped_refptr<UIResourceBitmap> ret = new UIResourceBitmap(); - ret->pixels_ = scoped_ptr<uint8_t[]>(pixels); - ret->format_ = format; - ret->size_ = size; +void UIResourceBitmap::Create(const skia::RefPtr<SkPixelRef>& pixel_ref, + UIResourceFormat format, + UIResourceWrapMode wrap_mode, + gfx::Size size) { + DCHECK(size.width()); + DCHECK(size.height()); + DCHECK(pixel_ref); + DCHECK(pixel_ref->isImmutable()); + format_ = format; + wrap_mode_ = wrap_mode; + size_ = size; + pixel_ref_ = pixel_ref; +} + +UIResourceBitmap::UIResourceBitmap(const SkBitmap& skbitmap, + UIResourceWrapMode wrap_mode) { + DCHECK_EQ(skbitmap.config(), SkBitmap::kARGB_8888_Config); + DCHECK_EQ(skbitmap.width(), skbitmap.rowBytesAsPixels()); + DCHECK(skbitmap.isImmutable()); - return ret; + skia::RefPtr<SkPixelRef> pixel_ref = skia::SharePtr(skbitmap.pixelRef()); + Create(pixel_ref, + UIResourceBitmap::RGBA8, + wrap_mode, + gfx::Size(skbitmap.width(), skbitmap.height())); } -UIResourceBitmap::UIResourceBitmap() {} UIResourceBitmap::~UIResourceBitmap() {} +AutoLockUIResourceBitmap::AutoLockUIResourceBitmap( + const UIResourceBitmap& bitmap) : bitmap_(bitmap) { + bitmap_.pixel_ref_->lockPixels(); +} + +AutoLockUIResourceBitmap::~AutoLockUIResourceBitmap() { + bitmap_.pixel_ref_->unlockPixels(); +} + +const uint8_t* AutoLockUIResourceBitmap::GetPixels() const { + return static_cast<const uint8_t*>(bitmap_.pixel_ref_->pixels()); +} + } // namespace cc diff --git a/chromium/cc/resources/ui_resource_bitmap.h b/chromium/cc/resources/ui_resource_bitmap.h index dbf70690792..78cb4658125 100644 --- a/chromium/cc/resources/ui_resource_bitmap.h +++ b/chromium/cc/resources/ui_resource_bitmap.h @@ -8,41 +8,62 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" +#include "skia/ext/refptr.h" +#include "third_party/skia/include/core/SkPixelRef.h" #include "third_party/skia/include/core/SkTypes.h" #include "ui/gfx/size.h" +class SkBitmap; + namespace cc { -// Ref-counted bitmap class (can’t use SkBitmap because of ETC1). Thread-safety -// ensures that both main and impl threads can hold references to the bitmap and -// that asynchronous uploads are allowed. -class CC_EXPORT UIResourceBitmap - : public base::RefCountedThreadSafe<UIResourceBitmap> { +// A bitmap class that contains a ref-counted reference to a SkPixelRef* that +// holds the content of the bitmap (cannot use SkBitmap because of ETC1). +// Thread-safety (by ways of SkPixelRef) ensures that both main and impl threads +// can hold references to the bitmap and that asynchronous uploads are allowed. +class CC_EXPORT UIResourceBitmap { public: enum UIResourceFormat { RGBA8 }; - - // Takes ownership of “pixels”. - static scoped_refptr<UIResourceBitmap> Create(uint8_t* pixels, - UIResourceFormat format, - gfx::Size size); + enum UIResourceWrapMode { + CLAMP_TO_EDGE, + REPEAT + }; gfx::Size GetSize() const { return size_; } UIResourceFormat GetFormat() const { return format_; } - uint8_t* GetPixels() { return pixels_.get(); } + UIResourceWrapMode GetWrapMode() const { return wrap_mode_; } - private: - friend class base::RefCountedThreadSafe<UIResourceBitmap>; + // The constructor for the UIResourceBitmap. User must ensure that |skbitmap| + // is immutable. The SkBitmap format should be in 32-bit RGBA. Wrap mode is + // unnecessary for most UI resources and is defaulted to CLAMP_TO_EDGE. + UIResourceBitmap(const SkBitmap& skbitmap, + UIResourceWrapMode wrap_mode = CLAMP_TO_EDGE); - UIResourceBitmap(); ~UIResourceBitmap(); - scoped_ptr<uint8_t[]> pixels_; + private: + friend class AutoLockUIResourceBitmap; + void Create(const skia::RefPtr<SkPixelRef>& pixel_ref, + UIResourceFormat format, + UIResourceWrapMode wrap_mode, + gfx::Size size); + + skia::RefPtr<SkPixelRef> pixel_ref_; UIResourceFormat format_; + UIResourceWrapMode wrap_mode_; gfx::Size size_; +}; - DISALLOW_COPY_AND_ASSIGN(UIResourceBitmap); +class CC_EXPORT AutoLockUIResourceBitmap { + public: + explicit AutoLockUIResourceBitmap(const UIResourceBitmap& bitmap); + ~AutoLockUIResourceBitmap(); + const uint8_t* GetPixels() const; + + private: + const UIResourceBitmap& bitmap_; }; } // namespace cc diff --git a/chromium/cc/resources/ui_resource_client.h b/chromium/cc/resources/ui_resource_client.h index d647936baee..24309a52bb7 100644 --- a/chromium/cc/resources/ui_resource_client.h +++ b/chromium/cc/resources/ui_resource_client.h @@ -24,8 +24,8 @@ class CC_EXPORT UIResourceClient { // delete a UIResourceClient object after DeleteUIResource has been called for // all IDs associated with it. A valid bitmap always must be returned but it // doesn't need to be the same size or format as the original. - virtual scoped_refptr<UIResourceBitmap> GetBitmap(UIResourceId uid, - bool resource_lost) = 0; + virtual UIResourceBitmap GetBitmap(UIResourceId uid, + bool resource_lost) = 0; virtual ~UIResourceClient() {} }; diff --git a/chromium/cc/resources/video_resource_updater.cc b/chromium/cc/resources/video_resource_updater.cc index 4239e989e56..34c7ab65c35 100644 --- a/chromium/cc/resources/video_resource_updater.cc +++ b/chromium/cc/resources/video_resource_updater.cc @@ -10,21 +10,24 @@ #include "gpu/GLES2/gl2extchromium.h" #include "media/base/video_frame.h" #include "media/filters/skcanvas_video_renderer.h" +#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" #include "ui/gfx/size_conversions.h" -const unsigned kYUVResourceFormat = GL_LUMINANCE; -const unsigned kRGBResourceFormat = GL_RGBA; - namespace cc { +const ResourceFormat kYUVResourceFormat = LUMINANCE_8; +const ResourceFormat kRGBResourceFormat = RGBA_8888; + VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {} VideoFrameExternalResources::~VideoFrameExternalResources() {} -VideoResourceUpdater::VideoResourceUpdater(ResourceProvider* resource_provider) - : resource_provider_(resource_provider) { +VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider, + ResourceProvider* resource_provider) + : context_provider_(context_provider), + resource_provider_(resource_provider) { } VideoResourceUpdater::~VideoResourceUpdater() { @@ -87,7 +90,7 @@ bool VideoResourceUpdater::VerifyFrame( static gfx::Size SoftwarePlaneDimension( media::VideoFrame::Format input_frame_format, gfx::Size coded_size, - GLenum output_resource_format, + ResourceFormat output_resource_format, int plane_index) { if (output_resource_format == kYUVResourceFormat) { if (plane_index == media::VideoFrame::kYPlane || @@ -113,7 +116,7 @@ static gfx::Size SoftwarePlaneDimension( } } - DCHECK_EQ(output_resource_format, static_cast<unsigned>(kRGBResourceFormat)); + DCHECK_EQ(output_resource_format, kRGBResourceFormat); return coded_size; } @@ -138,9 +141,9 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( input_frame_format != media::VideoFrame::YV16) return VideoFrameExternalResources(); - bool software_compositor = !resource_provider_->GraphicsContext3D(); + bool software_compositor = context_provider_ == NULL; - GLenum output_resource_format = kYUVResourceFormat; + ResourceFormat output_resource_format = kYUVResourceFormat; size_t output_plane_count = (input_frame_format == media::VideoFrame::YV12A) ? 4 : 3; @@ -191,15 +194,17 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( // ResourceProvider and stop using ResourceProvider in this class. resource_id = resource_provider_->CreateResource(output_plane_resource_size, - output_resource_format, - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + output_resource_format); DCHECK(mailbox.IsZero()); if (!software_compositor) { + DCHECK(context_provider_); + WebKit::WebGraphicsContext3D* context = - resource_provider_->GraphicsContext3D(); - DCHECK(context); + context_provider_->Context3d(); GLC(context, context->genMailboxCHROMIUM(mailbox.name)); if (mailbox.IsZero()) { @@ -266,13 +271,12 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( plane_resources[0].resource_format, gpu::Mailbox() }; - TextureMailbox::ReleaseCallback callback_to_free_resource = - base::Bind(&RecycleResource, - AsWeakPtr(), - recycle_data); + external_resources.software_resources.push_back( plane_resources[0].resource_id); - external_resources.software_release_callback = callback_to_free_resource; + external_resources.software_release_callback = + base::Bind(&RecycleResource, AsWeakPtr(), recycle_data); + external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; return external_resources; @@ -280,8 +284,7 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( for (size_t i = 0; i < plane_resources.size(); ++i) { // Update each plane's resource id with its content. - DCHECK_EQ(plane_resources[i].resource_format, - static_cast<unsigned>(kYUVResourceFormat)); + DCHECK_EQ(plane_resources[i].resource_format, kYUVResourceFormat); const uint8_t* input_plane_pixels = video_frame->data(i); @@ -302,13 +305,11 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( plane_resources[i].resource_format, plane_resources[i].mailbox }; - TextureMailbox::ReleaseCallback callback_to_free_resource = - base::Bind(&RecycleResource, - AsWeakPtr(), - recycle_data); + external_resources.mailboxes.push_back( - TextureMailbox(plane_resources[i].mailbox, - callback_to_free_resource)); + TextureMailbox(plane_resources[i].mailbox)); + external_resources.release_callbacks.push_back( + base::Bind(&RecycleResource, AsWeakPtr(), recycle_data)); } external_resources.type = VideoFrameExternalResources::YUV_RESOURCE; @@ -330,9 +331,7 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( if (frame_format != media::VideoFrame::NATIVE_TEXTURE) return VideoFrameExternalResources(); - WebKit::WebGraphicsContext3D* context = - resource_provider_->GraphicsContext3D(); - if (!context) + if (!context_provider_) return VideoFrameExternalResources(); VideoFrameExternalResources external_resources; @@ -355,14 +354,12 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( scoped_refptr<media::VideoFrame::MailboxHolder> mailbox_holder = video_frame->texture_mailbox(); - TextureMailbox::ReleaseCallback callback_to_return_resource = - base::Bind(&ReturnTexture, mailbox_holder); - external_resources.mailboxes.push_back( TextureMailbox(mailbox_holder->mailbox(), - callback_to_return_resource, video_frame->texture_target(), mailbox_holder->sync_point())); + external_resources.release_callbacks.push_back( + base::Bind(&ReturnTexture, mailbox_holder)); return external_resources; } @@ -377,10 +374,11 @@ void VideoResourceUpdater::RecycleResource( return; } - WebKit::WebGraphicsContext3D* context = - updater->resource_provider_->GraphicsContext3D(); - if (context && sync_point) - GLC(context, context->waitSyncPoint(sync_point)); + ContextProvider* context_provider = updater->context_provider_; + if (context_provider && sync_point) { + GLC(context_provider->Context3d(), + context_provider->Context3d()->waitSyncPoint(sync_point)); + } if (lost_resource) { updater->DeleteResource(data.resource_id); diff --git a/chromium/cc/resources/video_resource_updater.h b/chromium/cc/resources/video_resource_updater.h index 9be79d12b33..e3476d44bb4 100644 --- a/chromium/cc/resources/video_resource_updater.h +++ b/chromium/cc/resources/video_resource_updater.h @@ -12,6 +12,8 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "cc/base/cc_export.h" +#include "cc/resources/release_callback.h" +#include "cc/resources/resource_format.h" #include "cc/resources/texture_mailbox.h" #include "ui/gfx/size.h" @@ -21,6 +23,7 @@ class VideoFrame; } namespace cc { +class ContextProvider; class ResourceProvider; class CC_EXPORT VideoFrameExternalResources { @@ -47,10 +50,11 @@ class CC_EXPORT VideoFrameExternalResources { ResourceType type; std::vector<TextureMailbox> mailboxes; + std::vector<ReleaseCallback> release_callbacks; // TODO(danakj): Remove these too. std::vector<unsigned> software_resources; - TextureMailbox::ReleaseCallback software_release_callback; + ReleaseCallback software_release_callback; VideoFrameExternalResources(); ~VideoFrameExternalResources(); @@ -61,7 +65,8 @@ class CC_EXPORT VideoFrameExternalResources { class CC_EXPORT VideoResourceUpdater : public base::SupportsWeakPtr<VideoResourceUpdater> { public: - explicit VideoResourceUpdater(ResourceProvider* resource_provider); + explicit VideoResourceUpdater(ContextProvider* context_provider, + ResourceProvider* resource_provider); ~VideoResourceUpdater(); VideoFrameExternalResources CreateExternalResourcesFromVideoFrame( @@ -71,12 +76,12 @@ class CC_EXPORT VideoResourceUpdater struct PlaneResource { unsigned resource_id; gfx::Size resource_size; - unsigned resource_format; + ResourceFormat resource_format; gpu::Mailbox mailbox; PlaneResource(unsigned resource_id, gfx::Size resource_size, - unsigned resource_format, + ResourceFormat resource_format, gpu::Mailbox mailbox) : resource_id(resource_id), resource_size(resource_size), @@ -94,7 +99,7 @@ class CC_EXPORT VideoResourceUpdater struct RecycleResourceData { unsigned resource_id; gfx::Size resource_size; - unsigned resource_format; + ResourceFormat resource_format; gpu::Mailbox mailbox; }; static void RecycleResource(base::WeakPtr<VideoResourceUpdater> updater, @@ -102,6 +107,7 @@ class CC_EXPORT VideoResourceUpdater unsigned sync_point, bool lost_resource); + ContextProvider* context_provider_; ResourceProvider* resource_provider_; scoped_ptr<media::SkCanvasVideoRenderer> video_renderer_; diff --git a/chromium/cc/resources/video_resource_updater_unittest.cc b/chromium/cc/resources/video_resource_updater_unittest.cc index ff574fff7fb..c36689e16c1 100644 --- a/chromium/cc/resources/video_resource_updater_unittest.cc +++ b/chromium/cc/resources/video_resource_updater_unittest.cc @@ -5,9 +5,10 @@ #include "cc/resources/video_resource_updater.h" #include "base/memory/shared_memory.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/resources/resource_provider.h" #include "cc/test/fake_output_surface.h" -#include "cc/test/test_web_graphics_context_3d.h" +#include "cc/test/fake_output_surface_client.h" #include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,10 +22,11 @@ class VideoResourceUpdaterTest : public testing::Test { TestWebGraphicsContext3D::Create(); context3d_ = context3d.get(); - output_surface3d_ = FakeOutputSurface::Create3d( - context3d.PassAs<WebKit::WebGraphicsContext3D>()); + output_surface3d_ = + FakeOutputSurface::Create3d(context3d.Pass()); + CHECK(output_surface3d_->BindToClient(&client_)); resource_provider3d_ = - ResourceProvider::Create(output_surface3d_.get(), 0); + ResourceProvider::Create(output_surface3d_.get(), 0, false); } scoped_refptr<media::VideoFrame> CreateTestYUVVideoFrame() { @@ -50,12 +52,14 @@ class VideoResourceUpdaterTest : public testing::Test { } TestWebGraphicsContext3D* context3d_; + FakeOutputSurfaceClient client_; scoped_ptr<FakeOutputSurface> output_surface3d_; scoped_ptr<ResourceProvider> resource_provider3d_; }; TEST_F(VideoResourceUpdaterTest, SoftwareFrame) { - VideoResourceUpdater updater(resource_provider3d_.get()); + VideoResourceUpdater updater(output_surface3d_->context_provider().get(), + resource_provider3d_.get()); scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); VideoFrameExternalResources resources = @@ -64,7 +68,8 @@ TEST_F(VideoResourceUpdaterTest, SoftwareFrame) { } TEST_F(VideoResourceUpdaterTest, LostContextForSoftwareFrame) { - VideoResourceUpdater updater(resource_provider3d_.get()); + VideoResourceUpdater updater(output_surface3d_->context_provider().get(), + resource_provider3d_.get()); scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame(); // Fail while creating the mailbox for the second YUV plane. diff --git a/chromium/cc/resources/worker_pool.h b/chromium/cc/resources/worker_pool.h index c26ed077eb6..cc8c39f3642 100644 --- a/chromium/cc/resources/worker_pool.h +++ b/chromium/cc/resources/worker_pool.h @@ -10,12 +10,12 @@ #include <vector> #include "base/cancelable_callback.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "cc/base/cc_export.h" -#include "cc/base/scoped_ptr_hash_map.h" namespace cc { namespace internal { @@ -113,7 +113,7 @@ class CC_EXPORT WorkerPool { // dependencies pointing in the direction of the dependents. Each task // need to be assigned a unique priority and a run count that matches // the number of dependencies. - typedef ScopedPtrHashMap<internal::WorkerPoolTask*, internal::GraphNode> + typedef base::ScopedPtrHashMap<internal::WorkerPoolTask*, internal::GraphNode> GraphNodeMap; typedef GraphNodeMap TaskGraph; diff --git a/chromium/cc/resources/worker_pool_perftest.cc b/chromium/cc/resources/worker_pool_perftest.cc index aee1f24a27d..06a2b9806df 100644 --- a/chromium/cc/resources/worker_pool_perftest.cc +++ b/chromium/cc/resources/worker_pool_perftest.cc @@ -6,7 +6,9 @@ #include "base/time/time.h" #include "cc/base/completion_event.h" +#include "cc/test/lap_timer.h" #include "testing/gtest/include/gtest/gtest.h" +#include "testing/perf/perf_test.h" namespace cc { @@ -144,7 +146,10 @@ class PerfWorkerPool : public WorkerPool { class WorkerPoolPerfTest : public testing::Test { public: - WorkerPoolPerfTest() : num_runs_(0) {} + WorkerPoolPerfTest() + : timer_(kWarmupRuns, + base::TimeDelta::FromMilliseconds(kTimeLimitMillis), + kTimeCheckInterval) {} // Overridden from testing::Test: virtual void SetUp() OVERRIDE { @@ -155,38 +160,16 @@ class WorkerPoolPerfTest : public testing::Test { worker_pool_->CheckForCompletedTasks(); } - void EndTest() { - elapsed_ = base::TimeTicks::HighResNow() - start_time_; - } - - void AfterTest(const std::string test_name) { + void AfterTest(const std::string& test_name) { // Format matches chrome/test/perf/perf_test.h:PrintResult - printf("*RESULT %s: %.2f runs/s\n", - test_name.c_str(), - num_runs_ / elapsed_.InSecondsF()); - } - - bool DidRun() { - ++num_runs_; - if (num_runs_ == kWarmupRuns) - start_time_ = base::TimeTicks::HighResNow(); - - if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) { - base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_; - if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) { - elapsed_ = elapsed; - return false; - } - } - - return true; + printf( + "*RESULT %s: %.2f runs/s\n", test_name.c_str(), timer_.LapsPerSecond()); } - void RunScheduleTasksTest(const std::string test_name, + void RunScheduleTasksTest(const std::string& test_name, unsigned max_depth, unsigned num_children_per_node) { - start_time_ = base::TimeTicks(); - num_runs_ = 0; + timer_.Reset(); do { scoped_refptr<PerfControlWorkerPoolTaskImpl> leaf_task( new PerfControlWorkerPoolTaskImpl); @@ -196,16 +179,17 @@ class WorkerPoolPerfTest : public testing::Test { worker_pool_->ScheduleTasks(NULL, NULL, 0, 0); worker_pool_->CheckForCompletedTasks(); leaf_task->AllowTaskToFinish(); - } while (DidRun()); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); - AfterTest(test_name); + perf_test::PrintResult("schedule_tasks", "", test_name, + timer_.LapsPerSecond(), "runs/s", true); } - void RunExecuteTasksTest(const std::string test_name, + void RunExecuteTasksTest(const std::string& test_name, unsigned max_depth, unsigned num_children_per_node) { - start_time_ = base::TimeTicks(); - num_runs_ = 0; + timer_.Reset(); do { scoped_refptr<PerfControlWorkerPoolTaskImpl> root_task( new PerfControlWorkerPoolTaskImpl); @@ -214,36 +198,36 @@ class WorkerPoolPerfTest : public testing::Test { root_task->WaitForTaskToStartRunning(); root_task->AllowTaskToFinish(); worker_pool_->CheckForCompletedTasks(); - } while (DidRun()); + timer_.NextLap(); + } while (!timer_.HasTimeLimitExpired()); - AfterTest(test_name); + perf_test::PrintResult("execute_tasks", "", test_name, + timer_.LapsPerSecond(), "runs/s", true); } protected: scoped_ptr<PerfWorkerPool> worker_pool_; - base::TimeTicks start_time_; - base::TimeDelta elapsed_; - int num_runs_; + LapTimer timer_; }; TEST_F(WorkerPoolPerfTest, ScheduleTasks) { - RunScheduleTasksTest("schedule_tasks_1_10", 1, 10); - RunScheduleTasksTest("schedule_tasks_1_1000", 1, 1000); - RunScheduleTasksTest("schedule_tasks_2_10", 2, 10); - RunScheduleTasksTest("schedule_tasks_5_5", 5, 5); - RunScheduleTasksTest("schedule_tasks_10_2", 10, 2); - RunScheduleTasksTest("schedule_tasks_1000_1", 1000, 1); - RunScheduleTasksTest("schedule_tasks_10_1", 10, 1); + RunScheduleTasksTest("1_10", 1, 10); + RunScheduleTasksTest("1_1000", 1, 1000); + RunScheduleTasksTest("2_10", 2, 10); + RunScheduleTasksTest("5_5", 5, 5); + RunScheduleTasksTest("10_2", 10, 2); + RunScheduleTasksTest("1000_1", 1000, 1); + RunScheduleTasksTest("10_1", 10, 1); } TEST_F(WorkerPoolPerfTest, ExecuteTasks) { - RunExecuteTasksTest("execute_tasks_1_10", 1, 10); - RunExecuteTasksTest("execute_tasks_1_1000", 1, 1000); - RunExecuteTasksTest("execute_tasks_2_10", 2, 10); - RunExecuteTasksTest("execute_tasks_5_5", 5, 5); - RunExecuteTasksTest("execute_tasks_10_2", 10, 2); - RunExecuteTasksTest("execute_tasks_1000_1", 1000, 1); - RunExecuteTasksTest("execute_tasks_10_1", 10, 1); + RunExecuteTasksTest("1_10", 1, 10); + RunExecuteTasksTest("1_1000", 1, 1000); + RunExecuteTasksTest("2_10", 2, 10); + RunExecuteTasksTest("5_5", 5, 5); + RunExecuteTasksTest("10_2", 10, 2); + RunExecuteTasksTest("1000_1", 1000, 1); + RunExecuteTasksTest("10_1", 10, 1); } } // namespace diff --git a/chromium/cc/scheduler/delay_based_time_source.cc b/chromium/cc/scheduler/delay_based_time_source.cc index 44003970b35..d150c717a12 100644 --- a/chromium/cc/scheduler/delay_based_time_source.cc +++ b/chromium/cc/scheduler/delay_based_time_source.cc @@ -17,10 +17,10 @@ namespace cc { namespace { -// kDoubleTickThreshold prevents ticks from running within the specified +// kDoubleTickDivisor prevents ticks from running within the specified // fraction of an interval. This helps account for jitter in the timebase as // well as quick timer reactivation. -static const double kDoubleTickThreshold = 0.25; +static const int kDoubleTickDivisor = 2; // kIntervalChangeThreshold is the fraction of the interval that will trigger an // immediate interval change. kPhaseChangeThreshold is the fraction of the @@ -42,43 +42,42 @@ scoped_refptr<DelayBasedTimeSource> DelayBasedTimeSource::Create( DelayBasedTimeSource::DelayBasedTimeSource( base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner) : client_(NULL), - has_tick_target_(false), + last_tick_time_(base::TimeTicks() - interval), current_parameters_(interval, base::TimeTicks()), next_parameters_(interval, base::TimeTicks()), - state_(STATE_INACTIVE), + active_(false), task_runner_(task_runner), weak_factory_(this) {} DelayBasedTimeSource::~DelayBasedTimeSource() {} -void DelayBasedTimeSource::SetActive(bool active) { +base::TimeTicks DelayBasedTimeSource::SetActive(bool active) { TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); - if (!active) { - state_ = STATE_INACTIVE; + if (active == active_) + return base::TimeTicks(); + active_ = active; + + if (!active_) { weak_factory_.InvalidateWeakPtrs(); - return; + return base::TimeTicks(); } - if (state_ == STATE_STARTING || state_ == STATE_ACTIVE) - return; + PostNextTickTask(Now()); - if (!has_tick_target_) { - // Becoming active the first time is deferred: we post a 0-delay task. - // When it runs, we use that to establish the timebase, become truly - // active, and fire the first tick. - state_ = STATE_STARTING; - task_runner_->PostTask(FROM_HERE, - base::Bind(&DelayBasedTimeSource::OnTimerFired, - weak_factory_.GetWeakPtr())); - return; + // Determine if there was a tick that was missed while not active. + base::TimeTicks last_tick_time_if_always_active = + current_parameters_.tick_target - current_parameters_.interval; + base::TimeTicks new_tick_time_threshold = + last_tick_time_ + current_parameters_.interval / kDoubleTickDivisor; + if (last_tick_time_if_always_active > new_tick_time_threshold) { + last_tick_time_ = last_tick_time_if_always_active; + return last_tick_time_; } - state_ = STATE_ACTIVE; - - PostNextTickTask(Now()); + return base::TimeTicks(); } -bool DelayBasedTimeSource::Active() const { return state_ != STATE_INACTIVE; } +bool DelayBasedTimeSource::Active() const { return active_; } base::TimeTicks DelayBasedTimeSource::LastTickTime() { return last_tick_time_; } @@ -87,17 +86,11 @@ base::TimeTicks DelayBasedTimeSource::NextTickTime() { } void DelayBasedTimeSource::OnTimerFired() { - DCHECK(state_ != STATE_INACTIVE); + DCHECK(active_); - base::TimeTicks now = this->Now(); - last_tick_time_ = now; + last_tick_time_ = current_parameters_.tick_target; - if (state_ == STATE_STARTING) { - SetTimebaseAndInterval(now, current_parameters_.interval); - state_ = STATE_ACTIVE; - } - - PostNextTickTask(now); + PostNextTickTask(Now()); // Fire the tick. if (client_) @@ -112,9 +105,8 @@ void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, base::TimeDelta interval) { next_parameters_.interval = interval; next_parameters_.tick_target = timebase; - has_tick_target_ = true; - if (state_ != STATE_ACTIVE) { + if (!active_) { // If we aren't active, there's no need to reset the timer. return; } @@ -125,6 +117,8 @@ void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, std::abs((interval - current_parameters_.interval).InSecondsF()); double interval_change = interval_delta / interval.InSecondsF(); if (interval_change > kIntervalChangeThreshold) { + TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", + TRACE_EVENT_SCOPE_THREAD); SetActive(false); SetActive(true); return; @@ -142,6 +136,8 @@ void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); if (phase_change > kPhaseChangeThreshold && phase_change < (1.0 - kPhaseChangeThreshold)) { + TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", + TRACE_EVENT_SCOPE_THREAD); SetActive(false); SetActive(true); return; @@ -208,25 +204,32 @@ base::TimeTicks DelayBasedTimeSource::Now() const { // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { base::TimeDelta new_interval = next_parameters_.interval; - int intervals_elapsed = - static_cast<int>(floor((now - next_parameters_.tick_target).InSecondsF() / - new_interval.InSecondsF())); - base::TimeTicks last_effective_tick = - next_parameters_.tick_target + new_interval * intervals_elapsed; - base::TimeTicks new_tick_target = last_effective_tick + new_interval; - DCHECK(now < new_tick_target) + + // |interval_offset| is the offset from |now| to the next multiple of + // |interval| after |tick_target|, possibly negative if in the past. + base::TimeDelta interval_offset = base::TimeDelta::FromInternalValue( + (next_parameters_.tick_target - now).ToInternalValue() % + new_interval.ToInternalValue()); + // If |now| is exactly on the interval (i.e. offset==0), don't adjust. + // Otherwise, if |tick_target| was in the past, adjust forward to the next + // tick after |now|. + if (interval_offset.ToInternalValue() != 0 && + next_parameters_.tick_target < now) { + interval_offset += new_interval; + } + + base::TimeTicks new_tick_target = now + interval_offset; + DCHECK(now <= new_tick_target) << "now = " << now.ToInternalValue() << "; new_tick_target = " << new_tick_target.ToInternalValue() << "; new_interval = " << new_interval.InMicroseconds() << "; tick_target = " << next_parameters_.tick_target.ToInternalValue() - << "; intervals_elapsed = " << intervals_elapsed - << "; last_effective_tick = " << last_effective_tick.ToInternalValue(); + << "; interval_offset = " << interval_offset.ToInternalValue(); // Avoid double ticks when: // 1) Turning off the timer and turning it right back on. // 2) Jittery data is passed to SetTimebaseAndInterval(). - if (new_tick_target - last_tick_time_ <= - new_interval / static_cast<int>(1.0 / kDoubleTickThreshold)) + if (new_tick_target - last_tick_time_ <= new_interval / kDoubleTickDivisor) new_tick_target += new_interval; return new_tick_target; @@ -236,10 +239,9 @@ void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { base::TimeTicks new_tick_target = NextTickTarget(now); // Post another task *before* the tick and update state - base::TimeDelta delay = new_tick_target - now; - DCHECK(delay.InMillisecondsF() <= - next_parameters_.interval.InMillisecondsF() * - (1.0 + kDoubleTickThreshold)); + base::TimeDelta delay; + if (now <= new_tick_target) + delay = new_tick_target - now; task_runner_->PostDelayedTask(FROM_HERE, base::Bind(&DelayBasedTimeSource::OnTimerFired, weak_factory_.GetWeakPtr()), diff --git a/chromium/cc/scheduler/delay_based_time_source.h b/chromium/cc/scheduler/delay_based_time_source.h index 1dc4d6fff11..55aac5a97fb 100644 --- a/chromium/cc/scheduler/delay_based_time_source.h +++ b/chromium/cc/scheduler/delay_based_time_source.h @@ -27,7 +27,7 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource { virtual void SetTimebaseAndInterval(base::TimeTicks timebase, base::TimeDelta interval) OVERRIDE; - virtual void SetActive(bool active) OVERRIDE; + virtual base::TimeTicks SetActive(bool active) OVERRIDE; virtual bool Active() const OVERRIDE; // Get the last and next tick times. nextTimeTime() returns null when @@ -47,12 +47,6 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource { void PostNextTickTask(base::TimeTicks now); void OnTimerFired(); - enum State { - STATE_INACTIVE, - STATE_STARTING, - STATE_ACTIVE, - }; - struct Parameters { Parameters(base::TimeDelta interval, base::TimeTicks tick_target) : interval(interval), tick_target(tick_target) {} @@ -61,7 +55,6 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource { }; TimeSourceClient* client_; - bool has_tick_target_; base::TimeTicks last_tick_time_; // current_parameters_ should only be written by PostNextTickTask. @@ -71,7 +64,7 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource { Parameters current_parameters_; Parameters next_parameters_; - State state_; + bool active_; base::SingleThreadTaskRunner* task_runner_; base::WeakPtrFactory<DelayBasedTimeSource> weak_factory_; diff --git a/chromium/cc/scheduler/delay_based_time_source_unittest.cc b/chromium/cc/scheduler/delay_based_time_source_unittest.cc index df84c978e57..42652e21868 100644 --- a/chromium/cc/scheduler/delay_based_time_source_unittest.cc +++ b/chromium/cc/scheduler/delay_based_time_source_unittest.cc @@ -88,7 +88,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyOnRequestedTime) { FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); timer->SetClient(&client); timer->SetActive(true); - // Run the first task, as that activates the timer and picks up a timebase. + // Run the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -109,7 +109,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime) { FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); timer->SetClient(&client); timer->SetActive(true); - // Run the first task, as that activates the timer and picks up a timebase. + // Run the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -122,7 +122,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime) { } // At 60Hz, when the tick returns at exactly 2*interval after the requested next -// time, make sure a 16ms next delay is posted. +// time, make sure a 0ms next delay is posted. TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) { scoped_refptr<base::TestSimpleTaskRunner> task_runner = new base::TestSimpleTaskRunner; @@ -131,7 +131,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) { FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); timer->SetClient(&client); timer->SetActive(true); - // Run the first task, as that activates the timer and picks up a timebase. + // Run the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -139,7 +139,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) { timer->SetNow(timer->Now() + 2 * Interval()); task_runner->RunPendingTasks(); - EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); + EXPECT_EQ(0, task_runner->NextPendingTaskDelay().InMilliseconds()); } // At 60Hz, when the tick returns at 2*interval and a bit after the requested @@ -152,7 +152,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) { FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); timer->SetClient(&client); timer->SetActive(true); - // Run the first task, as that activates the timer and picks up a timebase. + // Run the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -174,7 +174,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenHalfAfterRequestedTime) { FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); timer->SetClient(&client); timer->SetActive(true); - // Run the first task, as that activates the timer and picks up a timebase. + // Run the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -196,7 +196,7 @@ TEST(DelayBasedTimeSource, SaneHandlingOfJitteryTimebase) { FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); timer->SetClient(&client); timer->SetActive(true); - // Run the first task, as that activates the timer and picks up a timebase. + // Run the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -227,7 +227,7 @@ TEST(DelayBasedTimeSource, HandlesSignificantTimebaseChangesImmediately) { FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); timer->SetClient(&client); timer->SetActive(true); - // Run the first task, as that activates the timer and picks up a timebase. + // Run the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -246,10 +246,10 @@ TEST(DelayBasedTimeSource, HandlesSignificantTimebaseChangesImmediately) { timer->SetTimebaseAndInterval(timer->Now() + jitter, Interval()); EXPECT_FALSE(client.TickCalled()); // Make sure pending tasks were canceled. - EXPECT_EQ(7, task_runner->NextPendingTaskDelay().InMilliseconds()); + EXPECT_EQ(16 + 7, task_runner->NextPendingTaskDelay().InMilliseconds()); // Tick, then shift timebase by -7ms. - timer->SetNow(timer->Now() + jitter); + timer->SetNow(timer->Now() + Interval() + jitter); task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -271,7 +271,7 @@ TEST(DelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately) { FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); timer->SetClient(&client); timer->SetActive(true); - // Run the first task, as that activates the timer and picks up a timebase. + // Run the first tick. task_runner->RunPendingTasks(); EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); @@ -305,6 +305,115 @@ TEST(DelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately) { EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); } +TEST(DelayBasedTimeSource, JitteryRuntimeWithFutureTimebases) { + scoped_refptr<base::TestSimpleTaskRunner> task_runner = + new base::TestSimpleTaskRunner; + FakeTimeSourceClient client; + scoped_refptr<FakeDelayBasedTimeSource> timer = + FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); + timer->SetClient(&client); + timer->SetActive(true); + + // Run the first tick. + task_runner->RunPendingTasks(); + EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); + + base::TimeTicks future_timebase = timer->Now() + Interval() * 10; + + // 1ms jitter + base::TimeDelta jitter1 = base::TimeDelta::FromMilliseconds(1); + + // Tick with +1ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() + jitter1); + task_runner->RunPendingTasks(); + EXPECT_EQ(15, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() - jitter1); + task_runner->RunPendingTasks(); + EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with -1ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() - jitter1); + task_runner->RunPendingTasks(); + EXPECT_EQ(17, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() + jitter1); + task_runner->RunPendingTasks(); + EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // 8 ms jitter + base::TimeDelta jitter8 = base::TimeDelta::FromMilliseconds(8); + + // Tick with +8ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() + jitter8); + task_runner->RunPendingTasks(); + EXPECT_EQ(8, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() - jitter8); + task_runner->RunPendingTasks(); + EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with -8ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() - jitter8); + task_runner->RunPendingTasks(); + EXPECT_EQ(24, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() + jitter8); + task_runner->RunPendingTasks(); + EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // 15 ms jitter + base::TimeDelta jitter15 = base::TimeDelta::FromMilliseconds(15); + + // Tick with +15ms jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() + jitter15); + task_runner->RunPendingTasks(); + EXPECT_EQ(1, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() - jitter15); + task_runner->RunPendingTasks(); + EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with -15ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() - jitter15); + task_runner->RunPendingTasks(); + EXPECT_EQ(31, task_runner->NextPendingTaskDelay().InMilliseconds()); + + // Tick with 0ms of jitter + future_timebase += Interval(); + timer->SetTimebaseAndInterval(future_timebase, Interval()); + timer->SetNow(timer->Now() + Interval() + jitter15); + task_runner->RunPendingTasks(); + EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); +} + TEST(DelayBasedTimeSourceTest, AchievesTargetRateWithNoNoise) { int num_iterations = 10; @@ -397,5 +506,22 @@ TEST(DelayBasedTimeSource, TestDeactivateAndReactivateAfterNextTickTime) { EXPECT_EQ(13, task_runner->NextPendingTaskDelay().InMilliseconds()); } +TEST(DelayBasedTimeSource, TestOverflow) { + // int(big_now / interval) < 0, so this causes a crash if the number of + // intervals elapsed is attempted to be stored in an int. + base::TimeDelta interval = base::TimeDelta::FromInternalValue(4000); + base::TimeTicks big_now = base::TimeTicks::FromInternalValue(8635916564000); + + scoped_refptr<base::TestSimpleTaskRunner> task_runner = + new base::TestSimpleTaskRunner; + FakeTimeSourceClient client; + scoped_refptr<FakeDelayBasedTimeSource> timer = + FakeDelayBasedTimeSource::Create(interval, task_runner.get()); + timer->SetClient(&client); + timer->SetNow(big_now); + timer->SetActive(true); + EXPECT_EQ(0, task_runner->NextPendingTaskDelay().InMilliseconds()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/scheduler/frame_rate_controller.cc b/chromium/cc/scheduler/frame_rate_controller.cc index 4844b16faee..cefe764fc78 100644 --- a/chromium/cc/scheduler/frame_rate_controller.cc +++ b/chromium/cc/scheduler/frame_rate_controller.cc @@ -70,11 +70,15 @@ BeginFrameArgs FrameRateController::SetActive(bool active) { if (active_ == active) return BeginFrameArgs(); TRACE_EVENT1("cc", "FrameRateController::SetActive", "active", active); - bool just_activated = active && !active_; active_ = active; if (is_time_source_throttling_) { - time_source_->SetActive(active); + base::TimeTicks missed_tick_time = time_source_->SetActive(active); + if (!missed_tick_time.is_null()) { + base::TimeTicks deadline = NextTickTime(); + return BeginFrameArgs::Create( + missed_tick_time, deadline + deadline_adjustment_, interval_); + } } else { if (active) PostManualTick(); @@ -82,12 +86,6 @@ BeginFrameArgs FrameRateController::SetActive(bool active) { weak_factory_.InvalidateWeakPtrs(); } - if (just_activated) { - // TODO(brianderson): Use an adaptive parent compositor deadline. - base::TimeTicks frame_time = NextTickTime() - interval_; - base::TimeTicks deadline = NextTickTime(); - return BeginFrameArgs::Create(frame_time, deadline, interval_); - } return BeginFrameArgs(); } @@ -119,10 +117,10 @@ void FrameRateController::OnTimerTick() { if (client_) { // TODO(brianderson): Use an adaptive parent compositor deadline. base::TimeTicks frame_time = LastTickTime(); - base::TimeTicks deadline = NextTickTime() + deadline_adjustment_; - client_->FrameRateControllerTick( - throttled, - BeginFrameArgs::Create(frame_time, deadline, interval_)); + base::TimeTicks deadline = NextTickTime(); + BeginFrameArgs args = BeginFrameArgs::Create( + frame_time, deadline + deadline_adjustment_, interval_); + client_->FrameRateControllerTick(throttled, args); } if (!is_time_source_throttling_ && !throttled) diff --git a/chromium/cc/scheduler/frame_rate_controller.h b/chromium/cc/scheduler/frame_rate_controller.h index ed769238fe3..b68c73db0c8 100644 --- a/chromium/cc/scheduler/frame_rate_controller.h +++ b/chromium/cc/scheduler/frame_rate_controller.h @@ -61,12 +61,6 @@ class CC_EXPORT FrameRateController { int MaxSwapsPending() const { return max_swaps_pending_; } int NumSwapsPendingForTesting() const { return num_frames_pending_; } - // This returns null for unthrottled frame-rate. - base::TimeTicks NextTickTime(); - - // This returns now for unthrottled frame-rate. - base::TimeTicks LastTickTime(); - void SetTimebaseAndInterval(base::TimeTicks timebase, base::TimeDelta interval); void SetDeadlineAdjustment(base::TimeDelta delta); @@ -78,6 +72,11 @@ class CC_EXPORT FrameRateController { void PostManualTick(); void ManualTick(); + // This returns null for unthrottled frame-rate. + base::TimeTicks NextTickTime(); + // This returns now for unthrottled frame-rate. + base::TimeTicks LastTickTime(); + FrameRateControllerClient* client_; int num_frames_pending_; int max_swaps_pending_; diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index 010cc990b47..c81513feebd 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -4,9 +4,11 @@ #include "cc/scheduler/scheduler.h" +#include <algorithm> #include "base/auto_reset.h" #include "base/debug/trace_event.h" #include "base/logging.h" +#include "cc/debug/traced_value.h" namespace cc { @@ -16,17 +18,14 @@ Scheduler::Scheduler(SchedulerClient* client, client_(client), weak_factory_(this), last_set_needs_begin_frame_(false), - has_pending_begin_frame_(false), - safe_to_expect_begin_frame_(false), state_machine_(scheduler_settings), - inside_process_scheduled_actions_(false) { + inside_process_scheduled_actions_(false), + inside_action_(SchedulerStateMachine::ACTION_NONE) { DCHECK(client_); - DCHECK(!state_machine_.BeginFrameNeededToDrawByImplThread()); + DCHECK(!state_machine_.BeginFrameNeededByImplThread()); } -Scheduler::~Scheduler() { - client_->SetNeedsBeginFrameOnImplThread(false); -} +Scheduler::~Scheduler() {} void Scheduler::SetCanStart() { state_machine_.SetCanStart(); @@ -43,19 +42,23 @@ void Scheduler::SetCanDraw(bool can_draw) { ProcessScheduledActions(); } -void Scheduler::SetHasPendingTree(bool has_pending_tree) { - state_machine_.SetHasPendingTree(has_pending_tree); +void Scheduler::NotifyReadyToActivate() { + state_machine_.NotifyReadyToActivate(); ProcessScheduledActions(); } +void Scheduler::ActivatePendingTree() { + client_->ScheduledActionActivatePendingTree(); +} + void Scheduler::SetNeedsCommit() { state_machine_.SetNeedsCommit(); ProcessScheduledActions(); } -void Scheduler::SetNeedsForcedCommit() { +void Scheduler::SetNeedsForcedCommitForReadback() { state_machine_.SetNeedsCommit(); - state_machine_.SetNeedsForcedCommit(); + state_machine_.SetNeedsForcedCommitForReadback(); ProcessScheduledActions(); } @@ -64,13 +67,19 @@ void Scheduler::SetNeedsRedraw() { ProcessScheduledActions(); } -void Scheduler::DidSwapUseIncompleteTile() { - state_machine_.DidSwapUseIncompleteTile(); +void Scheduler::SetNeedsManageTiles() { + DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES)); + state_machine_.SetNeedsManageTiles(); + ProcessScheduledActions(); +} + +void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) { + state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile); ProcessScheduledActions(); } -void Scheduler::SetNeedsForcedRedraw() { - state_machine_.SetNeedsForcedRedraw(); +void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority) { + state_machine_.SetSmoothnessTakesPriority(smoothness_takes_priority); ProcessScheduledActions(); } @@ -93,16 +102,17 @@ void Scheduler::BeginFrameAbortedByMainThread(bool did_handle) { void Scheduler::DidLoseOutputSurface() { TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); + last_set_needs_begin_frame_ = false; + begin_frame_deadline_closure_.Cancel(); state_machine_.DidLoseOutputSurface(); ProcessScheduledActions(); } void Scheduler::DidCreateAndInitializeOutputSurface() { TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); + DCHECK(!last_set_needs_begin_frame_); + DCHECK(begin_frame_deadline_closure_.IsCancelled()); state_machine_.DidCreateAndInitializeOutputSurface(); - has_pending_begin_frame_ = false; - last_set_needs_begin_frame_ = false; - safe_to_expect_begin_frame_ = false; ProcessScheduledActions(); } @@ -113,12 +123,11 @@ base::TimeTicks Scheduler::AnticipatedDrawTime() { last_begin_frame_args_.interval <= base::TimeDelta()) return base::TimeTicks(); - // TODO(brianderson): Express this in terms of the deadline. base::TimeTicks now = base::TimeTicks::Now(); - int64 intervals = 1 + ((now - last_begin_frame_args_.frame_time) / - last_begin_frame_args_.interval); - return last_begin_frame_args_.frame_time + - (last_begin_frame_args_.interval * intervals); + base::TimeTicks timebase = std::max(last_begin_frame_args_.frame_time, + last_begin_frame_args_.deadline); + int64 intervals = 1 + ((now - timebase) / last_begin_frame_args_.interval); + return timebase + (last_begin_frame_args_.interval * intervals); } base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() { @@ -126,69 +135,138 @@ base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() { } void Scheduler::SetupNextBeginFrameIfNeeded() { - bool needs_begin_frame_to_draw = - state_machine_.BeginFrameNeededToDrawByImplThread(); - // We want to avoid proactive begin frames with the synchronous compositor - // because every SetNeedsBeginFrame will force a redraw. - bool proactive_begin_frame_wanted = - state_machine_.ProactiveBeginFrameWantedByImplThread() && - !settings_.using_synchronous_renderer_compositor && - settings_.throttle_frame_production; - bool needs_begin_frame = needs_begin_frame_to_draw || - proactive_begin_frame_wanted; - bool immediate_disables_needed = - settings_.using_synchronous_renderer_compositor; - - if (needs_begin_frame_to_draw) - safe_to_expect_begin_frame_ = true; - - // Determine if we need BeginFrame notifications. - // If we do, always request the BeginFrame immediately. - // If not, only disable on the next BeginFrame to avoid unnecessary toggles. - // The synchronous renderer compositor requires immediate disables though. - if ((needs_begin_frame || - state_machine_.inside_begin_frame() || - immediate_disables_needed) && - (needs_begin_frame != last_set_needs_begin_frame_)) { - has_pending_begin_frame_ = false; + bool needs_begin_frame = + state_machine_.BeginFrameNeededByImplThread(); + + bool at_end_of_deadline = + state_machine_.begin_frame_state() == + SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE; + + bool should_call_set_needs_begin_frame = + // Always request the BeginFrame immediately if it wasn't needed before. + (needs_begin_frame && !last_set_needs_begin_frame_) || + // We always need to explicitly request our next BeginFrame. + at_end_of_deadline; + + if (should_call_set_needs_begin_frame) { client_->SetNeedsBeginFrameOnImplThread(needs_begin_frame); - if (safe_to_expect_begin_frame_) - last_set_needs_begin_frame_ = needs_begin_frame; + last_set_needs_begin_frame_ = needs_begin_frame; } - // Request another BeginFrame if we haven't drawn for now until we have - // deadlines implemented. - if (state_machine_.inside_begin_frame() && has_pending_begin_frame_) { - has_pending_begin_frame_ = false; - client_->SetNeedsBeginFrameOnImplThread(true); - return; + // Setup PollForAnticipatedDrawTriggers if we need to monitor state but + // aren't expecting any more BeginFrames. This should only be needed by the + // synchronous compositor when BeginFrameNeededByImplThread is false. + if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) { + DCHECK(settings_.using_synchronous_renderer_compositor); + DCHECK(!needs_begin_frame); + if (poll_for_draw_triggers_closure_.IsCancelled()) { + poll_for_draw_triggers_closure_.Reset( + base::Bind(&Scheduler::PollForAnticipatedDrawTriggers, + weak_factory_.GetWeakPtr())); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + poll_for_draw_triggers_closure_.callback(), + last_begin_frame_args_.interval); + } + } else { + poll_for_draw_triggers_closure_.Cancel(); } } void Scheduler::BeginFrame(const BeginFrameArgs& args) { TRACE_EVENT0("cc", "Scheduler::BeginFrame"); - DCHECK(!has_pending_begin_frame_); - has_pending_begin_frame_ = true; - safe_to_expect_begin_frame_ = true; + DCHECK(state_machine_.begin_frame_state() == + SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE); + DCHECK(state_machine_.HasInitializedOutputSurface()); last_begin_frame_args_ = args; - state_machine_.DidEnterBeginFrame(args); + last_begin_frame_args_.deadline -= client_->DrawDurationEstimate(); + state_machine_.OnBeginFrame(last_begin_frame_args_); + ProcessScheduledActions(); + + if (!state_machine_.HasInitializedOutputSurface()) + return; + + state_machine_.OnBeginFrameDeadlinePending(); + + if (settings_.using_synchronous_renderer_compositor) { + // The synchronous renderer compositor has to make its GL calls + // within this call to BeginFrame. + // TODO(brianderson): Have the OutputSurface initiate the deadline tasks + // so the sychronous renderer compoistor can take advantage of splitting + // up the BeginFrame and deadline as well. + OnBeginFrameDeadline(); + } else if (!settings_.deadline_scheduling_enabled) { + // We emulate the old non-deadline scheduler here by posting the + // deadline task without any delay. + PostBeginFrameDeadline(base::TimeTicks()); + } else if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly()) { + // We are ready to draw a new active tree immediately. + PostBeginFrameDeadline(base::TimeTicks()); + } else if (state_machine_.needs_redraw()) { + // We have an animation or fast input path on the impl thread that wants + // to draw, so don't wait too long for a new active tree. + PostBeginFrameDeadline(last_begin_frame_args_.deadline); + } else { + // The impl thread doesn't have anything it wants to draw and we are just + // waiting for a new active tree, so post the deadline for the next + // expected BeginFrame start. This allows us to draw immediately when + // there is a new active tree, instead of waiting for the next BeginFrame. + // TODO(brianderson): Handle long deadlines (that are past the next frame's + // frame time) properly instead of using this hack. + PostBeginFrameDeadline(last_begin_frame_args_.frame_time + + last_begin_frame_args_.interval); + } +} + +void Scheduler::PostBeginFrameDeadline(base::TimeTicks deadline) { + begin_frame_deadline_closure_.Cancel(); + begin_frame_deadline_closure_.Reset( + base::Bind(&Scheduler::OnBeginFrameDeadline, weak_factory_.GetWeakPtr())); + client_->PostBeginFrameDeadline(begin_frame_deadline_closure_.callback(), + deadline); +} + +void Scheduler::OnBeginFrameDeadline() { + TRACE_EVENT0("cc", "Scheduler::OnBeginFrameDeadline"); + DCHECK(state_machine_.HasInitializedOutputSurface()); + begin_frame_deadline_closure_.Cancel(); + state_machine_.OnBeginFrameDeadline(); + ProcessScheduledActions(); + + if (state_machine_.HasInitializedOutputSurface()) { + // We only transition out of BEGIN_FRAME_STATE_INSIDE_DEADLINE when all + // actions that occur back-to-back in response to entering + // BEGIN_FRAME_STATE_INSIDE_DEADLINE have completed. This is important + // because sending the BeginFrame to the main thread will not occur if + // we transition to BEGIN_FRAME_STATE_IDLE too early. + state_machine_.OnBeginFrameIdle(); + } + + client_->DidBeginFrameDeadlineOnImplThread(); +} + +void Scheduler::PollForAnticipatedDrawTriggers() { + TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers"); + state_machine_.DidEnterPollForAnticipatedDrawTriggers(); ProcessScheduledActions(); - state_machine_.DidLeaveBeginFrame(); + state_machine_.DidLeavePollForAnticipatedDrawTriggers(); + + poll_for_draw_triggers_closure_.Cancel(); } void Scheduler::DrawAndSwapIfPossible() { - ScheduledActionDrawAndSwapResult result = + DrawSwapReadbackResult result = client_->ScheduledActionDrawAndSwapIfPossible(); state_machine_.DidDrawIfPossibleCompleted(result.did_draw); - if (result.did_swap) - has_pending_begin_frame_ = false; } void Scheduler::DrawAndSwapForced() { - ScheduledActionDrawAndSwapResult result = - client_->ScheduledActionDrawAndSwapForced(); - if (result.did_swap) - has_pending_begin_frame_ = false; + client_->ScheduledActionDrawAndSwapForced(); +} + +void Scheduler::DrawAndReadback() { + DrawSwapReadbackResult result = client_->ScheduledActionDrawAndReadback(); + DCHECK(!result.did_swap); } void Scheduler::ProcessScheduledActions() { @@ -199,9 +277,17 @@ void Scheduler::ProcessScheduledActions() { base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true); - SchedulerStateMachine::Action action = state_machine_.NextAction(); - while (action != SchedulerStateMachine::ACTION_NONE) { + SchedulerStateMachine::Action action; + do { + state_machine_.CheckInvariants(); + action = state_machine_.NextAction(); + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), + "SchedulerStateMachine", + "state", + TracedValue::FromValue(state_machine_.AsValue().release())); state_machine_.UpdateState(action); + base::AutoReset<SchedulerStateMachine::Action> + mark_inside_action(&inside_action_, action); switch (action) { case SchedulerStateMachine::ACTION_NONE: break; @@ -214,31 +300,43 @@ void Scheduler::ProcessScheduledActions() { case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES: client_->ScheduledActionUpdateVisibleTiles(); break; - case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: - client_->ScheduledActionActivatePendingTreeIfNeeded(); + case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE: + ActivatePendingTree(); break; - case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: + case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: DrawAndSwapIfPossible(); break; - case SchedulerStateMachine::ACTION_DRAW_FORCED: + case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED: DrawAndSwapForced(); break; + case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT: + // No action is actually performed, but this allows the state machine to + // advance out of its waiting to draw state without actually drawing. + break; + case SchedulerStateMachine::ACTION_DRAW_AND_READBACK: + DrawAndReadback(); + break; case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: client_->ScheduledActionBeginOutputSurfaceCreation(); break; case SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD: client_->ScheduledActionAcquireLayerTexturesForMainThread(); break; + case SchedulerStateMachine::ACTION_MANAGE_TILES: + client_->ScheduledActionManageTiles(); + break; } - action = state_machine_.NextAction(); - } + } while (action != SchedulerStateMachine::ACTION_NONE); SetupNextBeginFrameIfNeeded(); client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); + + if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly()) + PostBeginFrameDeadline(base::TimeTicks()); } bool Scheduler::WillDrawIfNeeded() const { - return !state_machine_.DrawSuspendedUntilCommit(); + return !state_machine_.PendingDrawsShouldBeAborted(); } } // namespace cc diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h index 230f20d0ec9..3403b7a2e06 100644 --- a/chromium/cc/scheduler/scheduler.h +++ b/chromium/cc/scheduler/scheduler.h @@ -8,6 +8,7 @@ #include <string> #include "base/basictypes.h" +#include "base/cancelable_callback.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "cc/base/cc_export.h" @@ -20,34 +21,36 @@ namespace cc { class Thread; -struct ScheduledActionDrawAndSwapResult { - ScheduledActionDrawAndSwapResult() - : did_draw(false), - did_swap(false) {} - ScheduledActionDrawAndSwapResult(bool did_draw, bool did_swap) - : did_draw(did_draw), - did_swap(did_swap) {} +struct DrawSwapReadbackResult { + DrawSwapReadbackResult() + : did_draw(false), did_swap(false), did_readback(false) {} + DrawSwapReadbackResult(bool did_draw, bool did_swap, bool did_readback) + : did_draw(did_draw), did_swap(did_swap), did_readback(did_readback) {} bool did_draw; bool did_swap; + bool did_readback; }; class SchedulerClient { public: virtual void SetNeedsBeginFrameOnImplThread(bool enable) = 0; virtual void ScheduledActionSendBeginFrameToMainThread() = 0; - virtual ScheduledActionDrawAndSwapResult - ScheduledActionDrawAndSwapIfPossible() = 0; - virtual ScheduledActionDrawAndSwapResult - ScheduledActionDrawAndSwapForced() = 0; + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible() = 0; + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() = 0; + virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() = 0; virtual void ScheduledActionCommit() = 0; virtual void ScheduledActionUpdateVisibleTiles() = 0; - virtual void ScheduledActionActivatePendingTreeIfNeeded() = 0; + virtual void ScheduledActionActivatePendingTree() = 0; virtual void ScheduledActionBeginOutputSurfaceCreation() = 0; virtual void ScheduledActionAcquireLayerTexturesForMainThread() = 0; + virtual void ScheduledActionManageTiles() = 0; virtual void DidAnticipatedDrawTimeChange(base::TimeTicks time) = 0; virtual base::TimeDelta DrawDurationEstimate() = 0; virtual base::TimeDelta BeginFrameToCommitDurationEstimate() = 0; virtual base::TimeDelta CommitToActivateDurationEstimate() = 0; + virtual void PostBeginFrameDeadline(const base::Closure& closure, + base::TimeTicks deadline) = 0; + virtual void DidBeginFrameDeadlineOnImplThread() = 0; protected: virtual ~SchedulerClient() {} @@ -67,23 +70,23 @@ class CC_EXPORT Scheduler { void SetVisible(bool visible); void SetCanDraw(bool can_draw); - void SetHasPendingTree(bool has_pending_tree); + void NotifyReadyToActivate(); void SetNeedsCommit(); // Like SetNeedsCommit(), but ensures a commit will definitely happen even if - // we are not visible. - void SetNeedsForcedCommit(); + // we are not visible. Will eventually result in a forced draw internally. + void SetNeedsForcedCommitForReadback(); void SetNeedsRedraw(); + void SetNeedsManageTiles(); + void SetMainThreadNeedsLayerTextures(); - // Like SetNeedsRedraw(), but ensures the draw will definitely happen even if - // we are not visible. - void SetNeedsForcedRedraw(); + void SetSwapUsedIncompleteTile(bool used_incomplete_tile); - void DidSwapUseIncompleteTile(); + void SetSmoothnessTakesPriority(bool smoothness_takes_priority); void FinishCommit(); void BeginFrameAbortedByMainThread(bool did_handle); @@ -96,6 +99,9 @@ class CC_EXPORT Scheduler { bool CommitPending() const { return state_machine_.CommitPending(); } bool RedrawPending() const { return state_machine_.RedrawPending(); } + bool ManageTilesPending() const { + return state_machine_.ManageTilesPending(); + } bool WillDrawIfNeeded() const; @@ -104,16 +110,27 @@ class CC_EXPORT Scheduler { base::TimeTicks LastBeginFrameOnImplThreadTime(); void BeginFrame(const BeginFrameArgs& args); + void OnBeginFrameDeadline(); + void PollForAnticipatedDrawTriggers(); - std::string StateAsStringForTesting() { return state_machine_.ToString(); } + scoped_ptr<base::Value> StateAsValue() { + return state_machine_.AsValue().Pass(); + } + + bool IsInsideAction(SchedulerStateMachine::Action action) { + return inside_action_ == action; + } private: Scheduler(SchedulerClient* client, const SchedulerSettings& scheduler_settings); + void PostBeginFrameDeadline(base::TimeTicks deadline); void SetupNextBeginFrameIfNeeded(); + void ActivatePendingTree(); void DrawAndSwapIfPossible(); void DrawAndSwapForced(); + void DrawAndReadback(); void ProcessScheduledActions(); const SchedulerSettings settings_; @@ -121,14 +138,13 @@ class CC_EXPORT Scheduler { base::WeakPtrFactory<Scheduler> weak_factory_; bool last_set_needs_begin_frame_; - bool has_pending_begin_frame_; - // TODO(brianderson): crbug.com/249806 : Remove safe_to_expect_begin_frame_ - // workaround. - bool safe_to_expect_begin_frame_; BeginFrameArgs last_begin_frame_args_; + base::CancelableClosure begin_frame_deadline_closure_; + base::CancelableClosure poll_for_draw_triggers_closure_; SchedulerStateMachine state_machine_; bool inside_process_scheduled_actions_; + SchedulerStateMachine::Action inside_action_; DISALLOW_COPY_AND_ASSIGN(Scheduler); }; diff --git a/chromium/cc/scheduler/scheduler_settings.cc b/chromium/cc/scheduler/scheduler_settings.cc index 90e9e89bbdc..6c1db6bf10c 100644 --- a/chromium/cc/scheduler/scheduler_settings.cc +++ b/chromium/cc/scheduler/scheduler_settings.cc @@ -7,8 +7,10 @@ namespace cc { SchedulerSettings::SchedulerSettings() - : impl_side_painting(false), + : deadline_scheduling_enabled(false), + impl_side_painting(false), timeout_and_draw_when_animation_checkerboards(true), + maximum_number_of_failed_draws_before_draw_is_forced_(3), using_synchronous_renderer_compositor(false), throttle_frame_production(true) {} diff --git a/chromium/cc/scheduler/scheduler_settings.h b/chromium/cc/scheduler/scheduler_settings.h index ff00fc32b36..7857a70fe9e 100644 --- a/chromium/cc/scheduler/scheduler_settings.h +++ b/chromium/cc/scheduler/scheduler_settings.h @@ -14,8 +14,10 @@ class CC_EXPORT SchedulerSettings { SchedulerSettings(); ~SchedulerSettings(); + bool deadline_scheduling_enabled; bool impl_side_painting; bool timeout_and_draw_when_animation_checkerboards; + int maximum_number_of_failed_draws_before_draw_is_forced_; bool using_synchronous_renderer_compositor; bool throttle_frame_production; }; diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc index ed9c15f43f9..955a6b32eee 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.cc +++ b/chromium/cc/scheduler/scheduler_state_machine.cc @@ -7,166 +7,394 @@ #include "base/format_macros.h" #include "base/logging.h" #include "base/strings/stringprintf.h" +#include "base/values.h" namespace cc { SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) : settings_(settings), + output_surface_state_(OUTPUT_SURFACE_LOST), + begin_frame_state_(BEGIN_FRAME_STATE_IDLE), commit_state_(COMMIT_STATE_IDLE), + texture_state_(LAYER_TEXTURE_STATE_UNLOCKED), + forced_redraw_state_(FORCED_REDRAW_STATE_IDLE), + readback_state_(READBACK_STATE_IDLE), commit_count_(0), current_frame_number_(0), - last_frame_number_where_begin_frame_sent_to_main_thread_(-1), - last_frame_number_where_draw_was_called_(-1), - last_frame_number_where_tree_activation_attempted_(-1), - last_frame_number_where_update_visible_tiles_was_called_(-1), + last_frame_number_swap_performed_(-1), + last_frame_number_begin_frame_sent_to_main_thread_(-1), + last_frame_number_update_visible_tiles_was_called_(-1), consecutive_failed_draws_(0), - maximum_number_of_failed_draws_before_draw_is_forced_(3), needs_redraw_(false), + needs_manage_tiles_(false), swap_used_incomplete_tile_(false), - needs_forced_redraw_(false), - needs_forced_redraw_after_next_commit_(false), - needs_redraw_after_next_commit_(false), needs_commit_(false), - needs_forced_commit_(false), - expect_immediate_begin_frame_for_main_thread_(false), main_thread_needs_layer_textures_(false), - inside_begin_frame_(false), + inside_poll_for_anticipated_draw_triggers_(false), visible_(false), can_start_(false), can_draw_(false), has_pending_tree_(false), + pending_tree_is_ready_for_activation_(false), + active_tree_needs_first_draw_(false), draw_if_possible_failed_(false), - texture_state_(LAYER_TEXTURE_STATE_UNLOCKED), - output_surface_state_(OUTPUT_SURFACE_LOST), - did_create_and_initialize_first_output_surface_(false) {} - -std::string SchedulerStateMachine::ToString() { - std::string str; - base::StringAppendF(&str, - "settings_.impl_side_painting = %d; ", - settings_.impl_side_painting); - base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_); - base::StringAppendF(&str, "commit_count_ = %d; ", commit_count_); - base::StringAppendF( - &str, "current_frame_number_ = %d; ", current_frame_number_); - base::StringAppendF(&str, - "last_frame_number_where_draw_was_called_ = %d; ", - last_frame_number_where_draw_was_called_); - base::StringAppendF( - &str, - "last_frame_number_where_tree_activation_attempted_ = %d; ", - last_frame_number_where_tree_activation_attempted_); - base::StringAppendF( - &str, - "last_frame_number_where_update_visible_tiles_was_called_ = %d; ", - last_frame_number_where_update_visible_tiles_was_called_); - base::StringAppendF( - &str, "consecutive_failed_draws_ = %d; ", consecutive_failed_draws_); - base::StringAppendF( - &str, - "maximum_number_of_failed_draws_before_draw_is_forced_ = %d; ", - maximum_number_of_failed_draws_before_draw_is_forced_); - base::StringAppendF(&str, "needs_redraw_ = %d; ", needs_redraw_); - base::StringAppendF( - &str, "swap_used_incomplete_tile_ = %d; ", swap_used_incomplete_tile_); - base::StringAppendF( - &str, "needs_forced_redraw_ = %d; ", needs_forced_redraw_); - base::StringAppendF(&str, - "needs_forced_redraw_after_next_commit_ = %d; ", - needs_forced_redraw_after_next_commit_); - base::StringAppendF(&str, "needs_commit_ = %d; ", needs_commit_); - base::StringAppendF( - &str, "needs_forced_commit_ = %d; ", needs_forced_commit_); - base::StringAppendF(&str, - "expect_immediate_begin_frame_for_main_thread_ = %d; ", - expect_immediate_begin_frame_for_main_thread_); - base::StringAppendF(&str, - "main_thread_needs_layer_textures_ = %d; ", - main_thread_needs_layer_textures_); - base::StringAppendF(&str, "inside_begin_frame_ = %d; ", - inside_begin_frame_); - base::StringAppendF(&str, "last_frame_time_ = %" PRId64 "; ", - (last_begin_frame_args_.frame_time - base::TimeTicks()) - .InMilliseconds()); - base::StringAppendF(&str, "last_deadline_ = %" PRId64 "; ", - (last_begin_frame_args_.deadline - base::TimeTicks()).InMilliseconds()); - base::StringAppendF(&str, "last_interval_ = %" PRId64 "; ", - last_begin_frame_args_.interval.InMilliseconds()); - base::StringAppendF(&str, "visible_ = %d; ", visible_); - base::StringAppendF(&str, "can_start_ = %d; ", can_start_); - base::StringAppendF(&str, "can_draw_ = %d; ", can_draw_); - base::StringAppendF( - &str, "draw_if_possible_failed_ = %d; ", draw_if_possible_failed_); - base::StringAppendF(&str, "has_pending_tree_ = %d; ", has_pending_tree_); - base::StringAppendF(&str, "texture_state_ = %d; ", texture_state_); - base::StringAppendF( - &str, "output_surface_state_ = %d; ", output_surface_state_); - return str; -} - -bool SchedulerStateMachine::HasDrawnThisFrame() const { - return current_frame_number_ == last_frame_number_where_draw_was_called_; -} - -bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const { + did_create_and_initialize_first_output_surface_(false), + smoothness_takes_priority_(false) {} + +const char* SchedulerStateMachine::OutputSurfaceStateToString( + OutputSurfaceState state) { + switch (state) { + case OUTPUT_SURFACE_ACTIVE: + return "OUTPUT_SURFACE_ACTIVE"; + case OUTPUT_SURFACE_LOST: + return "OUTPUT_SURFACE_LOST"; + case OUTPUT_SURFACE_CREATING: + return "OUTPUT_SURFACE_CREATING"; + case OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT: + return "OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT"; + case OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION: + return "OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION"; + } + NOTREACHED(); + return "???"; +} + +const char* SchedulerStateMachine::BeginFrameStateToString( + BeginFrameState state) { + switch (state) { + case BEGIN_FRAME_STATE_IDLE: + return "BEGIN_FRAME_STATE_IDLE"; + case BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING: + return "BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING"; + case BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME: + return "BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME"; + case BEGIN_FRAME_STATE_INSIDE_DEADLINE: + return "BEGIN_FRAME_STATE_INSIDE_DEADLINE"; + } + NOTREACHED(); + return "???"; +} + +const char* SchedulerStateMachine::CommitStateToString(CommitState state) { + switch (state) { + case COMMIT_STATE_IDLE: + return "COMMIT_STATE_IDLE"; + case COMMIT_STATE_FRAME_IN_PROGRESS: + return "COMMIT_STATE_FRAME_IN_PROGRESS"; + case COMMIT_STATE_READY_TO_COMMIT: + return "COMMIT_STATE_READY_TO_COMMIT"; + case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: + return "COMMIT_STATE_WAITING_FOR_FIRST_DRAW"; + } + NOTREACHED(); + return "???"; +} + +const char* SchedulerStateMachine::TextureStateToString(TextureState state) { + switch (state) { + case LAYER_TEXTURE_STATE_UNLOCKED: + return "LAYER_TEXTURE_STATE_UNLOCKED"; + case LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD: + return "LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD"; + case LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD: + return "LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD"; + } + NOTREACHED(); + return "???"; +} + +const char* SchedulerStateMachine::SynchronousReadbackStateToString( + SynchronousReadbackState state) { + switch (state) { + case READBACK_STATE_IDLE: + return "READBACK_STATE_IDLE"; + case READBACK_STATE_NEEDS_BEGIN_FRAME: + return "READBACK_STATE_NEEDS_BEGIN_FRAME"; + case READBACK_STATE_WAITING_FOR_COMMIT: + return "READBACK_STATE_WAITING_FOR_COMMIT"; + case READBACK_STATE_WAITING_FOR_ACTIVATION: + return "READBACK_STATE_WAITING_FOR_ACTIVATION"; + case READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK: + return "READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK"; + case READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT: + return "READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT"; + case READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION: + return "READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION"; + } + NOTREACHED(); + return "???"; +} + +const char* SchedulerStateMachine::ForcedRedrawOnTimeoutStateToString( + ForcedRedrawOnTimeoutState state) { + switch (state) { + case FORCED_REDRAW_STATE_IDLE: + return "FORCED_REDRAW_STATE_IDLE"; + case FORCED_REDRAW_STATE_WAITING_FOR_COMMIT: + return "FORCED_REDRAW_STATE_WAITING_FOR_COMMIT"; + case FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION: + return "FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION"; + case FORCED_REDRAW_STATE_WAITING_FOR_DRAW: + return "FORCED_REDRAW_STATE_WAITING_FOR_DRAW"; + } + NOTREACHED(); + return "???"; +} + +const char* SchedulerStateMachine::ActionToString(Action action) { + switch (action) { + case ACTION_NONE: + return "ACTION_NONE"; + case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD: + return "ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD"; + case ACTION_COMMIT: + return "ACTION_COMMIT"; + case ACTION_UPDATE_VISIBLE_TILES: + return "ACTION_UPDATE_VISIBLE_TILES"; + case ACTION_ACTIVATE_PENDING_TREE: + return "ACTION_ACTIVATE_PENDING_TREE"; + case ACTION_DRAW_AND_SWAP_IF_POSSIBLE: + return "ACTION_DRAW_AND_SWAP_IF_POSSIBLE"; + case ACTION_DRAW_AND_SWAP_FORCED: + return "ACTION_DRAW_AND_SWAP_FORCED"; + case ACTION_DRAW_AND_SWAP_ABORT: + return "ACTION_DRAW_AND_SWAP_ABORT"; + case ACTION_DRAW_AND_READBACK: + return "ACTION_DRAW_AND_READBACK"; + case ACTION_BEGIN_OUTPUT_SURFACE_CREATION: + return "ACTION_BEGIN_OUTPUT_SURFACE_CREATION"; + case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD: + return "ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD"; + case ACTION_MANAGE_TILES: + return "ACTION_MANAGE_TILES"; + } + NOTREACHED(); + return "???"; +} + +scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const { + scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue); + + scoped_ptr<base::DictionaryValue> major_state(new base::DictionaryValue); + major_state->SetString("next_action", ActionToString(NextAction())); + major_state->SetString("begin_frame_state", + BeginFrameStateToString(begin_frame_state_)); + major_state->SetString("commit_state", CommitStateToString(commit_state_)); + major_state->SetString("texture_state_", + TextureStateToString(texture_state_)); + major_state->SetString("output_surface_state_", + OutputSurfaceStateToString(output_surface_state_)); + major_state->SetString( + "forced_redraw_state", + ForcedRedrawOnTimeoutStateToString(forced_redraw_state_)); + major_state->SetString("readback_state", + SynchronousReadbackStateToString(readback_state_)); + state->Set("major_state", major_state.release()); + + scoped_ptr<base::DictionaryValue> timestamps_state(new base::DictionaryValue); + base::TimeTicks now = base::TimeTicks::Now(); + timestamps_state->SetDouble( + "0_interval", last_begin_frame_args_.interval.InMicroseconds() / 1000.0L); + timestamps_state->SetDouble( + "1_now_to_deadline", + (last_begin_frame_args_.deadline - now).InMicroseconds() / 1000.0L); + timestamps_state->SetDouble( + "2_frame_time_to_now", + (now - last_begin_frame_args_.frame_time).InMicroseconds() / 1000.0L); + timestamps_state->SetDouble( + "3_frame_time_to_deadline", + (last_begin_frame_args_.deadline - last_begin_frame_args_.frame_time) + .InMicroseconds() / + 1000.0L); + timestamps_state->SetDouble( + "4_now", (now - base::TimeTicks()).InMicroseconds() / 1000.0L); + timestamps_state->SetDouble( + "5_frame_time", + (last_begin_frame_args_.frame_time - base::TimeTicks()).InMicroseconds() / + 1000.0L); + timestamps_state->SetDouble( + "6_deadline", + (last_begin_frame_args_.deadline - base::TimeTicks()).InMicroseconds() / + 1000.0L); + state->Set("major_timestamps_in_ms", timestamps_state.release()); + + scoped_ptr<base::DictionaryValue> minor_state(new base::DictionaryValue); + minor_state->SetInteger("commit_count", commit_count_); + minor_state->SetInteger("current_frame_number", current_frame_number_); + + minor_state->SetInteger("last_frame_number_swap_performed", + last_frame_number_swap_performed_); + minor_state->SetInteger( + "last_frame_number_begin_frame_sent_to_main_thread", + last_frame_number_begin_frame_sent_to_main_thread_); + minor_state->SetInteger( + "last_frame_number_update_visible_tiles_was_called", + last_frame_number_update_visible_tiles_was_called_); + + minor_state->SetInteger("consecutive_failed_draws", + consecutive_failed_draws_); + minor_state->SetBoolean("needs_redraw", needs_redraw_); + minor_state->SetBoolean("needs_manage_tiles", needs_manage_tiles_); + minor_state->SetBoolean("swap_used_incomplete_tile", + swap_used_incomplete_tile_); + minor_state->SetBoolean("needs_commit", needs_commit_); + minor_state->SetBoolean("main_thread_needs_layer_textures", + main_thread_needs_layer_textures_); + minor_state->SetBoolean("visible", visible_); + minor_state->SetBoolean("can_start", can_start_); + minor_state->SetBoolean("can_draw", can_draw_); + minor_state->SetBoolean("has_pending_tree", has_pending_tree_); + minor_state->SetBoolean("pending_tree_is_ready_for_activation", + pending_tree_is_ready_for_activation_); + minor_state->SetBoolean("active_tree_needs_first_draw", + active_tree_needs_first_draw_); + minor_state->SetBoolean("draw_if_possible_failed", draw_if_possible_failed_); + minor_state->SetBoolean("did_create_and_initialize_first_output_surface", + did_create_and_initialize_first_output_surface_); + minor_state->SetBoolean("smoothness_takes_priority", + smoothness_takes_priority_); + state->Set("minor_state", minor_state.release()); + + return state.PassAs<base::Value>(); +} + +bool SchedulerStateMachine::HasSentBeginFrameToMainThreadThisFrame() const { return current_frame_number_ == - last_frame_number_where_tree_activation_attempted_; + last_frame_number_begin_frame_sent_to_main_thread_; } bool SchedulerStateMachine::HasUpdatedVisibleTilesThisFrame() const { return current_frame_number_ == - last_frame_number_where_update_visible_tiles_was_called_; + last_frame_number_update_visible_tiles_was_called_; } -void SchedulerStateMachine::SetPostCommitFlags() { - // This post-commit work is common to both completed and aborted commits. - if (needs_forced_redraw_after_next_commit_) { - needs_forced_redraw_after_next_commit_ = false; - needs_forced_redraw_ = true; - } - if (needs_redraw_after_next_commit_) { - needs_redraw_after_next_commit_ = false; - needs_redraw_ = true; - } - texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD; +bool SchedulerStateMachine::HasSwappedThisFrame() const { + return current_frame_number_ == last_frame_number_swap_performed_; } -bool SchedulerStateMachine::DrawSuspendedUntilCommit() const { +bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const { + // These are all the cases where we normally cannot or do not want to draw + // but, if needs_redraw_ is true and we do not draw to make forward progress, + // we might deadlock with the main thread. + // This should be a superset of PendingActivationsShouldBeForced() since + // activation of the pending tree is blocked by drawing of the active tree and + // the main thread might be blocked on activation of the most recent commit. + if (PendingActivationsShouldBeForced()) + return true; + + // Additional states where we should abort draws. + // Note: We don't force activation in these cases because doing so would + // result in checkerboarding on resize, becoming visible, etc. if (!can_draw_) return true; if (!visible_) return true; + return false; +} + +bool SchedulerStateMachine::PendingActivationsShouldBeForced() const { + // These are all the cases where, if we do not force activations to make + // forward progress, we might deadlock with the main thread. + + // The impl thread cannot lock layer textures unless the pending + // tree can be activated to unblock the commit. if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD) return true; + + // There is no output surface to trigger our activations. + if (output_surface_state_ == OUTPUT_SURFACE_LOST) + return true; + return false; } -bool SchedulerStateMachine::ScheduledToDraw() const { - if (!needs_redraw_) +bool SchedulerStateMachine::ShouldBeginOutputSurfaceCreation() const { + // Don't try to initialize too early. + if (!can_start_) + return false; + + // We only want to start output surface initialization after the + // previous commit is complete. + if (commit_state_ != COMMIT_STATE_IDLE) return false; - if (DrawSuspendedUntilCommit()) + + // We want to clear the pipline of any pending draws and activations + // before starting output surface initialization. This allows us to avoid + // weird corner cases where we abort draws or force activation while we + // are initializing the output surface and can potentially have a pending + // readback. + if (active_tree_needs_first_draw_ || has_pending_tree_) return false; - return true; + + // We need to create the output surface if we don't have one and we haven't + // started creating one yet. + return output_surface_state_ == OUTPUT_SURFACE_LOST; } bool SchedulerStateMachine::ShouldDraw() const { - if (needs_forced_redraw_) + // After a readback, make sure not to draw again until we've replaced the + // readback commit with a real one. + if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT || + readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION) + return false; + + // Draw immediately for readbacks to unblock the main thread quickly. + if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) { + DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW); return true; + } - if (!ScheduledToDraw()) - return false; - if (!inside_begin_frame_) + // If we need to abort draws, we should do so ASAP since the draw could + // be blocking other important actions (like output surface initialization), + // from occuring. If we are waiting for the first draw, then perfom the + // aborted draw to keep things moving. If we are not waiting for the first + // draw however, we don't want to abort for no reason. + if (PendingDrawsShouldBeAborted()) + return active_tree_needs_first_draw_; + + // After this line, we only want to swap once per frame. + if (HasSwappedThisFrame()) return false; - if (HasDrawnThisFrame()) + + // Except for the cases above, do not draw outside of the BeginFrame deadline. + if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE) return false; - if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE) + + // Only handle forced redraws due to timeouts on the regular deadline. + if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) { + DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW); + return true; + } + + return needs_redraw_; +} + +bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const { + if (!main_thread_needs_layer_textures_) return false; - return true; + if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED) + return true; + DCHECK_EQ(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD); + return false; } -bool SchedulerStateMachine::ShouldAttemptTreeActivation() const { - return has_pending_tree_ && inside_begin_frame_ && - !HasAttemptedTreeActivationThisFrame(); +bool SchedulerStateMachine::ShouldActivatePendingTree() const { + // There is nothing to activate. + if (!has_pending_tree_) + return false; + + // We should not activate a second tree before drawing the first one. + // Even if we need to force activation of the pending tree, we should abort + // drawing the active tree first. + if (active_tree_needs_first_draw_) + return false; + + // If we want to force activation, do so ASAP. + if (PendingActivationsShouldBeForced()) + return true; + + // At this point, only activate if we are ready to activate. + return pending_tree_is_ready_for_activation_; } bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const { @@ -175,240 +403,535 @@ bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const { if (HasUpdatedVisibleTilesThisFrame()) return false; - return ShouldAttemptTreeActivation() || ShouldDraw() || - swap_used_incomplete_tile_; -} + // There's no reason to check for tiles if we don't have an output surface. + if (!HasInitializedOutputSurface()) + return false; -bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const { - if (!main_thread_needs_layer_textures_) + // We should not check for visible tiles until we've entered the deadline so + // we check as late as possible and give the tiles more time to initialize. + if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE) return false; - if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED) + + // If the last swap drew with checkerboard or missing tiles, we should + // poll for any new visible tiles so we can be notified to draw again + // when there are. + if (swap_used_incomplete_tile_) return true; - DCHECK_EQ(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD); - // Transfer the lock from impl thread to main thread immediately if the - // impl thread is not even scheduled to draw. Guards against deadlocking. - if (!ScheduledToDraw()) + + return false; +} + +bool SchedulerStateMachine::ShouldSendBeginFrameToMainThread() const { + if (!needs_commit_) + return false; + + // Only send BeginFrame to the main thread when there isn't another commit + // pending already. + if (commit_state_ != COMMIT_STATE_IDLE) + return false; + + // We can't accept a commit if we have a pending tree. + if (has_pending_tree_) + return false; + + // We want to handle readback commits immediately to unblock the main thread. + // Note: This BeginFrame will correspond to the replacement commit that comes + // after the readback commit itself, so we only send the BeginFrame if a + // commit isn't already pending behind the readback. + if (readback_state_ == READBACK_STATE_NEEDS_BEGIN_FRAME) + return !CommitPending(); + + // We do not need commits if we are not visible, unless there's a + // request for a readback. + if (!visible_) + return false; + + // We want to start the first commit after we get a new output surface ASAP. + if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) return true; - if (!BeginFrameNeededToDrawByImplThread()) + + // With deadline scheduling enabled, we should not send BeginFrame to the + // main thread while we are in BEGIN_FRAME_STATE_IDLE, since we might have + // new user input coming in soon. + // However, if we are not expecting a BeginFrame on the Impl thread to take + // us out of idle, we should not early out here to avoid blocking commits + // forever. + // This only works well when deadline scheduling is enabled because there is + // an interval over which to accept the commit and draw. Without deadline + // scheduling, delaying the commit could prevent us from having something + // to draw on the next BeginFrame. + // TODO(brianderson): Allow sending BeginFrame to main thread while idle + // when the main thread isn't consuming user input. + if (settings_.deadline_scheduling_enabled && + begin_frame_state_ == BEGIN_FRAME_STATE_IDLE && + BeginFrameNeededByImplThread()) + return false; + + // We need a new commit for the forced redraw. This honors the + // single commit per interval because the result will be swapped to screen. + if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) return true; - return false; + + // After this point, we only start a commit once per frame. + if (HasSentBeginFrameToMainThreadThisFrame()) + return false; + + // We shouldn't normally accept commits if there isn't an OutputSurface. + if (!HasInitializedOutputSurface()) + return false; + + return true; +} + +bool SchedulerStateMachine::ShouldCommit() const { + return commit_state_ == COMMIT_STATE_READY_TO_COMMIT; +} + +bool SchedulerStateMachine::ShouldManageTiles() const { + // Limiting to once per-frame is not enough, since we only want to + // manage tiles _after_ draws. Polling for draw triggers and + // begin-frame are mutually exclusive, so we limit to these two cases. + if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE && + !inside_poll_for_anticipated_draw_triggers_) + return false; + return needs_manage_tiles_; } SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { if (ShouldAcquireLayerTexturesForMainThread()) return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD; - - switch (commit_state_) { - case COMMIT_STATE_IDLE: { - if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE && - needs_forced_redraw_) - return ACTION_DRAW_FORCED; - if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE && - needs_forced_commit_) - // TODO(enne): Should probably drop the active tree on force commit. - return has_pending_tree_ ? ACTION_NONE - : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD; - if (output_surface_state_ == OUTPUT_SURFACE_LOST && can_start_) - return ACTION_BEGIN_OUTPUT_SURFACE_CREATION; - if (output_surface_state_ == OUTPUT_SURFACE_CREATING) - return ACTION_NONE; - if (ShouldUpdateVisibleTiles()) - return ACTION_UPDATE_VISIBLE_TILES; - if (ShouldAttemptTreeActivation()) - return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; - if (ShouldDraw()) { - return needs_forced_redraw_ ? ACTION_DRAW_FORCED - : ACTION_DRAW_IF_POSSIBLE; - } - bool can_commit_this_frame = - visible_ && - current_frame_number_ > - last_frame_number_where_begin_frame_sent_to_main_thread_; - if (needs_commit_ && ((can_commit_this_frame && - output_surface_state_ == OUTPUT_SURFACE_ACTIVE) || - needs_forced_commit_)) - // TODO(enne): Should probably drop the active tree on force commit. - return has_pending_tree_ ? ACTION_NONE - : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD; - return ACTION_NONE; - } - case COMMIT_STATE_FRAME_IN_PROGRESS: - if (ShouldUpdateVisibleTiles()) - return ACTION_UPDATE_VISIBLE_TILES; - if (ShouldAttemptTreeActivation()) - return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; - if (ShouldDraw()) { - return needs_forced_redraw_ ? ACTION_DRAW_FORCED - : ACTION_DRAW_IF_POSSIBLE; - } - return ACTION_NONE; - - case COMMIT_STATE_READY_TO_COMMIT: - return ACTION_COMMIT; - - case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: { - if (ShouldUpdateVisibleTiles()) - return ACTION_UPDATE_VISIBLE_TILES; - if (ShouldAttemptTreeActivation()) - return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; - if (ShouldDraw() || output_surface_state_ == OUTPUT_SURFACE_LOST) { - return needs_forced_redraw_ ? ACTION_DRAW_FORCED - : ACTION_DRAW_IF_POSSIBLE; - } - // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If - // can_draw_ is false or textures are not available, proceed to the next - // step (similar as in COMMIT_STATE_IDLE). - bool can_commit = - needs_forced_commit_ || - (visible_ && - current_frame_number_ > - last_frame_number_where_begin_frame_sent_to_main_thread_); - if (needs_commit_ && can_commit && DrawSuspendedUntilCommit()) - return has_pending_tree_ ? ACTION_NONE - : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD; - return ACTION_NONE; - } - - case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW: - if (ShouldUpdateVisibleTiles()) - return ACTION_UPDATE_VISIBLE_TILES; - if (ShouldAttemptTreeActivation()) - return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; - if (needs_forced_redraw_) - return ACTION_DRAW_FORCED; - return ACTION_NONE; + if (ShouldUpdateVisibleTiles()) + return ACTION_UPDATE_VISIBLE_TILES; + if (ShouldActivatePendingTree()) + return ACTION_ACTIVATE_PENDING_TREE; + if (ShouldCommit()) + return ACTION_COMMIT; + if (ShouldDraw()) { + if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) + return ACTION_DRAW_AND_READBACK; + else if (PendingDrawsShouldBeAborted()) + return ACTION_DRAW_AND_SWAP_ABORT; + else if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) + return ACTION_DRAW_AND_SWAP_FORCED; + else + return ACTION_DRAW_AND_SWAP_IF_POSSIBLE; } - NOTREACHED(); + if (ShouldManageTiles()) + return ACTION_MANAGE_TILES; + if (ShouldSendBeginFrameToMainThread()) + return ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD; + if (ShouldBeginOutputSurfaceCreation()) + return ACTION_BEGIN_OUTPUT_SURFACE_CREATION; return ACTION_NONE; } +void SchedulerStateMachine::CheckInvariants() { + // We should never try to perform a draw for readback and forced draw due to + // timeout simultaneously. + DCHECK(!(forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW && + readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK)); +} + void SchedulerStateMachine::UpdateState(Action action) { switch (action) { case ACTION_NONE: return; case ACTION_UPDATE_VISIBLE_TILES: - last_frame_number_where_update_visible_tiles_was_called_ = + last_frame_number_update_visible_tiles_was_called_ = current_frame_number_; return; - case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: - last_frame_number_where_tree_activation_attempted_ = - current_frame_number_; + case ACTION_ACTIVATE_PENDING_TREE: + UpdateStateOnActivation(); return; case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD: DCHECK(!has_pending_tree_); - if (!needs_forced_commit_) { - DCHECK(visible_); - DCHECK_GT(current_frame_number_, - last_frame_number_where_begin_frame_sent_to_main_thread_); - } + DCHECK(visible_ || readback_state_ == READBACK_STATE_NEEDS_BEGIN_FRAME); commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS; needs_commit_ = false; - needs_forced_commit_ = false; - last_frame_number_where_begin_frame_sent_to_main_thread_ = + if (readback_state_ == READBACK_STATE_NEEDS_BEGIN_FRAME) + readback_state_ = READBACK_STATE_WAITING_FOR_COMMIT; + last_frame_number_begin_frame_sent_to_main_thread_ = current_frame_number_; return; - case ACTION_COMMIT: - commit_count_++; - if (expect_immediate_begin_frame_for_main_thread_) - commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW; - else - commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; - // When impl-side painting, we draw on activation instead of on commit. - if (!settings_.impl_side_painting) - needs_redraw_ = true; - if (draw_if_possible_failed_) - last_frame_number_where_draw_was_called_ = -1; - SetPostCommitFlags(); + case ACTION_COMMIT: { + bool commit_was_aborted = false; + UpdateStateOnCommit(commit_was_aborted); return; + } - case ACTION_DRAW_FORCED: - case ACTION_DRAW_IF_POSSIBLE: - needs_redraw_ = false; - needs_forced_redraw_ = false; - draw_if_possible_failed_ = false; - swap_used_incomplete_tile_ = false; - if (inside_begin_frame_) - last_frame_number_where_draw_was_called_ = current_frame_number_; - if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW) { - DCHECK(expect_immediate_begin_frame_for_main_thread_); - commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS; - expect_immediate_begin_frame_for_main_thread_ = false; - } else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW) { - commit_state_ = COMMIT_STATE_IDLE; - } - if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD) - texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED; + case ACTION_DRAW_AND_SWAP_FORCED: + case ACTION_DRAW_AND_SWAP_IF_POSSIBLE: { + bool did_swap = true; + UpdateStateOnDraw(did_swap); + return; + } + + case ACTION_DRAW_AND_SWAP_ABORT: + case ACTION_DRAW_AND_READBACK: { + bool did_swap = false; + UpdateStateOnDraw(did_swap); return; + } case ACTION_BEGIN_OUTPUT_SURFACE_CREATION: - DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE); DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST); output_surface_state_ = OUTPUT_SURFACE_CREATING; + + // The following DCHECKs make sure we are in the proper quiescent state. + // The pipeline should be flushed entirely before we start output + // surface creation to avoid complicated corner cases. + DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE); + DCHECK(!has_pending_tree_); + DCHECK(!active_tree_needs_first_draw_); return; case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD: texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD; main_thread_needs_layer_textures_ = false; return; + + case ACTION_MANAGE_TILES: + UpdateStateOnManageTiles(); + return; } } +void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) { + commit_count_++; + + // If we are impl-side-painting but the commit was aborted, then we behave + // mostly as if we are not impl-side-painting since there is no pending tree. + has_pending_tree_ = settings_.impl_side_painting && !commit_was_aborted; + + // Update state related to readbacks. + if (readback_state_ == READBACK_STATE_WAITING_FOR_COMMIT) { + // Update the state if this is the readback commit. + readback_state_ = has_pending_tree_ + ? READBACK_STATE_WAITING_FOR_ACTIVATION + : READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK; + } else if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT) { + // Update the state if this is the commit replacing the readback commit. + readback_state_ = has_pending_tree_ + ? READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION + : READBACK_STATE_IDLE; + } else { + DCHECK(readback_state_ == READBACK_STATE_IDLE); + } + + // Readbacks can interrupt output surface initialization and forced draws, + // so we do not want to advance those states if we are in the middle of a + // readback. Note: It is possible for the readback's replacement commit to + // be the output surface's first commit and/or the forced redraw's commit. + if (readback_state_ == READBACK_STATE_IDLE || + readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION) { + // Update state related to forced draws. + if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) { + forced_redraw_state_ = has_pending_tree_ + ? FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION + : FORCED_REDRAW_STATE_WAITING_FOR_DRAW; + } + + // Update the output surface state. + DCHECK_NE(output_surface_state_, + OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION); + if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) { + if (has_pending_tree_) { + output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION; + } else { + output_surface_state_ = OUTPUT_SURFACE_ACTIVE; + needs_redraw_ = true; + } + } + } + + // Update the commit state. We expect and wait for a draw if the commit + // was not aborted or if we are in a readback or forced draw. + if (!commit_was_aborted) + commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; + else if (readback_state_ != READBACK_STATE_IDLE || + forced_redraw_state_ != FORCED_REDRAW_STATE_IDLE) + commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; + else + commit_state_ = COMMIT_STATE_IDLE; + + // Update state if we have a new active tree to draw, or if the active tree + // was unchanged but we need to do a readback or forced draw. + if (!has_pending_tree_ && + (!commit_was_aborted || + readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK || + forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)) { + needs_redraw_ = true; + active_tree_needs_first_draw_ = true; + } + + // This post-commit work is common to both completed and aborted commits. + pending_tree_is_ready_for_activation_ = false; + + if (draw_if_possible_failed_) + last_frame_number_swap_performed_ = -1; + + // If we are planing to draw with the new commit, lock the layer textures for + // use on the impl thread. Otherwise, leave them unlocked. + if (has_pending_tree_ || needs_redraw_) + texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD; + else + texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED; +} + +void SchedulerStateMachine::UpdateStateOnActivation() { + // Update output surface state. + if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) + output_surface_state_ = OUTPUT_SURFACE_ACTIVE; + + // Update readback state + if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION) + forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_DRAW; + + // Update forced redraw state + if (readback_state_ == READBACK_STATE_WAITING_FOR_ACTIVATION) + readback_state_ = READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK; + else if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION) + readback_state_ = READBACK_STATE_IDLE; + + has_pending_tree_ = false; + pending_tree_is_ready_for_activation_ = false; + active_tree_needs_first_draw_ = true; + needs_redraw_ = true; +} + +void SchedulerStateMachine::UpdateStateOnDraw(bool did_swap) { + DCHECK(readback_state_ != READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT && + readback_state_ != READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION) + << *AsValue(); + + if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) { + // The draw correspons to a readback commit. + DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW); + // We are blocking commits from the main thread until after this draw, so + // we should not have a pending tree. + DCHECK(!has_pending_tree_); + // We transition to COMMIT_STATE_FRAME_IN_PROGRESS because there is a + // pending BeginFrame on the main thread behind the readback request. + commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS; + readback_state_ = READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT; + } else if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) { + DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW); + commit_state_ = COMMIT_STATE_IDLE; + forced_redraw_state_ = FORCED_REDRAW_STATE_IDLE; + } else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW && + !has_pending_tree_) { + commit_state_ = COMMIT_STATE_IDLE; + } + + if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD) + texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED; + + needs_redraw_ = false; + draw_if_possible_failed_ = false; + active_tree_needs_first_draw_ = false; + + if (did_swap) + last_frame_number_swap_performed_ = current_frame_number_; +} + +void SchedulerStateMachine::UpdateStateOnManageTiles() { + needs_manage_tiles_ = false; +} + void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() { DCHECK(!main_thread_needs_layer_textures_); DCHECK_NE(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD); main_thread_needs_layer_textures_ = true; } +bool SchedulerStateMachine::BeginFrameNeededByImplThread() const { + // Proactive BeginFrames are bad for the synchronous compositor because we + // have to draw when we get the BeginFrame and could end up drawing many + // duplicate frames if our new frame isn't ready in time. + // To poll for state with the synchronous compositor without having to draw, + // we rely on ShouldPollForAnticipatedDrawTriggers instead. + if (settings_.using_synchronous_renderer_compositor) + return BeginFrameNeededToDrawByImplThread(); + + return BeginFrameNeededToDrawByImplThread() || + ProactiveBeginFrameWantedByImplThread(); +} + +bool SchedulerStateMachine::ShouldPollForAnticipatedDrawTriggers() const { + // ShouldPollForAnticipatedDrawTriggers is what we use in place of + // ProactiveBeginFrameWantedByImplThread when we are using the synchronous + // compositor. + if (settings_.using_synchronous_renderer_compositor) { + return !BeginFrameNeededToDrawByImplThread() && + ProactiveBeginFrameWantedByImplThread(); + } + + // Non synchronous compositors should rely on + // ProactiveBeginFrameWantedByImplThread to poll for state instead. + return false; +} + +// These are the cases where we definitely (or almost definitely) have a +// new frame to draw and can draw. bool SchedulerStateMachine::BeginFrameNeededToDrawByImplThread() const { + // The output surface is the provider of BeginFrames for the impl thread, + // so we are not going to get them even if we ask for them. + if (!HasInitializedOutputSurface()) + return false; + // If we can't draw, don't tick until we are notified that we can draw again. if (!can_draw_) return false; - if (needs_forced_redraw_) + // The forced draw respects our normal draw scheduling, so we need to + // request a BeginFrame on the impl thread for it. + if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) return true; - if (visible_ && swap_used_incomplete_tile_) + // There's no need to produce frames if we are not visible. + if (!visible_) + return false; + + // We need to draw a more complete frame than we did the last BeginFrame, + // so request another BeginFrame in anticipation that we will have + // additional visible tiles. + if (swap_used_incomplete_tile_) return true; - return needs_redraw_ && visible_ && - output_surface_state_ == OUTPUT_SURFACE_ACTIVE; + return needs_redraw_; } +// These are cases where we are very likely to draw soon, but might not +// actually have a new frame to draw when we receive the next BeginFrame. +// Proactively requesting the BeginFrame helps hide the round trip latency of +// the SetNeedsBeginFrame request that has to go to the Browser. bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() const { + // The output surface is the provider of BeginFrames for the impl thread, + // so we are not going to get them even if we ask for them. + if (!HasInitializedOutputSurface()) + return false; + + // Do not be proactive if vsync is off. + if (!settings_.throttle_frame_production) + return false; + // Do not be proactive when invisible. - if (!visible_ || output_surface_state_ != OUTPUT_SURFACE_ACTIVE) + if (!visible_) return false; - // We should proactively request a BeginFrame if a commit or a tree activation - // is pending. - return (needs_commit_ || needs_forced_commit_ || - commit_state_ != COMMIT_STATE_IDLE || has_pending_tree_); + // We should proactively request a BeginFrame if a commit is pending + // because we will want to draw if the commit completes quickly. + if (needs_commit_ || commit_state_ != COMMIT_STATE_IDLE) + return true; + + // If the pending tree activates quickly, we'll want a BeginFrame soon + // to draw the new active tree. + if (has_pending_tree_) + return true; + + // Changing priorities may allow us to activate (given the new priorities), + // which may result in a new frame. + if (needs_manage_tiles_) + return true; + + // If we just swapped, it's likely that we are going to produce another + // frame soon. This helps avoid negative glitches in our SetNeedsBeginFrame + // requests, which may propagate to the BeginFrame provider and get sampled + // at an inopportune time, delaying the next BeginFrame. + if (last_frame_number_swap_performed_ == current_frame_number_) + return true; + + return false; } -void SchedulerStateMachine::DidEnterBeginFrame(const BeginFrameArgs& args) { +void SchedulerStateMachine::OnBeginFrame(const BeginFrameArgs& args) { current_frame_number_++; - inside_begin_frame_ = true; last_begin_frame_args_ = args; + DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_IDLE) << *AsValue(); + begin_frame_state_ = BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING; +} + +void SchedulerStateMachine::OnBeginFrameDeadlinePending() { + DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING) + << *AsValue(); + begin_frame_state_ = BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME; +} + +void SchedulerStateMachine::OnBeginFrameDeadline() { + DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME) + << *AsValue(); + begin_frame_state_ = BEGIN_FRAME_STATE_INSIDE_DEADLINE; } -void SchedulerStateMachine::DidLeaveBeginFrame() { - inside_begin_frame_ = false; +void SchedulerStateMachine::OnBeginFrameIdle() { + DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_INSIDE_DEADLINE) + << *AsValue(); + begin_frame_state_ = BEGIN_FRAME_STATE_IDLE; +} + +bool SchedulerStateMachine::ShouldTriggerBeginFrameDeadlineEarly() const { + // TODO(brianderson): This should take into account multiple commit sources. + + // If we are in the middle of the readback, we won't swap, so there is + // no reason to trigger the deadline early. + if (readback_state_ != READBACK_STATE_IDLE) + return false; + + if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME) + return false; + + if (active_tree_needs_first_draw_) + return true; + + if (!needs_redraw_) + return false; + + // This is used to prioritize impl-thread draws when the main thread isn't + // producing anything, e.g., after an aborted commit. We also check that we + // don't have a pending tree -- otherwise we should give it a chance to + // activate. + // TODO(skyostil): Revisit this when we have more accurate deadline estimates. + if (commit_state_ == COMMIT_STATE_IDLE && !has_pending_tree_) + return true; + + // Prioritize impl-thread draws in smoothness mode. + if (smoothness_takes_priority_) + return true; + + return false; +} + +void SchedulerStateMachine::DidEnterPollForAnticipatedDrawTriggers() { + current_frame_number_++; + inside_poll_for_anticipated_draw_triggers_ = true; +} + +void SchedulerStateMachine::DidLeavePollForAnticipatedDrawTriggers() { + inside_poll_for_anticipated_draw_triggers_ = false; } void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; } +void SchedulerStateMachine::SetCanDraw(bool can_draw) { can_draw_ = can_draw; } + void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; } -void SchedulerStateMachine::DidSwapUseIncompleteTile() { - swap_used_incomplete_tile_ = true; +void SchedulerStateMachine::SetNeedsManageTiles() { + needs_manage_tiles_ = true; +} + +void SchedulerStateMachine::SetSwapUsedIncompleteTile( + bool used_incomplete_tile) { + swap_used_incomplete_tile_ = used_incomplete_tile; } -void SchedulerStateMachine::SetNeedsForcedRedraw() { - needs_forced_redraw_ = true; +void SchedulerStateMachine::SetSmoothnessTakesPriority( + bool smoothness_takes_priority) { + smoothness_takes_priority_ = smoothness_takes_priority; } void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) { @@ -419,11 +942,11 @@ void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) { consecutive_failed_draws_++; if (settings_.timeout_and_draw_when_animation_checkerboards && consecutive_failed_draws_ >= - maximum_number_of_failed_draws_before_draw_is_forced_) { + settings_.maximum_number_of_failed_draws_before_draw_is_forced_) { consecutive_failed_draws_ = 0; // We need to force a draw, but it doesn't make sense to do this until // we've committed and have new textures. - needs_forced_redraw_after_next_commit_ = true; + forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT; } } else { consecutive_failed_draws_ = 0; @@ -432,27 +955,39 @@ void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) { void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; } -void SchedulerStateMachine::SetNeedsForcedCommit() { - needs_forced_commit_ = true; - expect_immediate_begin_frame_for_main_thread_ = true; +void SchedulerStateMachine::SetNeedsForcedCommitForReadback() { + // If this is called in READBACK_STATE_IDLE, this is a "first" readback + // request. + // If this is called in READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT, this + // is a back-to-back readback request that started before the replacement + // commit had a chance to land. + DCHECK(readback_state_ == READBACK_STATE_IDLE || + readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT); + + // If there is already a commit in progress when we get the readback request + // (we are in COMMIT_STATE_FRAME_IN_PROGRESS), then we don't need to send a + // BeginFrame for the replacement commit, since there's already a BeginFrame + // behind the readback request. In that case, we can skip + // READBACK_STATE_NEEDS_BEGIN_FRAME and go directly to + // READBACK_STATE_WAITING_FOR_COMMIT + if (commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS) + readback_state_ = READBACK_STATE_WAITING_FOR_COMMIT; + else + readback_state_ = READBACK_STATE_NEEDS_BEGIN_FRAME; } void SchedulerStateMachine::FinishCommit() { - DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS || - (expect_immediate_begin_frame_for_main_thread_ && - commit_state_ != COMMIT_STATE_IDLE)) - << ToString(); + DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS) << *AsValue(); commit_state_ = COMMIT_STATE_READY_TO_COMMIT; } void SchedulerStateMachine::BeginFrameAbortedByMainThread(bool did_handle) { DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS); - if (expect_immediate_begin_frame_for_main_thread_) { - expect_immediate_begin_frame_for_main_thread_ = false; - } else if (did_handle) { - commit_state_ = COMMIT_STATE_IDLE; - SetPostCommitFlags(); + if (did_handle) { + bool commit_was_aborted = true; + UpdateStateOnCommit(commit_was_aborted); } else { + DCHECK_NE(readback_state_, READBACK_STATE_WAITING_FOR_COMMIT); commit_state_ = COMMIT_STATE_IDLE; SetNeedsCommit(); } @@ -463,38 +998,40 @@ void SchedulerStateMachine::DidLoseOutputSurface() { output_surface_state_ == OUTPUT_SURFACE_CREATING) return; output_surface_state_ = OUTPUT_SURFACE_LOST; + needs_redraw_ = false; + begin_frame_state_ = BEGIN_FRAME_STATE_IDLE; } -void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree) { - has_pending_tree_ = has_pending_tree; +void SchedulerStateMachine::NotifyReadyToActivate() { + if (has_pending_tree_) + pending_tree_is_ready_for_activation_ = true; } -void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; } - void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() { DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING); - output_surface_state_ = OUTPUT_SURFACE_ACTIVE; + output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT; if (did_create_and_initialize_first_output_surface_) { // TODO(boliu): See if we can remove this when impl-side painting is always // on. Does anything on the main thread need to update after recreate? needs_commit_ = true; - // If anything has requested a redraw, we don't want to actually draw - // when the output surface is restored until things have a chance to - // sort themselves out with a commit. - needs_redraw_ = false; } - needs_redraw_after_next_commit_ = true; did_create_and_initialize_first_output_surface_ = true; } bool SchedulerStateMachine::HasInitializedOutputSurface() const { - return output_surface_state_ == OUTPUT_SURFACE_ACTIVE; -} + switch (output_surface_state_) { + case OUTPUT_SURFACE_LOST: + case OUTPUT_SURFACE_CREATING: + return false; -void SchedulerStateMachine::SetMaximumNumberOfFailedDrawsBeforeDrawIsForced( - int num_draws) { - maximum_number_of_failed_draws_before_draw_is_forced_ = num_draws; + case OUTPUT_SURFACE_ACTIVE: + case OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT: + case OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION: + return true; + } + NOTREACHED(); + return false; } } // namespace cc diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h index 5c7d5b5b8a7..a3d67ef765e 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.h +++ b/chromium/cc/scheduler/scheduler_state_machine.h @@ -8,11 +8,16 @@ #include <string> #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "cc/base/cc_export.h" #include "cc/output/begin_frame_args.h" #include "cc/scheduler/scheduler_settings.h" +namespace base { +class Value; +} + namespace cc { // The SchedulerStateMachine decides how to coordinate main thread activites @@ -31,25 +36,62 @@ class CC_EXPORT SchedulerStateMachine { // settings must be valid for the lifetime of this class. explicit SchedulerStateMachine(const SchedulerSettings& settings); + enum OutputSurfaceState { + OUTPUT_SURFACE_ACTIVE, + OUTPUT_SURFACE_LOST, + OUTPUT_SURFACE_CREATING, + OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT, + OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION, + }; + static const char* OutputSurfaceStateToString(OutputSurfaceState state); + + // Note: BeginFrameState will always cycle through all the states in order. + // Whether or not it actually waits or draws, it will at least try to wait in + // BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME and try to draw in + // BEGIN_FRAME_STATE_INSIDE_DEADLINE + enum BeginFrameState { + BEGIN_FRAME_STATE_IDLE, + BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING, + BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME, + BEGIN_FRAME_STATE_INSIDE_DEADLINE, + }; + static const char* BeginFrameStateToString(BeginFrameState state); + enum CommitState { COMMIT_STATE_IDLE, COMMIT_STATE_FRAME_IN_PROGRESS, COMMIT_STATE_READY_TO_COMMIT, COMMIT_STATE_WAITING_FOR_FIRST_DRAW, - COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, }; + static const char* CommitStateToString(CommitState state); enum TextureState { LAYER_TEXTURE_STATE_UNLOCKED, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD, }; - - enum OutputSurfaceState { - OUTPUT_SURFACE_ACTIVE, - OUTPUT_SURFACE_LOST, - OUTPUT_SURFACE_CREATING, + static const char* TextureStateToString(TextureState state); + + enum SynchronousReadbackState { + READBACK_STATE_IDLE, + READBACK_STATE_NEEDS_BEGIN_FRAME, + READBACK_STATE_WAITING_FOR_COMMIT, + READBACK_STATE_WAITING_FOR_ACTIVATION, + READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK, + READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT, + READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION, + }; + static const char* SynchronousReadbackStateToString( + SynchronousReadbackState state); + + enum ForcedRedrawOnTimeoutState { + FORCED_REDRAW_STATE_IDLE, + FORCED_REDRAW_STATE_WAITING_FOR_COMMIT, + FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION, + FORCED_REDRAW_STATE_WAITING_FOR_DRAW, }; + static const char* ForcedRedrawOnTimeoutStateToString( + ForcedRedrawOnTimeoutState state); bool CommitPending() const { return commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS || @@ -57,32 +99,61 @@ class CC_EXPORT SchedulerStateMachine { } bool RedrawPending() const { return needs_redraw_; } + bool ManageTilesPending() const { return needs_manage_tiles_; } enum Action { ACTION_NONE, ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, ACTION_COMMIT, ACTION_UPDATE_VISIBLE_TILES, - ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED, - ACTION_DRAW_IF_POSSIBLE, - ACTION_DRAW_FORCED, + ACTION_ACTIVATE_PENDING_TREE, + ACTION_DRAW_AND_SWAP_IF_POSSIBLE, + ACTION_DRAW_AND_SWAP_FORCED, + ACTION_DRAW_AND_SWAP_ABORT, + ACTION_DRAW_AND_READBACK, ACTION_BEGIN_OUTPUT_SURFACE_CREATION, ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD, + ACTION_MANAGE_TILES, }; + static const char* ActionToString(Action action); + + scoped_ptr<base::Value> AsValue() const; + Action NextAction() const; void UpdateState(Action action); + void CheckInvariants(); + // Indicates whether the main thread needs a begin frame callback in order to // make progress. - bool BeginFrameNeededToDrawByImplThread() const; - bool ProactiveBeginFrameWantedByImplThread() const; + bool BeginFrameNeededByImplThread() const; + + // Idicates that we need to independently poll for new state and actions + // because we can't expect a BeginFrame. This is mostly used to avoid + // drawing repeat frames with the synchronous compositor without dropping + // necessary actions on the floor. + bool ShouldPollForAnticipatedDrawTriggers() const; // Indicates that the system has entered and left a BeginFrame callback. // The scheduler will not draw more than once in a given BeginFrame // callback nor send more than one BeginFrame message. - void DidEnterBeginFrame(const BeginFrameArgs& args); - void DidLeaveBeginFrame(); - bool inside_begin_frame() const { return inside_begin_frame_; } + void OnBeginFrame(const BeginFrameArgs& args); + void OnBeginFrameDeadlinePending(); + void OnBeginFrameDeadline(); + void OnBeginFrameIdle(); + bool ShouldTriggerBeginFrameDeadlineEarly() const; + BeginFrameState begin_frame_state() const { + return begin_frame_state_; + } + + // PollForAnticipatedDrawTriggers is used by the synchronous compositor to + // avoid requesting BeginImplFrames when we won't actually draw but still + // need to advance our state at vsync intervals. + void DidEnterPollForAnticipatedDrawTriggers(); + void DidLeavePollForAnticipatedDrawTriggers(); + bool inside_poll_for_anticipated_draw_triggers() const { + return inside_poll_for_anticipated_draw_triggers_; + } // Indicates whether the LayerTreeHostImpl is visible. void SetVisible(bool visible); @@ -90,16 +161,21 @@ class CC_EXPORT SchedulerStateMachine { // Indicates that a redraw is required, either due to the impl tree changing // or the screen being damaged and simply needing redisplay. void SetNeedsRedraw(); + bool needs_redraw() const { return needs_redraw_; } - // As SetNeedsRedraw(), but ensures the draw will definitely happen even if - // we are not visible. - void SetNeedsForcedRedraw(); + // Indicates that manage-tiles is required. This guarantees another + // ManageTiles will occur shortly (even if no redraw is required). + void SetNeedsManageTiles(); - // Indicates that a redraw is required because we are currently rendering + // Indicates whether a redraw is required because we are currently rendering // with a low resolution or checkerboarded tile. - void DidSwapUseIncompleteTile(); + void SetSwapUsedIncompleteTile(bool used_incomplete_tile); + + // Indicates whether to prioritize animation smoothness over new content + // activation. + void SetSmoothnessTakesPriority(bool smoothness_takes_priority); - // Indicates whether ACTION_DRAW_IF_POSSIBLE drew to the screen or not. + // Indicates whether ACTION_DRAW_AND_SWAP_IF_POSSIBLE drew to the screen. void DidDrawIfPossibleCompleted(bool success); // Indicates that a new commit flow needs to be performed, either to pull @@ -111,7 +187,7 @@ class CC_EXPORT SchedulerStateMachine { // thread even if we are not visible. After this call we expect to go through // the forced commit flow and then return to waiting for a non-forced // begin frame to finish. - void SetNeedsForcedCommit(); + void SetNeedsForcedCommitForReadback(); // Call this only in response to receiving an // ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD from NextAction. @@ -126,7 +202,8 @@ class CC_EXPORT SchedulerStateMachine { // Request exclusive access to the textures that back single buffered // layers on behalf of the main thread. Upon acquisition, - // ACTION_DRAW_IF_POSSIBLE will not draw until the main thread releases the + // ACTION_DRAW_AND_SWAP_IF_POSSIBLE will not draw until the main thread + // releases the // textures to the impl thread by committing the layers. void SetMainThreadNeedsLayerTextures(); @@ -138,70 +215,78 @@ class CC_EXPORT SchedulerStateMachine { // when such behavior would be undesirable. void SetCanDraw(bool can); - // Indicates whether or not there is a pending tree. This influences - // whether or not we can succesfully commit at this time. If the - // last commit is still being processed (but not blocking), it may not - // be possible to take another commit yet. This overrides force commit, - // as a commit is already still in flight. - void SetHasPendingTree(bool has_pending_tree); + // Indicates that the pending tree is ready for activation. + void NotifyReadyToActivate(); + bool has_pending_tree() const { return has_pending_tree_; } void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); bool HasInitializedOutputSurface() const; - // Exposed for testing purposes. - void SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(int num_draws); + // True if we need to abort draws to make forward progress. + bool PendingDrawsShouldBeAborted() const; - // False if drawing is not being prevented, true if drawing won't happen - // for some reason, such as not being visible. - bool DrawSuspendedUntilCommit() const; + protected: + bool BeginFrameNeededToDrawByImplThread() const; + bool ProactiveBeginFrameWantedByImplThread() const; - std::string ToString(); + // True if we need to force activations to make forward progress. + bool PendingActivationsShouldBeForced() const; - protected: + bool ShouldBeginOutputSurfaceCreation() const; bool ShouldDrawForced() const; - bool ScheduledToDraw() const; bool ShouldDraw() const; - bool ShouldAttemptTreeActivation() const; + bool ShouldActivatePendingTree() const; bool ShouldAcquireLayerTexturesForMainThread() const; bool ShouldUpdateVisibleTiles() const; - bool HasDrawnThisFrame() const; - bool HasAttemptedTreeActivationThisFrame() const; + bool ShouldSendBeginFrameToMainThread() const; + bool ShouldCommit() const; + bool ShouldManageTiles() const; + + bool HasSentBeginFrameToMainThreadThisFrame() const; + bool HasScheduledManageTilesThisFrame() const; bool HasUpdatedVisibleTilesThisFrame() const; - void SetPostCommitFlags(); + bool HasSwappedThisFrame() const; + + void UpdateStateOnCommit(bool commit_was_aborted); + void UpdateStateOnActivation(); + void UpdateStateOnDraw(bool did_swap); + void UpdateStateOnManageTiles(); const SchedulerSettings settings_; + OutputSurfaceState output_surface_state_; + BeginFrameState begin_frame_state_; CommitState commit_state_; - int commit_count_; + TextureState texture_state_; + ForcedRedrawOnTimeoutState forced_redraw_state_; + SynchronousReadbackState readback_state_; + + BeginFrameArgs last_begin_frame_args_; + int commit_count_; int current_frame_number_; - int last_frame_number_where_begin_frame_sent_to_main_thread_; - int last_frame_number_where_draw_was_called_; - int last_frame_number_where_tree_activation_attempted_; - int last_frame_number_where_update_visible_tiles_was_called_; + int last_frame_number_swap_performed_; + int last_frame_number_begin_frame_sent_to_main_thread_; + int last_frame_number_update_visible_tiles_was_called_; + int consecutive_failed_draws_; - int maximum_number_of_failed_draws_before_draw_is_forced_; bool needs_redraw_; + bool needs_manage_tiles_; bool swap_used_incomplete_tile_; - bool needs_forced_redraw_; - bool needs_forced_redraw_after_next_commit_; - bool needs_redraw_after_next_commit_; bool needs_commit_; - bool needs_forced_commit_; - bool expect_immediate_begin_frame_for_main_thread_; bool main_thread_needs_layer_textures_; - bool inside_begin_frame_; - BeginFrameArgs last_begin_frame_args_; + bool inside_poll_for_anticipated_draw_triggers_; bool visible_; bool can_start_; bool can_draw_; bool has_pending_tree_; + bool pending_tree_is_ready_for_activation_; + bool active_tree_needs_first_draw_; bool draw_if_possible_failed_; - TextureState texture_state_; - OutputSurfaceState output_surface_state_; bool did_create_and_initialize_first_output_surface_; + bool smoothness_takes_priority_; private: DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine); diff --git a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc index c7210eb2a05..af3231b0db3 100644 --- a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc @@ -7,35 +7,98 @@ #include "cc/scheduler/scheduler.h" #include "testing/gtest/include/gtest/gtest.h" +#define EXPECT_ACTION_UPDATE_STATE(action) \ + EXPECT_EQ(action, state.NextAction()) << *state.AsValue(); \ + if (action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE || \ + action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED) { \ + if (SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW == \ + state.CommitState() && \ + SchedulerStateMachine::OUTPUT_SURFACE_ACTIVE != \ + state.output_surface_state()) \ + return; \ + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, \ + state.begin_frame_state()) \ + << *state.AsValue(); \ + } \ + state.UpdateState(action); \ + if (action == SchedulerStateMachine::ACTION_NONE) { \ + if (state.begin_frame_state() == \ + SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING) \ + state.OnBeginFrameDeadlinePending(); \ + if (state.begin_frame_state() == \ + SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE) \ + state.OnBeginFrameIdle(); \ + } + namespace cc { namespace { +const SchedulerStateMachine::BeginFrameState all_begin_frame_states[] = { + SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE, + SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING, + SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME, + SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, }; + const SchedulerStateMachine::CommitState all_commit_states[] = { - SchedulerStateMachine::COMMIT_STATE_IDLE, - SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, - SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, - SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW -}; + SchedulerStateMachine::COMMIT_STATE_IDLE, + SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, + SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, + SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, }; // Exposes the protected state fields of the SchedulerStateMachine for testing class StateMachine : public SchedulerStateMachine { public: explicit StateMachine(const SchedulerSettings& scheduler_settings) : SchedulerStateMachine(scheduler_settings) {} + + void CreateAndInitializeOutputSurfaceWithActivatedCommit() { + DidCreateAndInitializeOutputSurface(); + output_surface_state_ = OUTPUT_SURFACE_ACTIVE; + } + void SetCommitState(CommitState cs) { commit_state_ = cs; } CommitState CommitState() const { return commit_state_; } + void SetBeginFrameState(BeginFrameState bfs) { begin_frame_state_ = bfs; } + + BeginFrameState begin_frame_state() const { return begin_frame_state_; } + + OutputSurfaceState output_surface_state() const { + return output_surface_state_; + } + bool NeedsCommit() const { return needs_commit_; } void SetNeedsRedraw(bool b) { needs_redraw_ = b; } - bool NeedsRedraw() const { return needs_redraw_; } - void SetNeedsForcedRedraw(bool b) { needs_forced_redraw_ = b; } - bool NeedsForcedRedraw() const { return needs_forced_redraw_; } + void SetNeedsForcedRedrawForTimeout(bool b) { + forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT; + commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; + } + bool NeedsForcedRedrawForTimeout() const { + return forced_redraw_state_ != FORCED_REDRAW_STATE_IDLE; + } + + void SetNeedsForcedRedrawForReadback() { + readback_state_ = READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK; + commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; + } + + bool NeedsForcedRedrawForReadback() const { + return readback_state_ != READBACK_STATE_IDLE; + } + + void SetActiveTreeNeedsFirstDraw(bool needs_first_draw) { + active_tree_needs_first_draw_ = needs_first_draw; + } bool CanDraw() const { return can_draw_; } bool Visible() const { return visible_; } + + bool PendingActivationsShouldBeForced() const { + return SchedulerStateMachine::PendingActivationsShouldBeForced(); + } }; TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { @@ -45,19 +108,21 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { { StateMachine state(default_scheduler_settings); state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION) + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE); state.SetNeedsRedraw(false); state.SetVisible(true); - EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededByImplThread()); - state.DidLeaveBeginFrame(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); } // If commit requested but can_start is still false, do nothing. @@ -67,13 +132,13 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { state.SetNeedsRedraw(false); state.SetVisible(true); - EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededByImplThread()); - state.DidLeaveBeginFrame(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); } // If commit requested, begin a main frame. @@ -83,7 +148,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { state.SetCanStart(); state.SetNeedsRedraw(false); state.SetVisible(true); - EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededByImplThread()); } // Begin the frame, make sure needs_commit and commit_state update correctly. @@ -91,52 +156,44 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.UpdateState( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); - EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); } } -TEST(SchedulerStateMachineTest, TestSetForcedRedrawDoesNotSetsNormalRedraw) { - SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); - state.SetCanDraw(true); - state.SetNeedsForcedRedraw(); - EXPECT_FALSE(state.RedrawPending()); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); -} - TEST(SchedulerStateMachineTest, TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain) { SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); + StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); - state.SetNeedsRedraw(); + state.SetNeedsRedraw(true); EXPECT_TRUE(state.RedrawPending()); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); // We're drawing now. - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_FALSE(state.RedrawPending()); EXPECT_FALSE(state.CommitPending()); // Failing the draw makes us require a commit. state.DidDrawIfPossibleCompleted(false); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState( + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_TRUE(state.RedrawPending()); EXPECT_TRUE(state.CommitPending()); @@ -145,316 +202,279 @@ TEST(SchedulerStateMachineTest, TEST(SchedulerStateMachineTest, TestsetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw) { SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); + StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); - state.SetNeedsRedraw(); + state.SetNeedsRedraw(true); EXPECT_TRUE(state.RedrawPending()); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); // We're drawing now. - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_FALSE(state.RedrawPending()); EXPECT_FALSE(state.CommitPending()); // While still in the same begin frame callback on the main thread, // set needs redraw again. This should not redraw. - state.SetNeedsRedraw(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + state.SetNeedsRedraw(true); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Failing the draw makes us require a commit. state.DidDrawIfPossibleCompleted(false); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - EXPECT_TRUE(state.RedrawPending()); -} - -TEST(SchedulerStateMachineTest, - TestCommitAfterFailedDrawAllowsDrawInSameFrame) { - SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); - state.SetVisible(true); - state.SetCanDraw(true); - - // Start a commit. - state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState( + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); - EXPECT_TRUE(state.CommitPending()); - - // Then initiate a draw. - state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - EXPECT_TRUE(state.RedrawPending()); - - // Fail the draw. - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - state.DidDrawIfPossibleCompleted(false); - EXPECT_TRUE(state.RedrawPending()); - // But the commit is ongoing. - EXPECT_TRUE(state.CommitPending()); - - // Finish the commit. - state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); EXPECT_TRUE(state.RedrawPending()); - - // And we should be allowed to draw again. - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); } -TEST(SchedulerStateMachineTest, - TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame) { - SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); +void TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit( + bool deadline_scheduling_enabled) { + SchedulerSettings scheduler_settings; + scheduler_settings.maximum_number_of_failed_draws_before_draw_is_forced_ = 1; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + StateMachine state(scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Start a commit. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState( - SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + if (!deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_TRUE(state.CommitPending()); // Then initiate a draw. - state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - EXPECT_TRUE(state.RedrawPending()); + state.SetNeedsRedraw(true); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); // Fail the draw. - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.DidDrawIfPossibleCompleted(false); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); EXPECT_TRUE(state.RedrawPending()); // But the commit is ongoing. EXPECT_TRUE(state.CommitPending()); - // Force a draw. - state.SetNeedsForcedRedraw(); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); - - // Do the forced draw. - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_FORCED); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - EXPECT_FALSE(state.RedrawPending()); - // And the commit is still ongoing. - EXPECT_TRUE(state.CommitPending()); - - // Finish the commit. + // Finish the commit. Note, we should not yet be forcing a draw, but should + // continue the commit as usual. state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_TRUE(state.RedrawPending()); - // And we should not be allowed to draw again in the same frame.. - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + // The redraw should be forced at the end of the next BeginFrame. + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED); } TEST(SchedulerStateMachineTest, TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit) { - SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); - state.SetCanStart(); - state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); - state.SetVisible(true); - state.SetCanDraw(true); - state.SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(1); - - // Start a commit. - state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState( - SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); - EXPECT_TRUE(state.CommitPending()); - - // Then initiate a draw. - state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - EXPECT_TRUE(state.RedrawPending()); - - // Fail the draw. - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - state.DidDrawIfPossibleCompleted(false); - EXPECT_TRUE(state.RedrawPending()); - // But the commit is ongoing. - EXPECT_TRUE(state.CommitPending()); - - // Finish the commit. Note, we should not yet be forcing a draw, but should - // continue the commit as usual. - state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); - EXPECT_TRUE(state.RedrawPending()); + bool deadline_scheduling_enabled = false; + TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit( + deadline_scheduling_enabled); +} - // The redraw should be forced in this case. - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); +TEST(SchedulerStateMachineTest, + TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit_Deadline) { + bool deadline_scheduling_enabled = true; + TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit( + deadline_scheduling_enabled); } TEST(SchedulerStateMachineTest, TestFailedDrawIsRetriedInNextBeginFrameForImplThread) { SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); + StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Start a draw. - state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); + state.SetNeedsRedraw(true); + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); EXPECT_TRUE(state.RedrawPending()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); - // Fail the draw. - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + // Fail the draw state.DidDrawIfPossibleCompleted(false); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_TRUE(state.RedrawPending()); // We should not be trying to draw again now, but we have a commit pending. - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - - state.DidLeaveBeginFrame(); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - - // We should try to draw again in the next begin frame on the impl thread. - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // We should try to draw again at the end of the next BeginFrame on + // the impl thread. + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); + StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); - state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); - - // While still in the same begin frame for the impl thread, set needs redraw - // again. This should not redraw. - state.SetNeedsRedraw(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + state.SetNeedsRedraw(true); - // Move to another frame. This should now draw. + // Draw the first frame. + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); - state.DidLeaveBeginFrame(); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + // Before the next begin frame for the impl thread, set needs redraw + // again. This should not redraw until the next begin frame. + state.SetNeedsRedraw(true); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // Move to another frame. This should now draw. + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); - EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // We just swapped, so we should proactively request another BeginFrame. + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); } TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginFrame) { SchedulerSettings default_scheduler_settings; - // When not in BeginFrame, or in BeginFrame but not visible, + // When not in BeginFrame deadline, or in BeginFrame deadline but not visible, // don't draw. size_t num_commit_states = sizeof(all_commit_states) / sizeof(SchedulerStateMachine::CommitState); + size_t num_begin_frame_states = + sizeof(all_begin_frame_states) / + sizeof(SchedulerStateMachine::BeginFrameState); for (size_t i = 0; i < num_commit_states; ++i) { - for (size_t j = 0; j < 2; ++j) { + for (size_t j = 0; j < num_begin_frame_states; ++j) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetCommitState(all_commit_states[i]); - bool visible = j; - if (!visible) { - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - state.SetVisible(false); - } else { - state.SetVisible(true); - } + state.SetBeginFrameState(all_begin_frame_states[j]); + bool visible = (all_begin_frame_states[j] != + SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE); + state.SetVisible(visible); // Case 1: needs_commit=false - EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, + EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, state.NextAction()); // Case 2: needs_commit=true state.SetNeedsCommit(); - EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, - state.NextAction()); + EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, + state.NextAction()) + << *state.AsValue(); } } - // When in BeginFrame, or not in BeginFrame but needs_forced_dedraw - // set, should always draw except if you're ready to commit, in which case - // commit. + // When in BeginFrame deadline we should always draw for SetNeedsRedraw or + // SetNeedsForcedRedrawForReadback have been called... except if we're + // ready to commit, in which case we expect a commit first. for (size_t i = 0; i < num_commit_states; ++i) { for (size_t j = 0; j < 2; ++j) { + bool request_readback = j; + + // Skip invalid states + if (request_readback && + (SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW != + all_commit_states[i])) + continue; + StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetCanDraw(true); state.SetCommitState(all_commit_states[i]); - bool forced_draw = j; - if (!forced_draw) { - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + state.SetBeginFrameState( + SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE); + if (request_readback) { + state.SetNeedsForcedRedrawForReadback(); + } else { state.SetNeedsRedraw(true); state.SetVisible(true); - } else { - state.SetNeedsForcedRedraw(true); } SchedulerStateMachine::Action expected_action; - if (all_commit_states[i] != + if (all_commit_states[i] == SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT) { - expected_action = - forced_draw ? SchedulerStateMachine::ACTION_DRAW_FORCED - : SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE; - } else { expected_action = SchedulerStateMachine::ACTION_COMMIT; + } else if (request_readback) { + if (all_commit_states[i] == + SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW) + expected_action = SchedulerStateMachine::ACTION_DRAW_AND_READBACK; + else + expected_action = SchedulerStateMachine::ACTION_NONE; + } else { + expected_action = + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE; } // Case 1: needs_commit=false. - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - EXPECT_EQ(expected_action, state.NextAction()); + EXPECT_NE(state.BeginFrameNeededByImplThread(), request_readback) + << *state.AsValue(); + EXPECT_EQ(expected_action, state.NextAction()) << *state.AsValue(); // Case 2: needs_commit=true. state.SetNeedsCommit(); - EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); - EXPECT_EQ(expected_action, state.NextAction()); + EXPECT_NE(state.BeginFrameNeededByImplThread(), request_readback) + << *state.AsValue(); + EXPECT_EQ(expected_action, state.NextAction()) << *state.AsValue(); } } } @@ -470,22 +490,24 @@ TEST(SchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetCommitState(all_commit_states[i]); state.SetVisible(false); state.SetNeedsRedraw(true); - state.SetNeedsForcedRedraw(false); - if (j == 1) - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + if (j == 1) { + state.SetBeginFrameState( + SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE); + } // Case 1: needs_commit=false. - EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, + EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, state.NextAction()); // Case 2: needs_commit=true. state.SetNeedsCommit(); - EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, - state.NextAction()); + EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, + state.NextAction()) + << *state.AsValue(); } } } @@ -501,16 +523,15 @@ TEST(SchedulerStateMachineTest, TestCanRedraw_StopsDraw) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetCommitState(all_commit_states[i]); state.SetVisible(false); state.SetNeedsRedraw(true); - state.SetNeedsForcedRedraw(false); if (j == 1) - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); state.SetCanDraw(false); - EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, + EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, state.NextAction()); } } @@ -522,15 +543,25 @@ TEST(SchedulerStateMachineTest, StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); + state.SetCommitState( SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW); + state.SetActiveTreeNeedsFirstDraw(true); state.SetNeedsCommit(); state.SetNeedsRedraw(true); state.SetVisible(true); state.SetCanDraw(false); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.FinishCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) { @@ -538,15 +569,17 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetNeedsCommit(); state.SetVisible(true); state.SetCanDraw(true); + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + // Begin the frame. - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState(state.NextAction()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); @@ -560,23 +593,44 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) { state.CommitState()); // Expect to commit regardless of BeginFrame state. - state.DidLeaveBeginFrame(); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING, + state.begin_frame_state()); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + + state.OnBeginFrameDeadlinePending(); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME, + state.begin_frame_state()); + EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + + state.OnBeginFrameDeadline(); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, + state.begin_frame_state()); + EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + + state.OnBeginFrameIdle(); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE, + state.begin_frame_state()); + EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING, + state.begin_frame_state()); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); // Commit and make sure we draw on next BeginFrame - state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); - // Verify that another commit will begin. - state.DidLeaveBeginFrame(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); + // Verify that another commit will start immediately after draw. + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, TestFullCycle) { @@ -584,49 +638,46 @@ TEST(SchedulerStateMachineTest, TestFullCycle) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Start clean and set commit. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); // Begin the frame. - state.UpdateState( + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Tell the scheduler the frame finished. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); // Commit. - state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - EXPECT_TRUE(state.NeedsRedraw()); + EXPECT_TRUE(state.needs_redraw()); - // Expect to do nothing until BeginFrame. - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + // Expect to do nothing until BeginFrame deadline + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - // At BeginFrame, draw. - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + // At BeginFrame deadline, draw. + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); - state.DidLeaveBeginFrame(); // Should be synchronized, no draw needed, no action needed. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); - EXPECT_FALSE(state.NeedsRedraw()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_FALSE(state.needs_redraw()); } TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) { @@ -634,54 +685,55 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Start clean and set commit. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); // Begin the frame. - state.UpdateState( + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Request another commit while the commit is in flight. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Tell the scheduler the frame finished. state.FinishCommit(); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - // Commit. - state.UpdateState(SchedulerStateMachine::ACTION_COMMIT); + // First commit. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - EXPECT_TRUE(state.NeedsRedraw()); + EXPECT_TRUE(state.needs_redraw()); - // Expect to do nothing until BeginFrame. - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + // Expect to do nothing until BeginFrame deadline. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - // At BeginFrame, draw. - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + // At BeginFrame deadline, draw. + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); - state.DidLeaveBeginFrame(); // Should be synchronized, no draw needed, no action needed. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); - EXPECT_FALSE(state.NeedsRedraw()); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); + EXPECT_FALSE(state.needs_redraw()); + + // Next BeginFrame should initiate second commit. + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); } TEST(SchedulerStateMachineTest, TestRequestCommitInvisible) { @@ -689,9 +741,9 @@ TEST(SchedulerStateMachineTest, TestRequestCommitInvisible) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { @@ -699,31 +751,33 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Start clean and set commit. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); // Begin the frame while visible. - state.UpdateState( + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Become invisible and abort the main thread's begin frame. state.SetVisible(false); state.BeginFrameAbortedByMainThread(false); - // We should now be back in the idle state as if we didn't start a frame at - // all. + // We should now be back in the idle state as if we never started the frame. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // We shouldn't do anything on the BeginFrame deadline. + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Become visible again. state.SetVisible(true); @@ -735,16 +789,14 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) { EXPECT_TRUE(state.NeedsCommit()); // Start a new frame. - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - - // Begin the frame. - state.UpdateState(state.NextAction()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); // We should be starting the commit now. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) { @@ -758,9 +810,8 @@ TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) { // Get into a begin frame / commit state. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState( + + EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); @@ -777,9 +828,11 @@ TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) { // Start a new frame; draw because this is the first frame since output // surface init'd. - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.DidLeaveBeginFrame(); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); // Verify another commit doesn't start on another frame either. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); @@ -800,15 +853,22 @@ TEST(SchedulerStateMachineTest, TestFirstContextCreation) { state.SetVisible(true); state.SetCanDraw(true); - EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, - state.NextAction()); - state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Check that the first init does not SetNeedsCommit. + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // Check that a needs commit initiates a BeginFrame to the main thread. state.SetNeedsCommit(); - EXPECT_NE(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); } TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) { @@ -816,7 +876,7 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); @@ -830,15 +890,15 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) { state.UpdateState(state.NextAction()); // Once context recreation begins, nothing should happen. - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Recreate the context. - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); // When the context is recreated, we should begin a commit. - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState(state.NextAction()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); } TEST(SchedulerStateMachineTest, @@ -847,7 +907,7 @@ TEST(SchedulerStateMachineTest, StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); @@ -855,65 +915,84 @@ TEST(SchedulerStateMachineTest, state.NextAction()); state.DidLoseOutputSurface(); - EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, - state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Once context recreation begins, nothing should happen. - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // While context is recreating, commits shouldn't begin. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Recreate the context state.DidCreateAndInitializeOutputSurface(); EXPECT_FALSE(state.RedrawPending()); // When the context is recreated, we should begin a commit - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Finishing the first commit after initializing an output surface should // automatically cause a redraw. EXPECT_TRUE(state.RedrawPending()); // Once the context is recreated, whether we draw should be based on // SetCanDraw. - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, + state.NextAction()); state.SetCanDraw(false); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT, + state.NextAction()); state.SetCanDraw(true); - state.DidLeaveBeginFrame(); + EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE, + state.NextAction()); } -TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { - SchedulerSettings default_scheduler_settings; - StateMachine state(default_scheduler_settings); +void TestContextLostWhileCommitInProgress(bool deadline_scheduling_enabled) { + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + StateMachine state(scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Get a commit in flight. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState(state.NextAction()); + if (!deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } // Set damage and expect a draw. state.SetNeedsRedraw(true); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(state.NextAction()); - state.DidLeaveBeginFrame(); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Cause a lost context while the begin frame is in flight // for the main thread. @@ -925,47 +1004,81 @@ TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { // Finish the frame, and commit. state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + // We will abort the draw when the output surface is lost if we are + // waiting for the first draw to unblock the main thread. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); // Expect to be told to begin context recreation, independent of // BeginFrame state. - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE, + state.begin_frame_state()); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); - state.DidLeaveBeginFrame(); + + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING, + state.begin_frame_state()); + EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, + state.NextAction()); + + state.OnBeginFrameDeadlinePending(); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME, + state.begin_frame_state()); + EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, + state.NextAction()); + + state.OnBeginFrameDeadline(); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, + state.begin_frame_state()); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); } -TEST(SchedulerStateMachineTest, - TestContextLostWhileCommitInProgressAndAnotherCommitRequested) { - SchedulerSettings default_scheduler_settings; - StateMachine state(default_scheduler_settings); +TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { + bool deadline_scheduling_enabled = false; + TestContextLostWhileCommitInProgress(deadline_scheduling_enabled); +} + +TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress_Deadline) { + bool deadline_scheduling_enabled = true; + TestContextLostWhileCommitInProgress(deadline_scheduling_enabled); +} + +void TestContextLostWhileCommitInProgressAndAnotherCommitRequested( + bool deadline_scheduling_enabled) { + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + StateMachine state(scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Get a commit in flight. state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - state.UpdateState(state.NextAction()); + if (!deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Set damage and expect a draw. state.SetNeedsRedraw(true); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(state.NextAction()); - state.DidLeaveBeginFrame(); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Cause a lost context while the begin frame is in flight // for the main thread. @@ -974,27 +1087,72 @@ TEST(SchedulerStateMachineTest, // Ask for another draw and also set needs commit. Expect nothing happens. state.SetNeedsRedraw(true); state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Finish the frame, and commit. state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - state.UpdateState(state.NextAction()); - + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); - state.UpdateState(state.NextAction()); + // Because the output surface is missing, we expect the draw to abort. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); // Expect to be told to begin context recreation, independent of // BeginFrame state - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE, + state.begin_frame_state()); + EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, + state.NextAction()); + + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING, + state.begin_frame_state()); + EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, + state.NextAction()); + + state.OnBeginFrameDeadlinePending(); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME, + state.begin_frame_state()); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); - state.DidLeaveBeginFrame(); + + state.OnBeginFrameDeadline(); + EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, + state.begin_frame_state()); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); + + // After we get a new output surface, the commit flow should start. + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); + state.OnBeginFrameIdle(); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.FinishCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + state.OnBeginFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); +} + +TEST(SchedulerStateMachineTest, + TestContextLostWhileCommitInProgressAndAnotherCommitRequested) { + bool deadline_scheduling_enabled = false; + TestContextLostWhileCommitInProgressAndAnotherCommitRequested( + deadline_scheduling_enabled); +} + +TEST(SchedulerStateMachineTest, + TestContextLostWhileCommitInProgressAndAnotherCommitRequested_Deadline) { + bool deadline_scheduling_enabled = true; + TestContextLostWhileCommitInProgressAndAnotherCommitRequested( + deadline_scheduling_enabled); } TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) { @@ -1002,33 +1160,47 @@ TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Cause a lost context lost. state.DidLoseOutputSurface(); - // Ask a forced redraw and verify it ocurrs. - state.SetNeedsForcedRedraw(true); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); - state.DidLeaveBeginFrame(); + // Ask a forced redraw for readback and verify it ocurrs. + state.SetCommitState( + SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW); + state.SetNeedsForcedRedrawForReadback(); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // Forced redraws for readbacks need to be followed by a new commit + // to replace the readback commit. + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, + state.CommitState()); + state.FinishCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); - // Clear the forced redraw bit. - state.SetNeedsForcedRedraw(false); + // We don't yet have an output surface, so we the draw and swap should abort. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); // Expect to be told to begin context recreation, independent of // BeginFrame state + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState()); EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, state.NextAction()); - state.UpdateState(state.NextAction()); - // Ask a forced redraw and verify it ocurrs. - state.SetNeedsForcedRedraw(true); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); - state.DidLeaveBeginFrame(); + state.OnBeginFrameDeadline(); + EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION, + state.NextAction()); + + // Ask a readback and verify it occurs. + state.SetCommitState( + SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW); + state.SetNeedsForcedRedrawForReadback(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) { @@ -1036,7 +1208,7 @@ TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); @@ -1050,6 +1222,7 @@ TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) { state.DidCreateAndInitializeOutputSurface(); EXPECT_FALSE(state.RedrawPending()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } @@ -1060,10 +1233,10 @@ TEST(SchedulerStateMachineTest, StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(false); state.SetNeedsCommit(); - state.SetNeedsForcedCommit(); + state.SetNeedsForcedCommitForReadback(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } @@ -1075,7 +1248,7 @@ TEST(SchedulerStateMachineTest, state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsCommit(); - state.SetNeedsForcedCommit(); + state.SetNeedsForcedCommitForReadback(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); } @@ -1085,7 +1258,7 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(false); state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS); state.SetNeedsCommit(); @@ -1096,8 +1269,7 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) { EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); } TEST(SchedulerStateMachineTest, TestFinishCommitWhenForcedCommitInProgress) { @@ -1105,37 +1277,64 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenForcedCommitInProgress) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(false); state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS); state.SetNeedsCommit(); - state.SetNeedsForcedCommit(); + state.SetNeedsForcedCommitForReadback(); + // The commit for readback interupts the normal commit. state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK); - // If we are waiting for forced draw then we know a begin frame is already - // in flight for the main thread. - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + // When the readback interrupts the normal commit, we should not get + // another BeginFrame on the impl thread when the readback completes. + EXPECT_NE(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, + state.NextAction()); + + // The normal commit can then proceed. + state.FinishCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); } -TEST(SchedulerStateMachineTest, TestSendBeginFrameToMainThreadWhenContextLost) { +TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsCommit(); - state.SetNeedsForcedCommit(); state.DidLoseOutputSurface(); + + // When we are visible, we normally want to begin output surface creation + // as soon as possible. + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION); + + state.DidCreateAndInitializeOutputSurface(); + EXPECT_EQ(state.output_surface_state(), + SchedulerStateMachine::OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT); + + // We should not send a BeginFrame to them main thread when we are invisible, + // even if we've lost the output surface and are trying to get the first + // commit, since the main thread will just abort anyway. + state.SetVisible(false); + EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, + state.NextAction()) << *state.AsValue(); + + // If there is a forced commit, however, we could be blocking a readback + // on the main thread, so we need to unblock it before we can get our + // output surface, even if we are not visible. + state.SetNeedsForcedCommitForReadback(); EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); + state.NextAction()) + << *state.AsValue(); } TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) { @@ -1143,112 +1342,130 @@ TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) { StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); - // Schedule a forced frame, commit it, draw it. + // Schedule a readback, commit it, draw it. state.SetNeedsCommit(); - state.SetNeedsForcedCommit(); - state.UpdateState(state.NextAction()); + state.SetNeedsForcedCommitForReadback(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - state.SetNeedsForcedRedraw(true); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK); state.DidDrawIfPossibleCompleted(true); - state.DidLeaveBeginFrame(); + + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Should be waiting for the normal begin frame from the main thread. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); } -TEST(SchedulerStateMachineTest, TestImmediateFinishCommitDuringCommit) { - SchedulerSettings default_scheduler_settings; - StateMachine state(default_scheduler_settings); +void TestImmediateFinishCommitDuringCommit(bool deadline_scheduling_enabled) { + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + StateMachine state(scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); // Start a normal commit. state.SetNeedsCommit(); - state.UpdateState(state.NextAction()); + if (!deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); - // Schedule a forced frame, commit it, draw it. - state.SetNeedsCommit(); - state.SetNeedsForcedCommit(); - state.UpdateState(state.NextAction()); + // Schedule a readback, commit it, draw it. + state.SetNeedsForcedCommitForReadback(); + if (deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - state.SetNeedsForcedRedraw(true); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK); state.DidDrawIfPossibleCompleted(true); - state.DidLeaveBeginFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Should be waiting for the normal begin frame from the main thread. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, - state.CommitState()) << state.ToString(); + state.CommitState()) + << *state.AsValue(); } TEST(SchedulerStateMachineTest, - ImmediateBeginFrameAbortedByMainThreadWhileInvisible) { - SchedulerSettings default_scheduler_settings; - StateMachine state(default_scheduler_settings); + TestImmediateFinishCommitDuringCommit) { + bool deadline_scheduling_enabled = false; + TestImmediateFinishCommitDuringCommit(deadline_scheduling_enabled); +} + +TEST(SchedulerStateMachineTest, + TestImmediateFinishCommitDuringCommit_Deadline) { + bool deadline_scheduling_enabled = true; + TestImmediateFinishCommitDuringCommit(deadline_scheduling_enabled); +} + +void ImmediateBeginFrameAbortedByMainThreadWhileInvisible( + bool deadline_scheduling_enabled) { + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + StateMachine state(scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsCommit(); - state.UpdateState(state.NextAction()); + if (!deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.SetNeedsCommit(); - state.SetNeedsForcedCommit(); - state.UpdateState(state.NextAction()); + state.SetNeedsForcedCommitForReadback(); + if (deadline_scheduling_enabled) { + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + } state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - state.SetNeedsForcedRedraw(true); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK); state.DidDrawIfPossibleCompleted(true); - state.DidLeaveBeginFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Should be waiting for the main thread's begin frame. EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, - state.CommitState()) << state.ToString(); + state.CommitState()) + << *state.AsValue(); // Become invisible and abort the main thread's begin frame. state.SetVisible(false); @@ -1259,12 +1476,26 @@ TEST(SchedulerStateMachineTest, EXPECT_TRUE(state.NeedsCommit()); } +TEST(SchedulerStateMachineTest, + ImmediateBeginFrameAbortedByMainThreadWhileInvisible) { + bool deadline_scheduling_enabled = false; + ImmediateBeginFrameAbortedByMainThreadWhileInvisible( + deadline_scheduling_enabled); +} + +TEST(SchedulerStateMachineTest, + ImmediateBeginFrameAbortedByMainThreadWhileInvisible_Deadline) { + bool deadline_scheduling_enabled = true; + ImmediateBeginFrameAbortedByMainThreadWhileInvisible( + deadline_scheduling_enabled); +} + TEST(SchedulerStateMachineTest, ImmediateFinishCommitWhileCantDraw) { SchedulerSettings default_scheduler_settings; StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetVisible(true); state.SetCanDraw(false); @@ -1272,92 +1503,87 @@ TEST(SchedulerStateMachineTest, ImmediateFinishCommitWhileCantDraw) { state.UpdateState(state.NextAction()); state.SetNeedsCommit(); - state.SetNeedsForcedCommit(); + state.SetNeedsForcedCommitForReadback(); state.UpdateState(state.NextAction()); state.FinishCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT, state.CommitState()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); - EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW, + EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.CommitState()); - state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - state.SetNeedsForcedRedraw(true); - EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction()); - state.UpdateState(state.NextAction()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK); state.DidDrawIfPossibleCompleted(true); - state.DidLeaveBeginFrame(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); } TEST(SchedulerStateMachineTest, ReportIfNotDrawing) { SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); + StateMachine state(default_scheduler_settings); + state.SetCanStart(); + state.UpdateState(state.NextAction()); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetCanDraw(true); state.SetVisible(true); - EXPECT_FALSE(state.DrawSuspendedUntilCommit()); + EXPECT_FALSE(state.PendingDrawsShouldBeAborted()); state.SetCanDraw(false); state.SetVisible(true); - EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + EXPECT_TRUE(state.PendingDrawsShouldBeAborted()); state.SetCanDraw(true); state.SetVisible(false); - EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + EXPECT_TRUE(state.PendingDrawsShouldBeAborted()); state.SetCanDraw(false); state.SetVisible(false); - EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + EXPECT_TRUE(state.PendingDrawsShouldBeAborted()); state.SetCanDraw(true); state.SetVisible(true); - EXPECT_FALSE(state.DrawSuspendedUntilCommit()); + EXPECT_FALSE(state.PendingDrawsShouldBeAborted()); } TEST(SchedulerStateMachineTest, ReportIfNotDrawingFromAcquiredTextures) { SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); + StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); - state.DidCreateAndInitializeOutputSurface(); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); state.SetCanDraw(true); state.SetVisible(true); - EXPECT_FALSE(state.DrawSuspendedUntilCommit()); + EXPECT_FALSE(state.PendingDrawsShouldBeAborted()); state.SetMainThreadNeedsLayerTextures(); - EXPECT_EQ( - SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD, - state.NextAction()); - state.UpdateState(state.NextAction()); - EXPECT_TRUE(state.DrawSuspendedUntilCommit()); - - EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD); + EXPECT_TRUE(state.PendingDrawsShouldBeAborted()); + EXPECT_TRUE(state.PendingActivationsShouldBeForced()); state.SetNeedsCommit(); - EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, - state.NextAction()); - - state.UpdateState(state.NextAction()); - EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_TRUE(state.PendingDrawsShouldBeAborted()); + EXPECT_TRUE(state.PendingActivationsShouldBeForced()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.FinishCommit(); - EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + EXPECT_TRUE(state.PendingDrawsShouldBeAborted()); EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction()); state.UpdateState(state.NextAction()); - EXPECT_FALSE(state.DrawSuspendedUntilCommit()); + EXPECT_FALSE(state.PendingDrawsShouldBeAborted()); } TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) { SchedulerSettings default_scheduler_settings; - SchedulerStateMachine state(default_scheduler_settings); + StateMachine state(default_scheduler_settings); state.SetCanStart(); state.UpdateState(state.NextAction()); state.DidCreateAndInitializeOutputSurface(); @@ -1369,7 +1595,7 @@ TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) { SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); - EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + EXPECT_TRUE(state.PendingDrawsShouldBeAborted()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); @@ -1377,14 +1603,75 @@ TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) { EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD, state.NextAction()); state.UpdateState(state.NextAction()); - EXPECT_TRUE(state.DrawSuspendedUntilCommit()); + EXPECT_TRUE(state.PendingDrawsShouldBeAborted()); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); state.BeginFrameAbortedByMainThread(true); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - EXPECT_FALSE(state.DrawSuspendedUntilCommit()); + EXPECT_FALSE(state.PendingDrawsShouldBeAborted()); +} + +TEST(SchedulerStateMachineTest, + TestTriggerDeadlineEarlyAfterAbortedCommit) { + SchedulerSettings settings; + settings.deadline_scheduling_enabled = true; + settings.impl_side_painting = true; + StateMachine state(settings); + state.SetCanStart(); + state.UpdateState(state.NextAction()); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); + state.SetVisible(true); + state.SetCanDraw(true); + + // This test mirrors what happens during the first frame of a scroll gesture. + // First we get the input event and a BeginFrame. + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + + // As a response the compositor requests a redraw and a commit to tell the + // main thread about the new scroll offset. + state.SetNeedsRedraw(true); + state.SetNeedsCommit(); + + // We should start the commit normally. + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // Since only the scroll offset changed, the main thread will abort the + // commit. + state.BeginFrameAbortedByMainThread(true); + + // Since the commit was aborted, we should draw right away instead of waiting + // for the deadline. + EXPECT_TRUE(state.ShouldTriggerBeginFrameDeadlineEarly()); +} + +TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyForSmoothness) { + SchedulerSettings settings; + settings.deadline_scheduling_enabled = true; + settings.impl_side_painting = true; + StateMachine state(settings); + state.SetCanStart(); + state.UpdateState(state.NextAction()); + state.CreateAndInitializeOutputSurfaceWithActivatedCommit(); + state.SetVisible(true); + state.SetCanDraw(true); + + // This test ensures that impl-draws are prioritized over main thread updates + // in prefer smoothness mode. + state.OnBeginFrame(BeginFrameArgs::CreateForTesting()); + state.SetNeedsRedraw(true); + state.SetNeedsCommit(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); + + // The deadline is not triggered early until we enter prefer smoothness mode. + EXPECT_FALSE(state.ShouldTriggerBeginFrameDeadlineEarly()); + state.SetSmoothnessTakesPriority(true); + EXPECT_TRUE(state.ShouldTriggerBeginFrameDeadlineEarly()); } } // namespace diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index 930111c1b2a..a1da822cff1 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -1,13 +1,13 @@ // Copyright 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - #include "cc/scheduler/scheduler.h" #include <string> #include <vector> #include "base/logging.h" +#include "base/memory/scoped_vector.h" #include "cc/test/scheduler_test_common.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -28,6 +28,18 @@ namespace cc { namespace { +void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler) { + scheduler->DidCreateAndInitializeOutputSurface(); + scheduler->SetNeedsCommit(); + scheduler->FinishCommit(); + // Go through the motions to draw the commit. + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); + // We need another BeginFrame so Scheduler calls SetNeedsBeginFrame(false). + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); +} + class FakeSchedulerClient : public SchedulerClient { public: FakeSchedulerClient() @@ -52,13 +64,17 @@ class FakeSchedulerClient : public SchedulerClient { int num_draws() const { return num_draws_; } int num_actions_() const { return static_cast<int>(actions_.size()); } const char* Action(int i) const { return actions_[i]; } - std::string StateForAction(int i) const { return states_[i]; } + base::Value& StateForAction(int i) const { return *states_[i]; } - bool HasAction(const char* action) const { + int ActionIndex(const char* action) const { for (size_t i = 0; i < actions_.size(); i++) if (!strcmp(actions_[i], action)) - return true; - return false; + return i; + return -1; + } + + bool HasAction(const char* action) const { + return ActionIndex(action) >= 0; } void SetDrawWillHappen(bool draw_will_happen) { @@ -71,48 +87,63 @@ class FakeSchedulerClient : public SchedulerClient { // Scheduler Implementation. virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE { actions_.push_back("SetNeedsBeginFrameOnImplThread"); - states_.push_back(scheduler_->StateAsStringForTesting()); + states_.push_back(scheduler_->StateAsValue().release()); needs_begin_frame_ = enable; } virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE { actions_.push_back("ScheduledActionSendBeginFrameToMainThread"); - states_.push_back(scheduler_->StateAsStringForTesting()); + states_.push_back(scheduler_->StateAsValue().release()); } - virtual ScheduledActionDrawAndSwapResult - ScheduledActionDrawAndSwapIfPossible() OVERRIDE { + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible() + OVERRIDE { actions_.push_back("ScheduledActionDrawAndSwapIfPossible"); - states_.push_back(scheduler_->StateAsStringForTesting()); + states_.push_back(scheduler_->StateAsValue().release()); num_draws_++; - return ScheduledActionDrawAndSwapResult(draw_will_happen_, - draw_will_happen_ && - swap_will_happen_if_draw_happens_); + bool did_readback = false; + return DrawSwapReadbackResult( + draw_will_happen_, + draw_will_happen_ && swap_will_happen_if_draw_happens_, + did_readback); } - virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced() - OVERRIDE { + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE { actions_.push_back("ScheduledActionDrawAndSwapForced"); - states_.push_back(scheduler_->StateAsStringForTesting()); - return ScheduledActionDrawAndSwapResult(true, - swap_will_happen_if_draw_happens_); + states_.push_back(scheduler_->StateAsValue().release()); + bool did_draw = true; + bool did_swap = swap_will_happen_if_draw_happens_; + bool did_readback = false; + return DrawSwapReadbackResult(did_draw, did_swap, did_readback); + } + virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() OVERRIDE { + actions_.push_back("ScheduledActionDrawAndReadback"); + states_.push_back(scheduler_->StateAsValue().release()); + bool did_draw = true; + bool did_swap = false; + bool did_readback = true; + return DrawSwapReadbackResult(did_draw, did_swap, did_readback); } virtual void ScheduledActionCommit() OVERRIDE { actions_.push_back("ScheduledActionCommit"); - states_.push_back(scheduler_->StateAsStringForTesting()); + states_.push_back(scheduler_->StateAsValue().release()); } virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE { actions_.push_back("ScheduledActionUpdateVisibleTiles"); - states_.push_back(scheduler_->StateAsStringForTesting()); + states_.push_back(scheduler_->StateAsValue().release()); } - virtual void ScheduledActionActivatePendingTreeIfNeeded() OVERRIDE { - actions_.push_back("ScheduledActionActivatePendingTreeIfNeeded"); - states_.push_back(scheduler_->StateAsStringForTesting()); + virtual void ScheduledActionActivatePendingTree() OVERRIDE { + actions_.push_back("ScheduledActionActivatePendingTree"); + states_.push_back(scheduler_->StateAsValue().release()); } virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE { actions_.push_back("ScheduledActionBeginOutputSurfaceCreation"); - states_.push_back(scheduler_->StateAsStringForTesting()); + states_.push_back(scheduler_->StateAsValue().release()); } virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE { actions_.push_back("ScheduledActionAcquireLayerTexturesForMainThread"); - states_.push_back(scheduler_->StateAsStringForTesting()); + states_.push_back(scheduler_->StateAsValue().release()); + } + virtual void ScheduledActionManageTiles() OVERRIDE { + actions_.push_back("ScheduledActionManageTiles"); + states_.push_back(scheduler_->StateAsValue().release()); } virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {} virtual base::TimeDelta DrawDurationEstimate() OVERRIDE { @@ -125,13 +156,21 @@ class FakeSchedulerClient : public SchedulerClient { return base::TimeDelta(); } + virtual void PostBeginFrameDeadline(const base::Closure& closure, + base::TimeTicks deadline) OVERRIDE { + actions_.push_back("PostBeginFrameDeadlineTask"); + states_.push_back(scheduler_->StateAsValue().release()); + } + + virtual void DidBeginFrameDeadlineOnImplThread() OVERRIDE {} + protected: bool needs_begin_frame_; bool draw_will_happen_; bool swap_will_happen_if_draw_happens_; int num_draws_; std::vector<const char*> actions_; - std::vector<std::string> states_; + ScopedVector<base::Value> states_; scoped_ptr<Scheduler> scheduler_; }; @@ -149,94 +188,205 @@ TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { EXPECT_EQ(0, client.num_actions_()); } -TEST(SchedulerTest, RequestCommit) { +void RequestCommit(bool deadline_scheduling_enabled) { FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + Scheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); - client.Reset(); - scheduler->DidCreateAndInitializeOutputSurface(); + InitializeOutputSurfaceAndFirstCommit(scheduler); - // SetNeedsCommit should begin the frame. + // SetNeedsCommit should begin the frame on the next BeginFrame. + client.Reset(); scheduler->SetNeedsCommit(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_TRUE(client.needs_begin_frame()); + if (deadline_scheduling_enabled) { + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + } else { + EXPECT_EQ(client.num_actions_(), 2); + EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread")); + EXPECT_TRUE(client.HasAction("SetNeedsBeginFrameOnImplThread")); + } + client.Reset(); + + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); + } else { + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + } + EXPECT_TRUE(client.needs_begin_frame()); + client.Reset(); + + // If we don't swap on the deadline, we need to request another BeginFrame. + scheduler->OnBeginFrameDeadline(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); // FinishCommit should commit scheduler->FinishCommit(); - EXPECT_ACTION("ScheduledActionCommit", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); - // BeginFrame should draw. + // BeginFrame should prepare the draw. scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + EXPECT_TRUE(client.needs_begin_frame()); + client.Reset(); + + // BeginFrame deadline should draw. + scheduler->OnBeginFrameDeadline(); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_TRUE(client.needs_begin_frame()); + client.Reset(); + + // The following BeginFrame deadline should SetNeedsBeginFrame(false) to avoid + // excessive toggles. + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + client.Reset(); + + scheduler->OnBeginFrameDeadline(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); EXPECT_FALSE(client.needs_begin_frame()); client.Reset(); } -TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { +TEST(SchedulerTest, RequestCommit) { + bool deadline_scheduling_enabled = false; + RequestCommit(deadline_scheduling_enabled); +} + +TEST(SchedulerTest, RequestCommit_Deadline) { + bool deadline_scheduling_enabled = true; + RequestCommit(deadline_scheduling_enabled); +} + +void RequestCommitAfterBeginFrameSentToMainThread( + bool deadline_scheduling_enabled) { FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + Scheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); + InitializeOutputSurfaceAndFirstCommit(scheduler); client.Reset(); - scheduler->DidCreateAndInitializeOutputSurface(); - // SetNedsCommit should begin the frame. + // SetNeedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + if (deadline_scheduling_enabled) { + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + } else { + EXPECT_EQ(client.num_actions_(), 2); + EXPECT_TRUE(client.HasAction("SetNeedsBeginFrameOnImplThread")); + EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread")); + } + + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_EQ(client.num_actions_(), 2); + EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread")); + EXPECT_TRUE(client.HasAction("PostBeginFrameDeadlineTask")); + } else { + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + } + + EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); - // Now SetNeedsCommit again. Calling here means we need a second frame. + // Now SetNeedsCommit again. Calling here means we need a second commit. scheduler->SetNeedsCommit(); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 1); + EXPECT_EQ(client.num_actions_(), 0); client.Reset(); - // Since another commit is needed, FinishCommit should commit, - // then begin another frame. + // Finish the first commit. scheduler->FinishCommit(); EXPECT_ACTION("ScheduledActionCommit", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); client.Reset(); + scheduler->OnBeginFrameDeadline(); + if (deadline_scheduling_enabled) { + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + } else { + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 3); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3); + } - // Tick should draw but then begin another frame. - scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + // Because we just swapped, the Scheduler should also request the next + // BeginFrame from the OutputSurface. EXPECT_TRUE(client.needs_begin_frame()); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); client.Reset(); - // Go back to quiescent state and verify we no longer request BeginFrames. + // Since another commit is needed, the next BeginFrame should initiate + // the second commit. + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_EQ(client.num_actions_(), 2); + EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread")); + EXPECT_TRUE(client.HasAction("PostBeginFrameDeadlineTask")); + } else { + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + } + client.Reset(); + + // Finishing the commit before the deadline should post a new deadline task + // to trigger the deadline early. scheduler->FinishCommit(); + EXPECT_ACTION("ScheduledActionCommit", client, 0, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); + client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_TRUE(client.needs_begin_frame()); + client.Reset(); + + // On the next BeginFrame, verify we go back to a quiescent state and + // no longer request BeginFrames. scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_FALSE(client.needs_begin_frame()); + client.Reset(); } -TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) { +TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { + bool deadline_scheduling_enabled = false; + RequestCommitAfterBeginFrameSentToMainThread(deadline_scheduling_enabled); +} + +TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread_Deadline) { + bool deadline_scheduling_enabled = true; + RequestCommitAfterBeginFrameSentToMainThread(deadline_scheduling_enabled); +} + +void TextureAcquisitionCausesCommitInsteadOfDraw( + bool deadline_scheduling_enabled) { FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + Scheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); + InitializeOutputSurfaceAndFirstCommit(scheduler); client.Reset(); - scheduler->DidCreateAndInitializeOutputSurface(); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); @@ -244,9 +394,21 @@ TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) { client.Reset(); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + client.Reset(); + scheduler->OnBeginFrameDeadline(); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); EXPECT_FALSE(scheduler->RedrawPending()); + EXPECT_TRUE(client.needs_begin_frame()); + + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + EXPECT_FALSE(scheduler->RedrawPending()); EXPECT_FALSE(client.needs_begin_frame()); client.Reset(); @@ -264,54 +426,103 @@ TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) { // No draw happens since the textures are acquired by the main thread. client.Reset(); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + client.Reset(); + scheduler->OnBeginFrameDeadline(); EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); EXPECT_TRUE(scheduler->RedrawPending()); EXPECT_TRUE(client.needs_begin_frame()); + client.Reset(); scheduler->SetNeedsCommit(); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 2); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); - EXPECT_TRUE(client.needs_begin_frame()); + if (deadline_scheduling_enabled) { + EXPECT_EQ(0, client.num_actions_()); + } else { + EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); + } + + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); + } else { + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + } // Commit will release the texture. client.Reset(); scheduler->FinishCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); + EXPECT_ACTION("ScheduledActionCommit", client, 0, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); // Now we can draw again after the commit happens. client.Reset(); - scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(client.needs_begin_frame()); + EXPECT_TRUE(client.needs_begin_frame()); + + // Make sure we stop requesting BeginFrames if we don't swap. + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + EXPECT_FALSE(client.needs_begin_frame()); } -TEST(SchedulerTest, TextureAcquisitionCollision) { +TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) { + bool deadline_scheduling_enabled = false; + TextureAcquisitionCausesCommitInsteadOfDraw(deadline_scheduling_enabled); +} + +TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw_Deadline) { + bool deadline_scheduling_enabled = true; + TextureAcquisitionCausesCommitInsteadOfDraw(deadline_scheduling_enabled); +} + +void TextureAcquisitionCollision(bool deadline_scheduling_enabled) { FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + Scheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); - client.Reset(); - scheduler->DidCreateAndInitializeOutputSurface(); + InitializeOutputSurfaceAndFirstCommit(scheduler); + client.Reset(); scheduler->SetNeedsCommit(); +if (deadline_scheduling_enabled) { + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + } else { + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + } + + client.Reset(); scheduler->SetMainThreadNeedsLayerTextures(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 4); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 4); - EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", - client, - 2, - 4); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 3, 4); + EXPECT_SINGLE_ACTION( + "ScheduledActionAcquireLayerTexturesForMainThread", client); + + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); + } else { + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + } + client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); // Although the compositor cannot draw because textures are locked by main // thread, we continue requesting SetNeedsBeginFrame in anticipation of the @@ -321,43 +532,88 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { // Trigger the commit scheduler->FinishCommit(); EXPECT_TRUE(client.needs_begin_frame()); - client.Reset(); // Between commit and draw, texture acquisition for main thread delayed, // and main thread blocks. + client.Reset(); scheduler->SetMainThreadNeedsLayerTextures(); EXPECT_EQ(0, client.num_actions_()); - client.Reset(); // No implicit commit is expected. + client.Reset(); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + + client.Reset(); + scheduler->OnBeginFrameDeadline(); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3); - EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", - client, - 1, - 3); + EXPECT_ACTION( + "ScheduledActionAcquireLayerTexturesForMainThread", client, 1, 3); EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3); - client.Reset(); + EXPECT_TRUE(client.needs_begin_frame()); - // Compositor not scheduled to draw because textures are locked by main + // The compositor should not draw because textures are locked by main // thread. + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); EXPECT_FALSE(client.needs_begin_frame()); - // Needs an explicit commit from the main thread. + // The impl thread need an explicit commit from the main thread to lock + // the textures. + client.Reset(); scheduler->SetNeedsCommit(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + if (deadline_scheduling_enabled) { + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + } else { + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + } + EXPECT_TRUE(client.needs_begin_frame()); + + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + if (deadline_scheduling_enabled) { + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); + } else { + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + } client.Reset(); - // Trigger the commit + // Trigger the commit, which will trigger the deadline task early. scheduler->FinishCommit(); + EXPECT_ACTION("ScheduledActionCommit", client, 0, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); EXPECT_TRUE(client.needs_begin_frame()); + client.Reset(); + + // Verify we draw on the next BeginFrame deadline + scheduler->OnBeginFrameDeadline(); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_TRUE(client.needs_begin_frame()); + client.Reset(); } -TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { +TEST(SchedulerTest, TextureAcquisitionCollision) { + bool deadline_scheduling_enabled = false; + TextureAcquisitionCollision(deadline_scheduling_enabled); +} + +TEST(SchedulerTest, TextureAcquisitionCollision_Deadline) { + bool deadline_scheduling_enabled = true; + TextureAcquisitionCollision(deadline_scheduling_enabled); +} + +void VisibilitySwitchWithTextureAcquisition(bool deadline_scheduling_enabled) { FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled; + Scheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -367,6 +623,10 @@ TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { scheduler->DidCreateAndInitializeOutputSurface(); scheduler->SetNeedsCommit(); + if (deadline_scheduling_enabled) { + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); + } scheduler->FinishCommit(); scheduler->SetMainThreadNeedsLayerTextures(); scheduler->SetNeedsCommit(); @@ -376,36 +636,48 @@ TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { scheduler->SetVisible(false); EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client); - client.Reset(); - // Already sent a begin frame on this current frame, so wait. + client.Reset(); scheduler->SetVisible(true); EXPECT_EQ(0, client.num_actions_()); - client.Reset(); + EXPECT_TRUE(client.needs_begin_frame()); // Regaining visibility with textures acquired by main thread while // compositor is waiting for first draw should result in a request // for a new frame in order to escape a deadlock. + client.Reset(); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2); +} + +TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { + bool deadline_scheduling_enabled = false; + VisibilitySwitchWithTextureAcquisition(deadline_scheduling_enabled); +} + +TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition_Deadline) { + bool deadline_scheduling_enabled = true; + VisibilitySwitchWithTextureAcquisition(deadline_scheduling_enabled); } class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient { public: virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {} - virtual ScheduledActionDrawAndSwapResult - ScheduledActionDrawAndSwapIfPossible() OVERRIDE { + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible() + OVERRIDE { // Only SetNeedsRedraw the first time this is called if (!num_draws_) scheduler_->SetNeedsRedraw(); return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible(); } - virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced() - OVERRIDE { + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE { NOTREACHED(); - return ScheduledActionDrawAndSwapResult(true, true); + bool did_draw = true; + bool did_swap = true; + bool did_readback = false; + return DrawSwapReadbackResult(did_draw, did_swap, did_readback); } virtual void ScheduledActionCommit() OVERRIDE {} @@ -424,7 +696,8 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) { scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - scheduler->DidCreateAndInitializeOutputSurface(); + InitializeOutputSurfaceAndFirstCommit(scheduler); + client.Reset(); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); @@ -432,11 +705,20 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) { EXPECT_EQ(0, client.num_draws()); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->RedrawPending()); EXPECT_TRUE(client.needs_begin_frame()); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); + EXPECT_EQ(2, client.num_draws()); + EXPECT_FALSE(scheduler->RedrawPending()); + EXPECT_TRUE(client.needs_begin_frame()); + + // We stop requesting BeginFrames after a BeginFrame where we don't swap. + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(2, client.num_draws()); EXPECT_FALSE(scheduler->RedrawPending()); EXPECT_FALSE(client.needs_begin_frame()); @@ -450,7 +732,8 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - scheduler->DidCreateAndInitializeOutputSurface(); + InitializeOutputSurfaceAndFirstCommit(scheduler); + client.Reset(); client.SetDrawWillHappen(false); @@ -461,6 +744,7 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { // Fail the draw. scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(1, client.num_draws()); // We have a commit pending and the draw failed, and we didn't lose the redraw @@ -471,6 +755,7 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { // Fail the draw again. scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); @@ -479,58 +764,86 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { // Draw successfully. client.SetDrawWillHappen(true); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); EXPECT_TRUE(client.needs_begin_frame()); } -class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient { +class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient { public: + SchedulerClientThatSetNeedsCommitInsideDraw() + : set_needs_commit_on_next_draw_(false) {} + virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {} - virtual ScheduledActionDrawAndSwapResult - ScheduledActionDrawAndSwapIfPossible() OVERRIDE { + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible() + OVERRIDE { // Only SetNeedsCommit the first time this is called - if (!num_draws_) + if (set_needs_commit_on_next_draw_) { scheduler_->SetNeedsCommit(); + set_needs_commit_on_next_draw_ = false; + } return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible(); } - virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced() - OVERRIDE { + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE { NOTREACHED(); - return ScheduledActionDrawAndSwapResult(true, true); + bool did_draw = true; + bool did_swap = false; + bool did_readback = false; + return DrawSwapReadbackResult(did_draw, did_swap, did_readback); } virtual void ScheduledActionCommit() OVERRIDE {} virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {} virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {} + + void SetNeedsCommitOnNextDraw() { set_needs_commit_on_next_draw_ = true; } + + private: + bool set_needs_commit_on_next_draw_; }; // Tests for the scheduler infinite-looping on SetNeedsCommit requests that // happen inside a ScheduledActionDrawAndSwap TEST(SchedulerTest, RequestCommitInsideDraw) { - SchedulerClientThatsetNeedsCommitInsideDraw client; + SchedulerClientThatSetNeedsCommitInsideDraw client; SchedulerSettings default_scheduler_settings; Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - scheduler->DidCreateAndInitializeOutputSurface(); + InitializeOutputSurfaceAndFirstCommit(scheduler); + client.Reset(); + EXPECT_FALSE(client.needs_begin_frame()); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); EXPECT_EQ(0, client.num_draws()); EXPECT_TRUE(client.needs_begin_frame()); + client.SetNeedsCommitOnNextDraw(); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + client.SetNeedsCommitOnNextDraw(); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(client.needs_begin_frame()); scheduler->FinishCommit(); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); - EXPECT_EQ(2, client.num_draws());; + scheduler->OnBeginFrameDeadline(); + EXPECT_EQ(2, client.num_draws()); + + EXPECT_FALSE(scheduler->RedrawPending()); + EXPECT_FALSE(scheduler->CommitPending()); + EXPECT_TRUE(client.needs_begin_frame()); + + // We stop requesting BeginFrames after a BeginFrame where we don't swap. + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); + EXPECT_EQ(2, client.num_draws()); EXPECT_FALSE(scheduler->RedrawPending()); EXPECT_FALSE(scheduler->CommitPending()); EXPECT_FALSE(client.needs_begin_frame()); @@ -544,7 +857,8 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - scheduler->DidCreateAndInitializeOutputSurface(); + InitializeOutputSurfaceAndFirstCommit(scheduler); + client.Reset(); client.SetDrawWillHappen(false); @@ -555,6 +869,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { // Fail the draw. scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(1, client.num_draws()); // We have a commit pending and the draw failed, and we didn't lose the commit @@ -565,6 +880,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { // Fail the draw again. scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); @@ -573,6 +889,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { // Draw successfully. client.SetDrawWillHappen(true); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); @@ -580,13 +897,14 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { } TEST(SchedulerTest, NoSwapWhenDrawFails) { - SchedulerClientThatsetNeedsCommitInsideDraw client; + SchedulerClientThatSetNeedsCommitInsideDraw client; SchedulerSettings default_scheduler_settings; Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - scheduler->DidCreateAndInitializeOutputSurface(); + InitializeOutputSurfaceAndFirstCommit(scheduler); + client.Reset(); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); @@ -594,7 +912,9 @@ TEST(SchedulerTest, NoSwapWhenDrawFails) { EXPECT_EQ(0, client.num_draws()); // Draw successfully, this starts a new frame. + client.SetNeedsCommitOnNextDraw(); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(1, client.num_draws()); scheduler->SetNeedsRedraw(); @@ -603,7 +923,9 @@ TEST(SchedulerTest, NoSwapWhenDrawFails) { // Fail to draw, this should not start a frame. client.SetDrawWillHappen(false); + client.SetNeedsCommitOnNextDraw(); scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + scheduler->OnBeginFrameDeadline(); EXPECT_EQ(2, client.num_draws()); } @@ -616,11 +938,142 @@ TEST(SchedulerTest, NoSwapWhenSwapFailsDuringForcedCommit) { client.SetDrawWillHappen(true); client.SetSwapWillHappenIfDrawHappens(false); - // Get the compositor to do a ScheduledActionDrawAndSwapForced. + // Get the compositor to do a ScheduledActionDrawAndReadback. scheduler->SetCanDraw(true); scheduler->SetNeedsRedraw(); - scheduler->SetNeedsForcedRedraw(); - EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapForced")); + scheduler->SetNeedsForcedCommitForReadback(); + scheduler->FinishCommit(); + EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback")); +} + +TEST(SchedulerTest, BackToBackReadbackAllowed) { + // Some clients call readbacks twice in a row before the replacement + // commit comes in. Make sure it is allowed. + FakeSchedulerClient client; + SchedulerSettings default_scheduler_settings; + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + + // Get the compositor to do 2 ScheduledActionDrawAndReadbacks before + // the replacement commit comes in. + scheduler->SetCanDraw(true); + scheduler->SetNeedsRedraw(); + scheduler->SetNeedsForcedCommitForReadback(); + scheduler->FinishCommit(); + EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback")); + + client.Reset(); + scheduler->SetNeedsForcedCommitForReadback(); + scheduler->FinishCommit(); + EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback")); + + // The replacement commit comes in after 2 readbacks. + client.Reset(); + scheduler->FinishCommit(); +} + + +class SchedulerClientNeedsManageTilesInDraw : public FakeSchedulerClient { + public: + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible() + OVERRIDE { + scheduler_->SetNeedsManageTiles(); + return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible(); + } +}; + +// Test manage tiles is independant of draws. +TEST(SchedulerTest, ManageTiles) { + SchedulerClientNeedsManageTilesInDraw client; + SchedulerSettings default_scheduler_settings; + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + scheduler->SetCanStart(); + scheduler->SetVisible(true); + scheduler->SetCanDraw(true); + InitializeOutputSurfaceAndFirstCommit(scheduler); + + // Request both draw and manage tiles. ManageTiles shouldn't + // be trigged until BeginFrame. + client.Reset(); + scheduler->SetNeedsManageTiles(); + scheduler->SetNeedsRedraw(); + EXPECT_TRUE(scheduler->RedrawPending()); + EXPECT_TRUE(scheduler->ManageTilesPending()); + EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_EQ(0, client.num_draws()); + EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles")); + EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible")); + + // We have no immediate actions to perform, so the BeginFrame should post + // the deadline task. + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + + // On the deadline, he actions should have occured in the right order. + client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_EQ(1, client.num_draws()); + EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles")); + EXPECT_LT(client.ActionIndex("ScheduledActionDrawAndSwapIfPossible"), + client.ActionIndex("ScheduledActionManageTiles")); + EXPECT_FALSE(scheduler->RedrawPending()); + EXPECT_FALSE(scheduler->ManageTilesPending()); + + // Request a draw. We don't need a ManageTiles yet. + client.Reset(); + scheduler->SetNeedsRedraw(); + EXPECT_TRUE(scheduler->RedrawPending()); + EXPECT_FALSE(scheduler->ManageTilesPending()); + EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_EQ(0, client.num_draws()); + + // We have no immediate actions to perform, so the BeginFrame should post + // the deadline task. + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + + // Draw. The draw will trigger SetNeedsManageTiles, and + // then the ManageTiles action will be triggered after the Draw. + // Afterwards, neither a draw nor ManageTiles are pending. + client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_EQ(1, client.num_draws()); + EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles")); + EXPECT_LT(client.ActionIndex("ScheduledActionDrawAndSwapIfPossible"), + client.ActionIndex("ScheduledActionManageTiles")); + EXPECT_FALSE(scheduler->RedrawPending()); + EXPECT_FALSE(scheduler->ManageTilesPending()); + + // We need a BeginFrame where we don't swap to go idle. + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);; + EXPECT_EQ(0, client.num_draws()); + + // Now trigger a ManageTiles outside of a draw. We will then need + // a begin-frame for the ManageTiles, but we don't need a draw. + client.Reset(); + EXPECT_FALSE(client.needs_begin_frame()); + scheduler->SetNeedsManageTiles(); + EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(scheduler->ManageTilesPending()); + EXPECT_FALSE(scheduler->RedrawPending()); + + // BeginFrame. There will be no draw, only ManageTiles. + client.Reset(); + scheduler->BeginFrame(BeginFrameArgs::CreateForTesting()); + EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client); + client.Reset(); + scheduler->OnBeginFrameDeadline(); + EXPECT_EQ(0, client.num_draws()); + EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles")); } } // namespace diff --git a/chromium/cc/scheduler/texture_uploader.cc b/chromium/cc/scheduler/texture_uploader.cc index 920c38e6692..677029081fc 100644 --- a/chromium/cc/scheduler/texture_uploader.cc +++ b/chromium/cc/scheduler/texture_uploader.cc @@ -7,7 +7,6 @@ #include <algorithm> #include <vector> -#include "base/debug/alias.h" #include "base/debug/trace_event.h" #include "base/metrics/histogram.h" #include "cc/base/util.h" @@ -135,7 +134,7 @@ void TextureUploader::Upload(const uint8* image, gfx::Rect image_rect, gfx::Rect source_rect, gfx::Vector2d dest_offset, - GLenum format, + ResourceFormat format, gfx::Size size) { CHECK(image_rect.Contains(source_rect)); @@ -178,39 +177,23 @@ void TextureUploader::UploadWithTexSubImage(const uint8* image, gfx::Rect image_rect, gfx::Rect source_rect, gfx::Vector2d dest_offset, - GLenum format) { - // Instrumentation to debug issue 156107 - int source_rect_x = source_rect.x(); - int source_rect_y = source_rect.y(); - int source_rect_width = source_rect.width(); - int source_rect_height = source_rect.height(); - int image_rect_x = image_rect.x(); - int image_rect_y = image_rect.y(); - int image_rect_width = image_rect.width(); - int image_rect_height = image_rect.height(); - int dest_offset_x = dest_offset.x(); - int dest_offset_y = dest_offset.y(); - base::debug::Alias(&image); - base::debug::Alias(&source_rect_x); - base::debug::Alias(&source_rect_y); - base::debug::Alias(&source_rect_width); - base::debug::Alias(&source_rect_height); - base::debug::Alias(&image_rect_x); - base::debug::Alias(&image_rect_y); - base::debug::Alias(&image_rect_width); - base::debug::Alias(&image_rect_height); - base::debug::Alias(&dest_offset_x); - base::debug::Alias(&dest_offset_y); + ResourceFormat format) { TRACE_EVENT0("cc", "TextureUploader::UploadWithTexSubImage"); + // Early-out if this is a no-op, and assert that |image| be valid if this is + // not a no-op. + if (source_rect.IsEmpty()) + return; + DCHECK(image); + // Offset from image-rect to source-rect. gfx::Vector2d offset(source_rect.origin() - image_rect.origin()); const uint8* pixel_source; - unsigned int bytes_per_pixel = Resource::BytesPerPixel(format); + unsigned bytes_per_pixel = ResourceProvider::BytesPerPixel(format); // Use 4-byte row alignment (OpenGL default) for upload performance. // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. - unsigned int upload_image_stride = + unsigned upload_image_stride = RoundUp(bytes_per_pixel * source_rect.width(), 4u); if (upload_image_stride == image_rect.width() * bytes_per_pixel && @@ -239,8 +222,8 @@ void TextureUploader::UploadWithTexSubImage(const uint8* image, dest_offset.y(), source_rect.width(), source_rect.height(), - format, - GL_UNSIGNED_BYTE, + ResourceProvider::GetGLDataFormat(format), + ResourceProvider::GetGLDataType(format), pixel_source); } @@ -248,39 +231,22 @@ void TextureUploader::UploadWithMapTexSubImage(const uint8* image, gfx::Rect image_rect, gfx::Rect source_rect, gfx::Vector2d dest_offset, - GLenum format) { - // Instrumentation to debug issue 156107 - int source_rect_x = source_rect.x(); - int source_rect_y = source_rect.y(); - int source_rect_width = source_rect.width(); - int source_rect_height = source_rect.height(); - int image_rect_x = image_rect.x(); - int image_rect_y = image_rect.y(); - int image_rect_width = image_rect.width(); - int image_rect_height = image_rect.height(); - int dest_offset_x = dest_offset.x(); - int dest_offset_y = dest_offset.y(); - base::debug::Alias(&image); - base::debug::Alias(&source_rect_x); - base::debug::Alias(&source_rect_y); - base::debug::Alias(&source_rect_width); - base::debug::Alias(&source_rect_height); - base::debug::Alias(&image_rect_x); - base::debug::Alias(&image_rect_y); - base::debug::Alias(&image_rect_width); - base::debug::Alias(&image_rect_height); - base::debug::Alias(&dest_offset_x); - base::debug::Alias(&dest_offset_y); - + ResourceFormat format) { TRACE_EVENT0("cc", "TextureUploader::UploadWithMapTexSubImage"); + // Early-out if this is a no-op, and assert that |image| be valid if this is + // not a no-op. + if (source_rect.IsEmpty()) + return; + DCHECK(image); + // Offset from image-rect to source-rect. gfx::Vector2d offset(source_rect.origin() - image_rect.origin()); - unsigned int bytes_per_pixel = Resource::BytesPerPixel(format); + unsigned bytes_per_pixel = ResourceProvider::BytesPerPixel(format); // Use 4-byte row alignment (OpenGL default) for upload performance. // Assuming that GL_UNPACK_ALIGNMENT has not changed from default. - unsigned int upload_image_stride = + unsigned upload_image_stride = RoundUp(bytes_per_pixel * source_rect.width(), 4u); // Upload tile data via a mapped transfer buffer @@ -291,8 +257,10 @@ void TextureUploader::UploadWithMapTexSubImage(const uint8* image, dest_offset.y(), source_rect.width(), source_rect.height(), - format, - GL_UNSIGNED_BYTE, + ResourceProvider::GetGLDataFormat( + format), + ResourceProvider::GetGLDataType( + format), GL_WRITE_ONLY)); if (!pixel_dest) { diff --git a/chromium/cc/scheduler/texture_uploader.h b/chromium/cc/scheduler/texture_uploader.h index 1457bedb727..a131ba04f9e 100644 --- a/chromium/cc/scheduler/texture_uploader.h +++ b/chromium/cc/scheduler/texture_uploader.h @@ -11,7 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/base/scoped_ptr_deque.h" -#include "third_party/khronos/GLES2/gl2.h" +#include "cc/resources/resource_provider.h" namespace WebKit { class WebGraphicsContext3D; } @@ -46,7 +46,7 @@ class CC_EXPORT TextureUploader { gfx::Rect content_rect, gfx::Rect source_rect, gfx::Vector2d dest_offset, - GLenum format, + ResourceFormat format, gfx::Size size); void Flush(); @@ -97,12 +97,12 @@ class CC_EXPORT TextureUploader { gfx::Rect image_rect, gfx::Rect source_rect, gfx::Vector2d dest_offset, - GLenum format); + ResourceFormat format); void UploadWithMapTexSubImage(const uint8* image, gfx::Rect image_rect, gfx::Rect source_rect, gfx::Vector2d dest_offset, - GLenum format); + ResourceFormat format); WebKit::WebGraphicsContext3D* context_; ScopedPtrDeque<Query> pending_queries_; diff --git a/chromium/cc/scheduler/texture_uploader_unittest.cc b/chromium/cc/scheduler/texture_uploader_unittest.cc index f4b4ce69b2e..68413df92e1 100644 --- a/chromium/cc/scheduler/texture_uploader_unittest.cc +++ b/chromium/cc/scheduler/texture_uploader_unittest.cc @@ -5,8 +5,8 @@ #include "cc/scheduler/texture_uploader.h" #include "cc/base/util.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/resources/prioritized_resource.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/khronos/GLES2/gl2.h" @@ -151,7 +151,7 @@ class TestWebGraphicsContext3DTextureUpload : public TestWebGraphicsContext3D { }; void UploadTexture(TextureUploader* uploader, - WGC3Denum format, + ResourceFormat format, gfx::Size size, const uint8* data) { uploader->Upload(data, @@ -170,17 +170,17 @@ TEST(TextureUploaderTest, NumBlockingUploads) { fake_context->SetResultAvailable(0); EXPECT_EQ(0u, uploader->NumBlockingUploads()); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); EXPECT_EQ(1u, uploader->NumBlockingUploads()); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); EXPECT_EQ(2u, uploader->NumBlockingUploads()); fake_context->SetResultAvailable(1); EXPECT_EQ(0u, uploader->NumBlockingUploads()); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); EXPECT_EQ(0u, uploader->NumBlockingUploads()); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); EXPECT_EQ(0u, uploader->NumBlockingUploads()); } @@ -192,18 +192,18 @@ TEST(TextureUploaderTest, MarkPendingUploadsAsNonBlocking) { fake_context->SetResultAvailable(0); EXPECT_EQ(0u, uploader->NumBlockingUploads()); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); EXPECT_EQ(2u, uploader->NumBlockingUploads()); uploader->MarkPendingUploadsAsNonBlocking(); EXPECT_EQ(0u, uploader->NumBlockingUploads()); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); EXPECT_EQ(1u, uploader->NumBlockingUploads()); fake_context->SetResultAvailable(1); EXPECT_EQ(0u, uploader->NumBlockingUploads()); - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL); uploader->MarkPendingUploadsAsNonBlocking(); EXPECT_EQ(0u, uploader->NumBlockingUploads()); } @@ -222,7 +222,7 @@ TEST(TextureUploaderTest, UploadContentsTest) { buffer[i * 4 * 256] = 0x1; buffer[(i + 1) * 4 * 256 - 1] = 0x2; } - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(256, 256), buffer); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(256, 256), buffer); // Upload a tightly packed 41x43 RGBA texture. memset(buffer, 0, sizeof(buffer)); @@ -231,7 +231,7 @@ TEST(TextureUploaderTest, UploadContentsTest) { buffer[i * 4 * 41] = 0x1; buffer[(i + 1) * 4 * 41 - 1] = 0x2; } - UploadTexture(uploader.get(), GL_RGBA, gfx::Size(41, 43), buffer); + UploadTexture(uploader.get(), RGBA_8888, gfx::Size(41, 43), buffer); // Upload a tightly packed 82x86 LUMINANCE texture. memset(buffer, 0, sizeof(buffer)); @@ -240,7 +240,7 @@ TEST(TextureUploaderTest, UploadContentsTest) { buffer[i * 1 * 82] = 0x1; buffer[(i + 1) * 82 - 1] = 0x2; } - UploadTexture(uploader.get(), GL_LUMINANCE, gfx::Size(82, 86), buffer); + UploadTexture(uploader.get(), LUMINANCE_8, gfx::Size(82, 86), buffer); } } // namespace diff --git a/chromium/cc/scheduler/time_source.h b/chromium/cc/scheduler/time_source.h index c02d4901a97..e45ffbb95aa 100644 --- a/chromium/cc/scheduler/time_source.h +++ b/chromium/cc/scheduler/time_source.h @@ -27,7 +27,12 @@ class TimeSourceClient { class CC_EXPORT TimeSource : public base::RefCounted<TimeSource> { public: virtual void SetClient(TimeSourceClient* client) = 0; - virtual void SetActive(bool active) = 0; + + // If transitioning from not active to active, SetActive will return the + // timestamp of the most recenly missed tick that did not have OnTimerTick + // called. + virtual base::TimeTicks SetActive(bool active) = 0; + virtual bool Active() const = 0; virtual void SetTimebaseAndInterval(base::TimeTicks timebase, base::TimeDelta interval) = 0; diff --git a/chromium/cc/trees/blocking_task_runner.cc b/chromium/cc/trees/blocking_task_runner.cc new file mode 100644 index 00000000000..576aa9b1bf9 --- /dev/null +++ b/chromium/cc/trees/blocking_task_runner.cc @@ -0,0 +1,105 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/blocking_task_runner.h" + +#include <utility> + +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/message_loop/message_loop_proxy.h" + +namespace cc { + +typedef std::pair<base::SingleThreadTaskRunner*, + scoped_refptr<BlockingTaskRunner> > TaskRunnerPair; + +struct TaskRunnerPairs { + static TaskRunnerPairs* GetInstance() { + return Singleton<TaskRunnerPairs>::get(); + } + + base::Lock lock; + std::vector<TaskRunnerPair> pairs; + + private: + friend struct DefaultSingletonTraits<TaskRunnerPairs>; +}; + +// static +scoped_refptr<BlockingTaskRunner> BlockingTaskRunner::current() { + TaskRunnerPairs* task_runners = TaskRunnerPairs::GetInstance(); + + base::AutoLock lock(task_runners->lock); + + for (size_t i = 0; i < task_runners->pairs.size(); ++i) { + if (task_runners->pairs[i].first->HasOneRef()) { + // The SingleThreadTaskRunner is kept alive by its MessageLoop, and we + // hold a second reference in the TaskRunnerPairs array. If the + // SingleThreadTaskRunner has one ref, then it is being held alive only + // by the BlockingTaskRunner and the MessageLoop is gone, so drop the + // BlockingTaskRunner from the TaskRunnerPairs array along with the + // SingleThreadTaskRunner. + task_runners->pairs.erase(task_runners->pairs.begin() + i); + --i; + } + } + + scoped_refptr<base::SingleThreadTaskRunner> current = + base::MessageLoopProxy::current(); + for (size_t i = 0; i < task_runners->pairs.size(); ++i) { + if (task_runners->pairs[i].first == current.get()) + return task_runners->pairs[i].second.get(); + } + + scoped_refptr<BlockingTaskRunner> runner = new BlockingTaskRunner(current); + task_runners->pairs.push_back(TaskRunnerPair(current, runner)); + return runner; +} + +BlockingTaskRunner::BlockingTaskRunner( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : task_runner_(task_runner), capture_(0) {} + +BlockingTaskRunner::~BlockingTaskRunner() {} + +bool BlockingTaskRunner::PostTask(const tracked_objects::Location& from_here, + const base::Closure& task) { + base::AutoLock lock(lock_); + if (!capture_) + return task_runner_->PostTask(from_here, task); + captured_tasks_.push_back(task); + return true; +} + +void BlockingTaskRunner::SetCapture(bool capture) { + DCHECK(BelongsToCurrentThread()); + + std::vector<base::Closure> tasks; + + { + base::AutoLock lock(lock_); + capture_ += capture ? 1 : -1; + DCHECK_GE(capture_, 0); + + if (capture_) + return; + + // We're done capturing, so grab all the captured tasks and run them. + tasks.swap(captured_tasks_); + } + for (size_t i = 0; i < tasks.size(); ++i) + tasks[i].Run(); +} + +BlockingTaskRunner::CapturePostTasks::CapturePostTasks() + : blocking_runner_(BlockingTaskRunner::current()) { + blocking_runner_->SetCapture(true); +} + +BlockingTaskRunner::CapturePostTasks::~CapturePostTasks() { + blocking_runner_->SetCapture(false); +} + +} // namespace cc diff --git a/chromium/cc/trees/blocking_task_runner.h b/chromium/cc/trees/blocking_task_runner.h new file mode 100644 index 00000000000..28633171c20 --- /dev/null +++ b/chromium/cc/trees/blocking_task_runner.h @@ -0,0 +1,94 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_TREES_BLOCKING_TASK_RUNNER_H_ +#define CC_TREES_BLOCKING_TASK_RUNNER_H_ + +#include <vector> + +#include "base/location.h" +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/lock.h" +#include "cc/base/cc_export.h" + +namespace cc { + +// This class wraps a SingleThreadTaskRunner but allows posted tasks to be +// run without a round trip through the message loop. This shortcutting +// removes guarantees about ordering. Tasks posted while the +// BlockingTaskRunner is in a capturing state will run in order, and tasks +// posted while the BlockingTaskRunner is /not/ in a capturing state will +// run in order, but the two sets of tasks will *not* run in order relative +// to when they were posted. +// +// To use this class, post tasks to the task runner returned by +// BlockingTaskRunner::current() on the thread you want the tasks to run. +// Hold a reference to the BlockingTaskRunner as long as you intend to +// post tasks to it. +// +// Then, on the thread from which the BlockingTaskRunner was created, you +// may instantiate a BlockingTaskRunner::CapturePostTasks. While this object +// exists, the task runner will collect any PostTasks called on it, posting +// tasks to that thread from anywhere. This CapturePostTasks object provides +// a window in time where tasks can shortcut past the MessageLoop. As soon +// as the CapturePostTasks object is destroyed (goes out of scope), all +// tasks that had been posted to the thread during the window will be exectuted +// immediately. +// +// Beware of re-entrancy, make sure the CapturePostTasks object is destroyed at +// a time when it makes sense for the embedder to call arbitrary things. +class CC_EXPORT BlockingTaskRunner + : public base::RefCountedThreadSafe<BlockingTaskRunner> { + public: + // Returns the BlockingTaskRunner for the current thread, creating one if + // necessary. + static scoped_refptr<BlockingTaskRunner> current(); + + // While an object of this type is held alive on a thread, any tasks + // posted to the thread will be captured and run as soon as the object + // is destroyed, shortcutting past the MessageLoop. + class CC_EXPORT CapturePostTasks { + public: + CapturePostTasks(); + ~CapturePostTasks(); + + private: + scoped_refptr<BlockingTaskRunner> blocking_runner_; + + DISALLOW_COPY_AND_ASSIGN(CapturePostTasks); + }; + + // True if tasks posted to the BlockingTaskRunner will run on the current + // thread. + bool BelongsToCurrentThread() { + return task_runner_->BelongsToCurrentThread(); + } + + // Posts a task using the contained SingleThreadTaskRunner unless |capture_| + // is true. When |capture_| is true, tasks posted will be caught and stored + // until the capturing stops. At that time the tasks will be run directly + // instead of being posted to the SingleThreadTaskRunner. + bool PostTask(const tracked_objects::Location& from_here, + const base::Closure& task); + + private: + friend class base::RefCountedThreadSafe<BlockingTaskRunner>; + + explicit BlockingTaskRunner( + scoped_refptr<base::SingleThreadTaskRunner> task_runner); + virtual ~BlockingTaskRunner(); + + void SetCapture(bool capture); + + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + + base::Lock lock_; + int capture_; + std::vector<base::Closure> captured_tasks_; +}; + +} // namespace cc + +#endif // CC_TREES_BLOCKING_TASK_RUNNER_H_ diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc index 9e4537974d9..8a862074410 100644 --- a/chromium/cc/trees/damage_tracker_unittest.cc +++ b/chromium/cc/trees/damage_tracker_unittest.cc @@ -340,14 +340,12 @@ TEST_F(DamageTrackerTest, VerifyDamageForTransformedLayer) { // should increase the size of the expected rect by sqrt(2), centered around // (100, 100). The old exposed region should be fully contained in the new // region. - double expected_width = 30.0 * sqrt(2.0); - double expected_position = 100.0 - 0.5 * expected_width; - gfx::RectF expected_rect(expected_position, - expected_position, - expected_width, - expected_width); + float expected_width = 30.f * sqrt(2.f); + float expected_position = 100.f - 0.5f * expected_width; + gfx::RectF expected_rect( + expected_position, expected_position, expected_width, expected_width); root_damage_rect = - root->render_surface()->damage_tracker()->current_damage_rect(); + root->render_surface()->damage_tracker()->current_damage_rect(); EXPECT_FLOAT_RECT_EQ(expected_rect, root_damage_rect); } diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index 9956f4c7359..f8f0d614440 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -26,8 +26,8 @@ #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/layers/layer.h" #include "cc/layers/layer_iterator.h" +#include "cc/layers/painted_scrollbar_layer.h" #include "cc/layers/render_surface.h" -#include "cc/layers/scrollbar_layer.h" #include "cc/resources/prioritized_resource_manager.h" #include "cc/resources/ui_resource_client.h" #include "cc/trees/layer_tree_host_client.h" @@ -47,7 +47,7 @@ static int s_num_layer_tree_instances; namespace cc { RendererCapabilities::RendererCapabilities() - : best_texture_format(0), + : best_texture_format(RGBA_8888), using_partial_swap(false), using_set_visibility(false), using_egl_image(false), @@ -56,12 +56,36 @@ RendererCapabilities::RendererCapabilities() max_texture_size(0), avoid_pow2_textures(false), using_map_image(false), - using_shared_memory_resources(false) {} + using_shared_memory_resources(false), + using_discard_framebuffer(false) {} RendererCapabilities::~RendererCapabilities() {} -UIResourceRequest::UIResourceRequest() - : type(UIResourceInvalidRequest), id(0), bitmap(NULL) {} +UIResourceRequest::UIResourceRequest(UIResourceRequestType type, + UIResourceId id) + : type_(type), id_(id) {} + +UIResourceRequest::UIResourceRequest(UIResourceRequestType type, + UIResourceId id, + const UIResourceBitmap& bitmap) + : type_(type), id_(id), bitmap_(new UIResourceBitmap(bitmap)) {} + +UIResourceRequest::UIResourceRequest(const UIResourceRequest& request) { + (*this) = request; +} + +UIResourceRequest& UIResourceRequest::operator=( + const UIResourceRequest& request) { + type_ = request.type_; + id_ = request.id_; + if (request.bitmap_) { + bitmap_ = make_scoped_ptr(new UIResourceBitmap(*request.bitmap_.get())); + } else { + bitmap_.reset(); + } + + return *this; +} UIResourceRequest::~UIResourceRequest() {} @@ -142,6 +166,9 @@ bool LayerTreeHost::InitializeProxy(scoped_ptr<Proxy> proxy) { LayerTreeHost::~LayerTreeHost() { TRACE_EVENT0("cc", "LayerTreeHost::~LayerTreeHost"); + + overhang_ui_resource_.reset(); + if (root_layer_.get()) root_layer_->SetLayerTreeHost(NULL); @@ -193,7 +220,7 @@ LayerTreeHost::OnCreateAndInitializeOutputSurfaceAttempted(bool success) { contents_texture_manager_ = PrioritizedResourceManager::Create(proxy_.get()); surface_memory_placeholder_ = - contents_texture_manager_->CreateTexture(gfx::Size(), GL_RGBA); + contents_texture_manager_->CreateTexture(gfx::Size(), RGBA_8888); } client_->DidInitializeOutputSurface(true); @@ -326,6 +353,19 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) { sync_tree->FindRootScrollLayer(); + // TODO(wjmaclean) For now, not all LTH clients will register viewports, so + // only set them when available.. + if (page_scale_layer_) { + DCHECK(inner_viewport_scroll_layer_); + sync_tree->SetViewportLayersFromIds( + page_scale_layer_->id(), + inner_viewport_scroll_layer_->id(), + outer_viewport_scroll_layer_ ? outer_viewport_scroll_layer_->id() + : Layer::INVALID_ID); + } else { + sync_tree->ClearViewportLayers(); + } + float page_scale_delta, sent_page_scale_delta; if (settings_.impl_side_painting) { // Update the delta from the active tree, which may have @@ -356,7 +396,6 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) { pending_page_scale_animation_->target_offset, pending_page_scale_animation_->use_anchor, pending_page_scale_animation_->scale, - base::TimeTicks::Now(), pending_page_scale_animation_->duration); pending_page_scale_animation_.reset(); } @@ -369,6 +408,11 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) { if (!settings_.impl_side_painting) sync_tree->ProcessUIResourceRequestQueue(); } + if (overhang_ui_resource_) { + host_impl->SetOverhangUIResource( + overhang_ui_resource_->id(), + GetUIResourceSize(overhang_ui_resource_->id())); + } DCHECK(!sync_tree->ViewportSizeInvalid()); @@ -435,8 +479,6 @@ void LayerTreeHost::DidLoseOutputSurface() { if (output_surface_lost_) return; - DidLoseUIResources(); - num_failed_recreate_attempts_ = 0; output_surface_lost_ = true; SetNeedsCommit(); @@ -518,6 +560,10 @@ bool LayerTreeHost::CommitRequested() const { return proxy_->CommitRequested(); } +void LayerTreeHost::SetNextCommitWaitsForActivation() { + proxy_->SetNextCommitWaitsForActivation(); +} + void LayerTreeHost::SetAnimationEvents(scoped_ptr<AnimationEventsVector> events, base::Time wall_clock_time) { DCHECK(proxy_->IsMainThread()); @@ -545,9 +591,6 @@ void LayerTreeHost::SetAnimationEvents(scoped_ptr<AnimationEventsVector> events, case AnimationEvent::PropertyUpdate: (*iter).second->NotifyAnimationPropertyUpdate((*events)[event_index]); break; - - default: - NOTREACHED(); } } } @@ -622,6 +665,22 @@ void LayerTreeHost::SetPageScaleFactorAndLimits(float page_scale_factor, SetNeedsCommit(); } +void LayerTreeHost::SetOverhangBitmap(const SkBitmap& bitmap) { + DCHECK(bitmap.width() && bitmap.height()); + DCHECK_EQ(bitmap.bytesPerPixel(), 4); + + SkBitmap bitmap_copy; + if (bitmap.isImmutable()) { + bitmap_copy = bitmap; + } else { + bitmap.copyTo(&bitmap_copy, bitmap.config()); + bitmap_copy.setImmutable(); + } + + overhang_ui_resource_ = ScopedUIResource::Create( + this, UIResourceBitmap(bitmap_copy, UIResourceBitmap::REPEAT)); +} + void LayerTreeHost::SetVisible(bool visible) { if (visible_ == visible) return; @@ -673,8 +732,7 @@ bool LayerTreeHost::InitializeOutputSurfaceIfNeeded() { return !output_surface_lost_; } -bool LayerTreeHost::UpdateLayers(ResourceUpdateQueue* queue, - size_t memory_allocation_limit_bytes) { +bool LayerTreeHost::UpdateLayers(ResourceUpdateQueue* queue) { DCHECK(!output_surface_lost_); if (!root_layer()) @@ -682,11 +740,6 @@ bool LayerTreeHost::UpdateLayers(ResourceUpdateQueue* queue, DCHECK(!root_layer()->parent()); - if (contents_texture_manager_ && memory_allocation_limit_bytes) { - contents_texture_manager_->SetMaxMemoryLimitBytes( - memory_allocation_limit_bytes); - } - return UpdateLayers(root_layer(), queue); } @@ -734,6 +787,9 @@ bool LayerTreeHost::UpdateLayers(Layer* root_layer, UpdateHudLayer(); Layer* root_scroll = FindFirstScrollableLayer(root_layer); + Layer* page_scale_layer = page_scale_layer_; + if (!page_scale_layer && root_scroll) + page_scale_layer = root_scroll->parent(); if (hud_layer_) { hud_layer_->PrepareForCalculateDrawProperties( @@ -747,7 +803,7 @@ bool LayerTreeHost::UpdateLayers(Layer* root_layer, gfx::Transform(), device_scale_factor_, page_scale_factor_, - root_scroll ? root_scroll->parent() : NULL, + page_scale_layer, GetRendererCapabilities().max_texture_size, settings_.can_use_lcd_text, settings_.layer_transforms_should_scale_layer_contents, @@ -890,7 +946,7 @@ size_t LayerTreeHost::CalculateMemoryForRenderSurfaces( size_t bytes = Resource::MemorySizeBytes(render_surface->content_rect().size(), - GL_RGBA); + RGBA_8888); contents_texture_bytes += bytes; if (render_surface_layer->background_filters().IsEmpty()) @@ -900,7 +956,7 @@ size_t LayerTreeHost::CalculateMemoryForRenderSurfaces( max_background_texture_bytes = bytes; if (!readback_bytes) { readback_bytes = Resource::MemorySizeBytes(device_viewport_size_, - GL_RGBA); + RGBA_8888); } } return readback_bytes + max_background_texture_bytes + contents_texture_bytes; @@ -1092,12 +1148,6 @@ void LayerTreeHost::UpdateTopControlsState(TopControlsState constraints, animate)); } -bool LayerTreeHost::BlocksPendingCommit() const { - if (!root_layer_.get()) - return false; - return root_layer_->BlocksPendingCommitRecursive(); -} - scoped_ptr<base::Value> LayerTreeHost::AsValue() const { scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue()); state->Set("proxy", proxy_->AsValue().release()); @@ -1128,54 +1178,66 @@ void LayerTreeHost::AnimateLayers(base::TimeTicks time) { UIResourceId LayerTreeHost::CreateUIResource(UIResourceClient* client) { DCHECK(client); - UIResourceRequest request; - bool resource_lost = false; - request.type = UIResourceRequest::UIResourceCreate; - request.id = next_ui_resource_id_++; - - DCHECK(ui_resource_client_map_.find(request.id) == + UIResourceId next_id = next_ui_resource_id_++; + DCHECK(ui_resource_client_map_.find(next_id) == ui_resource_client_map_.end()); - request.bitmap = client->GetBitmap(request.id, resource_lost); + bool resource_lost = false; + UIResourceRequest request(UIResourceRequest::UIResourceCreate, + next_id, + client->GetBitmap(next_id, resource_lost)); ui_resource_request_queue_.push_back(request); - ui_resource_client_map_[request.id] = client; - return request.id; -} -// Deletes a UI resource. May safely be called more than once. -void LayerTreeHost::DeleteUIResource(UIResourceId uid) { - UIResourceClientMap::iterator iter = ui_resource_client_map_.find(uid); - if (iter == ui_resource_client_map_.end()) - return; + UIResourceClientData data; + data.client = client; + data.size = request.GetBitmap().GetSize(); - UIResourceRequest request; - request.type = UIResourceRequest::UIResourceDelete; - request.id = uid; - ui_resource_request_queue_.push_back(request); - ui_resource_client_map_.erase(uid); + ui_resource_client_map_[request.GetId()] = data; + return request.GetId(); } -void LayerTreeHost::UIResourceLost(UIResourceId uid) { +// Deletes a UI resource. May safely be called more than once. +void LayerTreeHost::DeleteUIResource(UIResourceId uid) { UIResourceClientMap::iterator iter = ui_resource_client_map_.find(uid); if (iter == ui_resource_client_map_.end()) return; - UIResourceRequest request; - bool resource_lost = true; - request.type = UIResourceRequest::UIResourceCreate; - request.id = uid; - request.bitmap = iter->second->GetBitmap(uid, resource_lost); - DCHECK(request.bitmap.get()); + UIResourceRequest request(UIResourceRequest::UIResourceDelete, uid); ui_resource_request_queue_.push_back(request); + ui_resource_client_map_.erase(iter); } -void LayerTreeHost::DidLoseUIResources() { - // When output surface is lost, we need to recreate the resource. +void LayerTreeHost::RecreateUIResources() { for (UIResourceClientMap::iterator iter = ui_resource_client_map_.begin(); iter != ui_resource_client_map_.end(); ++iter) { - UIResourceLost(iter->first); + UIResourceId uid = iter->first; + const UIResourceClientData& data = iter->second; + bool resource_lost = true; + UIResourceRequest request(UIResourceRequest::UIResourceCreate, + uid, + data.client->GetBitmap(uid, resource_lost)); + ui_resource_request_queue_.push_back(request); } } +// Returns the size of a resource given its id. +gfx::Size LayerTreeHost::GetUIResourceSize(UIResourceId uid) const { + UIResourceClientMap::const_iterator iter = ui_resource_client_map_.find(uid); + if (iter == ui_resource_client_map_.end()) + return gfx::Size(); + + const UIResourceClientData& data = iter->second; + return data.size; +} + +void LayerTreeHost::RegisterViewportLayers( + scoped_refptr<Layer> page_scale_layer, + scoped_refptr<Layer> inner_viewport_scroll_layer, + scoped_refptr<Layer> outer_viewport_scroll_layer) { + page_scale_layer_ = page_scale_layer; + inner_viewport_scroll_layer_ = inner_viewport_scroll_layer; + outer_viewport_scroll_layer_ = outer_viewport_scroll_layer; +} + } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index 619c18e6574..72a5628a9ad 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -24,6 +24,8 @@ #include "cc/input/top_controls_state.h" #include "cc/layers/layer_lists.h" #include "cc/output/output_surface.h" +#include "cc/resources/resource_format.h" +#include "cc/resources/scoped_ui_resource.h" #include "cc/resources/ui_resource_bitmap.h" #include "cc/resources/ui_resource_client.h" #include "cc/scheduler/rate_limiter.h" @@ -33,7 +35,7 @@ #include "cc/trees/occlusion_tracker.h" #include "cc/trees/proxy.h" #include "third_party/skia/include/core/SkColor.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" #include "ui/gfx/rect.h" namespace WebKit { class WebGraphicsContext3D; } @@ -72,7 +74,7 @@ struct CC_EXPORT RendererCapabilities { RendererCapabilities(); ~RendererCapabilities(); - unsigned best_texture_format; + ResourceFormat best_texture_format; bool using_partial_swap; bool using_set_visibility; bool using_egl_image; @@ -82,20 +84,38 @@ struct CC_EXPORT RendererCapabilities { bool avoid_pow2_textures; bool using_map_image; bool using_shared_memory_resources; + bool using_discard_framebuffer; }; -struct CC_EXPORT UIResourceRequest { +class CC_EXPORT UIResourceRequest { + public: enum UIResourceRequestType { UIResourceCreate, UIResourceDelete, UIResourceInvalidRequest }; - UIResourceRequest(); + UIResourceRequest(UIResourceRequestType type, UIResourceId id); + UIResourceRequest(UIResourceRequestType type, + UIResourceId id, + const UIResourceBitmap& bitmap); + UIResourceRequest(const UIResourceRequest& request); + ~UIResourceRequest(); - UIResourceRequestType type; - UIResourceId id; - scoped_refptr<UIResourceBitmap> bitmap; + + UIResourceRequestType GetType() const { return type_; } + UIResourceId GetId() const { return id_; } + UIResourceBitmap GetBitmap() const { + DCHECK(bitmap_); + return *bitmap_.get(); + } + + UIResourceRequest& operator=(const UIResourceRequest& request); + + private: + UIResourceRequestType type_; + UIResourceId id_; + scoped_ptr<UIResourceBitmap> bitmap_; }; class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { @@ -144,8 +164,7 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { virtual void AcquireLayerTextures(); // Returns false if we should abort this frame due to initialization failure. bool InitializeOutputSurfaceIfNeeded(); - bool UpdateLayers(ResourceUpdateQueue* queue, - size_t contents_memory_limit_bytes); + bool UpdateLayers(ResourceUpdateQueue* queue); LayerTreeHostClient* client() { return client_; } const base::WeakPtr<InputHandler>& GetInputHandler() { @@ -191,12 +210,18 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { void SetNeedsRedrawRect(gfx::Rect damage_rect); bool CommitRequested() const; + void SetNextCommitWaitsForActivation(); + void SetAnimationEvents(scoped_ptr<AnimationEventsVector> events, base::Time wall_clock_time); void SetRootLayer(scoped_refptr<Layer> root_layer); Layer* root_layer() { return root_layer_.get(); } const Layer* root_layer() const { return root_layer_.get(); } + void RegisterViewportLayers( + scoped_refptr<Layer> page_scale_layer, + scoped_refptr<Layer> inner_viewport_scroll_layer, + scoped_refptr<Layer> outer_viewport_scroll_layer); const LayerTreeSettings& settings() const { return settings_; } @@ -222,6 +247,8 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { has_transparent_background_ = transparent; } + void SetOverhangBitmap(const SkBitmap& bitmap); + PrioritizedResourceManager* contents_texture_manager() const { return contents_texture_manager_.get(); } @@ -266,8 +293,6 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { return animation_registrar_.get(); } - bool BlocksPendingCommit() const; - // Obtains a thorough dump of the LayerTreeHost as a value. scoped_ptr<base::Value> AsValue() const; @@ -282,6 +307,11 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { virtual UIResourceId CreateUIResource(UIResourceClient* client); // Deletes a UI resource. May safely be called more than once. virtual void DeleteUIResource(UIResourceId id); + // Put the recreation of all UI resources into the resource queue after they + // were evicted on the impl thread. + void RecreateUIResources(); + + virtual gfx::Size GetUIResourceSize(UIResourceId id) const; bool UsingSharedMemoryResources(); int id() const { return tree_id_; } @@ -319,11 +349,13 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { bool AnimateLayersRecursive(Layer* current, base::TimeTicks time); - void UIResourceLost(UIResourceId id); - - void DidLoseUIResources(); + struct UIResourceClientData { + UIResourceClient* client; + gfx::Size size; + }; - typedef base::hash_map<UIResourceId, UIResourceClient*> UIResourceClientMap; + typedef base::hash_map<UIResourceId, UIResourceClientData> + UIResourceClientMap; UIResourceClientMap ui_resource_client_map_; int next_ui_resource_id_; @@ -379,6 +411,10 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { SkColor background_color_; bool has_transparent_background_; + // If set, this texture is used to fill in the parts of the screen not + // covered by layers. + scoped_ptr<ScopedUIResource> overhang_ui_resource_; + typedef ScopedPtrVector<PrioritizedResource> TextureList; size_t partial_texture_update_requests_; @@ -412,6 +448,10 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { LCDTextMetrics lcd_text_metrics_; int tree_id_; + scoped_refptr<Layer> page_scale_layer_; + scoped_refptr<Layer> inner_viewport_scroll_layer_; + scoped_refptr<Layer> outer_viewport_scroll_layer_; + DISALLOW_COPY_AND_ASSIGN(LayerTreeHost); }; diff --git a/chromium/cc/trees/layer_tree_host_common.cc b/chromium/cc/trees/layer_tree_host_common.cc index 68dadd5f9b9..8017fa9a933 100644 --- a/chromium/cc/trees/layer_tree_host_common.cc +++ b/chromium/cc/trees/layer_tree_host_common.cc @@ -40,6 +40,29 @@ static void SortLayers(LayerImplList::iterator first, layer_sorter->Sort(first, end); } +template <typename LayerType> +static gfx::Vector2dF GetEffectiveScrollDelta(LayerType* layer) { + gfx::Vector2dF scroll_delta = layer->ScrollDelta(); + // The scroll parent's scroll delta is the amount we've scrolled on the + // compositor thread since the commit for this layer tree's source frame. + // we last reported to the main thread. I.e., it's the discrepancy between + // a scroll parent's scroll delta and offset, so we must add it here. + if (layer->scroll_parent()) + scroll_delta += layer->scroll_parent()->ScrollDelta(); + return scroll_delta; +} + +template <typename LayerType> +static gfx::Vector2dF GetEffectiveTotalScrollOffset(LayerType* layer) { + gfx::Vector2dF offset = layer->TotalScrollOffset(); + // The scroll parent's total scroll offset (scroll offset + scroll delta) + // can't be used because its scroll offset has already been applied to the + // scroll children's positions by the main thread layer positioning code. + if (layer->scroll_parent()) + offset += layer->scroll_parent()->ScrollDelta(); + return offset; +} + inline gfx::Rect CalculateVisibleRectWithCachedLayerRect( gfx::Rect target_surface_rect, gfx::Rect layer_bound_rect, @@ -58,6 +81,9 @@ inline gfx::Rect CalculateVisibleRectWithCachedLayerRect( gfx::Rect minimal_surface_rect = target_surface_rect; minimal_surface_rect.Intersect(layer_rect_in_target_space); + if (minimal_surface_rect.IsEmpty()) + return gfx::Rect(); + // Project the corners of the target surface rect into the layer space. // This bounding rectangle may be larger than it needs to be (being // axis-aligned), but is a reasonable filter on the space to consider. @@ -65,11 +91,10 @@ inline gfx::Rect CalculateVisibleRectWithCachedLayerRect( gfx::Transform surface_to_layer(gfx::Transform::kSkipInitialization); if (!transform.GetInverse(&surface_to_layer)) { - // TODO(shawnsingh): Some uninvertible transforms may be visible, but - // their behaviour is undefined thoughout the compositor. Make their - // behaviour well-defined and allow the visible content rect to be non- - // empty when needed. - return gfx::Rect(); + // Because we cannot use the surface bounds to determine what portion of + // the layer is visible, we must conservatively assume the full layer is + // visible. + return layer_bound_rect; } gfx::Rect layer_rect = gfx::ToEnclosingRect(MathUtil::ProjectClippedRect( @@ -88,6 +113,201 @@ gfx::Rect LayerTreeHostCommon::CalculateVisibleRect( target_surface_rect, layer_bound_rect, layer_in_surface_space, transform); } +template <typename LayerType> +static LayerType* NextTargetSurface(LayerType* layer) { + return layer->parent() ? layer->parent()->render_target() : 0; +} + +// Given two layers, this function finds their respective render targets and, +// computes a change of basis translation. It does this by accumulating the +// translation components of the draw transforms of each target between the +// ancestor and descendant. These transforms must be 2D translations, and this +// requirement is enforced at every step. +template <typename LayerType, typename RenderSurfaceType> +static gfx::Vector2dF ComputeChangeOfBasisTranslation( + const LayerType& ancestor_layer, + const LayerType& descendant_layer) { + DCHECK(descendant_layer.HasAncestor(&ancestor_layer)); + const LayerType* descendant_target = descendant_layer.render_target(); + DCHECK(descendant_target); + const LayerType* ancestor_target = ancestor_layer.render_target(); + DCHECK(ancestor_target); + + gfx::Vector2dF translation; + for (const LayerType* target = descendant_target; target != ancestor_target; + target = NextTargetSurface(target)) { + const gfx::Transform& trans = target->render_surface()->draw_transform(); + // Ensure that this translation is truly 2d. + DCHECK(trans.IsIdentityOrTranslation()); + DCHECK_EQ(0.f, trans.matrix().get(2, 3)); + translation += trans.To2dTranslation(); + } + + return translation; +} + +enum TranslateRectDirection { + TranslateRectDirectionToAncestor, + TranslateRectDirectionToDescendant +}; + +template <typename LayerType, typename RenderSurfaceType> +static gfx::Rect TranslateRectToTargetSpace(const LayerType& ancestor_layer, + const LayerType& descendant_layer, + gfx::Rect rect, + TranslateRectDirection direction) { + gfx::Vector2dF translation = + ComputeChangeOfBasisTranslation<LayerType, RenderSurfaceType>( + ancestor_layer, descendant_layer); + if (direction == TranslateRectDirectionToDescendant) + translation.Scale(-1.f); + return gfx::ToEnclosingRect( + gfx::RectF(rect.origin() + translation, rect.size())); +} + +// Attempts to update the clip rects for the given layer. If the layer has a +// clip_parent, it may not inherit its immediate ancestor's clip. +template <typename LayerType, typename RenderSurfaceType> +static void UpdateClipRectsForClipChild( + const LayerType* layer, + gfx::Rect* clip_rect_in_parent_target_space, + bool* subtree_should_be_clipped) { + // If the layer has no clip_parent, or the ancestor is the same as its actual + // parent, then we don't need special clip rects. Bail now and leave the out + // parameters untouched. + const LayerType* clip_parent = layer->clip_parent(); + if (!clip_parent || clip_parent == layer->parent()) + return; + + // The root layer is never a clip child. + DCHECK(layer->parent()); + + // Grab the cached values. + *clip_rect_in_parent_target_space = clip_parent->clip_rect(); + *subtree_should_be_clipped = clip_parent->is_clipped(); + + // We may have to project the clip rect into our parent's target space. Note, + // it must be our parent's target space, not ours. For one, we haven't + // computed our transforms, so we couldn't put it in our space yet even if we + // wanted to. But more importantly, this matches the expectations of + // CalculateDrawPropertiesInternal. If we, say, create a render surface, these + // clip rects will want to be in its target space, not ours. + *clip_rect_in_parent_target_space = + TranslateRectToTargetSpace<LayerType, RenderSurfaceType>( + *clip_parent, + *layer->parent(), + *clip_rect_in_parent_target_space, + TranslateRectDirectionToDescendant); +} + +// We collect an accumulated drawable content rect per render surface. +// Typically, a layer will contribute to only one surface, the surface +// associated with its render target. Clip children, however, may affect +// several surfaces since there may be several surfaces between the clip child +// and its parent. +// +// NB: we accumulate the layer's *clipped* drawable content rect. +template <typename LayerType> +struct AccumulatedSurfaceState { + explicit AccumulatedSurfaceState(LayerType* render_target) + : render_target(render_target) {} + + // The accumulated drawable content rect for the surface associated with the + // given |render_target|. + gfx::Rect drawable_content_rect; + + // The target owning the surface. (We hang onto the target rather than the + // surface so that we can DCHECK that the surface's draw transform is simply + // a translation when |render_target| reports that it has no unclipped + // descendants). + LayerType* render_target; +}; + +template <typename LayerType, typename RenderSurfaceType> +void UpdateAccumulatedSurfaceState( + LayerType* layer, + gfx::Rect drawable_content_rect, + std::vector<AccumulatedSurfaceState<LayerType> >* + accumulated_surface_state) { + if (IsRootLayer(layer)) + return; + + // We will apply our drawable content rect to the accumulated rects for all + // surfaces between us and |render_target| (inclusive). This is either our + // clip parent's target if we are a clip child, or else simply our parent's + // target. We use our parent's target because we're either the owner of a + // render surface and we'll want to add our rect to our *surface's* target, or + // we're not and our target is the same as our parent's. In both cases, the + // parent's target gives us what we want. + LayerType* render_target = layer->clip_parent() + ? layer->clip_parent()->render_target() + : layer->parent()->render_target(); + + // If the layer owns a surface, then the content rect is in the wrong space. + // Instead, we will use the surface's DrawableContentRect which is in target + // space as required. + gfx::Rect target_rect = drawable_content_rect; + if (layer->render_surface()) { + target_rect = + gfx::ToEnclosedRect(layer->render_surface()->DrawableContentRect()); + } + + if (render_target->is_clipped()) { + gfx::Rect clip_rect = render_target->clip_rect(); + // If the layer has a clip parent, the clip rect may be in the wrong space, + // so we'll need to transform it before it is applied. + if (layer->clip_parent()) { + clip_rect = TranslateRectToTargetSpace<LayerType, RenderSurfaceType>( + *layer->clip_parent(), + *layer, + clip_rect, + TranslateRectDirectionToDescendant); + } + target_rect.Intersect(clip_rect); + } + + // We must have at least one entry in the vector for the root. + DCHECK_LT(0ul, accumulated_surface_state->size()); + + typedef typename std::vector<AccumulatedSurfaceState<LayerType> > + AccumulatedSurfaceStateVector; + typedef typename AccumulatedSurfaceStateVector::reverse_iterator + AccumulatedSurfaceStateIterator; + AccumulatedSurfaceStateIterator current_state = + accumulated_surface_state->rbegin(); + + // Add this rect to the accumulated content rect for all surfaces until we + // reach the target surface. + bool found_render_target = false; + for (; current_state != accumulated_surface_state->rend(); ++current_state) { + current_state->drawable_content_rect.Union(target_rect); + + // If we've reached |render_target| our work is done and we can bail. + if (current_state->render_target == render_target) { + found_render_target = true; + break; + } + + // Transform rect from the current target's space to the next. + LayerType* current_target = current_state->render_target; + DCHECK(current_target->render_surface()); + const gfx::Transform& current_draw_transform = + current_target->render_surface()->draw_transform(); + + // If we have unclipped descendants, the draw transform is a translation. + DCHECK(current_target->num_unclipped_descendants() == 0 || + current_draw_transform.IsIdentityOrTranslation()); + + target_rect = gfx::ToEnclosingRect( + MathUtil::MapClippedRect(current_draw_transform, target_rect)); + } + + // It is an error to not reach |render_target|. If this happens, it means that + // either the clip parent is not an ancestor of the clip child or the surface + // state vector is empty, both of which should be impossible. + DCHECK(found_render_target); +} + template <typename LayerType> static inline bool IsRootLayer(LayerType* layer) { return !layer->parent(); } @@ -414,10 +634,6 @@ static bool SubtreeShouldRenderToSeparateSurface( return false; } -static LayerImpl* NextTargetSurface(LayerImpl* layer) { - return layer->parent() ? layer->parent()->render_target() : 0; -} - // This function returns a translation matrix that can be applied on a vector // that's in the layer's target surface coordinate, while the position offset is // specified in some ancestor layer's coordinate. @@ -526,7 +742,8 @@ void ApplyPositionAdjustment( gfx::Transform ComputeScrollCompensationForThisLayer( LayerImpl* scrolling_layer, - const gfx::Transform& parent_matrix) { + const gfx::Transform& parent_matrix, + gfx::Vector2dF scroll_delta) { // For every layer that has non-zero scroll_delta, we have to compute a // transform that can undo the scroll_delta translation. In particular, we // want this matrix to premultiply a fixed-position layer's parent_matrix, so @@ -549,8 +766,8 @@ gfx::Transform ComputeScrollCompensationForThisLayer( gfx::Transform scroll_compensation_for_this_layer = parent_matrix; // Step 3 scroll_compensation_for_this_layer.Translate( - scrolling_layer->ScrollDelta().x(), - scrolling_layer->ScrollDelta().y()); // Step 2 + scroll_delta.x(), + scroll_delta.y()); // Step 2 gfx::Transform inverse_parent_matrix(gfx::Transform::kSkipInitialization); if (!parent_matrix.GetInverse(&inverse_parent_matrix)) { @@ -565,7 +782,8 @@ gfx::Transform ComputeScrollCompensationForThisLayer( gfx::Transform ComputeScrollCompensationMatrixForChildren( Layer* current_layer, const gfx::Transform& current_parent_matrix, - const gfx::Transform& current_scroll_compensation) { + const gfx::Transform& current_scroll_compensation, + gfx::Vector2dF scroll_delta) { // The main thread (i.e. Layer) does not need to worry about scroll // compensation. So we can just return an identity matrix here. return gfx::Transform(); @@ -574,7 +792,8 @@ gfx::Transform ComputeScrollCompensationMatrixForChildren( gfx::Transform ComputeScrollCompensationMatrixForChildren( LayerImpl* layer, const gfx::Transform& parent_matrix, - const gfx::Transform& current_scroll_compensation_matrix) { + const gfx::Transform& current_scroll_compensation_matrix, + gfx::Vector2dF scroll_delta) { // "Total scroll compensation" is the transform needed to cancel out all // scroll_delta translations that occurred since the nearest container layer, // even if there are render_surfaces in-between. @@ -600,7 +819,7 @@ gfx::Transform ComputeScrollCompensationMatrixForChildren( // initialization/copy) if we know that the scroll compensation doesn't need // to be reset or adjusted. if (!layer->IsContainerForFixedPositionLayers() && - layer->ScrollDelta().IsZero() && !layer->render_surface()) + scroll_delta.IsZero() && !layer->render_surface()) return current_scroll_compensation_matrix; // Start as identity matrix. @@ -614,10 +833,10 @@ gfx::Transform ComputeScrollCompensationMatrixForChildren( // If the current layer has a non-zero scroll_delta, then we should compute // its local scroll compensation and accumulate it to the // next_scroll_compensation_matrix. - if (!layer->ScrollDelta().IsZero()) { + if (!scroll_delta.IsZero()) { gfx::Transform scroll_compensation_for_this_layer = ComputeScrollCompensationForThisLayer( - layer, parent_matrix); + layer, parent_matrix, scroll_delta); next_scroll_compensation_matrix.PreconcatTransform( scroll_compensation_for_this_layer); } @@ -783,13 +1002,17 @@ static inline void RemoveSurfaceForEarlyExit( struct PreCalculateMetaInformationRecursiveData { bool layer_or_descendant_has_copy_request; + int num_unclipped_descendants; PreCalculateMetaInformationRecursiveData() - : layer_or_descendant_has_copy_request(false) {} + : layer_or_descendant_has_copy_request(false), + num_unclipped_descendants(0) {} void Merge(const PreCalculateMetaInformationRecursiveData& data) { layer_or_descendant_has_copy_request |= data.layer_or_descendant_has_copy_request; + num_unclipped_descendants += + data.num_unclipped_descendants; } }; @@ -812,6 +1035,9 @@ static void PreCalculateMetaInformation( descendants_can_clip_selves = false; } + if (layer->clip_parent()) + recursive_data->num_unclipped_descendants++; + for (size_t i = 0; i < layer->children().size(); ++i) { LayerType* child_layer = LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i); @@ -837,11 +1063,19 @@ static void PreCalculateMetaInformation( recursive_data->Merge(data_for_child); } + if (layer->clip_children()) { + int num_clip_children = layer->clip_children()->size(); + DCHECK_GE(recursive_data->num_unclipped_descendants, num_clip_children); + recursive_data->num_unclipped_descendants -= num_clip_children; + } + if (layer->HasCopyRequest()) recursive_data->layer_or_descendant_has_copy_request = true; layer->draw_properties().num_descendants_that_draw_content = num_descendants_that_draw_content; + layer->draw_properties().num_unclipped_descendants = + recursive_data->num_unclipped_descendants; layer->draw_properties().descendants_can_clip_selves = descendants_can_clip_selves; layer->draw_properties().layer_or_descendant_has_copy_request = @@ -915,7 +1149,8 @@ static void CalculateDrawPropertiesInternal( const DataForRecursion<LayerType, RenderSurfaceType>& data_from_ancestor, LayerListType* render_surface_layer_list, LayerListType* layer_list, - gfx::Rect* drawable_content_rect_of_subtree) { + std::vector<AccumulatedSurfaceState<LayerType> >* + accumulated_surface_state) { // This function computes the new matrix transformations recursively for this // layer and all its descendants. It also computes the appropriate render // surfaces. @@ -1043,10 +1278,6 @@ static void CalculateDrawPropertiesInternal( DCHECK(globals.page_scale_application_layer || (globals.page_scale_factor == 1.f)); - // If we early-exit anywhere in this function, the drawable_content_rect of - // this subtree should be considered empty. - *drawable_content_rect_of_subtree = gfx::Rect(); - DataForRecursion<LayerType, RenderSurfaceType> data_for_children; RenderSurfaceType* nearest_ancestor_surface_that_moves_pixels = data_from_ancestor.nearest_ancestor_surface_that_moves_pixels; @@ -1065,8 +1296,25 @@ static void CalculateDrawPropertiesInternal( layer_is_visible = true; // The root layer cannot skip CalcDrawProperties. - if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible)) + if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible)) { + if (layer->render_surface()) + layer->ClearRenderSurface(); return; + } + + // We need to circumvent the normal recursive flow of information for clip + // children (they don't inherit their direct ancestor's clip information). + // This is unfortunate, and would be unnecessary if we were to formally + // separate the clipping hierarchy from the layer hierarchy. + bool ancestor_clips_subtree = data_from_ancestor.ancestor_clips_subtree; + gfx::Rect ancestor_clip_rect_in_target_space = + data_from_ancestor.clip_rect_in_target_space; + + // Update our clipping state. If we have a clip parent we will need to pull + // from the clip state cache rather than using the clip state passed from our + // immediate ancestor. + UpdateClipRectsForClipChild<LayerType, RenderSurfaceType>( + layer, &ancestor_clip_rect_in_target_space, &ancestor_clips_subtree); // As this function proceeds, these are the properties for the current // layer that actually get computed. To avoid unnecessary copies @@ -1106,7 +1354,8 @@ static void CalculateDrawPropertiesInternal( gfx::Size bounds = layer->bounds(); gfx::PointF anchor_point = layer->anchor_point(); - gfx::PointF position = layer->position() - layer->TotalScrollOffset(); + gfx::Vector2dF scroll_offset = GetEffectiveTotalScrollOffset(layer); + gfx::PointF position = layer->position() - scroll_offset; gfx::Transform combined_transform = data_from_ancestor.parent_matrix; if (!layer->transform().IsIdentity()) { @@ -1125,12 +1374,19 @@ static void CalculateDrawPropertiesInternal( combined_transform.Translate(position.x(), position.y()); } + gfx::Vector2dF effective_scroll_delta = GetEffectiveScrollDelta(layer); if (!animating_transform_to_target && layer->scrollable() && combined_transform.IsScaleOrTranslation()) { // Align the scrollable layer's position to screen space pixels to avoid // blurriness. To avoid side-effects, do this only if the transform is // simple. + gfx::Vector2dF previous_translation = combined_transform.To2dTranslation(); RoundTranslationComponents(&combined_transform); + gfx::Vector2dF current_translation = combined_transform.To2dTranslation(); + + // This rounding changes the scroll delta, and so must be included + // in the scroll compensation matrix. + effective_scroll_delta -= current_translation - previous_translation; } // Apply adjustment from position constraints. @@ -1166,8 +1422,9 @@ static void CalculateDrawPropertiesInternal( // case, the render_surface re-parents the transforms. layer_draw_properties.target_space_transform = combined_transform; // M[draw] = M[parent] * LT * S[layer2content] - layer_draw_properties.target_space_transform.Scale - (1.f / layer->contents_scale_x(), 1.f / layer->contents_scale_y()); + layer_draw_properties.target_space_transform.Scale( + SK_MScalar1 / layer->contents_scale_x(), + SK_MScalar1 / layer->contents_scale_y()); // The layer's screen_space_transform represents the transform between root // layer's "screen space" and local content space. @@ -1212,8 +1469,10 @@ static void CalculateDrawPropertiesInternal( // Check back-face visibility before continuing with this surface and its // subtree if (!layer->double_sided() && TransformToParentIsKnown(layer) && - IsSurfaceBackFaceVisible(layer, combined_transform)) + IsSurfaceBackFaceVisible(layer, combined_transform)) { + layer->ClearRenderSurface(); return; + } RenderSurfaceType* render_surface = CreateOrReuseRenderSurface(layer); @@ -1283,13 +1542,6 @@ static void CalculateDrawPropertiesInternal( data_for_children.full_hierarchy_matrix.PreconcatTransform( render_surface->draw_transform()); - // The new render_surface here will correctly clip the entire subtree. So, - // we do not need to continue propagating the clipping state further down - // the tree. This way, we can avoid transforming clip rects from ancestor - // target surface space to current target surface space that could cause - // more w < 0 headaches. - layer_or_ancestor_clips_descendants = false; - if (layer->mask_layer()) { DrawProperties<LayerType, RenderSurfaceType>& mask_layer_draw_properties = layer->mask_layer()->draw_properties(); @@ -1312,14 +1564,15 @@ static void CalculateDrawPropertiesInternal( if (layer->filters().HasFilterThatMovesPixels() || layer->filter()) nearest_ancestor_surface_that_moves_pixels = render_surface; - // The render surface clip rect is expressed in the space where this surface - // draws, i.e. the same space as - // data_from_ancestor.clip_rect_in_target_space. - render_surface->SetIsClipped(data_from_ancestor.ancestor_clips_subtree); - if (data_from_ancestor.ancestor_clips_subtree) { - render_surface->SetClipRect( - data_from_ancestor.clip_rect_in_target_space); + render_surface->SetNearestAncestorThatMovesPixels( + nearest_ancestor_surface_that_moves_pixels); + layer_or_ancestor_clips_descendants = false; + bool subtree_is_clipped_by_surface_bounds = false; + if (ancestor_clips_subtree) { + // It may be the layer or the surface doing the clipping of the subtree, + // but in either case, we'll be clipping to the projected clip rect of our + // ancestor. gfx::Transform inverse_surface_draw_transform( gfx::Transform::kSkipInitialization); if (!render_surface->draw_transform().GetInverse( @@ -1327,18 +1580,48 @@ static void CalculateDrawPropertiesInternal( // TODO(shawnsingh): Either we need to handle uninvertible transforms // here, or DCHECK that the transform is invertible. } - clip_rect_of_target_surface_in_target_space = - gfx::ToEnclosingRect(MathUtil::ProjectClippedRect( - inverse_surface_draw_transform, render_surface->clip_rect())); - } else { + + gfx::Rect projected_surface_rect = gfx::ToEnclosingRect( + MathUtil::ProjectClippedRect(inverse_surface_draw_transform, + ancestor_clip_rect_in_target_space)); + + if (layer_draw_properties.num_unclipped_descendants > 0) { + // If we have unclipped descendants, we cannot count on the render + // surface's bounds clipping our subtree: the unclipped descendants + // could cause us to expand our bounds. In this case, we must rely on + // layer clipping for correctess. NB: since we can only encounter + // translations between a clip child and its clip parent, clipping is + // guaranteed to be exact in this case. + layer_or_ancestor_clips_descendants = true; + clip_rect_in_target_space = projected_surface_rect; + } else { + // The new render_surface here will correctly clip the entire subtree. + // So, we do not need to continue propagating the clipping state further + // down the tree. This way, we can avoid transforming clip rects from + // ancestor target surface space to current target surface space that + // could cause more w < 0 headaches. The render surface clip rect is + // expressed in the space where this surface draws, i.e. the same space + // as clip_rect_from_ancestor_in_ancestor_target_space. + render_surface->SetClipRect(ancestor_clip_rect_in_target_space); + clip_rect_of_target_surface_in_target_space = projected_surface_rect; + subtree_is_clipped_by_surface_bounds = true; + } + } + + DCHECK(layer->render_surface()); + DCHECK(!layer->parent() || layer->parent()->render_target() == + accumulated_surface_state->back().render_target); + + accumulated_surface_state->push_back( + AccumulatedSurfaceState<LayerType>(layer)); + + render_surface->SetIsClipped(subtree_is_clipped_by_surface_bounds); + if (!subtree_is_clipped_by_surface_bounds) { render_surface->SetClipRect(gfx::Rect()); clip_rect_of_target_surface_in_target_space = data_from_ancestor.clip_rect_of_target_surface_in_target_space; } - render_surface->SetNearestAncestorThatMovesPixels( - nearest_ancestor_surface_that_moves_pixels); - // If the new render surface is drawn translucent or with a non-integral // translation then the subtree that gets drawn on this render surface // cannot use LCD text. @@ -1364,11 +1647,10 @@ static void CalculateDrawPropertiesInternal( // Layers without render_surfaces directly inherit the ancestor's clip // status. - layer_or_ancestor_clips_descendants = - data_from_ancestor.ancestor_clips_subtree; - if (data_from_ancestor.ancestor_clips_subtree) { + layer_or_ancestor_clips_descendants = ancestor_clips_subtree; + if (ancestor_clips_subtree) { clip_rect_in_target_space = - data_from_ancestor.clip_rect_in_target_space; + ancestor_clip_rect_in_target_space; } // The surface's cached clip rect value propagates regardless of what @@ -1404,37 +1686,28 @@ static void CalculateDrawPropertiesInternal( if (LayerClipsSubtree(layer)) { layer_or_ancestor_clips_descendants = true; - if (data_from_ancestor.ancestor_clips_subtree && !layer->render_surface()) { + if (ancestor_clips_subtree && !layer->render_surface()) { // A layer without render surface shares the same target as its ancestor. clip_rect_in_target_space = - data_from_ancestor.clip_rect_in_target_space; + ancestor_clip_rect_in_target_space; clip_rect_in_target_space.Intersect(rect_in_target_space); } else { clip_rect_in_target_space = rect_in_target_space; } } - if (layer == globals.page_scale_application_layer) { - data_for_children.parent_matrix.Scale( - globals.page_scale_factor, - globals.page_scale_factor); - data_for_children.in_subtree_of_page_scale_application_layer = true; - } - - // Flatten to 2D if the layer doesn't preserve 3D. - if (!layer->preserves_3d()) - data_for_children.parent_matrix.FlattenTo2d(); - - // Apply the sublayer transform at the anchor point of the layer. - if (!layer->sublayer_transform().IsIdentity()) { - data_for_children.parent_matrix.Translate( - layer->anchor_point().x() * bounds.width(), - layer->anchor_point().y() * bounds.height()); - data_for_children.parent_matrix.PreconcatTransform( - layer->sublayer_transform()); - data_for_children.parent_matrix.Translate( - -layer->anchor_point().x() * bounds.width(), - -layer->anchor_point().y() * bounds.height()); + // Tell the layer the rect that it's clipped by. In theory we could use a + // tighter clip rect here (drawable_content_rect), but that actually does not + // reduce how much would be drawn, and instead it would create unnecessary + // changes to scissor state affecting GPU performance. Our clip information + // is used in the recursion below, so we must set it beforehand. + layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants; + if (layer_or_ancestor_clips_descendants) { + layer_draw_properties.clip_rect = clip_rect_in_target_space; + } else { + // Initialize the clip rect to a safe value that will not clip the + // layer, just in case clipping is still accidentally used. + layer_draw_properties.clip_rect = rect_in_target_space; } LayerListType& descendants = @@ -1448,25 +1721,50 @@ static void CalculateDrawPropertiesInternal( if (!LayerShouldBeSkipped(layer, layer_is_visible)) descendants.push_back(layer); - data_for_children.scroll_compensation_matrix = - ComputeScrollCompensationMatrixForChildren( - layer, - data_from_ancestor.parent_matrix, - data_from_ancestor.scroll_compensation_matrix); - data_for_children.fixed_container = - layer->IsContainerForFixedPositionLayers() ? - layer : data_from_ancestor.fixed_container; - - data_for_children.clip_rect_in_target_space = clip_rect_in_target_space; - data_for_children.clip_rect_of_target_surface_in_target_space = - clip_rect_of_target_surface_in_target_space; - data_for_children.ancestor_clips_subtree = - layer_or_ancestor_clips_descendants; - data_for_children.nearest_ancestor_surface_that_moves_pixels = - nearest_ancestor_surface_that_moves_pixels; - data_for_children.subtree_is_visible_from_ancestor = layer_is_visible; - - gfx::Rect accumulated_drawable_content_rect_of_children; + if (!layer->children().empty()) { + if (layer == globals.page_scale_application_layer) { + data_for_children.parent_matrix.Scale( + globals.page_scale_factor, + globals.page_scale_factor); + data_for_children.in_subtree_of_page_scale_application_layer = true; + } + + // Flatten to 2D if the layer doesn't preserve 3D. + if (!layer->preserves_3d()) + data_for_children.parent_matrix.FlattenTo2d(); + + // Apply the sublayer transform at the anchor point of the layer. + if (!layer->sublayer_transform().IsIdentity()) { + data_for_children.parent_matrix.Translate( + layer->anchor_point().x() * bounds.width(), + layer->anchor_point().y() * bounds.height()); + data_for_children.parent_matrix.PreconcatTransform( + layer->sublayer_transform()); + data_for_children.parent_matrix.Translate( + -layer->anchor_point().x() * bounds.width(), + -layer->anchor_point().y() * bounds.height()); + } + + data_for_children.scroll_compensation_matrix = + ComputeScrollCompensationMatrixForChildren( + layer, + data_from_ancestor.parent_matrix, + data_from_ancestor.scroll_compensation_matrix, + effective_scroll_delta); + data_for_children.fixed_container = + layer->IsContainerForFixedPositionLayers() ? + layer : data_from_ancestor.fixed_container; + + data_for_children.clip_rect_in_target_space = clip_rect_in_target_space; + data_for_children.clip_rect_of_target_surface_in_target_space = + clip_rect_of_target_surface_in_target_space; + data_for_children.ancestor_clips_subtree = + layer_or_ancestor_clips_descendants; + data_for_children.nearest_ancestor_surface_that_moves_pixels = + nearest_ancestor_surface_that_moves_pixels; + data_for_children.subtree_is_visible_from_ancestor = layer_is_visible; + } + for (size_t i = 0; i < layer->children().size(); ++i) { LayerType* child = LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i); @@ -1480,29 +1778,34 @@ static void CalculateDrawPropertiesInternal( data_for_children, render_surface_layer_list, &descendants, - &drawable_content_rect_of_child_subtree); - if (!drawable_content_rect_of_child_subtree.IsEmpty()) { - accumulated_drawable_content_rect_of_children.Union( - drawable_content_rect_of_child_subtree); - if (child->render_surface()) - descendants.push_back(child); + accumulated_surface_state); + if (child->render_surface() && + !child->render_surface()->content_rect().IsEmpty()) { + descendants.push_back(child); } } + // Compute the total drawable_content_rect for this subtree (the rect is in + // target surface space). + gfx::Rect local_drawable_content_rect_of_subtree = + accumulated_surface_state->back().drawable_content_rect; + if (layer->render_surface()) { + DCHECK(accumulated_surface_state->back().render_target == layer); + accumulated_surface_state->pop_back(); + } + if (layer->render_surface() && !IsRootLayer(layer) && layer->render_surface()->layer_list().empty()) { RemoveSurfaceForEarlyExit(layer, render_surface_layer_list); return; } - // Compute the total drawable_content_rect for this subtree (the rect is in - // target surface space). - gfx::Rect local_drawable_content_rect_of_subtree = - accumulated_drawable_content_rect_of_children; - if (layer->DrawsContent()) - local_drawable_content_rect_of_subtree.Union(rect_in_target_space); - if (layer_or_ancestor_clips_descendants) - local_drawable_content_rect_of_subtree.Intersect(clip_rect_in_target_space); + if (layer->DrawsContent()) { + gfx::Rect local_drawable_content_rect = rect_in_target_space; + if (layer_or_ancestor_clips_descendants) + local_drawable_content_rect.Intersect(clip_rect_in_target_space); + local_drawable_content_rect_of_subtree.Union(local_drawable_content_rect); + } // Compute the layer's drawable content rect (the rect is in target surface // space). @@ -1512,19 +1815,6 @@ static void CalculateDrawPropertiesInternal( Intersect(clip_rect_in_target_space); } - // Tell the layer the rect that is clipped by. In theory we could use a - // tighter clip rect here (drawable_content_rect), but that actually does not - // reduce how much would be drawn, and instead it would create unnecessary - // changes to scissor state affecting GPU performance. - layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants; - if (layer_or_ancestor_clips_descendants) { - layer_draw_properties.clip_rect = clip_rect_in_target_space; - } else { - // Initialize the clip rect to a safe value that will not clip the - // layer, just in case clipping is still accidentally used. - layer_draw_properties.clip_rect = rect_in_target_space; - } - // Compute the layer's visible content rect (the rect is in content space). layer_draw_properties.visible_content_rect = CalculateVisibleContentRect( layer, clip_rect_of_target_surface_in_target_space, rect_in_target_space); @@ -1535,7 +1825,7 @@ static void CalculateDrawPropertiesInternal( // The root layer's surface's content_rect is always the entire viewport. DCHECK(layer->render_surface()); layer->render_surface()->SetContentRect( - data_from_ancestor.clip_rect_in_target_space); + ancestor_clip_rect_in_target_space); } else if (layer->render_surface() && !IsRootLayer(layer)) { RenderSurfaceType* render_surface = layer->render_surface(); gfx::Rect clipped_content_rect = local_drawable_content_rect_of_subtree; @@ -1548,8 +1838,7 @@ static void CalculateDrawPropertiesInternal( // Note, it is correct to use data_from_ancestor.ancestor_clips_subtree // here, because we are looking at this layer's render_surface, not the // layer itself. - if (data_from_ancestor.ancestor_clips_subtree && - !clipped_content_rect.IsEmpty()) { + if (render_surface->is_clipped() && !clipped_content_rect.IsEmpty()) { gfx::Rect surface_clip_rect = LayerTreeHostCommon::CalculateVisibleRect( render_surface->clip_rect(), clipped_content_rect, @@ -1620,8 +1909,10 @@ static void CalculateDrawPropertiesInternal( SavePaintPropertiesLayer(layer); // If neither this layer nor any of its children were added, early out. - if (sorting_start_index == descendants.size()) + if (sorting_start_index == descendants.size()) { + DCHECK(!layer->render_surface() || IsRootLayer(layer)); return; + } // If preserves-3d then sort all the descendants in 3D so that they can be // drawn from back to front. If the preserves-3d property is also set on the @@ -1634,12 +1925,8 @@ static void CalculateDrawPropertiesInternal( globals.layer_sorter); } - if (layer->render_surface()) { - *drawable_content_rect_of_subtree = - gfx::ToEnclosingRect(layer->render_surface()->DrawableContentRect()); - } else { - *drawable_content_rect_of_subtree = local_drawable_content_rect_of_subtree; - } + UpdateAccumulatedSurfaceState<LayerType, RenderSurfaceType>( + layer, local_drawable_content_rect_of_subtree, accumulated_surface_state); if (layer->HasContributingDelegatedRenderPasses()) { layer->render_target()->render_surface()-> @@ -1652,7 +1939,6 @@ void LayerTreeHostCommon::CalculateDrawProperties( DCHECK(inputs->root_layer); DCHECK(IsRootLayer(inputs->root_layer)); DCHECK(inputs->render_surface_layer_list); - gfx::Rect total_drawable_content_rect; gfx::Transform identity_matrix; gfx::Transform scaled_device_transform = inputs->device_transform; scaled_device_transform.Scale(inputs->device_scale_factor, @@ -1687,14 +1973,14 @@ void LayerTreeHostCommon::CalculateDrawProperties( PreCalculateMetaInformationRecursiveData recursive_data; PreCalculateMetaInformation(inputs->root_layer, &recursive_data); - + std::vector<AccumulatedSurfaceState<Layer> > accumulated_surface_state; CalculateDrawPropertiesInternal<Layer, RenderSurfaceLayerList, RenderSurface>( inputs->root_layer, globals, data_for_recursion, inputs->render_surface_layer_list, &dummy_layer_list, - &total_drawable_content_rect); + &accumulated_surface_state); // The dummy layer list should not have been used. DCHECK_EQ(0u, dummy_layer_list.size()); @@ -1709,7 +1995,6 @@ void LayerTreeHostCommon::CalculateDrawProperties( DCHECK(IsRootLayer(inputs->root_layer)); DCHECK(inputs->render_surface_layer_list); - gfx::Rect total_drawable_content_rect; gfx::Transform identity_matrix; gfx::Transform scaled_device_transform = inputs->device_transform; scaled_device_transform.Scale(inputs->device_scale_factor, @@ -1745,14 +2030,15 @@ void LayerTreeHostCommon::CalculateDrawProperties( PreCalculateMetaInformationRecursiveData recursive_data; PreCalculateMetaInformation(inputs->root_layer, &recursive_data); - + std::vector<AccumulatedSurfaceState<LayerImpl> > + accumulated_surface_state; CalculateDrawPropertiesInternal<LayerImpl, LayerImplList, RenderSurfaceImpl>( inputs->root_layer, globals, data_for_recursion, inputs->render_surface_layer_list, &dummy_layer_list, - &total_drawable_content_rect); + &accumulated_surface_state); // The dummy layer list should not have been used. DCHECK_EQ(0u, dummy_layer_list.size()); diff --git a/chromium/cc/trees/layer_tree_host_common_unittest.cc b/chromium/cc/trees/layer_tree_host_common_unittest.cc index fa5450d45f4..a5546192365 100644 --- a/chromium/cc/trees/layer_tree_host_common_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_common_unittest.cc @@ -164,6 +164,10 @@ class LayerTreeHostCommonTestBase { false); } + RenderSurfaceLayerList* render_surface_layer_list() const { + return render_surface_layer_list_.get(); + } + private: scoped_ptr<RenderSurfaceLayerList> render_surface_layer_list_; }; @@ -1368,29 +1372,25 @@ TEST_F(LayerTreeHostCommonTest, TransformsForRenderSurfaceHierarchy) { // Sanity check. If these fail there is probably a bug in the test itself. It // is expected that we correctly set up transforms so that the y-component of // the screen-space transform encodes the "depth" of the layer in the tree. - EXPECT_FLOAT_EQ(1.0, - parent->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 2.0, child_of_root->screen_space_transform().matrix().getDouble(1, 3)); + EXPECT_FLOAT_EQ(1.0, parent->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(2.0, + child_of_root->screen_space_transform().matrix().get(1, 3)); EXPECT_FLOAT_EQ( - 3.0, - grand_child_of_root->screen_space_transform().matrix().getDouble(1, 3)); + 3.0, grand_child_of_root->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(2.0, + render_surface1->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(3.0, + child_of_rs1->screen_space_transform().matrix().get(1, 3)); EXPECT_FLOAT_EQ( - 2.0, render_surface1->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 3.0, child_of_rs1->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 4.0, - grand_child_of_rs1->screen_space_transform().matrix().getDouble(1, 3)); + 4.0, grand_child_of_rs1->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(3.0, + render_surface2->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(4.0, + child_of_rs2->screen_space_transform().matrix().get(1, 3)); EXPECT_FLOAT_EQ( - 3.0, render_surface2->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 4.0, child_of_rs2->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 5.0, - grand_child_of_rs2->screen_space_transform().matrix().getDouble(1, 3)); + 5.0, grand_child_of_rs2->screen_space_transform().matrix().get(1, 3)); } TEST_F(LayerTreeHostCommonTest, TransformsForFlatteningLayer) { @@ -1660,8 +1660,10 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { compositeSquared.ConcatTransform(composite); gfx::Transform compositeCubed = compositeSquared; compositeCubed.ConcatTransform(composite); - EXPECT_EQ(compositeSquared, root->draw_properties().target_space_transform); - EXPECT_EQ(compositeCubed, child->draw_properties().target_space_transform); + EXPECT_TRANSFORMATION_MATRIX_EQ( + compositeSquared, root->draw_properties().target_space_transform); + EXPECT_TRANSFORMATION_MATRIX_EQ( + compositeCubed, child->draw_properties().target_space_transform); EXPECT_EQ(identity_matrix, root->render_surface()->draw_transform()); } } @@ -2634,29 +2636,25 @@ TEST_F(LayerTreeHostCommonTest, AnimationsForRenderSurfaceHierarchy) { // Sanity check. If these fail there is probably a bug in the test itself. // It is expected that we correctly set up transforms so that the y-component // of the screen-space transform encodes the "depth" of the layer in the tree. - EXPECT_FLOAT_EQ(1.0, - parent->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 2.0, child_of_root->screen_space_transform().matrix().getDouble(1, 3)); + EXPECT_FLOAT_EQ(1.0, parent->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(2.0, + child_of_root->screen_space_transform().matrix().get(1, 3)); EXPECT_FLOAT_EQ( - 3.0, - grand_child_of_root->screen_space_transform().matrix().getDouble(1, 3)); + 3.0, grand_child_of_root->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(2.0, + render_surface1->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(3.0, + child_of_rs1->screen_space_transform().matrix().get(1, 3)); EXPECT_FLOAT_EQ( - 2.0, render_surface1->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 3.0, child_of_rs1->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 4.0, - grand_child_of_rs1->screen_space_transform().matrix().getDouble(1, 3)); + 4.0, grand_child_of_rs1->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(3.0, + render_surface2->screen_space_transform().matrix().get(1, 3)); + EXPECT_FLOAT_EQ(4.0, + child_of_rs2->screen_space_transform().matrix().get(1, 3)); EXPECT_FLOAT_EQ( - 3.0, render_surface2->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 4.0, child_of_rs2->screen_space_transform().matrix().getDouble(1, 3)); - EXPECT_FLOAT_EQ( - 5.0, - grand_child_of_rs2->screen_space_transform().matrix().getDouble(1, 3)); + 5.0, grand_child_of_rs2->screen_space_transform().matrix().get(1, 3)); } TEST_F(LayerTreeHostCommonTest, VisibleRectForIdentityTransform) { @@ -2792,7 +2790,8 @@ TEST_F(LayerTreeHostCommonTest, VisibleRectFor3dOrthographicTransform) { // degrees, but shifted to the side so only the right-half the layer would be // visible on the surface. // 100 is the un-rotated layer width; divided by sqrt(2) is the rotated width. - double half_width_of_rotated_layer = (100.0 / sqrt(2.0)) * 0.5; + SkMScalar half_width_of_rotated_layer = + SkDoubleToMScalar((100.0 / sqrt(2.0)) * 0.5); layer_to_surface_transform.MakeIdentity(); layer_to_surface_transform.Translate(-half_width_of_rotated_layer, 0.0); layer_to_surface_transform.RotateAboutYAxis(45.0); // Rotates about the left @@ -3185,8 +3184,10 @@ TEST_F(LayerTreeHostCommonTest, scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); host->SetRootLayer(root); + // Case 1: a truly degenerate matrix gfx::Transform identity_matrix; gfx::Transform uninvertible_matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + ASSERT_FALSE(uninvertible_matrix.IsInvertible()); SetLayerPropertiesForTesting(root.get(), identity_matrix, @@ -3207,6 +3208,48 @@ TEST_F(LayerTreeHostCommonTest, EXPECT_TRUE(child->visible_content_rect().IsEmpty()); EXPECT_TRUE(child->drawable_content_rect().IsEmpty()); + + // Case 2: a matrix with flattened z, technically uninvertible but still + // drawable and visible. In this case, we must assume that the entire layer + // bounds are visible since there is no way to inverse-project the surface + // bounds to intersect. + uninvertible_matrix.MakeIdentity(); + uninvertible_matrix.matrix().set(2, 2, 0.0); + ASSERT_FALSE(uninvertible_matrix.IsInvertible()); + + SetLayerPropertiesForTesting(child.get(), + uninvertible_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(5.f, 5.f), + gfx::Size(50, 50), + false); + + ExecuteCalculateDrawProperties(root.get()); + + EXPECT_RECT_EQ(gfx::Rect(0, 0, 50, 50), child->visible_content_rect()); + EXPECT_RECT_EQ(gfx::Rect(5, 5, 50, 50), child->drawable_content_rect()); + + // Case 3: a matrix with flattened z, technically uninvertible but still + // drawable, but not visible. In this case, we don't need to conservatively + // assume that the whole layer is visible. + uninvertible_matrix.MakeIdentity(); + uninvertible_matrix.Translate(500.0, 0.0); + uninvertible_matrix.matrix().set(2, 2, 0.0); + ASSERT_FALSE(uninvertible_matrix.IsInvertible()); + + SetLayerPropertiesForTesting(child.get(), + uninvertible_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(5.f, 5.f), + gfx::Size(50, 50), + false); + + ExecuteCalculateDrawProperties(root.get()); + + EXPECT_TRUE(child->visible_content_rect().IsEmpty()); + EXPECT_RECT_EQ(gfx::Rect(505, 5, 50, 50), child->drawable_content_rect()); } TEST_F(LayerTreeHostCommonTest, @@ -3460,10 +3503,10 @@ TEST_F(LayerTreeHostCommonTest, // regions of the subtree. int diagonal_radius = ceil(sqrt(2.0) * 25.0); gfx::Rect expected_surface_drawable_content = - gfx::Rect(50.0 - diagonal_radius, - 50.0 - diagonal_radius, - diagonal_radius * 2.0, - diagonal_radius * 2.0); + gfx::Rect(50 - diagonal_radius, + 50 - diagonal_radius, + diagonal_radius * 2, + diagonal_radius * 2); EXPECT_RECT_EQ(expected_surface_drawable_content, render_surface1->render_surface()->DrawableContentRect()); @@ -3522,10 +3565,10 @@ TEST_F(LayerTreeHostCommonTest, // The clipped surface clamps the DrawableContentRect that encloses the // rotated layer. int diagonal_radius = ceil(sqrt(2.0) * 25.0); - gfx::Rect unclipped_surface_content = gfx::Rect(50.0 - diagonal_radius, - 50.0 - diagonal_radius, - diagonal_radius * 2.0, - diagonal_radius * 2.0); + gfx::Rect unclipped_surface_content = gfx::Rect(50 - diagonal_radius, + 50 - diagonal_radius, + diagonal_radius * 2, + diagonal_radius * 2); gfx::Rect expected_surface_drawable_content = gfx::IntersectRects(unclipped_surface_content, gfx::Rect(0, 0, 50, 50)); EXPECT_RECT_EQ(expected_surface_drawable_content, @@ -3534,6 +3577,8 @@ TEST_F(LayerTreeHostCommonTest, // On the clipped surface, only a quarter of the child1 is visible, but when // rotating it back to child1's content space, the actual enclosing rect ends // up covering the full left half of child1. + // + // Given the floating point math, this number is a little bit fuzzy. EXPECT_RECT_EQ(gfx::Rect(0, 0, 26, 50), child1->visible_content_rect()); // The child's DrawableContentRect is unclipped. @@ -4420,10 +4465,10 @@ TEST_F(LayerTreeHostCommonTest, HitTestingForUninvertibleTransform) { LayerImpl::Create(host_impl.active_tree(), 12345); gfx::Transform uninvertible_transform; - uninvertible_transform.matrix().setDouble(0, 0, 0.0); - uninvertible_transform.matrix().setDouble(1, 1, 0.0); - uninvertible_transform.matrix().setDouble(2, 2, 0.0); - uninvertible_transform.matrix().setDouble(3, 3, 0.0); + uninvertible_transform.matrix().set(0, 0, 0.0); + uninvertible_transform.matrix().set(1, 1, 0.0); + uninvertible_transform.matrix().set(2, 2, 0.0); + uninvertible_transform.matrix().set(3, 3, 0.0); ASSERT_FALSE(uninvertible_transform.IsInvertible()); gfx::Transform identity_matrix; @@ -5006,7 +5051,7 @@ TEST_F(LayerTreeHostCommonTest, HitTestingForMultiClippedRotatedLayer) { // Around the middle, just to the right and up, would have hit the layer // except that that area should be clipped away by the parent. - test_point = gfx::Point(51, 51); + test_point = gfx::Point(51, 49); result_layer = LayerTreeHostCommon::FindLayerThatIsHitByPoint( test_point, render_surface_layer_list); EXPECT_FALSE(result_layer); @@ -5519,10 +5564,10 @@ TEST_F(LayerTreeHostCommonTest, LayerImpl::Create(host_impl.active_tree(), 12345); gfx::Transform uninvertible_transform; - uninvertible_transform.matrix().setDouble(0, 0, 0.0); - uninvertible_transform.matrix().setDouble(1, 1, 0.0); - uninvertible_transform.matrix().setDouble(2, 2, 0.0); - uninvertible_transform.matrix().setDouble(3, 3, 0.0); + uninvertible_transform.matrix().set(0, 0, 0.0); + uninvertible_transform.matrix().set(1, 1, 0.0); + uninvertible_transform.matrix().set(2, 2, 0.0); + uninvertible_transform.matrix().set(3, 3, 0.0); ASSERT_FALSE(uninvertible_transform.IsInvertible()); gfx::Transform identity_matrix; @@ -6422,11 +6467,11 @@ TEST_F(LayerTreeHostCommonTest, ContentsScale) { gfx::Transform identity_matrix; gfx::Transform parent_scale_matrix; - double initial_parent_scale = 1.75; + SkMScalar initial_parent_scale = 1.75; parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale); gfx::Transform child_scale_matrix; - double initial_child_scale = 1.25; + SkMScalar initial_child_scale = 1.25; child_scale_matrix.Scale(initial_child_scale, initial_child_scale); scoped_refptr<Layer> root = Layer::Create(); @@ -6507,22 +6552,18 @@ TEST_F(LayerTreeHostCommonTest, ContentsScale) { // child that can scale its contents should also not need to scale during // draw. This shouldn't change if the child has empty bounds. The other // children should. - EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().getDouble(0, 0)); - EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().getDouble(1, 1)); - EXPECT_FLOAT_EQ(1.0, - child_scale->draw_transform().matrix().getDouble(0, 0)); - EXPECT_FLOAT_EQ(1.0, - child_scale->draw_transform().matrix().getDouble(1, 1)); - EXPECT_FLOAT_EQ(1.0, - child_empty->draw_transform().matrix().getDouble(0, 0)); - EXPECT_FLOAT_EQ(1.0, - child_empty->draw_transform().matrix().getDouble(1, 1)); + EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().get(0, 0)); + EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().get(1, 1)); + EXPECT_FLOAT_EQ(1.0, child_scale->draw_transform().matrix().get(0, 0)); + EXPECT_FLOAT_EQ(1.0, child_scale->draw_transform().matrix().get(1, 1)); + EXPECT_FLOAT_EQ(1.0, child_empty->draw_transform().matrix().get(0, 0)); + EXPECT_FLOAT_EQ(1.0, child_empty->draw_transform().matrix().get(1, 1)); EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor * initial_parent_scale * initial_child_scale, - child_no_scale->draw_transform().matrix().getDouble(0, 0)); + child_no_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor * - initial_parent_scale * initial_child_scale, - child_no_scale->draw_transform().matrix().getDouble(1, 1)); + initial_parent_scale * initial_child_scale, + child_no_scale->draw_transform().matrix().get(1, 1)); } // If the device_scale_factor or page_scale_factor changes, then it should be @@ -6552,7 +6593,7 @@ TEST_F(LayerTreeHostCommonTest, ContentsScale) { } // If the transform changes, we expect the raster scale to be reset to 1.0. - double second_child_scale = 1.75; + SkMScalar second_child_scale = 1.75; child_scale_matrix.Scale(second_child_scale / initial_child_scale, second_child_scale / initial_child_scale); child_scale->SetTransform(child_scale_matrix); @@ -6610,11 +6651,11 @@ TEST_F(LayerTreeHostCommonTest, gfx::Transform identity_matrix; gfx::Transform parent_scale_matrix; - double initial_parent_scale = 1.75; + SkMScalar initial_parent_scale = 1.75; parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale); gfx::Transform child_scale_matrix; - double initial_child_scale = 1.25; + SkMScalar initial_child_scale = 1.25; child_scale_matrix.Scale(initial_child_scale, initial_child_scale); scoped_refptr<Layer> root = Layer::Create(); @@ -6690,23 +6731,23 @@ TEST_F(LayerTreeHostCommonTest, // Since the transform scale does not affect contents scale, it should affect // the draw transform instead. EXPECT_FLOAT_EQ(initial_parent_scale, - parent->draw_transform().matrix().getDouble(0, 0)); + parent->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ(initial_parent_scale, - parent->draw_transform().matrix().getDouble(1, 1)); + parent->draw_transform().matrix().get(1, 1)); EXPECT_FLOAT_EQ(initial_parent_scale * initial_child_scale, - child_scale->draw_transform().matrix().getDouble(0, 0)); + child_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ(initial_parent_scale * initial_child_scale, - child_scale->draw_transform().matrix().getDouble(1, 1)); + child_scale->draw_transform().matrix().get(1, 1)); EXPECT_FLOAT_EQ(initial_parent_scale * initial_child_scale, - child_empty->draw_transform().matrix().getDouble(0, 0)); + child_empty->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ(initial_parent_scale * initial_child_scale, - child_empty->draw_transform().matrix().getDouble(1, 1)); + child_empty->draw_transform().matrix().get(1, 1)); EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor * - initial_parent_scale * initial_child_scale, - child_no_scale->draw_transform().matrix().getDouble(0, 0)); + initial_parent_scale * initial_child_scale, + child_no_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor * - initial_parent_scale * initial_child_scale, - child_no_scale->draw_transform().matrix().getDouble(1, 1)); + initial_parent_scale * initial_child_scale, + child_no_scale->draw_transform().matrix().get(1, 1)); } TEST_F(LayerTreeHostCommonTest, SmallContentsScale) { @@ -6714,11 +6755,11 @@ TEST_F(LayerTreeHostCommonTest, SmallContentsScale) { gfx::Transform identity_matrix; gfx::Transform parent_scale_matrix; - double initial_parent_scale = 1.75; + SkMScalar initial_parent_scale = 1.75; parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale); gfx::Transform child_scale_matrix; - double initial_child_scale = 0.25; + SkMScalar initial_child_scale = 0.25; child_scale_matrix.Scale(initial_child_scale, initial_child_scale); scoped_refptr<Layer> root = Layer::Create(); @@ -6775,7 +6816,7 @@ TEST_F(LayerTreeHostCommonTest, SmallContentsScale) { // When chilld's total scale becomes >= 1, we should save and use that scale // factor. child_scale_matrix.MakeIdentity(); - double final_child_scale = 0.75; + SkMScalar final_child_scale = 0.75; child_scale_matrix.Scale(final_child_scale, final_child_scale); child_scale->SetTransform(child_scale_matrix); @@ -6803,11 +6844,11 @@ TEST_F(LayerTreeHostCommonTest, ContentsScaleForSurfaces) { gfx::Transform identity_matrix; gfx::Transform parent_scale_matrix; - double initial_parent_scale = 2.0; + SkMScalar initial_parent_scale = 2.0; parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale); gfx::Transform child_scale_matrix; - double initial_child_scale = 3.0; + SkMScalar initial_child_scale = 3.0; child_scale_matrix.Scale(initial_child_scale, initial_child_scale); scoped_refptr<Layer> root = Layer::Create(); @@ -6898,8 +6939,8 @@ TEST_F(LayerTreeHostCommonTest, ContentsScaleForSurfaces) { scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); host->SetRootLayer(root); - double device_scale_factor = 5; - double page_scale_factor = 7; + SkMScalar device_scale_factor = 5; + SkMScalar page_scale_factor = 7; RenderSurfaceLayerList render_surface_layer_list; LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( @@ -6928,86 +6969,74 @@ TEST_F(LayerTreeHostCommonTest, ContentsScaleForSurfaces) { EXPECT_CONTENTS_SCALE_EQ(1, surface_no_scale_child_no_scale); // The parent is scaled up and shouldn't need to scale during draw. - EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().getDouble(0, 0)); - EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().getDouble(1, 1)); + EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().get(0, 0)); + EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().get(1, 1)); // RenderSurfaces should always be 1:1 with their target. EXPECT_FLOAT_EQ( 1.0, - surface_scale->render_surface()->draw_transform().matrix().getDouble(0, - 0)); + surface_scale->render_surface()->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( 1.0, - surface_scale->render_surface()->draw_transform().matrix().getDouble(1, - 1)); + surface_scale->render_surface()->draw_transform().matrix().get(1, 1)); // The surface_scale can apply contents scale so the layer shouldn't need to // scale during draw. - EXPECT_FLOAT_EQ(1.0, - surface_scale->draw_transform().matrix().getDouble(0, 0)); - EXPECT_FLOAT_EQ(1.0, - surface_scale->draw_transform().matrix().getDouble(1, 1)); + EXPECT_FLOAT_EQ(1.0, surface_scale->draw_transform().matrix().get(0, 0)); + EXPECT_FLOAT_EQ(1.0, surface_scale->draw_transform().matrix().get(1, 1)); // The surface_scale_child_scale can apply contents scale so it shouldn't need // to scale during draw. EXPECT_FLOAT_EQ( - 1.0, - surface_scale_child_scale->draw_transform().matrix().getDouble(0, 0)); + 1.0, surface_scale_child_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( - 1.0, - surface_scale_child_scale->draw_transform().matrix().getDouble(1, 1)); + 1.0, surface_scale_child_scale->draw_transform().matrix().get(1, 1)); // The surface_scale_child_no_scale can not apply contents scale, so it needs // to be scaled during draw. EXPECT_FLOAT_EQ( device_scale_factor * page_scale_factor * initial_parent_scale * - initial_child_scale * initial_child_scale, - surface_scale_child_no_scale->draw_transform().matrix().getDouble(0, 0)); + initial_child_scale * initial_child_scale, + surface_scale_child_no_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( device_scale_factor * page_scale_factor * initial_parent_scale * - initial_child_scale * initial_child_scale, - surface_scale_child_no_scale->draw_transform().matrix().getDouble(1, 1)); + initial_child_scale * initial_child_scale, + surface_scale_child_no_scale->draw_transform().matrix().get(1, 1)); // RenderSurfaces should always be 1:1 with their target. EXPECT_FLOAT_EQ( 1.0, - surface_no_scale->render_surface()->draw_transform().matrix().getDouble( - 0, 0)); + surface_no_scale->render_surface()->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( 1.0, - surface_no_scale->render_surface()->draw_transform().matrix().getDouble( - 1, 1)); + surface_no_scale->render_surface()->draw_transform().matrix().get(1, 1)); // The surface_no_scale layer can not apply contents scale, so it needs to be // scaled during draw. EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor * - initial_parent_scale * initial_child_scale, - surface_no_scale->draw_transform().matrix().getDouble(0, 0)); + initial_parent_scale * initial_child_scale, + surface_no_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor * - initial_parent_scale * initial_child_scale, - surface_no_scale->draw_transform().matrix().getDouble(1, 1)); + initial_parent_scale * initial_child_scale, + surface_no_scale->draw_transform().matrix().get(1, 1)); // The surface_scale_child_scale can apply contents scale so it shouldn't need // to scale during draw. EXPECT_FLOAT_EQ( - 1.0, - surface_no_scale_child_scale->draw_transform().matrix().getDouble(0, 0)); + 1.0, surface_no_scale_child_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( - 1.0, - surface_no_scale_child_scale->draw_transform().matrix().getDouble(1, 1)); + 1.0, surface_no_scale_child_scale->draw_transform().matrix().get(1, 1)); // The surface_scale_child_no_scale can not apply contents scale, so it needs // to be scaled during draw. EXPECT_FLOAT_EQ( device_scale_factor * page_scale_factor * initial_parent_scale * - initial_child_scale * initial_child_scale, - surface_no_scale_child_no_scale->draw_transform().matrix().getDouble(0, - 0)); + initial_child_scale * initial_child_scale, + surface_no_scale_child_no_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( device_scale_factor * page_scale_factor * initial_parent_scale * - initial_child_scale * initial_child_scale, - surface_no_scale_child_no_scale->draw_transform().matrix().getDouble(1, - 1)); + initial_child_scale * initial_child_scale, + surface_no_scale_child_no_scale->draw_transform().matrix().get(1, 1)); } TEST_F(LayerTreeHostCommonTest, @@ -7016,11 +7045,11 @@ TEST_F(LayerTreeHostCommonTest, gfx::Transform identity_matrix; gfx::Transform parent_scale_matrix; - double initial_parent_scale = 2.0; + SkMScalar initial_parent_scale = 2.0; parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale); gfx::Transform child_scale_matrix; - double initial_child_scale = 3.0; + SkMScalar initial_child_scale = 3.0; child_scale_matrix.Scale(initial_child_scale, initial_child_scale); scoped_refptr<Layer> root = Layer::Create(); @@ -7113,8 +7142,8 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerList render_surface_layer_list; - double device_scale_factor = 5.0; - double page_scale_factor = 7.0; + SkMScalar device_scale_factor = 5.0; + SkMScalar page_scale_factor = 7.0; LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( root.get(), root->bounds(), &render_surface_layer_list); inputs.device_scale_factor = device_scale_factor; @@ -7137,88 +7166,80 @@ TEST_F(LayerTreeHostCommonTest, // The parent is scaled up during draw, since its contents are not scaled by // the transform hierarchy. EXPECT_FLOAT_EQ(initial_parent_scale, - parent->draw_transform().matrix().getDouble(0, 0)); + parent->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ(initial_parent_scale, - parent->draw_transform().matrix().getDouble(1, 1)); + parent->draw_transform().matrix().get(1, 1)); // The child surface is scaled up during draw since its subtree is not scaled // by the transform hierarchy. EXPECT_FLOAT_EQ( initial_parent_scale * initial_child_scale, - surface_scale->render_surface()->draw_transform().matrix().getDouble(0, - 0)); + surface_scale->render_surface()->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( initial_parent_scale * initial_child_scale, - surface_scale->render_surface()->draw_transform().matrix().getDouble(1, - 1)); + surface_scale->render_surface()->draw_transform().matrix().get(1, 1)); // The surface_scale's RenderSurface is scaled during draw, so the layer does // not need to be scaled when drawing into its surface. - EXPECT_FLOAT_EQ(1.0, - surface_scale->draw_transform().matrix().getDouble(0, 0)); - EXPECT_FLOAT_EQ(1.0, - surface_scale->draw_transform().matrix().getDouble(1, 1)); + EXPECT_FLOAT_EQ(1.0, surface_scale->draw_transform().matrix().get(0, 0)); + EXPECT_FLOAT_EQ(1.0, surface_scale->draw_transform().matrix().get(1, 1)); // The surface_scale_child_scale is scaled when drawing into its surface, // since its content bounds are not scaled by the transform hierarchy. EXPECT_FLOAT_EQ( initial_child_scale, - surface_scale_child_scale->draw_transform().matrix().getDouble(0, 0)); + surface_scale_child_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( initial_child_scale, - surface_scale_child_scale->draw_transform().matrix().getDouble(1, 1)); + surface_scale_child_scale->draw_transform().matrix().get(1, 1)); // The surface_scale_child_no_scale has a fixed contents scale of 1, so it // needs to be scaled by the device and page scale factors, along with the // transform hierarchy. EXPECT_FLOAT_EQ( device_scale_factor * page_scale_factor * initial_child_scale, - surface_scale_child_no_scale->draw_transform().matrix().getDouble(0, 0)); + surface_scale_child_no_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( device_scale_factor * page_scale_factor * initial_child_scale, - surface_scale_child_no_scale->draw_transform().matrix().getDouble(1, 1)); + surface_scale_child_no_scale->draw_transform().matrix().get(1, 1)); // The child surface is scaled up during draw since its subtree is not scaled // by the transform hierarchy. EXPECT_FLOAT_EQ( initial_parent_scale * initial_child_scale, - surface_no_scale->render_surface()->draw_transform().matrix().getDouble( - 0, 0)); + surface_no_scale->render_surface()->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( initial_parent_scale * initial_child_scale, - surface_no_scale->render_surface()->draw_transform().matrix().getDouble( - 1, 1)); + surface_no_scale->render_surface()->draw_transform().matrix().get(1, 1)); // The surface_no_scale layer has a fixed contents scale of 1, so it needs to // be scaled by the device and page scale factors. Its surface is already // scaled by the transform hierarchy so those don't need to scale the layer's // drawing. EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor, - surface_no_scale->draw_transform().matrix().getDouble(0, 0)); + surface_no_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor, - surface_no_scale->draw_transform().matrix().getDouble(1, 1)); + surface_no_scale->draw_transform().matrix().get(1, 1)); // The surface_no_scale_child_scale has its contents scaled by the page and // device scale factors, but needs to be scaled by the transform hierarchy // when drawing. EXPECT_FLOAT_EQ( initial_child_scale, - surface_no_scale_child_scale->draw_transform().matrix().getDouble(0, 0)); + surface_no_scale_child_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( initial_child_scale, - surface_no_scale_child_scale->draw_transform().matrix().getDouble(1, 1)); + surface_no_scale_child_scale->draw_transform().matrix().get(1, 1)); // The surface_no_scale_child_no_scale has a fixed contents scale of 1, so it // needs to be scaled by the device and page scale factors. It also needs to // be scaled by any transform heirarchy below its target surface. EXPECT_FLOAT_EQ( device_scale_factor * page_scale_factor * initial_child_scale, - surface_no_scale_child_no_scale->draw_transform().matrix().getDouble(0, - 0)); + surface_no_scale_child_no_scale->draw_transform().matrix().get(0, 0)); EXPECT_FLOAT_EQ( device_scale_factor * page_scale_factor * initial_child_scale, - surface_no_scale_child_no_scale->draw_transform().matrix().getDouble(1, - 1)); + surface_no_scale_child_no_scale->draw_transform().matrix().get(1, 1)); } TEST_F(LayerTreeHostCommonTest, ContentsScaleForAnimatingLayer) { @@ -7226,11 +7247,11 @@ TEST_F(LayerTreeHostCommonTest, ContentsScaleForAnimatingLayer) { gfx::Transform identity_matrix; gfx::Transform parent_scale_matrix; - double initial_parent_scale = 1.75; + SkMScalar initial_parent_scale = 1.75; parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale); gfx::Transform child_scale_matrix; - double initial_child_scale = 1.25; + SkMScalar initial_child_scale = 1.25; child_scale_matrix.Scale(initial_child_scale, initial_child_scale); scoped_refptr<Layer> root = Layer::Create(); @@ -7412,17 +7433,17 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceTransformsInHighDPI) { child->render_surface()->screen_space_transform()); gfx::Transform expected_replica_draw_transform; - expected_replica_draw_transform.matrix().setDouble(1, 1, -1.0); - expected_replica_draw_transform.matrix().setDouble(0, 3, 6.0); - expected_replica_draw_transform.matrix().setDouble(1, 3, 6.0); + expected_replica_draw_transform.matrix().set(1, 1, -1.0); + expected_replica_draw_transform.matrix().set(0, 3, 6.0); + expected_replica_draw_transform.matrix().set(1, 3, 6.0); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_replica_draw_transform, child->render_surface()->replica_draw_transform()); gfx::Transform expected_replica_screen_space_transform; - expected_replica_screen_space_transform.matrix().setDouble(1, 1, -1.0); - expected_replica_screen_space_transform.matrix().setDouble(0, 3, 6.0); - expected_replica_screen_space_transform.matrix().setDouble(1, 3, 6.0); + expected_replica_screen_space_transform.matrix().set(1, 1, -1.0); + expected_replica_screen_space_transform.matrix().set(0, 3, 6.0); + expected_replica_screen_space_transform.matrix().set(1, 3, 6.0); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_replica_screen_space_transform, child->render_surface()->replica_screen_space_transform()); @@ -7522,13 +7543,13 @@ TEST_F(LayerTreeHostCommonTest, identity_transform, child->render_surface()->screen_space_transform()); gfx::Transform expected_replica_draw_transform; - expected_replica_draw_transform.matrix().setDouble(1, 1, -1.0); + expected_replica_draw_transform.matrix().set(1, 1, -1.0); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_replica_draw_transform, child->render_surface()->replica_draw_transform()); gfx::Transform expected_replica_screen_space_transform; - expected_replica_screen_space_transform.matrix().setDouble(1, 1, -1.0); + expected_replica_screen_space_transform.matrix().set(1, 1, -1.0); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_replica_screen_space_transform, child->render_surface()->replica_screen_space_transform()); @@ -8306,5 +8327,736 @@ TEST_F(LayerTreeHostCommonTest, VisibleContentRectInsideSurface) { surface_child->visible_content_rect().ToString()); } +TEST_F(LayerTreeHostCommonTest, TransformedClipParent) { + // Ensure that a transform between the layer and its render surface is not a + // problem. Constructs the following layer tree. + // + // root (a render surface) + // + render_surface + // + clip_parent (scaled) + // + intervening_clipping_layer + // + clip_child + // + // The render surface should be resized correctly and the clip child should + // inherit the right clip rect. + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> render_surface = Layer::Create(); + scoped_refptr<Layer> clip_parent = Layer::Create(); + scoped_refptr<Layer> intervening = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> clip_child = + make_scoped_refptr(new LayerWithForcedDrawsContent); + + root->AddChild(render_surface); + render_surface->AddChild(clip_parent); + clip_parent->AddChild(intervening); + intervening->AddChild(clip_child); + + clip_child->SetClipParent(clip_parent.get()); + + intervening->SetMasksToBounds(true); + clip_parent->SetMasksToBounds(true); + + render_surface->SetForceRenderSurface(true); + + gfx::Transform scale_transform; + scale_transform.Scale(2, 2); + + gfx::Transform identity_transform; + + SetLayerPropertiesForTesting(root.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + SetLayerPropertiesForTesting(render_surface.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(10, 10), + false); + SetLayerPropertiesForTesting(clip_parent.get(), + scale_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(1.f, 1.f), + gfx::Size(10, 10), + false); + SetLayerPropertiesForTesting(intervening.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(1.f, 1.f), + gfx::Size(5, 5), + false); + SetLayerPropertiesForTesting(clip_child.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(1.f, 1.f), + gfx::Size(10, 10), + false); + + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + host->SetRootLayer(root); + + ExecuteCalculateDrawProperties(root.get()); + + ASSERT_TRUE(root->render_surface()); + ASSERT_TRUE(render_surface->render_surface()); + + // Ensure that we've inherited our clip parent's clip and weren't affected + // by the intervening clip layer. + ASSERT_EQ(gfx::Rect(1, 1, 20, 20).ToString(), + clip_parent->clip_rect().ToString()); + ASSERT_EQ(clip_parent->clip_rect().ToString(), + clip_child->clip_rect().ToString()); + ASSERT_EQ(gfx::Rect(3, 3, 10, 10).ToString(), + intervening->clip_rect().ToString()); + + // Ensure that the render surface reports a content rect that has been grown + // to accomodate for the clip child. + ASSERT_EQ(gfx::Rect(5, 5, 16, 16).ToString(), + render_surface->render_surface()->content_rect().ToString()); + + // The above check implies the two below, but they nicely demonstrate that + // we've grown, despite the intervening layer's clip. + ASSERT_TRUE(clip_parent->clip_rect().Contains( + render_surface->render_surface()->content_rect())); + ASSERT_FALSE(intervening->clip_rect().Contains( + render_surface->render_surface()->content_rect())); +} + +TEST_F(LayerTreeHostCommonTest, ClipParentWithInterveningRenderSurface) { + // Ensure that intervening render surfaces are not a problem in the basic + // case. In the following tree, both render surfaces should be resized to + // accomodate for the clip child, despite an intervening clip. + // + // root (a render surface) + // + clip_parent (masks to bounds) + // + render_surface1 (sets opacity) + // + intervening (masks to bounds) + // + render_surface2 (also sets opacity) + // + clip_child + // + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> clip_parent = Layer::Create(); + scoped_refptr<Layer> render_surface1 = Layer::Create(); + scoped_refptr<Layer> intervening = Layer::Create(); + scoped_refptr<Layer> render_surface2 = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> clip_child = + make_scoped_refptr(new LayerWithForcedDrawsContent); + + root->AddChild(clip_parent); + clip_parent->AddChild(render_surface1); + render_surface1->AddChild(intervening); + intervening->AddChild(render_surface2); + render_surface2->AddChild(clip_child); + + clip_child->SetClipParent(clip_parent.get()); + + intervening->SetMasksToBounds(true); + clip_parent->SetMasksToBounds(true); + + render_surface1->SetForceRenderSurface(true); + render_surface2->SetForceRenderSurface(true); + + gfx::Transform translation_transform; + translation_transform.Translate(2, 2); + + gfx::Transform identity_transform; + SetLayerPropertiesForTesting(root.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + SetLayerPropertiesForTesting(clip_parent.get(), + translation_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(1.f, 1.f), + gfx::Size(40, 40), + false); + SetLayerPropertiesForTesting(render_surface1.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(10, 10), + false); + SetLayerPropertiesForTesting(intervening.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(1.f, 1.f), + gfx::Size(5, 5), + false); + SetLayerPropertiesForTesting(render_surface2.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(10, 10), + false); + SetLayerPropertiesForTesting(clip_child.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(-10.f, -10.f), + gfx::Size(60, 60), + false); + + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + host->SetRootLayer(root); + + ExecuteCalculateDrawProperties(root.get()); + + EXPECT_TRUE(root->render_surface()); + EXPECT_TRUE(render_surface1->render_surface()); + EXPECT_TRUE(render_surface2->render_surface()); + + // Since the render surfaces could have expanded, they should not clip (their + // bounds would no longer be reliable). We should resort to layer clipping + // in this case. + EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(), + render_surface1->render_surface()->clip_rect().ToString()); + EXPECT_FALSE(render_surface1->render_surface()->is_clipped()); + EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(), + render_surface2->render_surface()->clip_rect().ToString()); + EXPECT_FALSE(render_surface2->render_surface()->is_clipped()); + + // NB: clip rects are in target space. + EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(), + render_surface1->clip_rect().ToString()); + EXPECT_TRUE(render_surface1->is_clipped()); + + // This value is inherited from the clipping ancestor layer, 'intervening'. + EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(), + render_surface2->clip_rect().ToString()); + EXPECT_TRUE(render_surface2->is_clipped()); + + // The content rects of both render surfaces should both have expanded to + // contain the clip child. + EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(), + render_surface1->render_surface()->content_rect().ToString()); + EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(), + render_surface2->render_surface()->content_rect().ToString()); + + // The clip child should have inherited the clip parent's clip (projected to + // the right space, of course), and should have the correctly sized visible + // content rect. + EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(), + clip_child->clip_rect().ToString()); + EXPECT_EQ(gfx::Rect(9, 9, 40, 40).ToString(), + clip_child->visible_content_rect().ToString()); + EXPECT_TRUE(clip_child->is_clipped()); +} + +TEST_F(LayerTreeHostCommonTest, ClipParentScrolledInterveningLayer) { + // Ensure that intervening render surfaces are not a problem, even if there + // is a scroll involved. Note, we do _not_ have to consider any other sort + // of transform. + // + // root (a render surface) + // + clip_parent (masks to bounds) + // + render_surface1 (sets opacity) + // + intervening (masks to bounds AND scrolls) + // + render_surface2 (also sets opacity) + // + clip_child + // + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> clip_parent = Layer::Create(); + scoped_refptr<Layer> render_surface1 = Layer::Create(); + scoped_refptr<Layer> intervening = Layer::Create(); + scoped_refptr<Layer> render_surface2 = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> clip_child = + make_scoped_refptr(new LayerWithForcedDrawsContent); + + root->AddChild(clip_parent); + clip_parent->AddChild(render_surface1); + render_surface1->AddChild(intervening); + intervening->AddChild(render_surface2); + render_surface2->AddChild(clip_child); + + clip_child->SetClipParent(clip_parent.get()); + + intervening->SetMasksToBounds(true); + clip_parent->SetMasksToBounds(true); + intervening->SetScrollable(true); + intervening->SetMaxScrollOffset(gfx::Vector2d(50, 50)); + intervening->SetScrollOffset(gfx::Vector2d(3, 3)); + + render_surface1->SetForceRenderSurface(true); + render_surface2->SetForceRenderSurface(true); + + gfx::Transform translation_transform; + translation_transform.Translate(2, 2); + + gfx::Transform identity_transform; + SetLayerPropertiesForTesting(root.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + SetLayerPropertiesForTesting(clip_parent.get(), + translation_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(1.f, 1.f), + gfx::Size(40, 40), + false); + SetLayerPropertiesForTesting(render_surface1.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(10, 10), + false); + SetLayerPropertiesForTesting(intervening.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(1.f, 1.f), + gfx::Size(5, 5), + false); + SetLayerPropertiesForTesting(render_surface2.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(10, 10), + false); + SetLayerPropertiesForTesting(clip_child.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(-10.f, -10.f), + gfx::Size(60, 60), + false); + + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + host->SetRootLayer(root); + + ExecuteCalculateDrawProperties(root.get()); + + EXPECT_TRUE(root->render_surface()); + EXPECT_TRUE(render_surface1->render_surface()); + EXPECT_TRUE(render_surface2->render_surface()); + + // Since the render surfaces could have expanded, they should not clip (their + // bounds would no longer be reliable). We should resort to layer clipping + // in this case. + EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(), + render_surface1->render_surface()->clip_rect().ToString()); + EXPECT_FALSE(render_surface1->render_surface()->is_clipped()); + EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(), + render_surface2->render_surface()->clip_rect().ToString()); + EXPECT_FALSE(render_surface2->render_surface()->is_clipped()); + + // NB: clip rects are in target space. + EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(), + render_surface1->clip_rect().ToString()); + EXPECT_TRUE(render_surface1->is_clipped()); + + // This value is inherited from the clipping ancestor layer, 'intervening'. + EXPECT_EQ(gfx::Rect(2, 2, 3, 3).ToString(), + render_surface2->clip_rect().ToString()); + EXPECT_TRUE(render_surface2->is_clipped()); + + // The content rects of both render surfaces should both have expanded to + // contain the clip child. + EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(), + render_surface1->render_surface()->content_rect().ToString()); + EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(), + render_surface2->render_surface()->content_rect().ToString()); + + // The clip child should have inherited the clip parent's clip (projected to + // the right space, of course), and should have the correctly sized visible + // content rect. + EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(), + clip_child->clip_rect().ToString()); + EXPECT_EQ(gfx::Rect(12, 12, 40, 40).ToString(), + clip_child->visible_content_rect().ToString()); + EXPECT_TRUE(clip_child->is_clipped()); +} + +TEST_F(LayerTreeHostCommonTest, DescendantsOfClipChildren) { + // Ensures that descendants of the clip child inherit the correct clip. + // + // root (a render surface) + // + clip_parent (masks to bounds) + // + intervening (masks to bounds) + // + clip_child + // + child + // + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> clip_parent = Layer::Create(); + scoped_refptr<Layer> intervening = Layer::Create(); + scoped_refptr<Layer> clip_child = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> child = + make_scoped_refptr(new LayerWithForcedDrawsContent); + + root->AddChild(clip_parent); + clip_parent->AddChild(intervening); + intervening->AddChild(clip_child); + clip_child->AddChild(child); + + clip_child->SetClipParent(clip_parent.get()); + + intervening->SetMasksToBounds(true); + clip_parent->SetMasksToBounds(true); + + gfx::Transform identity_transform; + SetLayerPropertiesForTesting(root.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + SetLayerPropertiesForTesting(clip_parent.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(40, 40), + false); + SetLayerPropertiesForTesting(intervening.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(5, 5), + false); + SetLayerPropertiesForTesting(clip_child.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(60, 60), + false); + SetLayerPropertiesForTesting(child.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(60, 60), + false); + + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + host->SetRootLayer(root); + + ExecuteCalculateDrawProperties(root.get()); + + EXPECT_TRUE(root->render_surface()); + + // Neither the clip child nor its descendant should have inherited the clip + // from |intervening|. + EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(), + clip_child->clip_rect().ToString()); + EXPECT_TRUE(clip_child->is_clipped()); + EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(), + child->visible_content_rect().ToString()); + EXPECT_TRUE(child->is_clipped()); +} + +TEST_F(LayerTreeHostCommonTest, + SurfacesShouldBeUnaffectedByNonDescendantClipChildren) { + // Ensures that non-descendant clip children in the tree do not affect + // render surfaces. + // + // root (a render surface) + // + clip_parent (masks to bounds) + // + render_surface1 + // + clip_child + // + render_surface2 + // + non_clip_child + // + // In this example render_surface2 should be unaffected by clip_child. + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> clip_parent = Layer::Create(); + scoped_refptr<Layer> render_surface1 = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> clip_child = + make_scoped_refptr(new LayerWithForcedDrawsContent); + scoped_refptr<Layer> render_surface2 = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> non_clip_child = + make_scoped_refptr(new LayerWithForcedDrawsContent); + + root->AddChild(clip_parent); + clip_parent->AddChild(render_surface1); + render_surface1->AddChild(clip_child); + clip_parent->AddChild(render_surface2); + render_surface2->AddChild(non_clip_child); + + clip_child->SetClipParent(clip_parent.get()); + + clip_parent->SetMasksToBounds(true); + render_surface1->SetMasksToBounds(true); + + gfx::Transform identity_transform; + SetLayerPropertiesForTesting(root.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(15, 15), + false); + SetLayerPropertiesForTesting(clip_parent.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(10, 10), + false); + SetLayerPropertiesForTesting(render_surface1.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(5, 5), + gfx::Size(5, 5), + false); + SetLayerPropertiesForTesting(render_surface2.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(5, 5), + false); + SetLayerPropertiesForTesting(clip_child.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(-1, 1), + gfx::Size(10, 10), + false); + SetLayerPropertiesForTesting(non_clip_child.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(5, 5), + false); + + render_surface1->SetForceRenderSurface(true); + render_surface2->SetForceRenderSurface(true); + + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + host->SetRootLayer(root); + + ExecuteCalculateDrawProperties(root.get()); + + EXPECT_TRUE(root->render_surface()); + EXPECT_TRUE(render_surface1->render_surface()); + EXPECT_TRUE(render_surface2->render_surface()); + + EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(), + render_surface1->clip_rect().ToString()); + EXPECT_TRUE(render_surface1->is_clipped()); + + // The render surface should not clip (it has unclipped descendants), instead + // it should rely on layer clipping. + EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(), + render_surface1->render_surface()->clip_rect().ToString()); + EXPECT_FALSE(render_surface1->render_surface()->is_clipped()); + + // That said, it should have grown to accomodate the unclipped descendant. + EXPECT_EQ(gfx::Rect(-1, 1, 6, 4).ToString(), + render_surface1->render_surface()->content_rect().ToString()); + + // This render surface should clip. It has no unclipped descendants. + EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(), + render_surface2->clip_rect().ToString()); + EXPECT_TRUE(render_surface2->render_surface()->is_clipped()); + + // It also shouldn't have grown to accomodate the clip child. + EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(), + render_surface2->render_surface()->content_rect().ToString()); + + // Sanity check our num_unclipped_descendants values. + EXPECT_EQ(1, render_surface1->num_unclipped_descendants()); + EXPECT_EQ(0, render_surface2->num_unclipped_descendants()); +} + +TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleSurfaces) { + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> render_surface = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> child = + make_scoped_refptr(new LayerWithForcedDrawsContent); + + root->AddChild(render_surface); + render_surface->AddChild(child); + + gfx::Transform identity_transform; + SetLayerPropertiesForTesting(root.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + SetLayerPropertiesForTesting(render_surface.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(30, 30), + false); + SetLayerPropertiesForTesting(child.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(20, 20), + false); + + root->SetPreserves3d(true); + render_surface->SetDoubleSided(false); + render_surface->SetForceRenderSurface(true); + + scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(); + host->SetRootLayer(root); + + ExecuteCalculateDrawProperties(root.get()); + + EXPECT_EQ(2u, render_surface_layer_list()->size()); + EXPECT_EQ(1u, + render_surface_layer_list()->at(0) + ->render_surface()->layer_list().size()); + EXPECT_EQ(1u, + render_surface_layer_list()->at(1) + ->render_surface()->layer_list().size()); + + gfx::Transform rotation_transform = identity_transform; + rotation_transform.RotateAboutXAxis(180.0); + + render_surface->SetTransform(rotation_transform); + + ExecuteCalculateDrawProperties(root.get()); + + EXPECT_EQ(1u, render_surface_layer_list()->size()); + EXPECT_EQ(0u, + render_surface_layer_list()->at(0) + ->render_surface()->layer_list().size()); +} + +TEST_F(LayerTreeHostCommonTest, ScrollCompensationWithRounding) { + // This test verifies that a scrolling layer that gets snapped to + // integer coordinates doesn't move a fixed position child. + // + // + root + // + container + // + scroller + // + fixed + // + FakeImplProxy proxy; + FakeLayerTreeHostImpl host_impl(&proxy); + host_impl.CreatePendingTree(); + scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.active_tree(), 1); + scoped_ptr<LayerImpl> container = + LayerImpl::Create(host_impl.active_tree(), 2); + LayerImpl* container_layer = container.get(); + scoped_ptr<LayerImpl> scroller = + LayerImpl::Create(host_impl.active_tree(), 3); + LayerImpl* scroll_layer = scroller.get(); + scoped_ptr<LayerImpl> fixed = LayerImpl::Create(host_impl.active_tree(), 4); + LayerImpl* fixed_layer = fixed.get(); + + container->SetIsContainerForFixedPositionLayers(true); + + LayerPositionConstraint constraint; + constraint.set_is_fixed_position(true); + fixed->SetPositionConstraint(constraint); + + scroller->SetScrollable(true); + + gfx::Transform identity_transform; + gfx::Transform container_transform; + container_transform.Translate3d(10.0, 20.0, 0.0); + gfx::Vector2dF container_offset = container_transform.To2dTranslation(); + + SetLayerPropertiesForTesting(root.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + SetLayerPropertiesForTesting(container.get(), + container_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(40, 40), + false); + SetLayerPropertiesForTesting(scroller.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(30, 30), + false); + SetLayerPropertiesForTesting(fixed.get(), + identity_transform, + identity_transform, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + + scroller->AddChild(fixed.Pass()); + container->AddChild(scroller.Pass()); + root->AddChild(container.Pass()); + + // Rounded to integers already. + { + gfx::Vector2dF scroll_delta(3.0, 5.0); + scroll_layer->SetScrollDelta(scroll_delta); + + LayerImplList render_surface_layer_list; + LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( + root.get(), root->bounds(), &render_surface_layer_list); + LayerTreeHostCommon::CalculateDrawProperties(&inputs); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + container_layer->draw_properties().screen_space_transform, + fixed_layer->draw_properties().screen_space_transform); + EXPECT_VECTOR_EQ( + fixed_layer->draw_properties().screen_space_transform.To2dTranslation(), + container_offset); + EXPECT_VECTOR_EQ(scroll_layer->draw_properties() + .screen_space_transform.To2dTranslation(), + container_offset - scroll_delta); + } + + // Scroll delta requiring rounding. + { + gfx::Vector2dF scroll_delta(4.1f, 8.1f); + scroll_layer->SetScrollDelta(scroll_delta); + + gfx::Vector2dF rounded_scroll_delta(4.f, 8.f); + + LayerImplList render_surface_layer_list; + LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( + root.get(), root->bounds(), &render_surface_layer_list); + LayerTreeHostCommon::CalculateDrawProperties(&inputs); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + container_layer->draw_properties().screen_space_transform, + fixed_layer->draw_properties().screen_space_transform); + EXPECT_VECTOR_EQ( + fixed_layer->draw_properties().screen_space_transform.To2dTranslation(), + container_offset); + EXPECT_VECTOR_EQ(scroll_layer->draw_properties() + .screen_space_transform.To2dTranslation(), + container_offset - rounded_scroll_delta); + } +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index 9519e745af4..a854dff2894 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -5,6 +5,7 @@ #include "cc/trees/layer_tree_host_impl.h" #include <algorithm> +#include <limits> #include "base/basictypes.h" #include "base/containers/hash_tables.h" @@ -28,8 +29,8 @@ #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/layers/layer_impl.h" #include "cc/layers/layer_iterator.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" #include "cc/layers/render_surface_impl.h" -#include "cc/layers/scrollbar_layer_impl.h" #include "cc/output/compositor_frame_metadata.h" #include "cc/output/copy_output_request.h" #include "cc/output/delegating_renderer.h" @@ -38,9 +39,11 @@ #include "cc/quads/render_pass_draw_quad.h" #include "cc/quads/shared_quad_state.h" #include "cc/quads/solid_color_draw_quad.h" +#include "cc/quads/texture_draw_quad.h" #include "cc/resources/memory_history.h" #include "cc/resources/picture_layer_tiling.h" #include "cc/resources/prioritized_resource_manager.h" +#include "cc/resources/texture_mailbox_deleter.h" #include "cc/resources/ui_resource_bitmap.h" #include "cc/scheduler/delay_based_time_source.h" #include "cc/scheduler/texture_uploader.h" @@ -69,6 +72,27 @@ void DidVisibilityChange(cc::LayerTreeHostImpl* id, bool visible) { TRACE_EVENT_ASYNC_END0("webkit", "LayerTreeHostImpl::SetVisible", id); } +size_t GetMaxTransferBufferUsageBytes(cc::ContextProvider* context_provider) { + if (context_provider) { + // We want to make sure the default transfer buffer size is equal to the + // amount of data that can be uploaded by the compositor to avoid stalling + // the pipeline. + // For reference Chromebook Pixel can upload 1MB in about 0.5ms. + const size_t kMaxBytesUploadedPerMs = 1024 * 1024 * 2; + // Assuming a two frame deep pipeline between CPU and GPU and we are + // drawing 60 frames per second which would require us to draw one + // frame in 16 milliseconds. + const size_t kMaxTransferBufferUsageBytes = 16 * 2 * kMaxBytesUploadedPerMs; + return std::min( + context_provider->ContextCapabilities().max_transfer_buffer_usage_bytes, + kMaxTransferBufferUsageBytes); + } else { + // Software compositing should not use this value in production. Just use a + // default value when testing uploads with the software compositor. + return std::numeric_limits<size_t>::max(); + } +} + } // namespace namespace cc { @@ -110,12 +134,8 @@ class LayerTreeHostImplTimeSourceAdapter : public TimeSourceClient { // TODO(enne): This should probably happen post-animate. if (layer_tree_host_impl_->pending_tree()) { - layer_tree_host_impl_->ActivatePendingTreeIfNeeded(); - - if (layer_tree_host_impl_->pending_tree()) { - layer_tree_host_impl_->pending_tree()->UpdateDrawProperties(); - layer_tree_host_impl_->ManageTiles(); - } + layer_tree_host_impl_->pending_tree()->UpdateDrawProperties(); + layer_tree_host_impl_->ManageTiles(); } layer_tree_host_impl_->Animate( @@ -132,6 +152,8 @@ class LayerTreeHostImplTimeSourceAdapter : public TimeSourceClient { time_source_->SetActive(active); } + bool Active() const { return time_source_->Active(); } + private: LayerTreeHostImplTimeSourceAdapter( LayerTreeHostImpl* layer_tree_host_impl, @@ -175,7 +197,7 @@ LayerTreeHostImpl::LayerTreeHostImpl( did_lock_scrolling_layer_(false), should_bubble_scrolls_(false), wheel_scrolling_(false), - manage_tiles_needed_(false), + tile_priorities_dirty_(false), root_layer_scroll_offset_delegate_(NULL), settings_(settings), visible_(true), @@ -190,13 +212,16 @@ LayerTreeHostImpl::LayerTreeHostImpl( paint_time_counter_(PaintTimeCounter::Create()), memory_history_(MemoryHistory::Create()), debug_rect_history_(DebugRectHistory::Create()), + texture_mailbox_deleter_(new TextureMailboxDeleter), max_memory_needed_bytes_(0), last_sent_memory_visible_bytes_(0), last_sent_memory_visible_and_nearby_bytes_(0), last_sent_memory_use_bytes_(0), zero_budget_(false), device_scale_factor_(1.f), + overhang_ui_resource_id_(0), overdraw_bottom_height_(0.f), + device_viewport_valid_for_tile_management_(true), external_stencil_test_enabled_(false), animation_registrar_(AnimationRegistrar::Create()), rendering_stats_instrumentation_(rendering_stats_instrumentation), @@ -246,16 +271,22 @@ void LayerTreeHostImpl::BeginCommit() {} void LayerTreeHostImpl::CommitComplete() { TRACE_EVENT0("cc", "LayerTreeHostImpl::CommitComplete"); - // Impl-side painting needs an update immediately post-commit to have the - // opportunity to create tilings. Other paths can call UpdateDrawProperties - // more lazily when needed prior to drawing. if (settings_.impl_side_painting) { + // Impl-side painting needs an update immediately post-commit to have the + // opportunity to create tilings. Other paths can call UpdateDrawProperties + // more lazily when needed prior to drawing. + pending_tree()->ApplyScrollDeltasSinceBeginFrame(); pending_tree_->set_needs_update_draw_properties(); pending_tree_->UpdateDrawProperties(); // Start working on newly created tiles immediately if needed. - ManageTiles(); + if (!tile_manager_ || !tile_priorities_dirty_) + NotifyReadyToActivate(); + else + ManageTiles(); } else { active_tree_->set_needs_update_draw_properties(); + if (time_source_client_adapter_ && time_source_client_adapter_->Active()) + DCHECK(active_tree_->root_layer()); } client_->SendManagedMemoryStats(); @@ -287,7 +318,7 @@ bool LayerTreeHostImpl::CanDraw() const { if (output_surface_->capabilities().draw_and_swap_full_viewport_every_frame) return true; - if (device_viewport_size_.IsEmpty()) { + if (DrawViewportSize().IsEmpty()) { TRACE_EVENT_INSTANT0("cc", "LayerTreeHostImpl::CanDraw empty viewport", TRACE_EVENT_SCOPE_THREAD); return false; @@ -304,6 +335,12 @@ bool LayerTreeHostImpl::CanDraw() const { TRACE_EVENT_SCOPE_THREAD); return false; } + if (EvictedUIResourcesExist()) { + TRACE_EVENT_INSTANT0( + "cc", "LayerTreeHostImpl::CanDraw UI resources evicted not recreated", + TRACE_EVENT_SCOPE_THREAD); + return false; + } return true; } @@ -320,16 +357,21 @@ void LayerTreeHostImpl::Animate(base::TimeTicks monotonic_time, void LayerTreeHostImpl::ManageTiles() { if (!tile_manager_) return; - if (!manage_tiles_needed_) + if (!tile_priorities_dirty_) + return; + if (!device_viewport_valid_for_tile_management_) return; - manage_tiles_needed_ = false; + + tile_priorities_dirty_ = false; tile_manager_->ManageTiles(); size_t memory_required_bytes; size_t memory_nice_to_have_bytes; + size_t memory_allocated_bytes; size_t memory_used_bytes; tile_manager_->GetMemoryStats(&memory_required_bytes, &memory_nice_to_have_bytes, + &memory_allocated_bytes, &memory_used_bytes); SendManagedMemoryStats(memory_required_bytes, memory_nice_to_have_bytes, @@ -339,7 +381,6 @@ void LayerTreeHostImpl::ManageTiles() { void LayerTreeHostImpl::StartPageScaleAnimation(gfx::Vector2d target_offset, bool anchor_point, float page_scale, - base::TimeTicks start_time, base::TimeDelta duration) { if (!RootScrollLayer()) return; @@ -347,9 +388,7 @@ void LayerTreeHostImpl::StartPageScaleAnimation(gfx::Vector2d target_offset, gfx::Vector2dF scroll_total = RootScrollLayer()->scroll_offset() + RootScrollLayer()->ScrollDelta(); gfx::SizeF scaled_scrollable_size = active_tree_->ScrollableSize(); - gfx::SizeF viewport_size = VisibleViewportSize(); - - double start_time_seconds = (start_time - base::TimeTicks()).InSecondsF(); + gfx::SizeF viewport_size = UnscaledScrollableViewportSize(); // Easing constants experimentally determined. scoped_ptr<TimingFunction> timing_function = @@ -360,7 +399,6 @@ void LayerTreeHostImpl::StartPageScaleAnimation(gfx::Vector2d target_offset, active_tree_->total_page_scale_factor(), viewport_size, scaled_scrollable_size, - start_time_seconds, timing_function.Pass()); if (anchor_point) { @@ -466,7 +504,7 @@ void LayerTreeHostImpl::FrameData::AppendRenderPass( static DrawMode GetDrawMode(OutputSurface* output_surface) { if (output_surface->ForcedDrawToSoftwareDevice()) { return DRAW_MODE_RESOURCELESS_SOFTWARE; - } else if (output_surface->context3d()) { + } else if (output_surface->context_provider()) { return DRAW_MODE_HARDWARE; } else { DCHECK(output_surface->software_device()); @@ -519,6 +557,8 @@ static void AppendQuadsForRenderSurfaceLayer( } static void AppendQuadsToFillScreen( + ResourceProvider::ResourceId resource_id, + gfx::SizeF resource_scaled_size, RenderPass* target_render_pass, LayerImpl* root_layer, SkColor screen_background_color, @@ -567,11 +607,31 @@ static void AppendQuadsToFillScreen( // no perspective, so mapping is sufficient (as opposed to projecting). gfx::Rect layer_rect = MathUtil::MapClippedRect(transform_to_layer_space, fill_rects.rect()); - // Skip the quad culler and just append the quads directly to avoid - // occlusion checks. - scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); - quad->SetNew(shared_quad_state, layer_rect, screen_background_color, false); - quad_culler.Append(quad.PassAs<DrawQuad>(), &append_quads_data); + if (resource_id) { + scoped_ptr<TextureDrawQuad> tex_quad = TextureDrawQuad::Create(); + const float vertex_opacity[4] = {1.f, 1.f, 1.f, 1.f}; + tex_quad->SetNew( + shared_quad_state, + layer_rect, + layer_rect, + resource_id, + false, + gfx::PointF(layer_rect.x() / resource_scaled_size.width(), + layer_rect.y() / resource_scaled_size.height()), + gfx::PointF(layer_rect.right() / resource_scaled_size.width(), + layer_rect.bottom() / resource_scaled_size.height()), + screen_background_color, + vertex_opacity, + false); + quad_culler.Append(tex_quad.PassAs<DrawQuad>(), &append_quads_data); + } else { + // Skip the quad culler and just append the quads directly to avoid + // occlusion checks. + scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); + quad->SetNew( + shared_quad_state, layer_rect, screen_background_color, false); + quad_culler.Append(quad.PassAs<DrawQuad>(), &append_quads_data); + } } } @@ -783,7 +843,10 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { if (!active_tree_->has_transparent_background()) { frame->render_passes.back()->has_transparent_background = false; - AppendQuadsToFillScreen(frame->render_passes.back(), + AppendQuadsToFillScreen(ResourceIdForUIResource(overhang_ui_resource_id_), + gfx::ScaleSize(overhang_ui_resource_size_, + device_scale_factor_), + frame->render_passes.back(), active_tree_->root_layer(), active_tree_->background_color(), occlusion_tracker); @@ -823,6 +886,8 @@ void LayerTreeHostImpl::MainThreadHasStoppedFlinging() { void LayerTreeHostImpl::UpdateBackgroundAnimateTicking( bool should_background_tick) { DCHECK(proxy_->IsImplThread()); + if (should_background_tick) + DCHECK(active_tree_->root_layer()); bool enabled = should_background_tick && !animation_registrar_->active_animation_controllers().empty(); @@ -985,11 +1050,11 @@ bool LayerTreeHostImpl::PrepareToDraw(FrameData* frame, "SourceFrameNumber", active_tree_->source_frame_number()); - if (need_to_update_visible_tiles_before_draw_) { - DCHECK(tile_manager_); - if (tile_manager_->UpdateVisibleTiles()) - DidInitializeVisibleTile(); + if (need_to_update_visible_tiles_before_draw_ && + tile_manager_ && tile_manager_->UpdateVisibleTiles()) { + DidInitializeVisibleTile(); } + need_to_update_visible_tiles_before_draw_ = true; active_tree_->UpdateDrawProperties(); @@ -1023,6 +1088,14 @@ void LayerTreeHostImpl::EvictTexturesForTesting() { EnforceManagedMemoryPolicy(ManagedMemoryPolicy(0)); } +void LayerTreeHostImpl::BlockNotifyReadyToActivateForTesting(bool block) { + NOTREACHED(); +} + +void LayerTreeHostImpl::DidInitializeVisibleTileForTesting() { + DidInitializeVisibleTile(); +} + void LayerTreeHostImpl::EnforceManagedMemoryPolicy( const ManagedMemoryPolicy& policy) { @@ -1065,31 +1138,28 @@ void LayerTreeHostImpl::UpdateTileManagerMemoryPolicy( policy.priority_cutoff_when_visible : policy.priority_cutoff_when_not_visible); new_state.num_resources_limit = policy.num_resources_limit; + tile_manager_->SetGlobalState(new_state); - manage_tiles_needed_ = true; + DidModifyTilePriorities(); } -bool LayerTreeHostImpl::HasImplThread() const { - return proxy_->HasImplThread(); +void LayerTreeHostImpl::DidModifyTilePriorities() { + DCHECK(settings_.impl_side_painting); + // Mark priorities as dirty and schedule a ManageTiles(). + tile_priorities_dirty_ = true; + client_->SetNeedsManageTilesOnImplThread(); } void LayerTreeHostImpl::DidInitializeVisibleTile() { // TODO(reveman): Determine tiles that changed and only damage // what's necessary. SetFullRootLayerDamage(); - if (client_) + if (client_ && !client_->IsInsideDraw()) client_->DidInitializeVisibleTileOnImplThread(); } void LayerTreeHostImpl::NotifyReadyToActivate() { - if (pending_tree_) { - need_to_update_visible_tiles_before_draw_ = true; - ActivatePendingTree(); - } -} - -bool LayerTreeHostImpl::ShouldClearRootRenderPass() const { - return settings_.should_clear_root_render_pass; + client_->NotifyReadyToActivate(); } void LayerTreeHostImpl::SetMemoryPolicy(const ManagedMemoryPolicy& policy) { @@ -1151,13 +1221,13 @@ void LayerTreeHostImpl::SetManagedMemoryPolicy( void LayerTreeHostImpl::SetExternalDrawConstraints( const gfx::Transform& transform, - gfx::Rect viewport) { + gfx::Rect viewport, + gfx::Rect clip, + bool valid_for_tile_management) { external_transform_ = transform; external_viewport_ = viewport; -} - -void LayerTreeHostImpl::SetExternalStencilTest(bool enabled) { - external_stencil_test_enabled_ = enabled; + external_clip_ = clip; + device_viewport_valid_for_tile_management_ = valid_for_tile_management; } void LayerTreeHostImpl::SetNeedsRedrawRect(gfx::Rect damage_rect) { @@ -1168,14 +1238,15 @@ void LayerTreeHostImpl::BeginFrame(const BeginFrameArgs& args) { client_->BeginFrameOnImplThread(args); } -void LayerTreeHostImpl::OnSwapBuffersComplete( - const CompositorFrameAck* ack) { +void LayerTreeHostImpl::OnSwapBuffersComplete() { + client_->OnSwapBuffersCompleteOnImplThread(); +} + +void LayerTreeHostImpl::ReclaimResources(const CompositorFrameAck* ack) { // TODO(piman): We may need to do some validation on this ack before // processing it. - if (ack && renderer_) + if (renderer_) renderer_->ReceiveSwapBuffersAck(*ack); - - client_->OnSwapBuffersCompleteOnImplThread(); } void LayerTreeHostImpl::OnCanDrawStateChangedForTree() { @@ -1207,17 +1278,6 @@ CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() const { return metadata; } -bool LayerTreeHostImpl::AllowPartialSwap() const { - // We don't track damage on the HUD layer (it interacts with damage tracking - // visualizations), so disable partial swaps to make the HUD layer display - // properly. - return !debug_state_.ShowHudRects(); -} - -bool LayerTreeHostImpl::ExternalStencilTestEnabled() const { - return external_stencil_test_enabled_; -} - static void LayerTreeHostImplDidBeginTracingCallback(LayerImpl* layer) { layer->DidBeginTracing(); } @@ -1236,12 +1296,15 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame, DCHECK(!frame->render_passes.empty()); - fps_counter_->SaveTimeStamp(frame_begin_time); + int old_dropped_frame_count = fps_counter_->dropped_frame_count(); + fps_counter_->SaveTimeStamp(frame_begin_time, + !output_surface_->context_provider()); - rendering_stats_instrumentation_->SetScreenFrameCount( - fps_counter_->current_frame_number()); - rendering_stats_instrumentation_->SetDroppedFrameCount( - fps_counter_->dropped_frame_count()); + bool on_main_thread = false; + rendering_stats_instrumentation_->IncrementScreenFrameCount( + 1, on_main_thread); + rendering_stats_instrumentation_->IncrementDroppedFrameCount( + fps_counter_->dropped_frame_count() - old_dropped_frame_count); if (tile_manager_) { memory_history_->SaveEntry( @@ -1260,7 +1323,7 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame, if (!settings_.impl_side_painting && debug_state_.continuous_painting) { const RenderingStats& stats = rendering_stats_instrumentation_->GetRenderingStats(); - paint_time_counter_->SavePaintTime(stats.total_paint_time); + paint_time_counter_->SavePaintTime(stats.main_stats.paint_time); } bool is_new_trace; @@ -1288,11 +1351,22 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame, active_tree_->hud_layer()->UpdateHudTexture(resource_provider_.get()); if (output_surface_->ForcedDrawToSoftwareDevice()) { + bool allow_partial_swap = false; + scoped_ptr<SoftwareRenderer> temp_software_renderer = - SoftwareRenderer::Create(this, output_surface_.get(), NULL); - temp_software_renderer->DrawFrame(&frame->render_passes); + SoftwareRenderer::Create(this, &settings_, output_surface_.get(), NULL); + temp_software_renderer->DrawFrame( + &frame->render_passes, NULL, device_scale_factor_, allow_partial_swap); } else { - renderer_->DrawFrame(&frame->render_passes); + // We don't track damage on the HUD layer (it interacts with damage tracking + // visualizations), so disable partial swaps to make the HUD layer display + // properly. + bool allow_partial_swap = !debug_state_.ShowHudRects(); + + renderer_->DrawFrame(&frame->render_passes, + offscreen_context_provider_.get(), + device_scale_factor_, + allow_partial_swap); } // The render passes should be consumed by the renderer. DCHECK(frame->render_passes.empty()); @@ -1300,11 +1374,16 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame, // The next frame should start by assuming nothing has changed, and changes // are noted as they occur. + // TODO(boliu): If we did a temporary software renderer frame, propogate the + // damage forward to the next frame. for (size_t i = 0; i < frame->render_surface_layer_list->size(); i++) { (*frame->render_surface_layer_list)[i]->render_surface()->damage_tracker()-> DidDrawDamagedArea(); } active_tree_->root_layer()->ResetAllChangeTrackingForSubtree(); + + rendering_stats_instrumentation_->IssueTraceEventForImplThreadStats(); + rendering_stats_instrumentation_->AccumulateAndClearImplThreadStats(); } void LayerTreeHostImpl::DidDrawAllLayers(const FrameData& frame) { @@ -1343,22 +1422,18 @@ void LayerTreeHostImpl::SetNeedsBeginFrame(bool enable) { output_surface_->SetNeedsBeginFrame(enable); } -float LayerTreeHostImpl::DeviceScaleFactor() const { - return device_scale_factor_; -} - -gfx::SizeF LayerTreeHostImpl::VisibleViewportSize() const { +gfx::SizeF LayerTreeHostImpl::UnscaledScrollableViewportSize() const { // The container layer bounds should be used if non-overlay scrollbars may // exist since it adjusts for them. LayerImpl* container_layer = active_tree_->RootContainerLayer(); - if (!Settings().solid_color_scrollbars && container_layer) { + if (!settings_.solid_color_scrollbars && container_layer) { DCHECK(!top_controls_manager_); DCHECK_EQ(0, overdraw_bottom_height_); return container_layer->bounds(); } gfx::SizeF dip_size = - gfx::ScaleSize(device_viewport_size(), 1.f / device_scale_factor()); + gfx::ScaleSize(device_viewport_size_, 1.f / device_scale_factor()); float top_offset = top_controls_manager_ ? top_controls_manager_->content_top_offset() : 0.f; @@ -1366,10 +1441,6 @@ gfx::SizeF LayerTreeHostImpl::VisibleViewportSize() const { dip_size.height() - top_offset - overdraw_bottom_height_); } -const LayerTreeSettings& LayerTreeHostImpl::Settings() const { - return settings(); -} - void LayerTreeHostImpl::DidLoseOutputSurface() { // TODO(jamesr): The renderer_ check is needed to make some of the // LayerTreeHostContextTest tests pass, but shouldn't be necessary (or @@ -1425,49 +1496,23 @@ void LayerTreeHostImpl::CreatePendingTree() { else pending_tree_ = LayerTreeImpl::create(this); client_->OnCanDrawStateChanged(CanDraw()); - client_->OnHasPendingTreeStateChanged(pending_tree_); TRACE_EVENT_ASYNC_BEGIN0("cc", "PendingTree", pending_tree_.get()); TRACE_EVENT_ASYNC_STEP0("cc", "PendingTree", pending_tree_.get(), "waiting"); } void LayerTreeHostImpl::UpdateVisibleTiles() { - DCHECK(!client_->IsInsideDraw()) << - "Updating visible tiles within a draw may trigger " - "spurious redraws."; if (tile_manager_ && tile_manager_->UpdateVisibleTiles()) DidInitializeVisibleTile(); - need_to_update_visible_tiles_before_draw_ = false; } -void LayerTreeHostImpl::ActivatePendingTreeIfNeeded() { - DCHECK(pending_tree_); - CHECK(settings_.impl_side_painting); - - if (!pending_tree_) - return; - - // The tile manager is usually responsible for notifying activation. - // If there is no tile manager, then we need to manually activate. - if (!tile_manager_ || tile_manager_->AreTilesRequiredForActivationReady()) { - ActivatePendingTree(); - return; - } - - // Manage tiles in case state affecting tile priority has changed. - ManageTiles(); - - TRACE_EVENT_ASYNC_STEP1( - "cc", - "PendingTree", pending_tree_.get(), "activate", - "state", TracedValue::FromValue(ActivationStateAsValue().release())); -} - void LayerTreeHostImpl::ActivatePendingTree() { CHECK(pending_tree_); TRACE_EVENT_ASYNC_END0("cc", "PendingTree", pending_tree_.get()); + need_to_update_visible_tiles_before_draw_ = true; + active_tree_->SetRootLayerScrollOffsetDelegate(NULL); active_tree_->PushPersistedState(pending_tree_.get()); if (pending_tree_->needs_full_tree_sync()) { @@ -1501,7 +1546,6 @@ void LayerTreeHostImpl::ActivatePendingTree() { client_->ReduceWastedContentsTextureMemoryOnImplThread(); client_->OnCanDrawStateChanged(CanDraw()); - client_->OnHasPendingTreeStateChanged(pending_tree_); client_->SetNeedsRedrawOnImplThread(); client_->RenewTreePriority(); @@ -1509,13 +1553,16 @@ void LayerTreeHostImpl::ActivatePendingTree() { const RenderingStats& stats = rendering_stats_instrumentation_->GetRenderingStats(); paint_time_counter_->SavePaintTime( - stats.total_paint_time + stats.total_record_time + - stats.total_rasterize_time_for_now_bins_on_pending_tree); + stats.main_stats.paint_time + stats.main_stats.record_time + + stats.impl_stats.rasterize_time_for_now_bins_on_pending_tree); } client_->DidActivatePendingTree(); if (!tree_activation_callback_.is_null()) tree_activation_callback_.Run(); + + if (time_source_client_adapter_ && time_source_client_adapter_->Active()) + DCHECK(active_tree_->root_layer()); } void LayerTreeHostImpl::SetVisible(bool visible) { @@ -1527,6 +1574,9 @@ void LayerTreeHostImpl::SetVisible(bool visible) { DidVisibilityChange(this, visible_); EnforceManagedMemoryPolicy(ActualManagedMemoryPolicy()); + if (!visible_) + EvictAllUIResources(); + // Evict tiles immediately if invisible since this tab may never get another // draw or timer tick. if (!visible_) @@ -1559,6 +1609,11 @@ size_t LayerTreeHostImpl::memory_allocation_limit_bytes() const { return ActualManagedMemoryPolicy().bytes_limit_when_visible; } +int LayerTreeHostImpl::memory_allocation_priority_cutoff() const { + return ManagedMemoryPolicy::PriorityCutoffToValue( + ActualManagedMemoryPolicy().priority_cutoff_when_visible); +} + void LayerTreeHostImpl::ReleaseTreeResources() { if (active_tree_->root_layer()) SendReleaseResourcesRecursive(active_tree_->root_layer()); @@ -1567,8 +1622,7 @@ void LayerTreeHostImpl::ReleaseTreeResources() { if (recycle_tree_ && recycle_tree_->root_layer()) SendReleaseResourcesRecursive(recycle_tree_->root_layer()); - // Remove all existing maps from UIResourceId to ResourceId. - ui_resource_map_.clear(); + EvictAllUIResources(); } void LayerTreeHostImpl::CreateAndSetRenderer( @@ -1577,35 +1631,48 @@ void LayerTreeHostImpl::CreateAndSetRenderer( bool skip_gl_renderer) { DCHECK(!renderer_); if (output_surface->capabilities().delegated_rendering) { - renderer_ = - DelegatingRenderer::Create(this, output_surface, resource_provider); - } else if (output_surface->context3d() && !skip_gl_renderer) { + renderer_ = DelegatingRenderer::Create( + this, &settings_, output_surface, resource_provider); + } else if (output_surface->context_provider() && !skip_gl_renderer) { renderer_ = GLRenderer::Create(this, + &settings_, output_surface, resource_provider, + texture_mailbox_deleter_.get(), settings_.highp_threshold_min, settings_.force_direct_layer_drawing); } else if (output_surface->software_device()) { - renderer_ = - SoftwareRenderer::Create(this, output_surface, resource_provider); + renderer_ = SoftwareRenderer::Create( + this, &settings_, output_surface, resource_provider); } if (renderer_) { renderer_->SetVisible(visible_); SetFullRootLayerDamage(); + + // See note in LayerTreeImpl::UpdateDrawProperties. Renderer needs to be + // initialized to get max texture size. Also, after releasing resources, + // trees need another update to generate new ones. + active_tree_->set_needs_update_draw_properties(); + if (pending_tree_) + pending_tree_->set_needs_update_draw_properties(); } } void LayerTreeHostImpl::CreateAndSetTileManager( ResourceProvider* resource_provider, + ContextProvider* context_provider, bool using_map_image) { DCHECK(settings_.impl_side_painting); DCHECK(resource_provider); - tile_manager_ = TileManager::Create(this, - resource_provider, - settings_.num_raster_threads, - rendering_stats_instrumentation_, - using_map_image); + tile_manager_ = + TileManager::Create(this, + resource_provider, + settings_.num_raster_threads, + rendering_stats_instrumentation_, + using_map_image, + GetMaxTransferBufferUsageBytes(context_provider)); + UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy()); need_to_update_visible_tiles_before_draw_ = false; } @@ -1633,7 +1700,9 @@ bool LayerTreeHostImpl::InitializeRenderer( return false; scoped_ptr<ResourceProvider> resource_provider = ResourceProvider::Create( - output_surface.get(), settings_.highp_threshold_min); + output_surface.get(), + settings_.highp_threshold_min, + settings_.use_rgba_4444_textures); if (!resource_provider) return false; @@ -1649,6 +1718,7 @@ bool LayerTreeHostImpl::InitializeRenderer( if (settings_.impl_side_painting) { CreateAndSetTileManager(resource_provider.get(), + output_surface->context_provider().get(), GetRendererCapabilities().using_map_image); } @@ -1676,12 +1746,6 @@ bool LayerTreeHostImpl::InitializeRenderer( client_->OnCanDrawStateChanged(CanDraw()); - // See note in LayerTreeImpl::UpdateDrawProperties. Renderer needs - // to be initialized to get max texture size. - active_tree_->set_needs_update_draw_properties(); - if (pending_tree_) - pending_tree_->set_needs_update_draw_properties(); - return true; } @@ -1690,22 +1754,56 @@ bool LayerTreeHostImpl::DeferredInitialize( DCHECK(output_surface_->capabilities().deferred_gl_initialization); DCHECK(settings_.impl_side_painting); DCHECK(settings_.solid_color_scrollbars); - DCHECK(output_surface_->context3d()); + DCHECK(output_surface_->context_provider()); ReleaseTreeResources(); renderer_.reset(); - resource_provider_->InitializeGL(); - bool skip_gl_renderer = false; - CreateAndSetRenderer( - output_surface_.get(), resource_provider_.get(), skip_gl_renderer); - bool success = !!renderer_.get(); - client_->DidTryInitializeRendererOnImplThread(success, - offscreen_context_provider); + bool resource_provider_success = resource_provider_->InitializeGL(); + + bool success = resource_provider_success; + if (success) { + bool skip_gl_renderer = false; + CreateAndSetRenderer( + output_surface_.get(), resource_provider_.get(), skip_gl_renderer); + if (!renderer_) + success = false; + } + + if (success) { + if (offscreen_context_provider.get() && + !offscreen_context_provider->BindToCurrentThread()) + success = false; + } + if (success) { EnforceZeroBudget(false); client_->SetNeedsCommitOnImplThread(); + } else { + if (offscreen_context_provider.get()) { + if (offscreen_context_provider->BindToCurrentThread()) + offscreen_context_provider->VerifyContexts(); + offscreen_context_provider = NULL; + } + + client_->DidLoseOutputSurfaceOnImplThread(); + + if (resource_provider_success) { + // If this fails the context provider will be dropped from the output + // surface and destroyed. But the GLRenderer expects the output surface + // to stick around - and hold onto the context3d - as long as it is alive. + // TODO(danakj): Remove the need for this code path: crbug.com/276411 + renderer_.reset(); + + // The resource provider can't stay in GL mode or it tries to clean up GL + // stuff, but the context provider is going away on the output surface + // which contradicts being in GL mode. + // TODO(danakj): Remove the need for this code path: crbug.com/276411 + resource_provider_->InitializeSoftware(); + } } + + SetOffscreenContextProvider(offscreen_context_provider); return success; } @@ -1713,7 +1811,7 @@ void LayerTreeHostImpl::ReleaseGL() { DCHECK(output_surface_->capabilities().deferred_gl_initialization); DCHECK(settings_.impl_side_painting); DCHECK(settings_.solid_color_scrollbars); - DCHECK(output_surface_->context3d()); + DCHECK(output_surface_->context_provider()); ReleaseTreeResources(); renderer_.reset(); @@ -1727,12 +1825,12 @@ void LayerTreeHostImpl::ReleaseGL() { EnforceZeroBudget(true); CreateAndSetTileManager(resource_provider_.get(), + NULL, GetRendererCapabilities().using_map_image); DCHECK(tile_manager_); - bool success = true; - client_->DidTryInitializeRendererOnImplThread( - success, scoped_refptr<ContextProvider>()); + SetOffscreenContextProvider(NULL); + client_->SetNeedsCommitOnImplThread(); } @@ -1740,7 +1838,7 @@ void LayerTreeHostImpl::SetViewportSize(gfx::Size device_viewport_size) { if (device_viewport_size == device_viewport_size_) return; - if (pending_tree_ && device_viewport_size_ != device_viewport_size) + if (pending_tree_) active_tree_->SetViewportSizeInvalid(); device_viewport_size_ = device_viewport_size; @@ -1763,6 +1861,13 @@ void LayerTreeHostImpl::SetOverdrawBottomHeight(float overdraw_bottom_height) { SetFullRootLayerDamage(); } +void LayerTreeHostImpl::SetOverhangUIResource( + UIResourceId overhang_ui_resource_id, + gfx::Size overhang_ui_resource_size) { + overhang_ui_resource_id_ = overhang_ui_resource_id; + overhang_ui_resource_size_ = overhang_ui_resource_size; +} + void LayerTreeHostImpl::SetDeviceScaleFactor(float device_scale_factor) { if (device_scale_factor == device_scale_factor_) return; @@ -1775,6 +1880,10 @@ void LayerTreeHostImpl::SetDeviceScaleFactor(float device_scale_factor) { SetFullRootLayerDamage(); } +gfx::Size LayerTreeHostImpl::DrawViewportSize() const { + return DeviceViewport().size(); +} + gfx::Rect LayerTreeHostImpl::DeviceViewport() const { if (external_viewport_.IsEmpty()) return gfx::Rect(device_viewport_size_); @@ -1782,7 +1891,14 @@ gfx::Rect LayerTreeHostImpl::DeviceViewport() const { return external_viewport_; } -const gfx::Transform& LayerTreeHostImpl::DeviceTransform() const { +gfx::Rect LayerTreeHostImpl::DeviceClip() const { + if (external_clip_.IsEmpty()) + return DeviceViewport(); + + return external_clip_; +} + +const gfx::Transform& LayerTreeHostImpl::DrawTransform() const { return external_transform_; } @@ -1806,6 +1922,12 @@ void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) { input_handler_client_ = client; } +static LayerImpl* NextScrollLayer(LayerImpl* layer) { + if (LayerImpl* scroll_parent = layer->scroll_parent()) + return scroll_parent; + return layer->parent(); +} + InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin( gfx::Point viewport_point, InputHandler::ScrollInputType type) { TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBegin"); @@ -1829,7 +1951,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin( // Walk up the hierarchy and look for a scrollable layer. LayerImpl* potentially_scrolling_layer_impl = 0; - for (; layer_impl; layer_impl = layer_impl->parent()) { + for (; layer_impl; layer_impl = NextScrollLayer(layer_impl)) { // The content layer can also block attempts to scroll outside the main // thread. ScrollStatus status = layer_impl->TryScroll(device_viewport_point, type); @@ -2229,7 +2351,7 @@ scoped_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() { } void LayerTreeHostImpl::SetFullRootLayerDamage() { - SetViewportDamage(gfx::Rect(device_viewport_size_)); + SetViewportDamage(gfx::Rect(DrawViewportSize())); } void LayerTreeHostImpl::AnimatePageScale(base::TimeTicks time) { @@ -2240,6 +2362,9 @@ void LayerTreeHostImpl::AnimatePageScale(base::TimeTicks time) { gfx::Vector2dF scroll_total = RootScrollLayer()->scroll_offset() + RootScrollLayer()->ScrollDelta(); + if (!page_scale_animation_->IsAnimationStarted()) + page_scale_animation_->StartAnimation(monotonic_time); + active_tree_->SetPageScaleDelta( page_scale_animation_->PageScaleFactorAtTime(monotonic_time) / active_tree_->page_scale_factor()); @@ -2327,6 +2452,21 @@ void LayerTreeHostImpl::SendReleaseResourcesRecursive(LayerImpl* current) { SendReleaseResourcesRecursive(current->children()[i]); } +void LayerTreeHostImpl::SetOffscreenContextProvider( + const scoped_refptr<ContextProvider>& offscreen_context_provider) { + if (!offscreen_context_provider.get()) { + offscreen_context_provider_ = NULL; + return; + } + + if (!offscreen_context_provider->BindToCurrentThread()) { + offscreen_context_provider_ = NULL; + return; + } + + offscreen_context_provider_ = offscreen_context_provider; +} + std::string LayerTreeHostImpl::LayerTreeAsJson() const { std::string str; if (active_tree_->root_layer()) { @@ -2426,7 +2566,7 @@ void LayerTreeHostImpl::SetTreePriority(TreePriority priority) { new_state.tree_priority = priority; tile_manager_->SetGlobalState(new_state); - manage_tiles_needed_ = true; + DidModifyTilePriorities(); } void LayerTreeHostImpl::ResetCurrentFrameTimeForNextFrame() { @@ -2493,11 +2633,20 @@ void LayerTreeHostImpl::SetDebugState( SetFullRootLayerDamage(); } -void LayerTreeHostImpl::CreateUIResource( - UIResourceId uid, - scoped_refptr<UIResourceBitmap> bitmap) { +void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, + const UIResourceBitmap& bitmap) { DCHECK_GT(uid, 0); - DCHECK_EQ(bitmap->GetFormat(), UIResourceBitmap::RGBA8); + DCHECK_EQ(bitmap.GetFormat(), UIResourceBitmap::RGBA8); + + GLint wrap_mode = 0; + switch (bitmap.GetWrapMode()) { + case UIResourceBitmap::CLAMP_TO_EDGE: + wrap_mode = GL_CLAMP_TO_EDGE; + break; + case UIResourceBitmap::REPEAT: + wrap_mode = GL_REPEAT; + break; + } // Allow for multiple creation requests with the same UIResourceId. The // previous resource is simply deleted. @@ -2505,14 +2654,20 @@ void LayerTreeHostImpl::CreateUIResource( if (id) DeleteUIResource(uid); id = resource_provider_->CreateResource( - bitmap->GetSize(), GL_RGBA, ResourceProvider::TextureUsageAny); + bitmap.GetSize(), + wrap_mode, + ResourceProvider::TextureUsageAny, + resource_provider_->best_texture_format()); ui_resource_map_[uid] = id; + + AutoLockUIResourceBitmap bitmap_lock(bitmap); resource_provider_->SetPixels(id, - reinterpret_cast<uint8_t*>(bitmap->GetPixels()), - gfx::Rect(bitmap->GetSize()), - gfx::Rect(bitmap->GetSize()), + bitmap_lock.GetPixels(), + gfx::Rect(bitmap.GetSize()), + gfx::Rect(bitmap.GetSize()), gfx::Vector2d(0, 0)); + MarkUIResourceNotEvicted(uid); } void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) { @@ -2521,6 +2676,24 @@ void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) { resource_provider_->DeleteResource(id); ui_resource_map_.erase(uid); } + MarkUIResourceNotEvicted(uid); +} + +void LayerTreeHostImpl::EvictAllUIResources() { + if (ui_resource_map_.empty()) + return; + + for (UIResourceMap::const_iterator iter = ui_resource_map_.begin(); + iter != ui_resource_map_.end(); + ++iter) { + evicted_ui_resources_.insert(iter->first); + resource_provider_->DeleteResource(iter->second); + } + ui_resource_map_.clear(); + + client_->SetNeedsCommitOnImplThread(); + client_->OnCanDrawStateChanged(CanDraw()); + client_->RenewTreePriority(); } ResourceProvider::ResourceId LayerTreeHostImpl::ResourceIdForUIResource( @@ -2531,4 +2704,18 @@ ResourceProvider::ResourceId LayerTreeHostImpl::ResourceIdForUIResource( return 0; } +bool LayerTreeHostImpl::EvictedUIResourcesExist() const { + return !evicted_ui_resources_.empty(); +} + +void LayerTreeHostImpl::MarkUIResourceNotEvicted(UIResourceId uid) { + std::set<UIResourceId>::iterator found_in_evicted = + evicted_ui_resources_.find(uid); + if (found_in_evicted == evicted_ui_resources_.end()) + return; + evicted_ui_resources_.erase(found_in_evicted); + if (evicted_ui_resources_.empty()) + client_->OnCanDrawStateChanged(CanDraw()); +} + } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index 31870ac48bd..6d969012deb 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -6,6 +6,7 @@ #define CC_TREES_LAYER_TREE_HOST_IMPL_H_ #include <list> +#include <set> #include <string> #include <vector> @@ -46,26 +47,25 @@ class PaintTimeCounter; class MemoryHistory; class RenderingStatsInstrumentation; class RenderPassDrawQuad; +class TextureMailboxDeleter; class TopControlsManager; class UIResourceBitmap; +class UIResourceRequest; struct RendererCapabilities; -struct UIResourceRequest; // LayerTreeHost->Proxy callback interface. class LayerTreeHostImplClient { public: - virtual void DidTryInitializeRendererOnImplThread( - bool success, - scoped_refptr<ContextProvider> offscreen_context_provider) = 0; virtual void DidLoseOutputSurfaceOnImplThread() = 0; virtual void OnSwapBuffersCompleteOnImplThread() = 0; virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) = 0; virtual void OnCanDrawStateChanged(bool can_draw) = 0; - virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) = 0; + virtual void NotifyReadyToActivate() = 0; virtual void SetNeedsRedrawOnImplThread() = 0; virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) = 0; virtual void DidInitializeVisibleTileOnImplThread() = 0; virtual void SetNeedsCommitOnImplThread() = 0; + virtual void SetNeedsManageTilesOnImplThread() = 0; virtual void PostAnimationEventsToMainThreadOnImplThread( scoped_ptr<AnimationEventsVector> events, base::Time wall_clock_time) = 0; @@ -123,7 +123,6 @@ class CC_EXPORT LayerTreeHostImpl virtual void StartPageScaleAnimation(gfx::Vector2d target_offset, bool anchor_point, float page_scale, - base::TimeTicks start_time, base::TimeDelta duration) OVERRIDE; virtual void ScheduleAnimation() OVERRIDE; virtual bool HaveTouchEventHandlersAt(gfx::Point viewport_port) OVERRIDE; @@ -163,7 +162,7 @@ class CC_EXPORT LayerTreeHostImpl void UpdateBackgroundAnimateTicking(bool should_background_tick); void SetViewportDamage(gfx::Rect damage_rect); - void ManageTiles(); + virtual void ManageTiles(); // Returns false if problems occured preparing the frame, and we should try // to avoid displaying the frame. If PrepareToDraw is called, DidDrawAllLayers @@ -177,25 +176,39 @@ class CC_EXPORT LayerTreeHostImpl const LayerTreeSettings& settings() const { return settings_; } - // Returns the currently visible viewport size in DIP. This value excludes - // the URL bar and non-overlay scrollbars. - gfx::SizeF VisibleViewportSize() const; - // Evict all textures by enforcing a memory policy with an allocation of 0. void EvictTexturesForTesting(); + // When blocking, this prevents client_->NotifyReadyToActivate() from being + // called. When disabled, it calls client_->NotifyReadyToActivate() + // immediately if any notifications had been blocked while blocking. + virtual void BlockNotifyReadyToActivateForTesting(bool block); + + // This allows us to inject DidInitializeVisibleTile events for testing. + void DidInitializeVisibleTileForTesting(); + + bool device_viewport_valid_for_tile_management() const { + return device_viewport_valid_for_tile_management_; + } + + // Viewport size in draw space: this size is in physical pixels and is used + // for draw properties, tilings, quads and render passes. + gfx::Size DrawViewportSize() const; + + // Viewport size for scrolling and fixed-position compensation. This value + // excludes the URL bar and non-overlay scrollbars and is in DIP (and + // invariant relative to page scale). + gfx::SizeF UnscaledScrollableViewportSize() const; + // RendererClient implementation + + // Viewport rectangle and clip in nonflipped window space. These rects + // should only be used by Renderer subclasses to populate glViewport/glClip + // and their software-mode equivalents. virtual gfx::Rect DeviceViewport() const OVERRIDE; - private: - virtual float DeviceScaleFactor() const OVERRIDE; - virtual const LayerTreeSettings& Settings() const OVERRIDE; - public: + virtual gfx::Rect DeviceClip() const OVERRIDE; virtual void SetFullRootLayerDamage() OVERRIDE; - virtual bool HasImplThread() const OVERRIDE; - virtual bool ShouldClearRootRenderPass() const OVERRIDE; virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE; - virtual bool AllowPartialSwap() const OVERRIDE; - virtual bool ExternalStencilTestEnabled() const OVERRIDE; // TileManagerClient implementation. virtual void NotifyReadyToActivate() OVERRIDE; @@ -206,11 +219,14 @@ class CC_EXPORT LayerTreeHostImpl virtual void ReleaseGL() OVERRIDE; virtual void SetNeedsRedrawRect(gfx::Rect rect) OVERRIDE; virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE; - virtual void SetExternalDrawConstraints(const gfx::Transform& transform, - gfx::Rect viewport) OVERRIDE; - virtual void SetExternalStencilTest(bool enabled) OVERRIDE; + virtual void SetExternalDrawConstraints( + const gfx::Transform& transform, + gfx::Rect viewport, + gfx::Rect clip, + bool valid_for_tile_management) OVERRIDE; virtual void DidLoseOutputSurface() OVERRIDE; - virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE; + virtual void OnSwapBuffersComplete() OVERRIDE; + virtual void ReclaimResources(const CompositorFrameAck* ack) OVERRIDE; virtual void SetMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE; virtual void SetDiscardBackBufferWhenNotVisible(bool discard) OVERRIDE; virtual void SetTreeActivationCallback(const base::Closure& callback) @@ -219,10 +235,16 @@ class CC_EXPORT LayerTreeHostImpl // Called from LayerTreeImpl. void OnCanDrawStateChangedForTree(); - // Implementation + // Implementation. bool CanDraw() const; OutputSurface* output_surface() const { return output_surface_.get(); } + void SetOffscreenContextProvider( + const scoped_refptr<ContextProvider>& offscreen_context_provider); + ContextProvider* offscreen_context_provider() const { + return offscreen_context_provider_.get(); + } + std::string LayerTreeAsJson() const; void FinishAllRendering(); @@ -236,7 +258,7 @@ class CC_EXPORT LayerTreeHostImpl virtual bool SwapBuffers(const FrameData& frame); void SetNeedsBeginFrame(bool enable); - void SetNeedsManageTiles() { manage_tiles_needed_ = true; } + void DidModifyTilePriorities(); void Readback(void* pixels, gfx::Rect rect_in_device_viewport); @@ -246,8 +268,8 @@ class CC_EXPORT LayerTreeHostImpl const LayerTreeImpl* pending_tree() const { return pending_tree_.get(); } const LayerTreeImpl* recycle_tree() const { return recycle_tree_.get(); } virtual void CreatePendingTree(); - void UpdateVisibleTiles(); - virtual void ActivatePendingTreeIfNeeded(); + virtual void UpdateVisibleTiles(); + virtual void ActivatePendingTree(); // Shortcuts to layers on the active tree. LayerImpl* RootLayer() const; @@ -263,17 +285,20 @@ class CC_EXPORT LayerTreeHostImpl ManagedMemoryPolicy ActualManagedMemoryPolicy() const; size_t memory_allocation_limit_bytes() const; + int memory_allocation_priority_cutoff() const; void SetViewportSize(gfx::Size device_viewport_size); - gfx::Size device_viewport_size() const { return device_viewport_size_; } void SetOverdrawBottomHeight(float overdraw_bottom_height); float overdraw_bottom_height() const { return overdraw_bottom_height_; } + void SetOverhangUIResource(UIResourceId overhang_ui_resource_id, + gfx::Size overhang_ui_resource_size); + void SetDeviceScaleFactor(float device_scale_factor); float device_scale_factor() const { return device_scale_factor_; } - const gfx::Transform& DeviceTransform() const; + const gfx::Transform& DrawTransform() const; scoped_ptr<ScrollAndScaleSet> ProcessScrollDeltas(); @@ -367,7 +392,7 @@ class CC_EXPORT LayerTreeHostImpl void SetTreePriority(TreePriority priority); void ResetCurrentFrameTimeForNextFrame(); - base::TimeTicks CurrentFrameTimeTicks(); + virtual base::TimeTicks CurrentFrameTimeTicks(); base::Time CurrentFrameTime(); virtual base::TimeTicks CurrentPhysicalTimeTicks() const; @@ -378,12 +403,15 @@ class CC_EXPORT LayerTreeHostImpl bool page_scale_animation_active() const { return !!page_scale_animation_; } - void CreateUIResource(UIResourceId uid, - scoped_refptr<UIResourceBitmap> bitmap); + virtual void CreateUIResource(UIResourceId uid, + const UIResourceBitmap& bitmap); // Deletes a UI resource. May safely be called more than once. - void DeleteUIResource(UIResourceId uid); + virtual void DeleteUIResource(UIResourceId uid); + void EvictAllUIResources(); + bool EvictedUIResourcesExist() const; - ResourceProvider::ResourceId ResourceIdForUIResource(UIResourceId uid) const; + virtual ResourceProvider::ResourceId ResourceIdForUIResource( + UIResourceId uid) const; protected: LayerTreeHostImpl( @@ -391,7 +419,6 @@ class CC_EXPORT LayerTreeHostImpl LayerTreeHostImplClient* client, Proxy* proxy, RenderingStatsInstrumentation* rendering_stats_instrumentation); - virtual void ActivatePendingTree(); // Virtual for testing. virtual void AnimateLayers(base::TimeTicks monotonic_time, @@ -405,14 +432,18 @@ class CC_EXPORT LayerTreeHostImpl return animation_registrar_->active_animation_controllers(); } + bool manage_tiles_needed() const { return tile_priorities_dirty_; } + LayerTreeHostImplClient* client_; Proxy* proxy_; private: - void CreateAndSetRenderer(OutputSurface* output_surface, - ResourceProvider* resource_provider, - bool skip_gl_renderer); + void CreateAndSetRenderer( + OutputSurface* output_surface, + ResourceProvider* resource_provider, + bool skip_gl_renderer); void CreateAndSetTileManager(ResourceProvider* resource_provider, + ContextProvider* context_provider, bool using_map_image); void ReleaseTreeResources(); void EnforceZeroBudget(bool zero_budget); @@ -455,11 +486,19 @@ class CC_EXPORT LayerTreeHostImpl void DidInitializeVisibleTile(); + void MarkUIResourceNotEvicted(UIResourceId uid); + typedef base::hash_map<UIResourceId, ResourceProvider::ResourceId> UIResourceMap; UIResourceMap ui_resource_map_; + // Resources that were evicted by EvictAllUIResources. Resources are removed + // from this when they are touched by a create or destroy from the UI resource + // request queue. + std::set<UIResourceId> evicted_ui_resources_; + scoped_ptr<OutputSurface> output_surface_; + scoped_refptr<ContextProvider> offscreen_context_provider_; // |resource_provider_| and |tile_manager_| can be NULL, e.g. when using tile- // free rendering - see OutputSurface::ForcedDrawToSoftwareDevice(). @@ -471,7 +510,7 @@ class CC_EXPORT LayerTreeHostImpl scoped_ptr<LayerTreeImpl> active_tree_; // In impl-side painting mode, tree with possibly incomplete rasterized - // content. May be promoted to active by ActivatePendingTreeIfNeeded(). + // content. May be promoted to active by ActivatePendingTree(). scoped_ptr<LayerTreeImpl> pending_tree_; // In impl-side painting mode, inert tree with layers that can be recycled @@ -483,7 +522,7 @@ class CC_EXPORT LayerTreeHostImpl bool should_bubble_scrolls_; bool wheel_scrolling_; - bool manage_tiles_needed_; + bool tile_priorities_dirty_; // The optional delegate for the root layer scroll offset. LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate_; @@ -514,6 +553,8 @@ class CC_EXPORT LayerTreeHostImpl scoped_ptr<MemoryHistory> memory_history_; scoped_ptr<DebugRectHistory> debug_rect_history_; + scoped_ptr<TextureMailboxDeleter> texture_mailbox_deleter_; + // The maximum memory that would be used by the prioritized resource // manager, if there were no limit on memory usage. size_t max_memory_needed_bytes_; @@ -523,25 +564,38 @@ class CC_EXPORT LayerTreeHostImpl size_t last_sent_memory_use_bytes_; bool zero_budget_; - // Viewport size passed in from the main thread, in physical pixels. + // Viewport size passed in from the main thread, in physical pixels. This + // value is the default size for all concepts of physical viewport (draw + // viewport, scrolling viewport and device viewport), but it can be + // overridden. gfx::Size device_viewport_size_; // Conversion factor from CSS pixels to physical pixels when // pageScaleFactor=1. float device_scale_factor_; + // UI resource to use for drawing overhang gutters. + UIResourceId overhang_ui_resource_id_; + gfx::Size overhang_ui_resource_size_; + // Vertical amount of the viewport size that's known to covered by a // browser-side UI element, such as an on-screen-keyboard. This affects // scrollable size since we want to still be able to scroll to the bottom of // the page when the keyboard is up. float overdraw_bottom_height_; - // Optional top-level constraints that can be set by the OutputSurface. The - // external_viewport_'s size takes precedence over device_viewport_size_ for - // DrawQuad generation and Renderer; however, device_viewport_size_ is still - // used for scrollable size. + // Optional top-level constraints that can be set by the OutputSurface. + // - external_transform_ applies a transform above the root layer + // - external_viewport_ is used DrawProperties, tile management and + // glViewport/window projection matrix. + // - external_clip_ specifies a top-level clip rect + // - external_stencil_test_enabled_ tells CC to respect existing stencil bits + // (When these are specified, device_viewport_size_ remains used only for + // scrollable size.) gfx::Transform external_transform_; gfx::Rect external_viewport_; + gfx::Rect external_clip_; + bool device_viewport_valid_for_tile_management_; bool external_stencil_test_enabled_; gfx::Rect viewport_damage_rect_; diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc index ab81c672b8c..6bd815b9424 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -9,15 +9,17 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/containers/hash_tables.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "cc/base/math_util.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/input/top_controls_manager.h" #include "cc/layers/delegated_renderer_layer_impl.h" #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/layers/io_surface_layer_impl.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/painted_scrollbar_layer_impl.h" #include "cc/layers/quad_sink.h" #include "cc/layers/render_surface_impl.h" -#include "cc/layers/scrollbar_layer_impl.h" #include "cc/layers/solid_color_layer_impl.h" #include "cc/layers/texture_layer_impl.h" #include "cc/layers/tiled_layer_impl.h" @@ -25,6 +27,8 @@ #include "cc/output/begin_frame_args.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/compositor_frame_metadata.h" +#include "cc/output/copy_output_request.h" +#include "cc/output/copy_output_result.h" #include "cc/output/gl_renderer.h" #include "cc/quads/render_pass_draw_quad.h" #include "cc/quads/solid_color_draw_quad.h" @@ -32,19 +36,23 @@ #include "cc/quads/tile_draw_quad.h" #include "cc/resources/layer_tiling_data.h" #include "cc/test/animation_test_common.h" +#include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" +#include "cc/test/fake_picture_layer_impl.h" +#include "cc/test/fake_picture_pile_impl.h" #include "cc/test/fake_proxy.h" #include "cc/test/fake_rendering_stats_instrumentation.h" #include "cc/test/fake_video_frame_provider.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_test_common.h" #include "cc/test/render_pass_test_common.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" #include "media/base/media.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/rect_conversions.h" #include "ui/gfx/size_conversions.h" #include "ui/gfx/vector2d_conversions.h" @@ -65,12 +73,13 @@ class LayerTreeHostImplTest : public testing::Test, : proxy_(), always_impl_thread_(&proxy_), always_main_thread_blocked_(&proxy_), - did_try_initialize_renderer_(false), on_can_draw_state_changed_called_(false), - has_pending_tree_(false), + did_notify_ready_to_activate_(false), did_request_commit_(false), did_request_redraw_(false), + did_request_manage_tiles_(false), did_upload_visible_tile_(false), + did_lose_output_surface_(false), reduce_memory_result_(true), current_limit_bytes_(0), current_priority_cutoff_value_(0) { @@ -93,20 +102,18 @@ class LayerTreeHostImplTest : public testing::Test, virtual void TearDown() OVERRIDE {} - virtual void DidTryInitializeRendererOnImplThread( - bool success, - scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE { - did_try_initialize_renderer_ = true; + virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE { + did_lose_output_surface_ = true; } - virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {} virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE { on_can_draw_state_changed_called_ = true; } - virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE { - has_pending_tree_ = has_pending_tree; + virtual void NotifyReadyToActivate() OVERRIDE { + did_notify_ready_to_activate_ = true; + host_impl_->ActivatePendingTree(); } virtual void SetNeedsRedrawOnImplThread() OVERRIDE { did_request_redraw_ = true; @@ -114,6 +121,9 @@ class LayerTreeHostImplTest : public testing::Test, virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) OVERRIDE { did_request_redraw_ = true; } + virtual void SetNeedsManageTilesOnImplThread() OVERRIDE { + did_request_manage_tiles_ = true; + } virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE { did_upload_visible_tile_ = true; } @@ -350,12 +360,13 @@ class LayerTreeHostImplTest : public testing::Test, scoped_ptr<LayerTreeHostImpl> host_impl_; FakeRenderingStatsInstrumentation stats_instrumentation_; - bool did_try_initialize_renderer_; bool on_can_draw_state_changed_called_; - bool has_pending_tree_; + bool did_notify_ready_to_activate_; bool did_request_commit_; bool did_request_redraw_; + bool did_request_manage_tiles_; bool did_upload_visible_tile_; + bool did_lose_output_surface_; bool reduce_memory_result_; base::TimeDelta requested_scrollbar_animation_delay_; size_t current_limit_bytes_; @@ -372,20 +383,18 @@ TEST_F(LayerTreeHostImplTest, CanDrawIncompleteFrames) { settings.impl_side_painting = true; host_impl_ = LayerTreeHostImpl::Create( settings, this, &proxy_, &stats_instrumentation_); + + scoped_ptr<FakeOutputSurface> output_surface( + FakeOutputSurface::CreateAlwaysDrawAndSwap3d()); + host_impl_->InitializeRenderer( - FakeOutputSurface::CreateAlwaysDrawAndSwap3d().PassAs<OutputSurface>()); + output_surface.PassAs<OutputSurface>()); host_impl_->SetViewportSize(gfx::Size(10, 10)); bool always_draw = true; CheckNotifyCalledIfCanDrawChanged(always_draw); } -class TestWebGraphicsContext3DMakeCurrentFails - : public TestWebGraphicsContext3D { - public: - virtual bool makeContextCurrent() OVERRIDE { return false; } -}; - TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) { ASSERT_FALSE(host_impl_->active_tree()->root_layer()); @@ -480,12 +489,15 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) { this, &proxy_, &stats_instrumentation_); + scoped_ptr<TestWebGraphicsContext3D> context_owned = + TestWebGraphicsContext3D::Create(); + context_owned->set_times_make_current_succeeds(0); + + scoped_ptr<FakeOutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.Pass())); // Initialization will fail here. - host_impl_->InitializeRenderer(FakeOutputSurface::Create3d( - scoped_ptr<WebKit::WebGraphicsContext3D>( - new TestWebGraphicsContext3DMakeCurrentFails)) - .PassAs<OutputSurface>()); + host_impl_->InitializeRenderer(output_surface.PassAs<OutputSurface>()); host_impl_->SetViewportSize(gfx::Size(10, 10)); SetupScrollAndContentsLayers(gfx::Size(100, 100)); @@ -721,8 +733,8 @@ TEST_F(LayerTreeHostImplTest, ScrollVerticallyByPageReturnsCorrectValue) { EXPECT_FALSE(host_impl_->ScrollVerticallyByPage( gfx::Point(), SCROLL_BACKWARD)); - scoped_ptr<cc::ScrollbarLayerImpl> vertical_scrollbar( - cc::ScrollbarLayerImpl::Create( + scoped_ptr<cc::PaintedScrollbarLayerImpl> vertical_scrollbar( + cc::PaintedScrollbarLayerImpl::Create( host_impl_->active_tree(), 20, VERTICAL)); @@ -982,13 +994,17 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimation) { max_page_scale); scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50)); - host_impl_->StartPageScaleAnimation(gfx::Vector2d(), - false, - 2.f, - start_time, - duration); + host_impl_->StartPageScaleAnimation(gfx::Vector2d(), false, 2.f, duration); + did_request_redraw_ = false; + host_impl_->Animate(start_time, base::Time()); + EXPECT_TRUE(did_request_redraw_); + + did_request_redraw_ = false; host_impl_->Animate(halfway_through_animation, base::Time()); EXPECT_TRUE(did_request_redraw_); + + did_request_redraw_ = false; + did_request_commit_ = false; host_impl_->Animate(end_time, base::Time()); EXPECT_TRUE(did_request_commit_); @@ -1005,10 +1021,14 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimation) { max_page_scale); scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50)); - host_impl_->StartPageScaleAnimation(gfx::Vector2d(25, 25), - true, - min_page_scale, - start_time, duration); + host_impl_->StartPageScaleAnimation( + gfx::Vector2d(25, 25), true, min_page_scale, duration); + did_request_redraw_ = false; + host_impl_->Animate(start_time, base::Time()); + EXPECT_TRUE(did_request_redraw_); + + did_request_redraw_ = false; + did_request_commit_ = false; host_impl_->Animate(end_time, base::Time()); EXPECT_TRUE(did_request_redraw_); EXPECT_TRUE(did_request_commit_); @@ -1044,11 +1064,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) { max_page_scale); scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50)); - host_impl_->StartPageScaleAnimation(gfx::Vector2d(), - true, - 1.f, - start_time, - duration); + host_impl_->StartPageScaleAnimation(gfx::Vector2d(), true, 1.f, duration); + host_impl_->Animate(start_time, base::Time()); host_impl_->Animate(halfway_through_animation, base::Time()); EXPECT_TRUE(did_request_redraw_); host_impl_->Animate(end_time, base::Time()); @@ -1086,9 +1103,9 @@ class LayerTreeHostImplOverridePhysicalTime : public LayerTreeHostImpl { base::TimeTicks fake_current_physical_time_; }; -TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) { +TEST_F(LayerTreeHostImplTest, DISABLED_ScrollbarLinearFadeScheduling) { LayerTreeSettings settings; - settings.use_linear_fade_scrollbar_animator = true; + settings.scrollbar_animator = LayerTreeSettings::LinearFade; settings.scrollbar_linear_fade_delay_ms = 20; settings.scrollbar_linear_fade_length_ms = 20; @@ -1121,10 +1138,8 @@ TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) { contents->SetBounds(content_size); contents->SetContentBounds(content_size); - scoped_ptr<ScrollbarLayerImpl> scrollbar = ScrollbarLayerImpl::Create( - host_impl_->active_tree(), - 4, - VERTICAL); + scoped_ptr<PaintedScrollbarLayerImpl> scrollbar = + PaintedScrollbarLayerImpl::Create(host_impl_->active_tree(), 4, VERTICAL); scroll->SetVerticalScrollbarLayer(scrollbar.get()); scroll->AddChild(contents.Pass()); @@ -1144,9 +1159,19 @@ TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) { EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_); EXPECT_FALSE(did_request_redraw_); + // If no scroll happened during a scroll gesture, StartScrollbarAnimation + // should have no effect. + host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel); + host_impl_->ScrollEnd(); + host_impl_->StartScrollbarAnimation(); + EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_); + EXPECT_FALSE(did_request_redraw_); + // After a scroll, a fade animation should be scheduled about 20ms from now. host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel); + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(5, 0)); host_impl_->ScrollEnd(); + did_request_redraw_ = false; host_impl_->StartScrollbarAnimation(); EXPECT_LT(base::TimeDelta::FromMilliseconds(19), requested_scrollbar_animation_delay_); @@ -1509,8 +1534,9 @@ class MissingTextureAnimatingLayer : public DidDrawCheckLayer { if (!tile_missing) { ResourceProvider::ResourceId resource = resource_provider->CreateResource(gfx::Size(1, 1), - GL_RGBA, - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + RGBA_8888); resource_provider->AllocateForTesting(resource); PushTileProperties(0, 0, resource, gfx::Rect(), false); } @@ -2207,7 +2233,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) { // Scroll down in screen coordinates with a gesture. gfx::Vector2d gesture_scroll_delta(0, 10); EXPECT_EQ(InputHandler::ScrollStarted, - host_impl_->ScrollBegin(gfx::Point(), + host_impl_->ScrollBegin(gfx::Point(1, 1), InputHandler::Gesture)); host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta); host_impl_->ScrollEnd(); @@ -2232,7 +2258,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) { gfx::Vector2dF()); gfx::Vector2d gesture_scroll_delta(10, 0); EXPECT_EQ(InputHandler::ScrollStarted, - host_impl_->ScrollBegin(gfx::Point(), + host_impl_->ScrollBegin(gfx::Point(1, 1), InputHandler::Gesture)); host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta); host_impl_->ScrollEnd(); @@ -2600,8 +2626,9 @@ class BlendStateCheckLayer : public LayerImpl { quad_visible_rect_(5, 5, 5, 5), resource_id_(resource_provider->CreateResource( gfx::Size(1, 1), - GL_RGBA, - ResourceProvider::TextureUsageAny)) { + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + RGBA_8888)) { resource_provider->AllocateForTesting(resource_id_); SetAnchorPoint(gfx::PointF()); SetBounds(gfx::Size(10, 10)); @@ -2844,20 +2871,25 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { public: + LayerTreeHostImplViewportCoveredTest() : + gutter_quad_material_(DrawQuad::SOLID_COLOR), + child_(NULL), + did_activate_pending_tree_(false) {} + void CreateLayerTreeHostImpl(bool always_draw) { LayerTreeSettings settings; settings.minimum_occlusion_tracking_size = gfx::Size(); settings.impl_side_painting = true; host_impl_ = LayerTreeHostImpl::Create( settings, this, &proxy_, &stats_instrumentation_); - scoped_ptr<OutputSurface> output_surface; - if (always_draw) { - output_surface = FakeOutputSurface::CreateAlwaysDrawAndSwap3d() - .PassAs<OutputSurface>(); - } else { - output_surface = CreateFakeOutputSurface(); - } - host_impl_->InitializeRenderer(output_surface.Pass()); + + scoped_ptr<FakeOutputSurface> output_surface; + if (always_draw) + output_surface = FakeOutputSurface::CreateAlwaysDrawAndSwap3d().Pass(); + else + output_surface = FakeOutputSurface::Create3d().Pass(); + + host_impl_->InitializeRenderer(output_surface.PassAs<OutputSurface>()); viewport_size_ = gfx::Size(1000, 1000); } @@ -2888,15 +2920,11 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); ASSERT_EQ(1u, frame.render_passes.size()); - size_t num_gutter_quads = 0; - for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i) - num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material == - DrawQuad::SOLID_COLOR) ? 1 : 0; - EXPECT_EQ(0u, num_gutter_quads); + EXPECT_EQ(0u, CountGutterQuads(frame.render_passes[0]->quad_list)); EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size()); + ValidateTextureDrawQuads(frame.render_passes[0]->quad_list); - LayerTestCommon::VerifyQuadsExactlyCoverRect( - frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_)); + VerifyQuadsExactlyCoverViewport(frame.render_passes[0]->quad_list); host_impl_->DidDrawAllLayers(frame); } @@ -2913,15 +2941,11 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); ASSERT_EQ(1u, frame.render_passes.size()); - size_t num_gutter_quads = 0; - for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i) - num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material == - DrawQuad::SOLID_COLOR) ? 1 : 0; - EXPECT_EQ(1u, num_gutter_quads); + EXPECT_EQ(1u, CountGutterQuads(frame.render_passes[0]->quad_list)); EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size()); + ValidateTextureDrawQuads(frame.render_passes[0]->quad_list); - LayerTestCommon::VerifyQuadsExactlyCoverRect( - frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_)); + VerifyQuadsExactlyCoverViewport(frame.render_passes[0]->quad_list); host_impl_->DidDrawAllLayers(frame); } @@ -2938,15 +2962,11 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); ASSERT_EQ(1u, frame.render_passes.size()); - size_t num_gutter_quads = 0; - for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i) - num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material == - DrawQuad::SOLID_COLOR) ? 1 : 0; - EXPECT_EQ(4u, num_gutter_quads); + EXPECT_EQ(4u, CountGutterQuads(frame.render_passes[0]->quad_list)); EXPECT_EQ(5u, frame.render_passes[0]->quad_list.size()); + ValidateTextureDrawQuads(frame.render_passes[0]->quad_list); - LayerTestCommon::VerifyQuadsExactlyCoverRect( - frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_)); + VerifyQuadsExactlyCoverViewport(frame.render_passes[0]->quad_list); host_impl_->DidDrawAllLayers(frame); } @@ -2964,12 +2984,9 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); ASSERT_EQ(1u, frame.render_passes.size()); - size_t num_gutter_quads = 0; - for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i) - num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material == - DrawQuad::SOLID_COLOR) ? 1 : 0; - EXPECT_EQ(0u, num_gutter_quads); + EXPECT_EQ(0u, CountGutterQuads(frame.render_passes[0]->quad_list)); EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size()); + ValidateTextureDrawQuads(frame.render_passes[0]->quad_list); host_impl_->DidDrawAllLayers(frame); } @@ -2978,7 +2995,54 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest { did_activate_pending_tree_ = true; } + void set_gutter_quad_material(DrawQuad::Material material) { + gutter_quad_material_ = material; + } + void set_gutter_texture_size(gfx::Size gutter_texture_size) { + gutter_texture_size_ = gutter_texture_size; + } + protected: + size_t CountGutterQuads(const QuadList& quad_list) { + size_t num_gutter_quads = 0; + for (size_t i = 0; i < quad_list.size(); ++i) { + num_gutter_quads += (quad_list[i]->material == + gutter_quad_material_) ? 1 : 0; + } + return num_gutter_quads; + } + + void VerifyQuadsExactlyCoverViewport(const QuadList& quad_list) { + LayerTestCommon::VerifyQuadsExactlyCoverRect( + quad_list, gfx::Rect(DipSizeToPixelSize(viewport_size_))); + } + + // Make sure that the texture coordinates match their expectations. + void ValidateTextureDrawQuads(const QuadList& quad_list) { + for (size_t i = 0; i < quad_list.size(); ++i) { + if (quad_list[i]->material != DrawQuad::TEXTURE_CONTENT) + continue; + const TextureDrawQuad* quad = TextureDrawQuad::MaterialCast(quad_list[i]); + gfx::SizeF gutter_texture_size_pixels = gfx::ScaleSize( + gutter_texture_size_, host_impl_->device_scale_factor()); + EXPECT_EQ(quad->uv_top_left.x(), + quad->rect.x() / gutter_texture_size_pixels.width()); + EXPECT_EQ(quad->uv_top_left.y(), + quad->rect.y() / gutter_texture_size_pixels.height()); + EXPECT_EQ(quad->uv_bottom_right.x(), + quad->rect.right() / gutter_texture_size_pixels.width()); + EXPECT_EQ(quad->uv_bottom_right.y(), + quad->rect.bottom() / gutter_texture_size_pixels.height()); + } + } + + gfx::Size DipSizeToPixelSize(gfx::Size size) { + return gfx::ToRoundedSize( + gfx::ScaleSize(size, host_impl_->device_scale_factor())); + } + + DrawQuad::Material gutter_quad_material_; + gfx::Size gutter_texture_size_; gfx::Size viewport_size_; BlendStateCheckLayer* child_; bool did_activate_pending_tree_; @@ -2988,7 +3052,7 @@ TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCovered) { bool always_draw = false; CreateLayerTreeHostImpl(always_draw); - host_impl_->SetViewportSize(viewport_size_); + host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_)); SetupActiveTreeLayers(); TestLayerCoversFullViewport(); TestEmptyLayer(); @@ -2996,13 +3060,70 @@ TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCovered) { TestLayerIsLargerThanViewport(); } +TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCoveredScaled) { + bool always_draw = false; + CreateLayerTreeHostImpl(always_draw); + + host_impl_->SetDeviceScaleFactor(2.f); + host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_)); + SetupActiveTreeLayers(); + TestLayerCoversFullViewport(); + TestEmptyLayer(); + TestLayerInMiddleOfViewport(); + TestLayerIsLargerThanViewport(); +} + +TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCoveredOverhangBitmap) { + bool always_draw = false; + CreateLayerTreeHostImpl(always_draw); + + host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_)); + SetupActiveTreeLayers(); + + SkBitmap skbitmap; + skbitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); + skbitmap.allocPixels(); + skbitmap.setImmutable(); + + // Specify an overhang bitmap to use. + UIResourceBitmap ui_resource_bitmap(skbitmap, UIResourceBitmap::REPEAT); + UIResourceId ui_resource_id = 12345; + host_impl_->CreateUIResource(ui_resource_id, ui_resource_bitmap); + host_impl_->SetOverhangUIResource(ui_resource_id, gfx::Size(32, 32)); + set_gutter_quad_material(DrawQuad::TEXTURE_CONTENT); + set_gutter_texture_size(gfx::Size(32, 32)); + + TestLayerCoversFullViewport(); + TestEmptyLayer(); + TestLayerInMiddleOfViewport(); + TestLayerIsLargerThanViewport(); + + // Change the resource size. + host_impl_->SetOverhangUIResource(ui_resource_id, gfx::Size(128, 16)); + set_gutter_texture_size(gfx::Size(128, 16)); + + TestLayerCoversFullViewport(); + TestEmptyLayer(); + TestLayerInMiddleOfViewport(); + TestLayerIsLargerThanViewport(); + + // Change the device scale factor + host_impl_->SetDeviceScaleFactor(2.f); + host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_)); + + TestLayerCoversFullViewport(); + TestEmptyLayer(); + TestLayerInMiddleOfViewport(); + TestLayerIsLargerThanViewport(); +} + TEST_F(LayerTreeHostImplViewportCoveredTest, ActiveTreeGrowViewportInvalid) { bool always_draw = true; CreateLayerTreeHostImpl(always_draw); // Pending tree to force active_tree size invalid. Not used otherwise. host_impl_->CreatePendingTree(); - host_impl_->SetViewportSize(viewport_size_); + host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_)); EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid()); SetupActiveTreeLayers(); @@ -3019,16 +3140,15 @@ TEST_F(LayerTreeHostImplViewportCoveredTest, ActiveTreeShrinkViewportInvalid) { host_impl_->CreatePendingTree(); gfx::Size larger_viewport(viewport_size_.width() + 100, viewport_size_.height() + 100); - host_impl_->SetViewportSize(larger_viewport); + host_impl_->SetViewportSize(DipSizeToPixelSize(larger_viewport)); EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid()); - did_activate_pending_tree_ = false; - host_impl_->ActivatePendingTreeIfNeeded(); + host_impl_->ActivatePendingTree(); EXPECT_TRUE(did_activate_pending_tree_); EXPECT_FALSE(host_impl_->active_tree()->ViewportSizeInvalid()); // Shrink pending tree viewport without activating. host_impl_->CreatePendingTree(); - host_impl_->SetViewportSize(viewport_size_); + host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_)); EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid()); SetupActiveTreeLayers(); @@ -3081,11 +3201,11 @@ class FakeDrawableLayerImpl: public LayerImpl { // can leave the window at the wrong size if we never draw and the proper // viewport size is never set. TEST_F(LayerTreeHostImplTest, ReshapeNotCalledUntilDraw) { - scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d( - scoped_ptr<WebKit::WebGraphicsContext3D>(new ReshapeTrackerContext)) - .PassAs<OutputSurface>(); - ReshapeTrackerContext* reshape_tracker = - static_cast<ReshapeTrackerContext*>(output_surface->context3d()); + scoped_ptr<ReshapeTrackerContext> owned_reshape_tracker( + new ReshapeTrackerContext); + ReshapeTrackerContext* reshape_tracker = owned_reshape_tracker.get(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + owned_reshape_tracker.PassAs<TestWebGraphicsContext3D>())); host_impl_->InitializeRenderer(output_surface.Pass()); scoped_ptr<LayerImpl> root = @@ -3133,7 +3253,11 @@ TEST_F(LayerTreeHostImplTest, ReshapeNotCalledUntilDraw) { class SwapTrackerContext : public TestWebGraphicsContext3D { public: - SwapTrackerContext() : last_update_type_(NoUpdate) {} + SwapTrackerContext() + : last_update_type_(NoUpdate) { + test_capabilities_.post_sub_buffer = true; + test_capabilities_.set_visibility = true; + } virtual void prepareTexture() OVERRIDE { update_rect_ = gfx::Rect(width_, height_); @@ -3146,15 +3270,6 @@ class SwapTrackerContext : public TestWebGraphicsContext3D { last_update_type_ = PostSubBuffer; } - virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE { - if (name == GL_EXTENSIONS) { - return WebKit::WebString( - "GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_set_visibility"); - } - - return WebKit::WebString(); - } - gfx::Rect update_rect() const { return update_rect_; } enum UpdateType { @@ -3175,11 +3290,11 @@ class SwapTrackerContext : public TestWebGraphicsContext3D { // Make sure damage tracking propagates all the way to the graphics context, // where it should request to swap only the sub-buffer that is damaged. TEST_F(LayerTreeHostImplTest, PartialSwapReceivesDamageRect) { - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new SwapTrackerContext)).PassAs<OutputSurface>(); - SwapTrackerContext* swap_tracker = - static_cast<SwapTrackerContext*>(output_surface->context3d()); + scoped_ptr<SwapTrackerContext> context(new SwapTrackerContext); + SwapTrackerContext* swap_tracker = context.get(); + + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context.PassAs<TestWebGraphicsContext3D>())); // This test creates its own LayerTreeHostImpl, so // that we can force partial swap enabled. @@ -3329,7 +3444,6 @@ class MockContext : public TestWebGraphicsContext3D { WebKit::WGC3Dsizei count, WebKit::WGC3Denum type, WebKit::WGC3Dintptr offset)); - MOCK_METHOD1(getString, WebKit::WebString(WebKit::WGC3Denum name)); MOCK_METHOD0(getRequestableExtensionsCHROMIUM, WebKit::WebString()); MOCK_METHOD1(enable, void(WebKit::WGC3Denum cap)); MOCK_METHOD1(disable, void(WebKit::WGC3Denum cap)); @@ -3346,6 +3460,8 @@ class MockContextHarness { public: explicit MockContextHarness(MockContext* context) : context_(context) { + context_->set_have_post_sub_buffer(true); + // Catch "uninteresting" calls EXPECT_CALL(*context_, useProgram(_)) .Times(0); @@ -3360,19 +3476,6 @@ class MockContextHarness { EXPECT_CALL(*context_, uniform4f(_, _, _, _, _)) .WillRepeatedly(Return()); - // Any other strings are empty - EXPECT_CALL(*context_, getString(_)) - .WillRepeatedly(Return(WebKit::WebString())); - - // Support for partial swap, if needed - EXPECT_CALL(*context_, getString(GL_EXTENSIONS)) - .WillRepeatedly(Return( - WebKit::WebString("GL_CHROMIUM_post_sub_buffer"))); - - EXPECT_CALL(*context_, getRequestableExtensionsCHROMIUM()) - .WillRepeatedly(Return( - WebKit::WebString("GL_CHROMIUM_post_sub_buffer"))); - // Any un-sanctioned calls to enable() are OK EXPECT_CALL(*context_, enable(_)) .WillRepeatedly(Return()); @@ -3414,11 +3517,11 @@ class MockContextHarness { }; TEST_F(LayerTreeHostImplTest, NoPartialSwap) { - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new MockContext)).PassAs<OutputSurface>(); - MockContext* mock_context = - static_cast<MockContext*>(output_surface->context3d()); + scoped_ptr<MockContext> mock_context_owned(new MockContext); + MockContext* mock_context = mock_context_owned.get(); + + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + mock_context_owned.PassAs<TestWebGraphicsContext3D>())); MockContextHarness harness(mock_context); // Run test case @@ -3451,11 +3554,10 @@ TEST_F(LayerTreeHostImplTest, NoPartialSwap) { } TEST_F(LayerTreeHostImplTest, PartialSwap) { - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new MockContext)).PassAs<OutputSurface>(); - MockContext* mock_context = - static_cast<MockContext*>(output_surface->context3d()); + scoped_ptr<MockContext> context_owned(new MockContext); + MockContext* mock_context = context_owned.get(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + context_owned.PassAs<TestWebGraphicsContext3D>())); MockContextHarness harness(mock_context); CreateLayerTreeHost(true, output_surface.Pass()); @@ -3490,14 +3592,8 @@ TEST_F(LayerTreeHostImplTest, PartialSwap) { class PartialSwapContext : public TestWebGraphicsContext3D { public: - virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE { - if (name == GL_EXTENSIONS) - return WebKit::WebString("GL_CHROMIUM_post_sub_buffer"); - return WebKit::WebString(); - } - - virtual WebKit::WebString getRequestableExtensionsCHROMIUM() OVERRIDE { - return WebKit::WebString("GL_CHROMIUM_post_sub_buffer"); + PartialSwapContext() { + test_capabilities_.post_sub_buffer = true; } // Unlimited texture size. @@ -3515,9 +3611,8 @@ static scoped_ptr<LayerTreeHostImpl> SetupLayersForOpacity( LayerTreeHostImplClient* client, Proxy* proxy, RenderingStatsInstrumentation* stats_instrumentation) { - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new PartialSwapContext)).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext))); LayerTreeSettings settings; settings.partial_swap_enabled = partial_swap; @@ -3634,7 +3729,10 @@ class TrackingWebGraphicsContext3D : public TestWebGraphicsContext3D { public: TrackingWebGraphicsContext3D() : TestWebGraphicsContext3D(), - num_textures_(0) {} + num_textures_(0) { + test_capabilities_.iosurface = true; + test_capabilities_.texture_rectangle = true; + } virtual WebKit::WebGLId createTexture() OVERRIDE { WebKit::WebGLId id = TestWebGraphicsContext3D::createTexture(); @@ -3652,15 +3750,6 @@ class TrackingWebGraphicsContext3D : public TestWebGraphicsContext3D { --num_textures_; } - virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE { - if (name == GL_EXTENSIONS) { - return WebKit::WebString( - "GL_CHROMIUM_iosurface GL_ARB_texture_rectangle"); - } - - return WebKit::WebString(); - } - unsigned num_textures() const { return num_textures_; } private: @@ -3672,8 +3761,8 @@ TEST_F(LayerTreeHostImplTest, LayersFreeTextures) { scoped_ptr<TestWebGraphicsContext3D> context = TestWebGraphicsContext3D::Create(); TestWebGraphicsContext3D* context3d = context.get(); - scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface( + FakeOutputSurface::Create3d(context.Pass())); host_impl_->InitializeRenderer(output_surface.Pass()); scoped_ptr<LayerImpl> root_layer = @@ -3732,12 +3821,12 @@ class MockDrawQuadsToFillScreenContext : public TestWebGraphicsContext3D { }; TEST_F(LayerTreeHostImplTest, HasTransparentBackground) { - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new MockDrawQuadsToFillScreenContext)).PassAs<OutputSurface>(); - MockDrawQuadsToFillScreenContext* mock_context = - static_cast<MockDrawQuadsToFillScreenContext*>( - output_surface->context3d()); + scoped_ptr<MockDrawQuadsToFillScreenContext> mock_context_owned( + new MockDrawQuadsToFillScreenContext); + MockDrawQuadsToFillScreenContext* mock_context = mock_context_owned.get(); + + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + mock_context_owned.PassAs<TestWebGraphicsContext3D>())); // Run test case CreateLayerTreeHost(false, output_surface.Pass()); @@ -3790,9 +3879,8 @@ static void SetupLayersForTextureCaching( LayerImpl*& surface_layer_ptr, LayerImpl*& child_ptr, gfx::Size root_size) { - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new PartialSwapContext)).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext))); layer_tree_host_impl->InitializeRenderer(output_surface.Pass()); layer_tree_host_impl->SetViewportSize(root_size); @@ -3869,9 +3957,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusion) { LayerImpl* layer_s1_ptr; LayerImpl* layer_s2_ptr; - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new PartialSwapContext)).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext))); gfx::Size root_size(1000, 1000); @@ -3988,9 +4075,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusionEarlyOut) { LayerImpl* layer_s1_ptr; LayerImpl* layer_s2_ptr; - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new PartialSwapContext)).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext))); gfx::Size root_size(1000, 1000); @@ -4109,9 +4195,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusionExternalOverInternal) { LayerImpl* layer_s1_ptr; LayerImpl* layer_s2_ptr; - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new PartialSwapContext)).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext))); gfx::Size root_size(1000, 1000); @@ -4199,9 +4284,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusionExternalNotAligned) { LayerImpl* root_ptr; LayerImpl* layer_s1_ptr; - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new PartialSwapContext)).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext))); gfx::Size root_size(1000, 1000); @@ -4290,9 +4374,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusionPartialSwap) { LayerImpl* layer_s1_ptr; LayerImpl* layer_s2_ptr; - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new PartialSwapContext)).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext))); gfx::Size root_size(1000, 1000); @@ -4420,9 +4503,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithScissor) { gfx::Rect child_rect(10, 10, 50, 50); gfx::Rect grand_child_rect(5, 5, 150, 150); - scoped_ptr<OutputSurface> output_surface = - FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>( - new PartialSwapContext)).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext))); my_host_impl->InitializeRenderer(output_surface.Pass()); root->SetAnchorPoint(gfx::PointF()); @@ -4924,18 +5006,18 @@ TEST_F(LayerTreeHostImplTest, ReleaseContentsTextureShouldTriggerCommit) { } struct RenderPassRemovalTestData : public LayerTreeHostImpl::FrameData { - ScopedPtrHashMap<RenderPass::Id, TestRenderPass> render_pass_cache; + base::ScopedPtrHashMap<RenderPass::Id, TestRenderPass> render_pass_cache; scoped_ptr<SharedQuadState> shared_quad_state; }; class TestRenderer : public GLRenderer, public RendererClient { public: - static scoped_ptr<TestRenderer> Create(ResourceProvider* resource_provider, + static scoped_ptr<TestRenderer> Create(const LayerTreeSettings* settings, + ResourceProvider* resource_provider, OutputSurface* output_surface, Proxy* proxy) { - scoped_ptr<TestRenderer> renderer(new TestRenderer(resource_provider, - output_surface, - proxy)); + scoped_ptr<TestRenderer> renderer( + new TestRenderer(settings, resource_provider, output_surface, proxy)); if (!renderer->Initialize()) return scoped_ptr<TestRenderer>(); @@ -4956,27 +5038,19 @@ class TestRenderer : public GLRenderer, public RendererClient { virtual gfx::Rect DeviceViewport() const OVERRIDE { return gfx::Rect(viewport_size_); } - virtual float DeviceScaleFactor() const OVERRIDE { - return 1.f; - } - virtual const LayerTreeSettings& Settings() const OVERRIDE { - return settings_; - } + virtual gfx::Rect DeviceClip() const OVERRIDE { return DeviceViewport(); } virtual void SetFullRootLayerDamage() OVERRIDE {} - virtual bool HasImplThread() const OVERRIDE { return false; } - virtual bool ShouldClearRootRenderPass() const OVERRIDE { return true; } - virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const - OVERRIDE { return CompositorFrameMetadata(); } - virtual bool AllowPartialSwap() const OVERRIDE { - return true; + virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE { + return CompositorFrameMetadata(); } - virtual bool ExternalStencilTestEnabled() const OVERRIDE { return false; } protected: - TestRenderer(ResourceProvider* resource_provider, + TestRenderer(const LayerTreeSettings* settings, + ResourceProvider* resource_provider, OutputSurface* output_surface, Proxy* proxy) - : GLRenderer(this, output_surface, resource_provider, 0) {} + : GLRenderer(this, settings, output_surface, resource_provider, NULL, 0) { + } private: LayerTreeSettings settings_; @@ -5296,15 +5370,17 @@ static void VerifyRenderPassTestData( } TEST_F(LayerTreeHostImplTest, TestRemoveRenderPasses) { + LayerTreeSettings settings; + FakeOutputSurfaceClient output_surface_client; scoped_ptr<OutputSurface> output_surface(CreateOutputSurface()); - ASSERT_TRUE(output_surface->context3d()); + ASSERT_TRUE(output_surface->BindToClient(&output_surface_client)); + ASSERT_TRUE(output_surface->context_provider()); + scoped_ptr<ResourceProvider> resource_provider = - ResourceProvider::Create(output_surface.get(), 0); + ResourceProvider::Create(output_surface.get(), 0, false); - scoped_ptr<TestRenderer> renderer = - TestRenderer::Create(resource_provider.get(), - output_surface.get(), - &proxy_); + scoped_ptr<TestRenderer> renderer = TestRenderer::Create( + &settings, resource_provider.get(), output_surface.get(), &proxy_); int test_case_index = 0; while (remove_render_passes_cases[test_case_index].name) { @@ -5388,7 +5464,7 @@ TEST_F(LayerTreeHostImplTestWithDelegatingRenderer, FrameIncludesDamageRect) { host_impl_->active_tree()->SetRootLayer(root.PassAs<LayerImpl>()); // Draw a frame. In the first frame, the entire viewport should be damaged. - gfx::Rect full_frame_damage = gfx::Rect(host_impl_->device_viewport_size()); + gfx::Rect full_frame_damage(host_impl_->DrawViewportSize()); DrawFrameAndTestDamage(full_frame_damage); // The second frame has damage that doesn't touch the child layer. Its quads @@ -6085,6 +6161,79 @@ TEST_F(LayerTreeHostImplTest, MaskLayerForSurfaceWithClippedLayer) { } } +class GLRendererWithSetupQuadForAntialiasing : public GLRenderer { + public: + using GLRenderer::SetupQuadForAntialiasing; +}; + +TEST_F(LayerTreeHostImplTest, FarAwayQuadsDontNeedAA) { + // Due to precision issues (especially on Android), sometimes far + // away quads can end up thinking they need AA. + float device_scale_factor = 4.f / 3.f; + host_impl_->SetDeviceScaleFactor(device_scale_factor); + gfx::Size root_size(2000, 1000); + gfx::Size device_viewport_size = + gfx::ToCeiledSize(gfx::ScaleSize(root_size, device_scale_factor)); + host_impl_->SetViewportSize(device_viewport_size); + + host_impl_->CreatePendingTree(); + host_impl_->pending_tree() + ->SetPageScaleFactorAndLimits(1.f, 1.f / 16.f, 16.f); + + scoped_ptr<LayerImpl> scoped_root = + LayerImpl::Create(host_impl_->pending_tree(), 1); + LayerImpl* root = scoped_root.get(); + + host_impl_->pending_tree()->SetRootLayer(scoped_root.Pass()); + + scoped_ptr<LayerImpl> scoped_scrolling_layer = + LayerImpl::Create(host_impl_->pending_tree(), 2); + LayerImpl* scrolling_layer = scoped_scrolling_layer.get(); + root->AddChild(scoped_scrolling_layer.Pass()); + + gfx::Size content_layer_bounds(100000, 100); + gfx::Size pile_tile_size(3000, 3000); + scoped_refptr<FakePicturePileImpl> pile(FakePicturePileImpl::CreateFilledPile( + pile_tile_size, content_layer_bounds)); + + scoped_ptr<FakePictureLayerImpl> scoped_content_layer = + FakePictureLayerImpl::CreateWithPile(host_impl_->pending_tree(), 3, pile); + LayerImpl* content_layer = scoped_content_layer.get(); + scrolling_layer->AddChild(scoped_content_layer.PassAs<LayerImpl>()); + content_layer->SetBounds(content_layer_bounds); + content_layer->SetDrawsContent(true); + + root->SetBounds(root_size); + + gfx::Vector2d scroll_offset(100000, 0); + scrolling_layer->SetScrollable(true); + scrolling_layer->SetMaxScrollOffset(scroll_offset); + scrolling_layer->SetScrollOffset(scroll_offset); + + host_impl_->ActivatePendingTree(); + + host_impl_->active_tree()->UpdateDrawProperties(); + ASSERT_EQ(1u, host_impl_->active_tree()->RenderSurfaceLayerList().size()); + + LayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); + + ASSERT_EQ(1u, frame.render_passes.size()); + ASSERT_LE(1u, frame.render_passes[0]->quad_list.size()); + const DrawQuad* quad = frame.render_passes[0]->quad_list[0]; + + float edge[24]; + gfx::QuadF device_layer_quad; + bool antialiased = + GLRendererWithSetupQuadForAntialiasing::SetupQuadForAntialiasing( + quad->quadTransform(), quad, &device_layer_quad, edge); + EXPECT_FALSE(antialiased); + + host_impl_->DrawLayers(&frame, base::TimeTicks::Now()); + host_impl_->DidDrawAllLayers(frame); +} + + class CompositorFrameMetadataTest : public LayerTreeHostImplTest { public: CompositorFrameMetadataTest() @@ -6106,7 +6255,8 @@ TEST_F(CompositorFrameMetadataTest, CompositorFrameAckCountsAsSwapComplete) { host_impl_->DidDrawAllLayers(frame); } CompositorFrameAck ack; - host_impl_->OnSwapBuffersComplete(&ack); + host_impl_->ReclaimResources(&ack); + host_impl_->OnSwapBuffersComplete(); EXPECT_EQ(swap_buffers_complete_, 1); } @@ -6187,55 +6337,152 @@ TEST_F(LayerTreeHostImplTest, EXPECT_EQ(host_impl_->active_tree()->root_layer(), frame.will_draw_layers[0]); } -TEST_F(LayerTreeHostImplTest, DeferredInitializeSmoke) { - set_reduce_memory_result(false); - scoped_ptr<FakeOutputSurface> output_surface( - FakeOutputSurface::CreateDeferredGL( - scoped_ptr<SoftwareOutputDevice>(new CountingSoftwareDevice()))); - FakeOutputSurface* output_surface_ptr = output_surface.get(); - EXPECT_TRUE( - host_impl_->InitializeRenderer(output_surface.PassAs<OutputSurface>())); +class LayerTreeHostImplTestDeferredInitialize : public LayerTreeHostImplTest { + protected: + virtual void SetUp() OVERRIDE { + LayerTreeHostImplTest::SetUp(); + + set_reduce_memory_result(false); + + scoped_ptr<FakeOutputSurface> output_surface( + FakeOutputSurface::CreateDeferredGL( + scoped_ptr<SoftwareOutputDevice>(new CountingSoftwareDevice()))); + output_surface_ = output_surface.get(); + + EXPECT_TRUE(host_impl_->InitializeRenderer( + output_surface.PassAs<OutputSurface>())); + + scoped_ptr<SolidColorLayerImpl> root_layer = + SolidColorLayerImpl::Create(host_impl_->active_tree(), 1); + SetupRootLayerImpl(root_layer.PassAs<LayerImpl>()); + + onscreen_context_provider_ = TestContextProvider::Create(); + offscreen_context_provider_ = TestContextProvider::Create(); + } + + FakeOutputSurface* output_surface_; + scoped_refptr<TestContextProvider> onscreen_context_provider_; + scoped_refptr<TestContextProvider> offscreen_context_provider_; +}; - // Add two layers. - scoped_ptr<SolidColorLayerImpl> root_layer = - SolidColorLayerImpl::Create(host_impl_->active_tree(), 1); - FakeVideoFrameProvider provider; - scoped_ptr<VideoLayerImpl> video_layer = - VideoLayerImpl::Create(host_impl_->active_tree(), 2, &provider); - video_layer->SetBounds(gfx::Size(10, 10)); - video_layer->SetContentBounds(gfx::Size(10, 10)); - video_layer->SetDrawsContent(true); - root_layer->AddChild(video_layer.PassAs<LayerImpl>()); - SetupRootLayerImpl(root_layer.PassAs<LayerImpl>()); +TEST_F(LayerTreeHostImplTestDeferredInitialize, Success) { // Software draw. DrawFrame(); + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + // DeferredInitialize and hardware draw. - EXPECT_FALSE(did_try_initialize_renderer_); - EXPECT_TRUE(output_surface_ptr->SetAndInitializeContext3D( - scoped_ptr<WebKit::WebGraphicsContext3D>( - TestWebGraphicsContext3D::Create()))); - EXPECT_TRUE(did_try_initialize_renderer_); + EXPECT_TRUE(output_surface_->InitializeAndSetContext3d( + onscreen_context_provider_, offscreen_context_provider_)); + EXPECT_EQ(onscreen_context_provider_, + host_impl_->output_surface()->context_provider()); + EXPECT_EQ(offscreen_context_provider_, + host_impl_->offscreen_context_provider()); // Defer intialized GL draw. DrawFrame(); // Revert back to software. - did_try_initialize_renderer_ = false; - output_surface_ptr->ReleaseGL(); - EXPECT_TRUE(did_try_initialize_renderer_); + output_surface_->ReleaseGL(); + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + + // Software draw again. DrawFrame(); } -class ContextThatDoesNotSupportMemoryManagmentExtensions - : public TestWebGraphicsContext3D { - public: - // WebGraphicsContext3D methods. - virtual WebKit::WebString getString(WebKit::WGC3Denum name) { - return WebKit::WebString(); - } -}; +TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_0) { + // Software draw. + DrawFrame(); + + // Fail initialization of the onscreen context before the OutputSurface binds + // it to the thread. + onscreen_context_provider_->UnboundTestContext3d() + ->set_times_make_current_succeeds(0); + + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + EXPECT_FALSE(did_lose_output_surface_); + + // DeferredInitialize fails. + EXPECT_FALSE(output_surface_->InitializeAndSetContext3d( + onscreen_context_provider_, offscreen_context_provider_)); + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + + // Software draw again. + DrawFrame(); +} + +TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_1) { + // Software draw. + DrawFrame(); + + // Fail initialization of the onscreen context after the OutputSurface binds + // it to the thread. + onscreen_context_provider_->UnboundTestContext3d() + ->set_times_make_current_succeeds(2); + + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + EXPECT_FALSE(did_lose_output_surface_); + + // DeferredInitialize fails. + EXPECT_FALSE(output_surface_->InitializeAndSetContext3d( + onscreen_context_provider_, offscreen_context_provider_)); + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + + // We lose the output surface. + EXPECT_TRUE(did_lose_output_surface_); +} + +TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_2) { + // Software draw. + DrawFrame(); + + // Fail initialization of the onscreen context after the OutputSurface binds + // it to the thread and during renderer initialization. + onscreen_context_provider_->UnboundTestContext3d() + ->set_times_make_current_succeeds(1); + + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + EXPECT_FALSE(did_lose_output_surface_); + + // DeferredInitialize fails. + EXPECT_FALSE(output_surface_->InitializeAndSetContext3d( + onscreen_context_provider_, offscreen_context_provider_)); + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + + // We lose the output surface. + EXPECT_TRUE(did_lose_output_surface_); +} + +TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OffscreenContext) { + // Software draw. + DrawFrame(); + + // Fail initialization of the offscreen context. + offscreen_context_provider_->UnboundTestContext3d() + ->set_times_make_current_succeeds(0); + + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + EXPECT_FALSE(did_lose_output_surface_); + + // DeferredInitialize fails. + EXPECT_FALSE(output_surface_->InitializeAndSetContext3d( + onscreen_context_provider_, offscreen_context_provider_)); + EXPECT_FALSE(host_impl_->output_surface()->context_provider()); + EXPECT_FALSE(host_impl_->offscreen_context_provider()); + + // We lose the output surface. + EXPECT_TRUE(did_lose_output_surface_); +} // Checks that we have a non-0 default allocation if we pass a context that // doesn't support memory management extensions. @@ -6246,10 +6493,9 @@ TEST_F(LayerTreeHostImplTest, DefaultMemoryAllocation) { &proxy_, &stats_instrumentation_); - host_impl_->InitializeRenderer(FakeOutputSurface::Create3d( - scoped_ptr<WebKit::WebGraphicsContext3D>( - new ContextThatDoesNotSupportMemoryManagmentExtensions)) - .PassAs<OutputSurface>()); + scoped_ptr<OutputSurface> output_surface( + FakeOutputSurface::Create3d(TestWebGraphicsContext3D::Create())); + host_impl_->InitializeRenderer(output_surface.Pass()); EXPECT_LT(0ul, host_impl_->memory_allocation_limit_bytes()); } @@ -6276,19 +6522,44 @@ TEST_F(LayerTreeHostImplTest, MemoryPolicy) { EXPECT_EQ(visible_cutoff_value, current_priority_cutoff_value_); } +class LayerTreeHostImplTestManageTiles : public LayerTreeHostImplTest { + public: + virtual void SetUp() OVERRIDE { + LayerTreeSettings settings; + settings.impl_side_painting = true; + + fake_host_impl_ = new FakeLayerTreeHostImpl(settings, &proxy_); + host_impl_.reset(fake_host_impl_); + host_impl_->InitializeRenderer(CreateOutputSurface()); + host_impl_->SetViewportSize(gfx::Size(10, 10)); + } + + FakeLayerTreeHostImpl* fake_host_impl_; +}; + +TEST_F(LayerTreeHostImplTestManageTiles, ManageTilesWhenInvisible) { + fake_host_impl_->DidModifyTilePriorities(); + EXPECT_TRUE(fake_host_impl_->manage_tiles_needed()); + fake_host_impl_->SetVisible(false); + EXPECT_FALSE(fake_host_impl_->manage_tiles_needed()); +} + TEST_F(LayerTreeHostImplTest, UIResourceManagement) { scoped_ptr<TestWebGraphicsContext3D> context = TestWebGraphicsContext3D::Create(); TestWebGraphicsContext3D* context3d = context.get(); - scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>(); + scoped_ptr<OutputSurface> output_surface = CreateFakeOutputSurface(); host_impl_->InitializeRenderer(output_surface.Pass()); EXPECT_EQ(0u, context3d->NumTextures()); + SkBitmap skbitmap; + skbitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); + skbitmap.allocPixels(); + skbitmap.setImmutable(); + UIResourceId ui_resource_id = 1; - scoped_refptr<UIResourceBitmap> bitmap = UIResourceBitmap::Create( - new uint8_t[1], UIResourceBitmap::RGBA8, gfx::Size(1, 1)); + UIResourceBitmap bitmap(skbitmap); host_impl_->CreateUIResource(ui_resource_id, bitmap); EXPECT_EQ(1u, context3d->NumTextures()); ResourceProvider::ResourceId id1 = @@ -6322,5 +6593,46 @@ TEST_F(LayerTreeHostImplTest, UIResourceManagement) { EXPECT_EQ(0u, context3d->NumTextures()); } +void ShutdownReleasesContext_Callback(scoped_ptr<CopyOutputResult> result) { +} + +TEST_F(LayerTreeHostImplTest, ShutdownReleasesContext) { + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create(); + + host_impl_ = LayerTreeHostImpl::Create(LayerTreeSettings(), + this, + &proxy_, + &stats_instrumentation_); + host_impl_->InitializeRenderer( + FakeOutputSurface::Create3d(context_provider).PassAs<OutputSurface>()); + host_impl_->SetViewportSize(gfx::Size(10, 10)); + + SetupRootLayerImpl(LayerImpl::Create(host_impl_->active_tree(), 1)); + + ScopedPtrVector<CopyOutputRequest> requests; + requests.push_back(CopyOutputRequest::CreateRequest( + base::Bind(&ShutdownReleasesContext_Callback))); + + host_impl_->active_tree()->root_layer()->PassCopyRequests(&requests); + + LayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect())); + host_impl_->DrawLayers(&frame, base::TimeTicks::Now()); + host_impl_->DidDrawAllLayers(frame); + + // The CopyOutputResult's callback has a ref on the ContextProvider and a + // texture in a texture mailbox. + EXPECT_FALSE(context_provider->HasOneRef()); + EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures()); + + host_impl_.reset(); + + // The CopyOutputResult's callback was cancelled, the CopyOutputResult + // released, and the texture deleted. + EXPECT_TRUE(context_provider->HasOneRef()); + EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_perftest.cc b/chromium/cc/trees/layer_tree_host_perftest.cc index fc7673cd7ba..5ca78b64d49 100644 --- a/chromium/cc/trees/layer_tree_host_perftest.cc +++ b/chromium/cc/trees/layer_tree_host_perftest.cc @@ -8,14 +8,17 @@ #include "base/files/file_path.h" #include "base/path_service.h" #include "base/strings/string_piece.h" +#include "base/time/time.h" #include "cc/layers/content_layer.h" #include "cc/layers/nine_patch_layer.h" #include "cc/layers/solid_color_layer.h" #include "cc/test/fake_content_layer_client.h" +#include "cc/test/lap_timer.h" #include "cc/test/layer_tree_json_parser.h" #include "cc/test/layer_tree_test.h" #include "cc/test/paths.h" #include "cc/trees/layer_tree_impl.h" +#include "testing/perf/perf_test.h" namespace cc { namespace { @@ -27,8 +30,10 @@ static const int kTimeCheckInterval = 10; class LayerTreeHostPerfTest : public LayerTreeTest { public: LayerTreeHostPerfTest() - : num_draws_(0), - num_commits_(0), + : draw_timer_(kWarmupRuns, + base::TimeDelta::FromMilliseconds(kTimeLimitMillis), + kTimeCheckInterval), + commit_timer_(0, base::TimeDelta(), 1), full_damage_each_frame_(false), animation_driven_drawing_(false), measure_commit_cost_(false) { @@ -47,28 +52,23 @@ class LayerTreeHostPerfTest : public LayerTreeTest { virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (measure_commit_cost_) - commit_start_time_ = base::TimeTicks::HighResNow(); + commit_timer_.Start(); } virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { - if (measure_commit_cost_ && num_draws_ >= kWarmupRuns) { - total_commit_time_ += base::TimeTicks::HighResNow() - commit_start_time_; - ++num_commits_; + if (measure_commit_cost_ && draw_timer_.IsWarmedUp()) { + commit_timer_.NextLap(); } } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { - ++num_draws_; - if (num_draws_ == kWarmupRuns) - start_time_ = base::TimeTicks::HighResNow(); - - if (!start_time_.is_null() && (num_draws_ % kTimeCheckInterval) == 0) { - base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_; - if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) { - elapsed_ = elapsed; - EndTest(); - return; - } + if (TestEnded()) { + return; + } + draw_timer_.NextLap(); + if (draw_timer_.HasTimeLimitExpired()) { + EndTest(); + return; } if (!animation_driven_drawing_) impl->SetNeedsRedraw(); @@ -79,34 +79,29 @@ class LayerTreeHostPerfTest : public LayerTreeTest { virtual void BuildTree() {} virtual void AfterTest() OVERRIDE { - num_draws_ -= kWarmupRuns; - - // Format matches chrome/test/perf/perf_test.h:PrintResult - printf("*RESULT %s: frames: %d, %.2f ms/frame\n", - test_name_.c_str(), - num_draws_, - elapsed_.InMillisecondsF() / num_draws_); + CHECK(!test_name_.empty()) << "Must SetTestName() before AfterTest()."; + perf_test::PrintResult("layer_tree_host_frame_count", "", test_name_, + draw_timer_.NumLaps(), "frame_count", true); + perf_test::PrintResult("layer_tree_host_frame_time", "", test_name_, + 1000 * draw_timer_.MsPerLap(), "us", true); if (measure_commit_cost_) { - printf("*RESULT %s: commits: %d, %.2f ms/commit\n", - test_name_.c_str(), - num_commits_, - total_commit_time_.InMillisecondsF() / num_commits_); + perf_test::PrintResult("layer_tree_host_commit_count", "", test_name_, + commit_timer_.NumLaps(), "commit_count", true); + perf_test::PrintResult("layer_tree_host_commit_time", "", test_name_, + 1000 * commit_timer_.MsPerLap(), "us", true); } } protected: - base::TimeTicks start_time_; - int num_draws_; - int num_commits_; + LapTimer draw_timer_; + LapTimer commit_timer_; + std::string test_name_; - base::TimeDelta elapsed_; FakeContentLayerClient fake_content_layer_client_; bool full_damage_each_frame_; bool animation_driven_drawing_; bool measure_commit_cost_; - base::TimeTicks commit_start_time_; - base::TimeDelta total_commit_time_; }; @@ -116,12 +111,15 @@ class LayerTreeHostPerfTestJsonReader : public LayerTreeHostPerfTest { : LayerTreeHostPerfTest() { } - void ReadTestFile(std::string name) { + void SetTestName(const std::string& name) { test_name_ = name; + } + + void ReadTestFile(const std::string& name) { base::FilePath test_data_dir; ASSERT_TRUE(PathService::Get(cc::DIR_TEST_DATA, &test_data_dir)); base::FilePath json_file = test_data_dir.AppendASCII(name + ".json"); - ASSERT_TRUE(file_util::ReadFileToString(json_file, &json_)); + ASSERT_TRUE(base::ReadFileToString(json_file, &json_)); } virtual void BuildTree() OVERRIDE { @@ -139,6 +137,7 @@ class LayerTreeHostPerfTestJsonReader : public LayerTreeHostPerfTest { // Simulates a tab switcher scene with two stacks of 10 tabs each. TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread) { + SetTestName("10_10_single_thread"); ReadTestFile("10_10_layer_tree"); RunTest(false, false, false); } @@ -147,6 +146,7 @@ TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread) { TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread_FullDamageEachFrame) { full_damage_each_frame_ = true; + SetTestName("10_10_single_thread_full_damage_each_frame"); ReadTestFile("10_10_layer_tree"); RunTest(false, false, false); } @@ -180,6 +180,7 @@ class LayerTreeHostPerfTestLeafInvalidates // Simulates a tab switcher scene with two stacks of 10 tabs each. Invalidate a // property on a leaf layer in the tree every commit. TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenSingleThread) { + SetTestName("10_10_single_thread_leaf_invalidates"); ReadTestFile("10_10_layer_tree"); RunTest(false, false, false); } @@ -207,6 +208,7 @@ class ScrollingLayerTreePerfTest : public LayerTreeHostPerfTestJsonReader { }; TEST_F(ScrollingLayerTreePerfTest, LongScrollablePage) { + SetTestName("long_scrollable_page"); ReadTestFile("long_scrollable_page"); RunTest(false, false, false); } @@ -221,6 +223,7 @@ class ImplSidePaintingPerfTest : public LayerTreeHostPerfTestJsonReader { TEST_F(ImplSidePaintingPerfTest, HeavyPage) { animation_driven_drawing_ = true; measure_commit_cost_ = true; + SetTestName("heavy_page"); ReadTestFile("heavy_layer_tree"); RunTestWithImplSidePainting(); } @@ -284,6 +287,7 @@ class PageScaleImplSidePaintingPerfTest : public ImplSidePaintingPerfTest { TEST_F(PageScaleImplSidePaintingPerfTest, HeavyPage) { measure_commit_cost_ = true; + SetTestName("heavy_page_page_scale"); ReadTestFile("heavy_layer_tree"); RunTestWithImplSidePainting(); } diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc index 553e66e8dba..7068b4b1047 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc @@ -6,6 +6,8 @@ #include "cc/layers/solid_color_layer.h" #include "cc/test/layer_tree_pixel_test.h" #include "cc/test/pixel_comparator.h" +#include "third_party/skia/include/effects/SkColorFilterImageFilter.h" +#include "third_party/skia/include/effects/SkColorMatrixFilter.h" #if !defined(OS_ANDROID) @@ -52,7 +54,7 @@ TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlur) { base::FilePath(FILE_PATH_LITERAL("background_filter_blur.png"))); } -TEST_F(LayerTreeHostFiltersPixelTest, DISABLED_BackgroundFilterBlurOutsets) { +TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlurOutsets) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( gfx::Rect(200, 200), SK_ColorWHITE); @@ -73,8 +75,8 @@ TEST_F(LayerTreeHostFiltersPixelTest, DISABLED_BackgroundFilterBlurOutsets) { blur->SetBackgroundFilters(filters); #if defined(OS_WIN) - // Windows has 2250 pixels off by at most 2: crbug.com/259922 - float percentage_pixels_large_error = 5.625f; // 2250px / (200*200) + // Windows has 2596 pixels off by at most 2: crbug.com/259922 + float percentage_pixels_large_error = 6.5f; // 2596px / (200*200), rounded up float percentage_pixels_small_error = 0.0f; float average_error_allowed_in_bad_pixels = 1.f; int large_error_allowed = 2; @@ -148,6 +150,51 @@ TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlurOffAxis) { "background_filter_blur_off_axis.png"))); } +TEST_F(LayerTreeHostFiltersPixelTest, ImageFilterClipped) { + scoped_refptr<SolidColorLayer> root = CreateSolidColorLayer( + gfx::Rect(200, 200), SK_ColorBLACK); + + scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( + gfx::Rect(200, 200), SK_ColorYELLOW); + root->AddChild(background); + + scoped_refptr<SolidColorLayer> foreground = CreateSolidColorLayer( + gfx::Rect(200, 200), SK_ColorRED); + background->AddChild(foreground); + + SkScalar matrix[20]; + memset(matrix, 0, 20 * sizeof(matrix[0])); + // This filter does a red-blue swap, so the foreground becomes blue. + matrix[2] = matrix[6] = matrix[10] = matrix[18] = SK_Scalar1; + skia::RefPtr<SkColorFilter> colorFilter(skia::AdoptRef( + new SkColorMatrixFilter(matrix))); + // We filter only the bottom 200x100 pixels of the foreground. + SkIRect cropRect = SkIRect::MakeXYWH(0, 100, 200, 100); + skia::RefPtr<SkImageFilter> filter = + skia::AdoptRef(SkColorFilterImageFilter::Create( + colorFilter.get(), + NULL, + &cropRect)); + + // Make the foreground layer's render surface be clipped by the background + // layer. + background->SetMasksToBounds(true); + foreground->SetFilter(filter); + + // Then we translate the foreground up by 100 pixels in Y, so the cropped + // region is moved to to the top. This ensures that the crop rect is being + // correctly transformed in skia by the amount of clipping that the + // compositor performs. + gfx::Transform transform; + transform.Translate(0.0, -100.0); + foreground->SetTransform(transform); + + RunPixelTest(GL_WITH_BITMAP, + background, + base::FilePath(FILE_PATH_LITERAL( + "blue_yellow.png"))); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc index a9bc6ceff1b..d8e286183f5 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc @@ -58,13 +58,15 @@ class LayerTreeHostReadbackPixelTest : public LayerTreePixelTest { EXPECT_TRUE(proxy()->IsMainThread()); EXPECT_TRUE(result->HasTexture()); - scoped_ptr<TextureMailbox> texture_mailbox = result->TakeTexture().Pass(); - EXPECT_TRUE(texture_mailbox->IsValid()); - EXPECT_TRUE(texture_mailbox->IsTexture()); + TextureMailbox texture_mailbox; + scoped_ptr<SingleReleaseCallback> release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + EXPECT_TRUE(texture_mailbox.IsValid()); + EXPECT_TRUE(texture_mailbox.IsTexture()); scoped_ptr<SkBitmap> bitmap = - CopyTextureMailboxToBitmap(result->size(), *texture_mailbox); - texture_mailbox->RunReleaseCallback(0, false); + CopyTextureMailboxToBitmap(result->size(), texture_mailbox); + release_callback->Run(0, false); ReadbackResultAsBitmap(CopyOutputResult::CreateBitmapResult(bitmap.Pass())); } @@ -333,6 +335,156 @@ TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSmallNonRootLayerWithChild_GL) { "green_small_with_blue_corner.png"))); } +TEST_F(LayerTreeHostReadbackPixelTest, + ReadbackSubtreeSurroundsTargetLayer_Software) { + scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( + gfx::Rect(0, 0, 200, 200), SK_ColorWHITE); + + scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer( + gfx::Rect(100, 100, 100, 100), SK_ColorRED); + background->AddChild(target); + + scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer( + gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN); + target->AddChild(green); + + scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer( + gfx::Rect(50, 50, 50, 50), SK_ColorBLUE); + target->AddChild(blue); + + copy_subrect_ = gfx::Rect(0, 0, 100, 100); + RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT, + background, + target.get(), + base::FilePath(FILE_PATH_LITERAL( + "green_small_with_blue_corner.png"))); +} + +TEST_F(LayerTreeHostReadbackPixelTest, + ReadbackSubtreeSurroundsLayer_GL_Bitmap) { + scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( + gfx::Rect(0, 0, 200, 200), SK_ColorWHITE); + + scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer( + gfx::Rect(100, 100, 100, 100), SK_ColorRED); + background->AddChild(target); + + scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer( + gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN); + target->AddChild(green); + + scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer( + gfx::Rect(50, 50, 50, 50), SK_ColorBLUE); + target->AddChild(blue); + + copy_subrect_ = gfx::Rect(0, 0, 100, 100); + RunPixelTestWithReadbackTarget(GL_WITH_BITMAP, + background, + target.get(), + base::FilePath(FILE_PATH_LITERAL( + "green_small_with_blue_corner.png"))); +} + +TEST_F(LayerTreeHostReadbackPixelTest, + ReadbackSubtreeSurroundsTargetLayer_GL) { + scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( + gfx::Rect(0, 0, 200, 200), SK_ColorWHITE); + + scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer( + gfx::Rect(100, 100, 100, 100), SK_ColorRED); + background->AddChild(target); + + scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer( + gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN); + target->AddChild(green); + + scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer( + gfx::Rect(50, 50, 50, 50), SK_ColorBLUE); + target->AddChild(blue); + + copy_subrect_ = gfx::Rect(0, 0, 100, 100); + RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT, + background, + target.get(), + base::FilePath(FILE_PATH_LITERAL( + "green_small_with_blue_corner.png"))); +} + +TEST_F(LayerTreeHostReadbackPixelTest, + ReadbackSubtreeExtendsBeyondTargetLayer_Software) { + scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( + gfx::Rect(0, 0, 200, 200), SK_ColorWHITE); + + scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer( + gfx::Rect(50, 50, 150, 150), SK_ColorRED); + background->AddChild(target); + + scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer( + gfx::Rect(50, 50, 200, 200), SK_ColorGREEN); + target->AddChild(green); + + scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer( + gfx::Rect(100, 100, 50, 50), SK_ColorBLUE); + target->AddChild(blue); + + copy_subrect_ = gfx::Rect(50, 50, 100, 100); + RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT, + background, + target.get(), + base::FilePath(FILE_PATH_LITERAL( + "green_small_with_blue_corner.png"))); +} + +TEST_F(LayerTreeHostReadbackPixelTest, + ReadbackSubtreeExtendsBeyondTargetLayer_GL_Bitmap) { + scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( + gfx::Rect(0, 0, 200, 200), SK_ColorWHITE); + + scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer( + gfx::Rect(50, 50, 150, 150), SK_ColorRED); + background->AddChild(target); + + scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer( + gfx::Rect(50, 50, 200, 200), SK_ColorGREEN); + target->AddChild(green); + + scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer( + gfx::Rect(100, 100, 50, 50), SK_ColorBLUE); + target->AddChild(blue); + + copy_subrect_ = gfx::Rect(50, 50, 100, 100); + RunPixelTestWithReadbackTarget(GL_WITH_BITMAP, + background, + target.get(), + base::FilePath(FILE_PATH_LITERAL( + "green_small_with_blue_corner.png"))); +} + +TEST_F(LayerTreeHostReadbackPixelTest, + ReadbackSubtreeExtendsBeyondTargetLayer_GL) { + scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( + gfx::Rect(0, 0, 200, 200), SK_ColorWHITE); + + scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer( + gfx::Rect(50, 50, 150, 150), SK_ColorRED); + background->AddChild(target); + + scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer( + gfx::Rect(50, 50, 200, 200), SK_ColorGREEN); + target->AddChild(green); + + scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer( + gfx::Rect(100, 100, 50, 50), SK_ColorBLUE); + target->AddChild(blue); + + copy_subrect_ = gfx::Rect(50, 50, 100, 100); + RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT, + background, + target.get(), + base::FilePath(FILE_PATH_LITERAL( + "green_small_with_blue_corner.png"))); +} + TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSubrect_Software) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( gfx::Rect(200, 200), SK_ColorWHITE); @@ -852,7 +1004,7 @@ TEST_F(LayerTreeHostReadbackPixelTest, bitmap.allocPixels(); bitmap.eraseColor(SK_ColorGREEN); { - SkDevice device(bitmap); + SkBitmapDevice device(bitmap); skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(new SkCanvas(&device)); SkPaint paint; paint.setStyle(SkPaint::kFill_Style); diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index b18a88a7865..92cba325587 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -10,12 +10,13 @@ #include "base/synchronization/lock.h" #include "cc/animation/timing_function.h" #include "cc/debug/frame_rate_counter.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/layers/content_layer.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/io_surface_layer.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/painted_scrollbar_layer.h" #include "cc/layers/picture_layer.h" -#include "cc/layers/scrollbar_layer.h" #include "cc/layers/solid_color_layer.h" #include "cc/layers/video_layer.h" #include "cc/output/begin_frame_args.h" @@ -30,11 +31,11 @@ #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_painted_scrollbar_layer.h" #include "cc/test/fake_picture_layer.h" #include "cc/test/fake_picture_layer_impl.h" #include "cc/test/fake_proxy.h" #include "cc/test/fake_scoped_ui_resource.h" -#include "cc/test/fake_scrollbar_layer.h" #include "cc/test/fake_video_frame_provider.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_tree_test.h" @@ -393,9 +394,8 @@ class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate bool paint_scrollbar = true; bool has_thumb = false; - scrollbar_ = FakeScrollbarLayer::Create(paint_scrollbar, - has_thumb, - root_layer_->id()); + scrollbar_ = FakePaintedScrollbarLayer::Create( + paint_scrollbar, has_thumb, root_layer_->id()); scrollbar_->SetPosition(gfx::Point(0, 10)); scrollbar_->SetBounds(gfx::Size(10, 10)); @@ -436,7 +436,7 @@ class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate private: FakeContentLayerClient client_; scoped_refptr<Layer> root_layer_; - scoped_refptr<FakeScrollbarLayer> scrollbar_; + scoped_refptr<FakePaintedScrollbarLayer> scrollbar_; }; SINGLE_AND_MULTI_THREAD_TEST_F( @@ -503,6 +503,139 @@ class LayerTreeHostTestCompositeAndReadbackBeforePreviousCommitDraws MULTI_THREAD_TEST_F( LayerTreeHostTestCompositeAndReadbackBeforePreviousCommitDraws); +class LayerTreeHostTestCompositeAndReadbackDuringForcedDraw + : public LayerTreeHostTest { + protected: + static const int kFirstCommitSourceFrameNumber = 0; + static const int kReadbackSourceFrameNumber = 1; + static const int kReadbackReplacementAndForcedDrawSourceFrameNumber = 2; + + LayerTreeHostTestCompositeAndReadbackDuringForcedDraw() + : did_post_readback_(false) {} + + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + // This enables forced draws after a single prepare to draw failure. + settings->timeout_and_draw_when_animation_checkerboards = true; + settings->maximum_number_of_failed_draws_before_draw_is_forced_ = 1; + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kFirstCommitSourceFrameNumber || + sfn == kReadbackSourceFrameNumber || + sfn == kReadbackReplacementAndForcedDrawSourceFrameNumber) + << sfn; + + // Before we react to the failed draw by initiating the forced draw + // sequence, start a readback on the main thread. + if (sfn == kFirstCommitSourceFrameNumber && !did_post_readback_) { + did_post_readback_ = true; + PostReadbackToMainThread(); + } + + // Returning false will result in a forced draw. + return false; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + // We should only draw for the readback and the forced draw. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kReadbackSourceFrameNumber || + sfn == kReadbackReplacementAndForcedDrawSourceFrameNumber) + << sfn; + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + // We should only swap for the forced draw. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kReadbackReplacementAndForcedDrawSourceFrameNumber) + << sfn; + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + bool did_post_readback_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestCompositeAndReadbackDuringForcedDraw); + +class LayerTreeHostTestCompositeAndReadbackAfterForcedDraw + : public LayerTreeHostTest { + protected: + static const int kFirstCommitSourceFrameNumber = 0; + static const int kForcedDrawSourceFrameNumber = 1; + static const int kReadbackSourceFrameNumber = 2; + static const int kReadbackReplacementSourceFrameNumber = 3; + + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + // This enables forced draws after a single prepare to draw failure. + settings->timeout_and_draw_when_animation_checkerboards = true; + settings->maximum_number_of_failed_draws_before_draw_is_forced_ = 1; + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kFirstCommitSourceFrameNumber || + sfn == kForcedDrawSourceFrameNumber || + sfn == kReadbackSourceFrameNumber || + sfn == kReadbackReplacementSourceFrameNumber) + << sfn; + + // Returning false will result in a forced draw. + return false; + } + + virtual void DidCommit() OVERRIDE { + if (layer_tree_host()->source_frame_number() == + kForcedDrawSourceFrameNumber) { + // Avoid aborting the forced draw commit so source_frame_number + // increments. + layer_tree_host()->SetNeedsCommit(); + } else if (layer_tree_host()->source_frame_number() == + kReadbackSourceFrameNumber) { + // Perform a readback immediately after the forced draw's commit. + char pixels[4]; + layer_tree_host()->CompositeAndReadback(&pixels, gfx::Rect(0, 0, 1, 1)); + } + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + // We should only draw for the the forced draw, readback, and + // replacement commit. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kForcedDrawSourceFrameNumber || + sfn == kReadbackSourceFrameNumber || + sfn == kReadbackReplacementSourceFrameNumber) + << sfn; + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + // We should only swap for the forced draw and replacement commit. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kForcedDrawSourceFrameNumber || + sfn == kReadbackReplacementSourceFrameNumber) + << sfn; + + if (sfn == kReadbackReplacementSourceFrameNumber) + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestCompositeAndReadbackAfterForcedDraw); + // If the layerTreeHost says it can't draw, Then we should not try to draw. class LayerTreeHostTestCanDrawBlocksDrawing : public LayerTreeHostTest { public: @@ -664,8 +797,6 @@ class LayerTreeHostTestAbortFrameWhenInvisible : public LayerTreeHostTest { } virtual void AfterTest() OVERRIDE {} - - private: }; MULTI_THREAD_TEST_F(LayerTreeHostTestAbortFrameWhenInvisible); @@ -684,7 +815,7 @@ class LayerTreeHostTestCommit : public LayerTreeHostTest { } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { - EXPECT_EQ(gfx::Size(20, 20), impl->device_viewport_size()); + EXPECT_EQ(gfx::Size(20, 20), impl->DrawViewportSize()); EXPECT_EQ(SK_ColorGRAY, impl->active_tree()->background_color()); EndTest(); @@ -701,7 +832,8 @@ MULTI_THREAD_TEST_F(LayerTreeHostTestCommit); class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails : public LayerTreeHostTest { public: - LayerTreeHostTestFrameTimeUpdatesAfterActivationFails() : frame_(0) {} + LayerTreeHostTestFrameTimeUpdatesAfterActivationFails() + : frame_count_with_pending_tree_(0) {} virtual void BeginTest() OVERRIDE { layer_tree_host()->SetViewportSize(gfx::Size(20, 20)); @@ -710,9 +842,18 @@ class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails PostSetNeedsCommitToMainThread(); } + virtual void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl, + const BeginFrameArgs& args) OVERRIDE { + if (host_impl->pending_tree()) + frame_count_with_pending_tree_++; + host_impl->BlockNotifyReadyToActivateForTesting( + frame_count_with_pending_tree_ <= 1); + } + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { - if (frame_ >= 1) { - EXPECT_NE(first_frame_time_, impl->CurrentFrameTimeTicks()); + if (frame_count_with_pending_tree_ > 1) { + EXPECT_NE(first_frame_time_.ToInternalValue(), + impl->CurrentFrameTimeTicks().ToInternalValue()); EndTest(); return; } @@ -720,35 +861,15 @@ class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails EXPECT_FALSE(impl->settings().impl_side_painting); EndTest(); } - - virtual bool CanActivatePendingTree(LayerTreeHostImpl* impl) OVERRIDE { - if (frame_ >= 1) - return true; - - return false; - } - - virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* impl) - OVERRIDE { - frame_++; - if (frame_ == 1) { - first_frame_time_ = impl->CurrentFrameTimeTicks(); - - // Since base::TimeTicks::Now() uses a low-resolution clock on - // Windows, we need to make sure that the clock has incremented past - // first_frame_time_. - while (first_frame_time_ == base::TimeTicks::Now()) {} - - return false; - } - - return true; + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (impl->settings().impl_side_painting) + EXPECT_NE(frame_count_with_pending_tree_, 1); } virtual void AfterTest() OVERRIDE {} private: - int frame_; + int frame_count_with_pending_tree_; base::TimeTicks first_frame_time_; }; @@ -806,7 +927,8 @@ SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestFrameTimeUpdatesAfterDraw); // Verifies that StartPageScaleAnimation events propagate correctly // from LayerTreeHost to LayerTreeHostImpl in the MT compositor. -class LayerTreeHostTestStartPageScaleAnimation : public LayerTreeHostTest { +class LayerTreeHostTestStartPageScaleAnimation + : public LayerTreeHostTest { public: LayerTreeHostTestStartPageScaleAnimation() {} @@ -1044,7 +1166,7 @@ class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers ASSERT_EQ(1u, impl->active_tree()->root_layer()->children().size()); // Device viewport is scaled. - EXPECT_EQ(gfx::Size(60, 60), impl->device_viewport_size()); + EXPECT_EQ(gfx::Size(60, 60), impl->DrawViewportSize()); LayerImpl* root = impl->active_tree()->root_layer(); LayerImpl* child = impl->active_tree()->root_layer()->children()[0]; @@ -1111,13 +1233,13 @@ class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers); // Verify atomicity of commits and reuse of textures. -class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { +class LayerTreeHostTestDirectRendererAtomicCommit : public LayerTreeHostTest { public: virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { // Make sure partial texture updates are turned off. settings->max_partial_texture_updates = 0; // Linear fade animator prevents scrollbars from drawing immediately. - settings->use_linear_fade_scrollbar_animator = false; + settings->scrollbar_animator = LayerTreeSettings::NoAnimator; } virtual void SetupTree() OVERRIDE { @@ -1126,8 +1248,8 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { bool paint_scrollbar = true; bool has_thumb = false; - scrollbar_ = - FakeScrollbarLayer::Create(paint_scrollbar, has_thumb, layer_->id()); + scrollbar_ = FakePaintedScrollbarLayer::Create( + paint_scrollbar, has_thumb, layer_->id()); scrollbar_->SetPosition(gfx::Point(0, 10)); scrollbar_->SetBounds(gfx::Size(10, 10)); @@ -1146,7 +1268,7 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates); TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( - impl->output_surface()->context3d()); + impl->output_surface()->context_provider()->Context3d()); switch (impl->active_tree()->source_frame_number()) { case 0: @@ -1162,19 +1284,18 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { PostSetNeedsCommitToMainThread(); break; case 1: - // Number of textures should be doubled as the first textures - // are used by impl thread and cannot by used for update. - ASSERT_EQ(4u, context->NumTextures()); - // Number of textures used for commit should still be - // one for each layer. + // Number of textures should be one for scrollbar layer since it was + // requested and deleted on the impl-thread, and double for the content + // layer since its first texture is used by impl thread and cannot by + // used for update. + ASSERT_EQ(3u, context->NumTextures()); + // Number of textures used for commit should be one for each layer. EXPECT_EQ(2u, context->NumUsedTextures()); // First textures should not have been used. EXPECT_FALSE(context->UsedTexture(context->TextureAt(0))); - EXPECT_FALSE(context->UsedTexture(context->TextureAt(1))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); // New textures should have been used. EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); - EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); - context->ResetUsedTextures(); PostSetNeedsCommitToMainThread(); break; @@ -1189,7 +1310,7 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( - impl->output_surface()->context3d()); + impl->output_surface()->context_provider()->Context3d()); if (drew_frame_ == impl->active_tree()->source_frame_number()) { EXPECT_EQ(0u, context->NumUsedTextures()) << "For frame " << drew_frame_; @@ -1209,14 +1330,68 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest { virtual void AfterTest() OVERRIDE {} - private: + protected: FakeContentLayerClient client_; scoped_refptr<FakeContentLayer> layer_; - scoped_refptr<FakeScrollbarLayer> scrollbar_; + scoped_refptr<FakePaintedScrollbarLayer> scrollbar_; int drew_frame_; }; -MULTI_THREAD_TEST_F(LayerTreeHostTestAtomicCommit); +MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestDirectRendererAtomicCommit); + +class LayerTreeHostTestDelegatingRendererAtomicCommit + : public LayerTreeHostTestDirectRendererAtomicCommit { + public: + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates); + + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context_provider()->Context3d()); + + switch (impl->active_tree()->source_frame_number()) { + case 0: + // Number of textures should be one for each layer + ASSERT_EQ(2u, context->NumTextures()); + // Number of textures used for commit should be one for each layer. + EXPECT_EQ(2u, context->NumUsedTextures()); + // Verify that used texture is correct. + EXPECT_TRUE(context->UsedTexture(context->TextureAt(0))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(1))); + context->ResetUsedTextures(); + PostSetNeedsCommitToMainThread(); + break; + case 1: + // Number of textures should be doubled as the first context layer + // texture is being used by the impl-thread and cannot be used for + // update. The scrollbar behavior is different direct renderer because + // UI resource deletion with delegating renderer occurs after tree + // activation. + ASSERT_EQ(4u, context->NumTextures()); + // Number of textures used for commit should still be + // one for each layer. + EXPECT_EQ(2u, context->NumUsedTextures()); + // First textures should not have been used. + EXPECT_FALSE(context->UsedTexture(context->TextureAt(0))); + EXPECT_FALSE(context->UsedTexture(context->TextureAt(1))); + // New textures should have been used. + EXPECT_TRUE(context->UsedTexture(context->TextureAt(2))); + EXPECT_TRUE(context->UsedTexture(context->TextureAt(3))); + context->ResetUsedTextures(); + PostSetNeedsCommitToMainThread(); + break; + case 2: + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } +}; + +MULTI_THREAD_DELEGATING_RENDERER_TEST_F( + LayerTreeHostTestDelegatingRendererAtomicCommit); static void SetLayerPropertiesForTesting(Layer* layer, Layer* parent, @@ -1294,7 +1469,7 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate ASSERT_EQ(1u, layer_tree_host()->settings().max_partial_texture_updates); TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( - impl->output_surface()->context3d()); + impl->output_surface()->context_provider()->Context3d()); switch (impl->active_tree()->source_frame_number()) { case 0: @@ -1359,7 +1534,7 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate EXPECT_LT(impl->active_tree()->source_frame_number(), 5); TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( - impl->output_surface()->context3d()); + impl->output_surface()->context_provider()->Context3d()); // Number of textures used for drawing should one per layer except for // frame 3 where the viewport only contains one layer. @@ -1565,8 +1740,9 @@ class EvictionTestLayer : public Layer { return; texture_ = PrioritizedResource::Create( layer_tree_host()->contents_texture_manager()); - texture_->SetDimensions(gfx::Size(10, 10), GL_RGBA); + texture_->SetDimensions(gfx::Size(10, 10), RGBA_8888); bitmap_.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); + bitmap_.allocPixels(); } scoped_ptr<PrioritizedResource> texture_; @@ -1654,7 +1830,6 @@ class LayerTreeHostTestEvictTextures : public LayerTreeHostTest { } void PostEvictTextures() { - DCHECK(HasImplThread()); ImplThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&LayerTreeHostTestEvictTextures::EvictTexturesOnImplThread, @@ -2228,6 +2403,51 @@ class LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled MULTI_THREAD_TEST_F( LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled); +class LayerTreeHostTestAbortedCommitDoesntStallNextCommitWhenIdle + : public LayerTreeHostTest { + protected: + LayerTreeHostTestAbortedCommitDoesntStallNextCommitWhenIdle() + : commit_count_(0), commit_complete_count_(0) {} + + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->begin_frame_scheduling_enabled = true; + settings->using_synchronous_renderer_compositor = true; + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommit() OVERRIDE { + commit_count_++; + if (commit_count_ == 2) { + // A commit was just aborted, request a real commit now to make sure a + // real commit following an aborted commit will still complete and + // end the test even when the Impl thread is idle. + layer_tree_host()->SetNeedsCommit(); + } + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + commit_complete_count_++; + if (commit_complete_count_ == 1) { + // Initiate an aborted commit after the first commit. + host_impl->SetNeedsCommit(); + } else { + EndTest(); + } + } + + virtual void AfterTest() OVERRIDE { + EXPECT_EQ(commit_count_, 3); + EXPECT_EQ(commit_complete_count_, 2); + } + + int commit_count_; + int commit_complete_count_; +}; + +MULTI_THREAD_TEST_F( + LayerTreeHostTestAbortedCommitDoesntStallNextCommitWhenIdle); + class LayerTreeHostTestUninvertibleTransformDoesNotBlockActivation : public LayerTreeHostTest { protected: @@ -2317,23 +2537,17 @@ class LayerTreeHostTestChangeLayerPropertiesInPaintContents SINGLE_THREAD_TEST_F(LayerTreeHostTestChangeLayerPropertiesInPaintContents); -class MockIOSurfaceWebGraphicsContext3D : public FakeWebGraphicsContext3D { +class MockIOSurfaceWebGraphicsContext3D : public TestWebGraphicsContext3D { public: - MockIOSurfaceWebGraphicsContext3D() - : FakeWebGraphicsContext3D() {} + MockIOSurfaceWebGraphicsContext3D() { + test_capabilities_.iosurface = true; + test_capabilities_.texture_rectangle = true; + } virtual WebKit::WebGLId createTexture() OVERRIDE { return 1; } - virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE { - if (name == GL_EXTENSIONS) { - return WebKit::WebString( - "GL_CHROMIUM_iosurface GL_ARB_texture_rectangle"); - } - return WebKit::WebString(); - } - MOCK_METHOD1(activeTexture, void(WebKit::WGC3Denum texture)); MOCK_METHOD2(bindTexture, void(WebKit::WGC3Denum target, WebKit::WebGLId texture_id)); @@ -2349,6 +2563,7 @@ class MockIOSurfaceWebGraphicsContext3D : public FakeWebGraphicsContext3D { WebKit::WGC3Dsizei count, WebKit::WGC3Denum type, WebKit::WGC3Dintptr offset)); + MOCK_METHOD1(deleteTexture, void(WebKit::WGC3Denum texture)); }; @@ -2356,11 +2571,12 @@ class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest { protected: virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback) OVERRIDE { - scoped_ptr<MockIOSurfaceWebGraphicsContext3D> context( + scoped_ptr<MockIOSurfaceWebGraphicsContext3D> mock_context_owned( new MockIOSurfaceWebGraphicsContext3D); - mock_context_ = context.get(); - scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>(); + mock_context_ = mock_context_owned.get(); + + scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d( + mock_context_owned.PassAs<TestWebGraphicsContext3D>())); return output_surface.Pass(); } @@ -2439,6 +2655,8 @@ class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest { virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { Mock::VerifyAndClearExpectations(&mock_context_); + + EXPECT_CALL(*mock_context_, deleteTexture(1)).Times(1); EndTest(); } @@ -2542,10 +2760,14 @@ class LayerTreeHostTestAsyncReadback : public LayerTreeHostTest { virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback) OVERRIDE { - if (use_gl_renderer_) - return FakeOutputSurface::Create3d().PassAs<OutputSurface>(); - return FakeOutputSurface::CreateSoftware( - make_scoped_ptr(new SoftwareOutputDevice)).PassAs<OutputSurface>(); + scoped_ptr<FakeOutputSurface> output_surface; + if (use_gl_renderer_) { + output_surface = FakeOutputSurface::Create3d().Pass(); + } else { + output_surface = FakeOutputSurface::CreateSoftware( + make_scoped_ptr(new SoftwareOutputDevice)).Pass(); + } + return output_surface.PassAs<OutputSurface>(); } bool use_gl_renderer_; @@ -2984,6 +3206,141 @@ class LayerTreeHostTestAsyncTwoReadbacksWithoutDraw : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( LayerTreeHostTestAsyncTwoReadbacksWithoutDraw); +class LayerTreeHostTestAsyncReadbackLostOutputSurface + : public LayerTreeHostTest { + protected: + virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback) + OVERRIDE { + if (!first_context_provider_.get()) { + first_context_provider_ = TestContextProvider::Create(); + return FakeOutputSurface::Create3d(first_context_provider_) + .PassAs<OutputSurface>(); + } + + EXPECT_FALSE(second_context_provider_.get()); + second_context_provider_ = TestContextProvider::Create(); + return FakeOutputSurface::Create3d(second_context_provider_) + .PassAs<OutputSurface>(); + } + + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + copy_layer_ = FakeContentLayer::Create(&client_); + copy_layer_->SetBounds(gfx::Size(10, 10)); + root_->AddChild(copy_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_EQ(gfx::Size(10, 10).ToString(), result->size().ToString()); + EXPECT_TRUE(result->HasTexture()); + + // Save the result for later. + EXPECT_FALSE(result_); + result_ = result.Pass(); + + // Post a commit to lose the output surface. + layer_tree_host()->SetNeedsCommit(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 1: + // The layers have been pushed to the impl side. The layer textures have + // been allocated. + + // Request a copy of the layer. This will use another texture. + copy_layer_->RequestCopyOfOutput( + CopyOutputRequest::CreateRequest(base::Bind( + &LayerTreeHostTestAsyncReadbackLostOutputSurface:: + CopyOutputCallback, + base::Unretained(this)))); + break; + case 4: + // With SingleThreadProxy it takes two commits to finally swap after a + // context loss. + case 5: + // Now destroy the CopyOutputResult, releasing the texture inside back + // to the compositor. + EXPECT_TRUE(result_); + result_.reset(); + + // Check that it is released. + ImplThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&LayerTreeHostTestAsyncReadbackLostOutputSurface:: + CheckNumTextures, + base::Unretained(this), + num_textures_after_loss_ - 1)); + break; + } + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl *impl, bool result) + OVERRIDE { + switch (impl->active_tree()->source_frame_number()) { + case 0: + // The layers have been drawn, so their textures have been allocated. + EXPECT_FALSE(result_); + num_textures_without_readback_ = + first_context_provider_->TestContext3d()->NumTextures(); + break; + case 1: + // We did a readback, so there will be a readback texture around now. + EXPECT_LT(num_textures_without_readback_, + first_context_provider_->TestContext3d()->NumTextures()); + break; + case 2: + // The readback texture is collected. + EXPECT_TRUE(result_); + + // Lose the output surface. + first_context_provider_->TestContext3d()->loseContextCHROMIUM( + GL_GUILTY_CONTEXT_RESET_ARB, + GL_INNOCENT_CONTEXT_RESET_ARB); + break; + case 3: + // With SingleThreadProxy it takes two commits to finally swap after a + // context loss. + case 4: + // The output surface has been recreated. + EXPECT_TRUE(second_context_provider_.get()); + + num_textures_after_loss_ = + first_context_provider_->TestContext3d()->NumTextures(); + break; + } + } + + void CheckNumTextures(size_t expected_num_textures) { + EXPECT_EQ(expected_num_textures, + first_context_provider_->TestContext3d()->NumTextures()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + scoped_refptr<TestContextProvider> first_context_provider_; + scoped_refptr<TestContextProvider> second_context_provider_; + size_t num_textures_without_readback_; + size_t num_textures_after_loss_; + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> copy_layer_; + scoped_ptr<CopyOutputResult> result_; +}; + +// No output to copy for delegated renderers. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestAsyncReadbackLostOutputSurface); + class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest { public: virtual void BeginTest() OVERRIDE { @@ -3130,10 +3487,12 @@ class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest { void DeferredInitializeAndRedraw(LayerTreeHostImpl* host_impl) { EXPECT_FALSE(did_initialize_gl_); // SetAndInitializeContext3D calls SetNeedsCommit. - EXPECT_TRUE(static_cast<FakeOutputSurface*>(host_impl->output_surface()) - ->SetAndInitializeContext3D( - scoped_ptr<WebKit::WebGraphicsContext3D>( - TestWebGraphicsContext3D::Create()))); + FakeOutputSurface* fake_output_surface = + static_cast<FakeOutputSurface*>(host_impl->output_surface()); + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create(); // Not bound to thread. + EXPECT_TRUE(fake_output_surface->InitializeAndSetContext3d( + context_provider, NULL)); did_initialize_gl_ = true; } @@ -3201,7 +3560,7 @@ class LayerTreeHostTestUIResource : public LayerTreeHostTest { void PerformTest(LayerTreeHostImpl* impl) { TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( - impl->output_surface()->context3d()); + impl->output_surface()->context_provider()->Context3d()); int frame = num_commits_; switch (frame) { @@ -3313,19 +3672,13 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { child_ = PushPropertiesCountingLayer::Create(); child2_ = PushPropertiesCountingLayer::Create(); grandchild_ = PushPropertiesCountingLayer::Create(); - - if (layer_tree_host()->settings().impl_side_painting) - leaf_picture_layer_ = FakePictureLayer::Create(&client_); - else - leaf_content_layer_ = FakeContentLayer::Create(&client_); + leaf_scrollbar_layer_ = + FakePaintedScrollbarLayer::Create(false, false, root_->id()); root_->AddChild(child_); root_->AddChild(child2_); child_->AddChild(grandchild_); - if (leaf_picture_layer_) - child2_->AddChild(leaf_picture_layer_); - if (leaf_content_layer_) - child2_->AddChild(leaf_content_layer_); + child2_->AddChild(leaf_scrollbar_layer_); other_root_ = PushPropertiesCountingLayer::Create(); @@ -3344,16 +3697,10 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { child2_->push_properties_count()); EXPECT_EQ(expected_push_properties_other_root_, other_root_->push_properties_count()); - if (leaf_content_layer_) { - EXPECT_EQ(expected_push_properties_leaf_layer_, - leaf_content_layer_->push_properties_count()); - } - if (leaf_picture_layer_) { - EXPECT_EQ(expected_push_properties_leaf_layer_, - leaf_picture_layer_->push_properties_count()); - } + EXPECT_EQ(expected_push_properties_leaf_layer_, + leaf_scrollbar_layer_->push_properties_count()); - // The content/picture layer always needs to be pushed. + // The scrollbar layer always needs to be pushed. if (root_->layer_tree_host()) { EXPECT_TRUE(root_->descendant_needs_push_properties()); EXPECT_FALSE(root_->needs_push_properties()); @@ -3362,13 +3709,9 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { EXPECT_TRUE(child2_->descendant_needs_push_properties()); EXPECT_FALSE(child2_->needs_push_properties()); } - if (leaf_content_layer_.get() && leaf_content_layer_->layer_tree_host()) { - EXPECT_FALSE(leaf_content_layer_->descendant_needs_push_properties()); - EXPECT_TRUE(leaf_content_layer_->needs_push_properties()); - } - if (leaf_picture_layer_.get() && leaf_picture_layer_->layer_tree_host()) { - EXPECT_FALSE(leaf_picture_layer_->descendant_needs_push_properties()); - EXPECT_TRUE(leaf_picture_layer_->needs_push_properties()); + if (leaf_scrollbar_layer_->layer_tree_host()) { + EXPECT_FALSE(leaf_scrollbar_layer_->descendant_needs_push_properties()); + EXPECT_TRUE(leaf_scrollbar_layer_->needs_push_properties()); } // child_ and grandchild_ don't persist their need to push properties. @@ -3479,8 +3822,7 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { // Content/Picture layers require PushProperties every commit that they are // in the tree. - if ((leaf_content_layer_.get() && leaf_content_layer_->layer_tree_host()) || - (leaf_picture_layer_.get() && leaf_picture_layer_->layer_tree_host())) + if (leaf_scrollbar_layer_->layer_tree_host()) ++expected_push_properties_leaf_layer_; } @@ -3493,8 +3835,7 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { scoped_refptr<PushPropertiesCountingLayer> child2_; scoped_refptr<PushPropertiesCountingLayer> grandchild_; scoped_refptr<PushPropertiesCountingLayer> other_root_; - scoped_refptr<FakeContentLayer> leaf_content_layer_; - scoped_refptr<FakePictureLayer> leaf_picture_layer_; + scoped_refptr<FakePaintedScrollbarLayer> leaf_scrollbar_layer_; size_t expected_push_properties_root_; size_t expected_push_properties_child_; size_t expected_push_properties_child2_; @@ -3518,8 +3859,8 @@ class LayerTreeHostTestPropertyChangesDuringUpdateArePushed bool paint_scrollbar = true; bool has_thumb = false; - scrollbar_layer_ = - FakeScrollbarLayer::Create(paint_scrollbar, has_thumb, root_->id()); + scrollbar_layer_ = FakePaintedScrollbarLayer::Create( + paint_scrollbar, has_thumb, root_->id()); root_->AddChild(scrollbar_layer_); @@ -3559,7 +3900,7 @@ class LayerTreeHostTestPropertyChangesDuringUpdateArePushed virtual void AfterTest() OVERRIDE {} scoped_refptr<Layer> root_; - scoped_refptr<FakeScrollbarLayer> scrollbar_layer_; + scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer_; }; MULTI_THREAD_TEST_F(LayerTreeHostTestPropertyChangesDuringUpdateArePushed); @@ -4295,6 +4636,149 @@ class LayerTreeHostTestAbortEvictedTextures : public LayerTreeHostTest { // Commits can only be aborted when using the thread proxy. MULTI_THREAD_TEST_F(LayerTreeHostTestAbortEvictedTextures); +class LayerTreeHostTestMaxTransferBufferUsageBytes : public LayerTreeHostTest { + protected: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->impl_side_painting = true; + } + + virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback) + OVERRIDE { + scoped_refptr<TestContextProvider> context_provider = + TestContextProvider::Create(); + context_provider->SetMaxTransferBufferUsageBytes(1024 * 1024); + return FakeOutputSurface::Create3d(context_provider) + .PassAs<OutputSurface>(); + } + + virtual void SetupTree() OVERRIDE { + scoped_refptr<FakePictureLayer> root_layer = + FakePictureLayer::Create(&client_); + root_layer->SetBounds(gfx::Size(6000, 6000)); + root_layer->SetIsDrawable(true); + + layer_tree_host()->SetRootLayer(root_layer); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context_provider()->Context3d()); + + // Expect that the transfer buffer memory used is equal to the + // MaxTransferBufferUsageBytes value set in CreateOutputSurface. + EXPECT_EQ(1024 * 1024u, + context->GetTransferBufferMemoryUsedBytes()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + private: + FakeContentLayerClient client_; +}; + +// Impl-side painting is a multi-threaded compositor feature. +MULTI_THREAD_TEST_F(LayerTreeHostTestMaxTransferBufferUsageBytes); + +// Test ensuring that memory limits are sent to the prioritized resource +// manager. +class LayerTreeHostTestMemoryLimits : public LayerTreeHostTest { + public: + LayerTreeHostTestMemoryLimits() : num_commits_(0) {} + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void DidCommit() OVERRIDE { + int frame = num_commits_; + switch (frame) { + case 0: + // Verify default values. + EXPECT_EQ( + PrioritizedResourceManager::DefaultMemoryAllocationLimit(), + layer_tree_host()->contents_texture_manager()-> + MaxMemoryLimitBytes()); + EXPECT_EQ( + PriorityCalculator::AllowEverythingCutoff(), + layer_tree_host()->contents_texture_manager()-> + ExternalPriorityCutoff()); + PostSetNeedsCommitToMainThread(); + break; + case 1: + // The values should remain the same until the commit after the policy + // is changed. + EXPECT_EQ( + PrioritizedResourceManager::DefaultMemoryAllocationLimit(), + layer_tree_host()->contents_texture_manager()-> + MaxMemoryLimitBytes()); + EXPECT_EQ( + PriorityCalculator::AllowEverythingCutoff(), + layer_tree_host()->contents_texture_manager()-> + ExternalPriorityCutoff()); + break; + case 2: + // Verify values were correctly passed. + EXPECT_EQ( + 16u*1024u*1024u, + layer_tree_host()->contents_texture_manager()-> + MaxMemoryLimitBytes()); + EXPECT_EQ( + PriorityCalculator::AllowVisibleAndNearbyCutoff(), + layer_tree_host()->contents_texture_manager()-> + ExternalPriorityCutoff()); + EndTest(); + break; + case 3: + // Make sure no extra commits happen. + NOTREACHED(); + break; + } + + ++num_commits_; + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + int frame = num_commits_; + switch (frame) { + case 0: + break; + case 1: + // This will trigger a commit because the priority cutoff has changed. + impl->SetMemoryPolicy(ManagedMemoryPolicy( + 16u*1024u*1024u, + ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE, + 0, + ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING, + 1000)); + break; + case 2: + // This will not trigger a commit because the priority cutoff has not + // changed, and there is already enough memory for all allocations. + impl->SetMemoryPolicy(ManagedMemoryPolicy( + 32u*1024u*1024u, + ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE, + 0, + ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING, + 1000)); + break; + case 3: + NOTREACHED(); + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + int num_commits_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestMemoryLimits); + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index dbc427504d8..70e2a9ec6d8 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -293,6 +293,136 @@ class LayerTreeHostAnimationTestTickAnimationWhileBackgrounded SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostAnimationTestTickAnimationWhileBackgrounded); +// Ensures that animations do not tick when we are backgrounded and +// and we have an empty active tree. +class LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree + : public LayerTreeHostAnimationTest { + protected: + LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree() + : active_tree_was_animated_(false) {} + + virtual base::TimeDelta LowFrequencyAnimationInterval() const OVERRIDE { + return base::TimeDelta::FromMilliseconds(4); + } + + virtual void BeginTest() OVERRIDE { + PostAddAnimationToMainThread(layer_tree_host()->root_layer()); + } + + virtual void NotifyAnimationFinished(double time) OVERRIDE { + // Replace animated commits with an empty tree. + layer_tree_host()->SetRootLayer(make_scoped_refptr<Layer>(NULL)); + } + + virtual void DidCommit() OVERRIDE { + // This alternates setting an empty tree and a non-empty tree with an + // animation. + switch (layer_tree_host()->source_frame_number()) { + case 1: + // Wait for NotifyAnimationFinished to commit an empty tree. + break; + case 2: + SetupTree(); + AddOpacityTransitionToLayer( + layer_tree_host()->root_layer(), 0.000001, 0, 0.5, true); + break; + case 3: + // Wait for NotifyAnimationFinished to commit an empty tree. + break; + case 4: + EndTest(); + break; + } + } + + virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + // At the start of every commit, block activations and make sure + // we are backgrounded. + host_impl->BlockNotifyReadyToActivateForTesting(true); + PostSetVisibleToMainThread(false); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + if (!host_impl->settings().impl_side_painting) { + // There are no activations to block if we're not impl-side-painting, + // so just advance the test immediately. + if (host_impl->active_tree()->source_frame_number() < 3) + UnblockActivations(host_impl); + return; + } + + // We block activation for several ticks to make sure that, even though + // there is a pending tree with animations, we still do not background + // tick if the active tree is empty. + if (host_impl->pending_tree()->source_frame_number() < 3) { + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind( + &LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree:: + UnblockActivations, + base::Unretained(this), + host_impl), + 4 * LowFrequencyAnimationInterval()); + } + } + + virtual void UnblockActivations(LayerTreeHostImpl* host_impl) { + host_impl->BlockNotifyReadyToActivateForTesting(false); + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + active_tree_was_animated_ = false; + + // Verify that commits are actually alternating with empty / non-empty + // trees. + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + case 2: + EXPECT_TRUE(host_impl->active_tree()->root_layer()); + break; + case 1: + case 3: + EXPECT_FALSE(host_impl->active_tree()->root_layer()); + break; + } + + if (host_impl->active_tree()->source_frame_number() < 3) { + // Initiate the next commit after a delay to give us a chance to + // background tick if the active tree isn't empty. + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind( + &LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree:: + InitiateNextCommit, + base::Unretained(this), + host_impl), + 4 * LowFrequencyAnimationInterval()); + } + } + + virtual void WillAnimateLayers(LayerTreeHostImpl* host_impl, + base::TimeTicks monotonic_time) OVERRIDE { + EXPECT_TRUE(host_impl->active_tree()->root_layer()); + active_tree_was_animated_ = true; + } + + void InitiateNextCommit(LayerTreeHostImpl* host_impl) { + // Verify that we actually animated when we should have. + bool has_active_tree = host_impl->active_tree()->root_layer(); + EXPECT_EQ(has_active_tree, active_tree_was_animated_); + + // The next commit is blocked until we become visible again. + PostSetVisibleToMainThread(true); + } + + virtual void AfterTest() OVERRIDE {} + + bool active_tree_was_animated_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree); + // Ensure that an animation's timing function is respected. class LayerTreeHostAnimationTestAddAnimationWithTimingFunction : public LayerTreeHostAnimationTest { diff --git a/chromium/cc/trees/layer_tree_host_unittest_context.cc b/chromium/cc/trees/layer_tree_host_unittest_context.cc index 1787d31ddf3..18683e826b6 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_context.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_context.cc @@ -5,12 +5,14 @@ #include "cc/trees/layer_tree_host.h" #include "base/basictypes.h" +#include "cc/debug/test_context_provider.h" +#include "cc/debug/test_web_graphics_context_3d.h" #include "cc/layers/content_layer.h" #include "cc/layers/heads_up_display_layer.h" #include "cc/layers/io_surface_layer.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/painted_scrollbar_layer.h" #include "cc/layers/picture_layer.h" -#include "cc/layers/scrollbar_layer.h" #include "cc/layers/texture_layer.h" #include "cc/layers/texture_layer_impl.h" #include "cc/layers/video_layer.h" @@ -19,18 +21,16 @@ #include "cc/test/fake_content_layer.h" #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_content_layer_impl.h" -#include "cc/test/fake_context_provider.h" #include "cc/test/fake_delegated_renderer_layer.h" #include "cc/test/fake_delegated_renderer_layer_impl.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_output_surface.h" +#include "cc/test/fake_painted_scrollbar_layer.h" #include "cc/test/fake_scoped_ui_resource.h" #include "cc/test/fake_scrollbar.h" -#include "cc/test/fake_scrollbar_layer.h" #include "cc/test/fake_video_frame_provider.h" #include "cc/test/layer_tree_test.h" #include "cc/test/render_pass_test_common.h" -#include "cc/test/test_web_graphics_context_3d.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" @@ -108,11 +108,11 @@ class LayerTreeHostContextTest : public LayerTreeTest { } if (delegating_renderer()) { - return FakeOutputSurface::CreateDelegating3d( - context3d.PassAs<WebGraphicsContext3D>()).PassAs<OutputSurface>(); + return FakeOutputSurface::CreateDelegating3d(context3d.Pass()) + .PassAs<OutputSurface>(); } - return FakeOutputSurface::Create3d( - context3d.PassAs<WebGraphicsContext3D>()).PassAs<OutputSurface>(); + return FakeOutputSurface::Create3d(context3d.Pass()) + .PassAs<OutputSurface>(); } scoped_ptr<TestWebGraphicsContext3D> CreateOffscreenContext3d() { @@ -141,10 +141,11 @@ class LayerTreeHostContextTest : public LayerTreeTest { if (!offscreen_contexts_main_thread_.get() || offscreen_contexts_main_thread_->DestroyedOnMainThread()) { - offscreen_contexts_main_thread_ = FakeContextProvider::Create( - base::Bind(&LayerTreeHostContextTest::CreateOffscreenContext3d, - base::Unretained(this))); - if (offscreen_contexts_main_thread_.get() && + offscreen_contexts_main_thread_ = + TestContextProvider::Create( + base::Bind(&LayerTreeHostContextTest::CreateOffscreenContext3d, + base::Unretained(this))); + if (offscreen_contexts_main_thread_ && !offscreen_contexts_main_thread_->BindToCurrentThread()) offscreen_contexts_main_thread_ = NULL; } @@ -157,9 +158,10 @@ class LayerTreeHostContextTest : public LayerTreeTest { if (!offscreen_contexts_compositor_thread_.get() || offscreen_contexts_compositor_thread_->DestroyedOnMainThread()) { - offscreen_contexts_compositor_thread_ = FakeContextProvider::Create( - base::Bind(&LayerTreeHostContextTest::CreateOffscreenContext3d, - base::Unretained(this))); + offscreen_contexts_compositor_thread_ = + TestContextProvider::Create( + base::Bind(&LayerTreeHostContextTest::CreateOffscreenContext3d, + base::Unretained(this))); } return offscreen_contexts_compositor_thread_; } @@ -236,8 +238,8 @@ class LayerTreeHostContextTest : public LayerTreeTest { bool context_should_support_io_surface_; bool fallback_context_works_; - scoped_refptr<FakeContextProvider> offscreen_contexts_main_thread_; - scoped_refptr<FakeContextProvider> offscreen_contexts_compositor_thread_; + scoped_refptr<TestContextProvider> offscreen_contexts_main_thread_; + scoped_refptr<TestContextProvider> offscreen_contexts_compositor_thread_; }; class LayerTreeHostContextTestLostContextSucceeds @@ -474,9 +476,9 @@ class LayerTreeHostContextTestLostContextSucceedsWithContent // the active context. EXPECT_TRUE(content_impl->HaveResourceForTileAt(0, 0)); - cc::ContextProvider* contexts = - host_impl->resource_provider()->offscreen_context_provider(); + cc::ContextProvider* contexts = host_impl->offscreen_context_provider(); if (use_surface_) { + ASSERT_TRUE(contexts); EXPECT_TRUE(contexts->Context3d()); // TODO(danakj): Make a fake GrContext. // EXPECT_TRUE(contexts->GrContext()); @@ -595,8 +597,7 @@ class LayerTreeHostContextTestOffscreenContextFails } virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { - cc::ContextProvider* contexts = - host_impl->resource_provider()->offscreen_context_provider(); + cc::ContextProvider* contexts = host_impl->offscreen_context_provider(); EXPECT_FALSE(contexts); // This did not lead to create failure. @@ -1198,9 +1199,9 @@ class LayerTreeHostContextTestDontUseLostResources debug_state.show_property_changed_rects = true; layer_tree_host()->SetDebugState(debug_state); - scoped_refptr<ScrollbarLayer> scrollbar_ = ScrollbarLayer::Create( - scoped_ptr<Scrollbar>(new FakeScrollbar).Pass(), - content_->id()); + scoped_refptr<PaintedScrollbarLayer> scrollbar_ = + PaintedScrollbarLayer::Create( + scoped_ptr<Scrollbar>(new FakeScrollbar).Pass(), content_->id()); scrollbar_->SetBounds(gfx::Size(10, 10)); scrollbar_->SetAnchorPoint(gfx::PointF()); scrollbar_->SetIsDrawable(true); @@ -1219,6 +1220,10 @@ class LayerTreeHostContextTestDontUseLostResources LayerTreeHostContextTest::CommitCompleteOnThread(host_impl); ResourceProvider* resource_provider = host_impl->resource_provider(); + ContextProvider* context_provider = + host_impl->output_surface()->context_provider(); + + DCHECK(context_provider); if (host_impl->active_tree()->source_frame_number() == 0) { // Set up impl resources on the first commit. @@ -1255,19 +1260,18 @@ class LayerTreeHostContextTestDontUseLostResources static_cast<TextureLayerImpl*>( host_impl->active_tree()->root_layer()->children()[2]); texture_impl->set_texture_id( - resource_provider->GraphicsContext3D()->createTexture()); + context_provider->Context3d()->createTexture()); - DCHECK(resource_provider->GraphicsContext3D()); ResourceProvider::ResourceId texture = resource_provider->CreateResource( gfx::Size(4, 4), - resource_provider->default_resource_type(), - ResourceProvider::TextureUsageAny); + GL_CLAMP_TO_EDGE, + ResourceProvider::TextureUsageAny, + RGBA_8888); ResourceProvider::ScopedWriteLockGL lock(resource_provider, texture); gpu::Mailbox mailbox; - resource_provider->GraphicsContext3D()->genMailboxCHROMIUM(mailbox.name); - unsigned sync_point = - resource_provider->GraphicsContext3D()->insertSyncPoint(); + context_provider->Context3d()->genMailboxCHROMIUM(mailbox.name); + unsigned sync_point = context_provider->Context3d()->insertSyncPoint(); color_video_frame_ = VideoFrame::CreateColorFrame( gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta()); @@ -1343,7 +1347,7 @@ class LayerTreeHostContextTestDontUseLostResources scoped_refptr<VideoLayer> video_hw_; scoped_refptr<VideoLayer> video_scaled_hw_; scoped_refptr<IOSurfaceLayer> io_surface_; - scoped_refptr<ScrollbarLayer> scrollbar_; + scoped_refptr<PaintedScrollbarLayer> scrollbar_; scoped_refptr<VideoFrame> color_video_frame_; scoped_refptr<VideoFrame> hw_video_frame_; @@ -1457,6 +1461,29 @@ class LayerTreeHostContextTestCompositeAndReadbackBeforeOutputSurfaceInit EXPECT_EQ(1, times_output_surface_created_); } + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + EXPECT_GE(host_impl->active_tree()->source_frame_number(), 0); + EXPECT_LE(host_impl->active_tree()->source_frame_number(), 1); + return true; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + // We should only draw for the readback and the replacement commit. + // The replacement commit will also be the first commit after output + // surface initialization. + EXPECT_GE(host_impl->active_tree()->source_frame_number(), 0); + EXPECT_LE(host_impl->active_tree()->source_frame_number(), 1); + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + // We should only swap for the replacement commit. + EXPECT_EQ(host_impl->active_tree()->source_frame_number(), 1); + EndTest(); + } + private: int times_output_surface_created_; }; @@ -1464,6 +1491,172 @@ class LayerTreeHostContextTestCompositeAndReadbackBeforeOutputSurfaceInit SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostContextTestCompositeAndReadbackBeforeOutputSurfaceInit); +// This test verifies that losing an output surface during a +// simultaneous readback and forced redraw works and does not deadlock. +class LayerTreeHostContextTestLoseOutputSurfaceDuringReadbackAndForcedDraw + : public LayerTreeHostContextTest { + protected: + static const int kFirstOutputSurfaceInitSourceFrameNumber = 0; + static const int kReadbackSourceFrameNumber = 1; + static const int kReadbackReplacementSourceFrameNumber = 2; + static const int kSecondOutputSurfaceInitSourceFrameNumber = 3; + + LayerTreeHostContextTestLoseOutputSurfaceDuringReadbackAndForcedDraw() + : did_react_to_first_commit_(false) {} + + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + // This enables forced draws after a single prepare to draw failure. + settings->timeout_and_draw_when_animation_checkerboards = true; + settings->maximum_number_of_failed_draws_before_draw_is_forced_ = 1; + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kFirstOutputSurfaceInitSourceFrameNumber || + sfn == kSecondOutputSurfaceInitSourceFrameNumber || + sfn == kReadbackSourceFrameNumber) + << sfn; + + // Before we react to the failed draw by initiating the forced draw + // sequence, start a readback on the main thread and then lose the context + // to start output surface initialization all at the same time. + if (sfn == kFirstOutputSurfaceInitSourceFrameNumber && + !did_react_to_first_commit_) { + did_react_to_first_commit_ = true; + PostReadbackToMainThread(); + LoseContext(); + } + + return false; + } + + virtual void InitializedRendererOnThread(LayerTreeHostImpl* host_impl, + bool success) OVERRIDE { + // -1 is for the first output surface initialization. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == -1 || sfn == kReadbackReplacementSourceFrameNumber) + << sfn; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + // We should only draw the first commit after output surface initialization + // and attempt to draw the readback commit (which will fail). + // All others should abort because the output surface is lost. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kSecondOutputSurfaceInitSourceFrameNumber || + sfn == kReadbackSourceFrameNumber) + << sfn; + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + // We should only swap the first commit after the second output surface + // initialization. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kSecondOutputSurfaceInitSourceFrameNumber) << sfn; + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + int did_react_to_first_commit_; +}; + +MULTI_THREAD_TEST_F( + LayerTreeHostContextTestLoseOutputSurfaceDuringReadbackAndForcedDraw); + +// This test verifies that losing an output surface right before a +// simultaneous readback and forced redraw works and does not deadlock. +class LayerTreeHostContextTestReadbackWithForcedDrawAndOutputSurfaceInit + : public LayerTreeHostContextTest { + protected: + static const int kFirstOutputSurfaceInitSourceFrameNumber = 0; + static const int kReadbackSourceFrameNumber = 1; + static const int kReadbackReplacementSourceFrameNumber = 2; + static const int kForcedDrawCommitSourceFrameNumber = 2; + static const int kSecondOutputSurfaceInitSourceFrameNumber = 2; + + LayerTreeHostContextTestReadbackWithForcedDrawAndOutputSurfaceInit() + : did_lose_context_(false) {} + + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + // This enables forced draws after a single prepare to draw failure. + settings->timeout_and_draw_when_animation_checkerboards = true; + settings->maximum_number_of_failed_draws_before_draw_is_forced_ = 1; + } + + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kFirstOutputSurfaceInitSourceFrameNumber || + sfn == kSecondOutputSurfaceInitSourceFrameNumber || + sfn == kReadbackSourceFrameNumber) + << sfn; + + // Before we react to the failed draw by initiating the forced draw + // sequence, start a readback on the main thread and then lose the context + // to start output surface initialization all at the same time. + if (sfn == kFirstOutputSurfaceInitSourceFrameNumber && !did_lose_context_) { + did_lose_context_ = true; + LoseContext(); + } + + // Returning false will result in a forced draw. + return false; + } + + virtual void DidInitializeOutputSurface(bool succeeded) OVERRIDE { + EXPECT_TRUE(succeeded); + if (layer_tree_host()->source_frame_number() > 0) { + // Perform a readback right after the second output surface + // initialization. + char pixels[4]; + layer_tree_host()->CompositeAndReadback(&pixels, gfx::Rect(0, 0, 1, 1)); + } + } + + virtual void InitializedRendererOnThread(LayerTreeHostImpl* host_impl, + bool success) OVERRIDE { + // -1 is for the first output surface initialization. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == -1 || sfn == kFirstOutputSurfaceInitSourceFrameNumber) + << sfn; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + // We should only draw the first commit after output surface initialization + // and attempt to draw the readback commit (which will fail). + // All others should abort because the output surface is lost. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kForcedDrawCommitSourceFrameNumber || + sfn == kReadbackSourceFrameNumber) + << sfn; + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + // We should only swap the first commit after the second output surface + // initialization. + int sfn = host_impl->active_tree()->source_frame_number(); + EXPECT_TRUE(sfn == kForcedDrawCommitSourceFrameNumber) << sfn; + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + int did_lose_context_; +}; + +MULTI_THREAD_TEST_F( + LayerTreeHostContextTestReadbackWithForcedDrawAndOutputSurfaceInit); + class ImplSidePaintingLayerTreeHostContextTest : public LayerTreeHostContextTest { public: @@ -1515,7 +1708,7 @@ class ScrollbarLayerLostContext : public LayerTreeHostContextTest { virtual void BeginTest() OVERRIDE { scoped_refptr<Layer> scroll_layer = Layer::Create(); - scrollbar_layer_ = FakeScrollbarLayer::Create( + scrollbar_layer_ = FakePaintedScrollbarLayer::Create( false, true, scroll_layer->id()); scrollbar_layer_->SetBounds(gfx::Size(10, 100)); layer_tree_host()->root_layer()->AddChild(scrollbar_layer_); @@ -1542,6 +1735,12 @@ class ScrollbarLayerLostContext : public LayerTreeHostContextTest { EXPECT_EQ(2, scrollbar_layer_->update_count()); EndTest(); break; + case 3: + // Single thread proxy issues extra commits after context lost. + // http://crbug.com/287250 + if (HasImplThread()) + NOTREACHED(); + break; default: NOTREACHED(); } @@ -1549,7 +1748,7 @@ class ScrollbarLayerLostContext : public LayerTreeHostContextTest { private: int commits_; - scoped_refptr<FakeScrollbarLayer> scrollbar_layer_; + scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer_; }; SINGLE_AND_MULTI_THREAD_TEST_F(ScrollbarLayerLostContext); @@ -1633,27 +1832,107 @@ class UIResourceLostTest : public LayerTreeHostContextTest { virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void AfterTest() OVERRIDE {} + // This is called on the main thread after each commit and + // DidActivateTreeOnThread, with the value of time_step_ at the time + // of the call to DidActivateTreeOnThread. Similar tests will do + // work on the main thread in DidCommit but that is unsuitable because + // the main thread work for these tests must happen after + // DidActivateTreeOnThread, which happens after DidCommit with impl-side + // painting. + virtual void StepCompleteOnMainThread(int time_step) = 0; + + // Called after DidActivateTreeOnThread. If this is done during the commit, + // the call to StepCompleteOnMainThread will not occur until after + // the commit completes, because the main thread is blocked. + void PostStepCompleteToMainThread() { + proxy()->MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind( + &UIResourceLostTest::StepCompleteOnMainThreadInternal, + base::Unretained(this), + time_step_)); + } + + void PostLoseContextToImplThread() { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + base::SingleThreadTaskRunner* task_runner = + HasImplThread() ? ImplThreadTaskRunner() + : base::MessageLoopProxy::current(); + task_runner->PostTask( + FROM_HERE, + base::Bind( + &LayerTreeHostContextTest::LoseContext, + base::Unretained(this))); + } + protected: int time_step_; scoped_ptr<FakeScopedUIResource> ui_resource_; + + private: + void StepCompleteOnMainThreadInternal(int step) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + StepCompleteOnMainThread(step); + } }; -// Losing context after an UI resource has been created. -class UIResourceLostAfterCommit : public UIResourceLostTest { +class UIResourceLostTestSimple : public UIResourceLostTest { public: + // This is called when the commit is complete and the new layer tree has been + // activated. + virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) = 0; + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { - LayerTreeHostContextTest::CommitCompleteOnThread(impl); - switch (time_step_) { + if (!layer_tree_host()->settings().impl_side_painting) { + StepCompleteOnImplThread(impl); + PostStepCompleteToMainThread(); + ++time_step_; + } + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + if (layer_tree_host()->settings().impl_side_painting) { + StepCompleteOnImplThread(impl); + PostStepCompleteToMainThread(); + ++time_step_; + } + } +}; + +// Losing context after an UI resource has been created. +class UIResourceLostAfterCommit : public UIResourceLostTestSimple { + public: + virtual void StepCompleteOnMainThread(int step) OVERRIDE { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + switch (step) { case 0: ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); // Expects a valid UIResourceId. EXPECT_NE(0, ui_resource_->id()); PostSetNeedsCommitToMainThread(); break; + case 4: + // Release resource before ending the test. + ui_resource_.reset(); + EndTest(); + break; + case 5: + // Single thread proxy issues extra commits after context lost. + // http://crbug.com/287250 + if (HasImplThread()) + NOTREACHED(); + break; + case 6: + NOTREACHED(); + } + } + + virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::CommitCompleteOnThread(impl); + switch (time_step_) { case 1: // The resource should have been created on LTHI after the commit. - if (!layer_tree_host()->settings().impl_side_painting) - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); PostSetNeedsCommitToMainThread(); break; case 2: @@ -1665,32 +1944,11 @@ class UIResourceLostAfterCommit : public UIResourceLostTest { EXPECT_EQ(1, ui_resource_->lost_resource_count); // Resource Id on the impl-side have been recreated as well. Note // that the same UIResourceId persists after the context lost. - if (!layer_tree_host()->settings().impl_side_painting) - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); PostSetNeedsCommitToMainThread(); break; - case 4: - // Release resource before ending test. - ui_resource_.reset(); - EndTest(); - break; } } - - virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { - LayerTreeHostContextTest::DidActivateTreeOnThread(impl); - switch (time_step_) { - case 1: - if (layer_tree_host()->settings().impl_side_painting) - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); - break; - case 3: - if (layer_tree_host()->settings().impl_side_painting) - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); - break; - } - ++time_step_; - } }; SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostAfterCommit); @@ -1704,34 +1962,18 @@ SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostAfterCommit); // test_id1_ to have been created. // 3. Create one resource -> Delete that same resource -> Context Lost => Expect // the resource to not exist in the manager. -class UIResourceLostBeforeCommit : public UIResourceLostTest { +class UIResourceLostBeforeCommit : public UIResourceLostTestSimple { public: UIResourceLostBeforeCommit() : test_id0_(0), test_id1_(0) {} - virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { - LayerTreeHostContextTest::CommitCompleteOnThread(impl); - switch (time_step_) { + virtual void StepCompleteOnMainThread(int step) OVERRIDE { + switch (step) { case 0: - // Sequence 1: ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); - LoseContext(); - // Resource Id on the impl-side should no longer be valid after - // context is lost. - EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); - break; - case 1: - // The resources should have been recreated. - EXPECT_EQ(2, ui_resource_->resource_create_count); - // "resource lost" callback was called once for the resource in the - // resource map. - EXPECT_EQ(1, ui_resource_->lost_resource_count); - // Resource Id on the impl-side have been recreated as well. Note - // that the same UIResourceId persists after the context lost. - if (!layer_tree_host()->settings().impl_side_painting) - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); - PostSetNeedsCommitToMainThread(); + // Lose the context on the impl thread before the commit. + PostLoseContextToImplThread(); break; case 2: // Sequence 2: @@ -1744,21 +1986,10 @@ class UIResourceLostBeforeCommit : public UIResourceLostTest { test_id1_ = ui_resource_->id(); // Sanity check that two resource creations return different ids. EXPECT_NE(test_id0_, test_id1_); - // Lose the context before commit. - LoseContext(); + // Lose the context on the impl thread before the commit. + PostLoseContextToImplThread(); break; case 3: - if (!layer_tree_host()->settings().impl_side_painting) { - // The previous resource should have been deleted. - EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); - // The second resource should have been created. - EXPECT_NE(0u, impl->ResourceIdForUIResource(test_id1_)); - } - - // The second resource called the resource callback once and since the - // context is lost, a "resource lost" callback was also issued. - EXPECT_EQ(2, ui_resource_->resource_create_count); - EXPECT_EQ(1, ui_resource_->lost_resource_count); // Clear the manager of resources. ui_resource_.reset(); PostSetNeedsCommitToMainThread(); @@ -1773,45 +2004,66 @@ class UIResourceLostBeforeCommit : public UIResourceLostTest { // destructor (so usually ui_resource_.reset()). But here we need // ui_resource_ for the next step, so call DeleteUIResource directly. layer_tree_host()->DeleteUIResource(test_id0_); - LoseContext(); + // Delete the resouce and then lose the context. + PostLoseContextToImplThread(); break; case 5: - // Expect the resource callback to have been called once. - EXPECT_EQ(1, ui_resource_->resource_create_count); - // No "resource lost" callbacks. - EXPECT_EQ(0, ui_resource_->lost_resource_count); - if (!layer_tree_host()->settings().impl_side_painting) { - // The UI resource id should not be valid - EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); - } - PostSetNeedsCommitToMainThread(); - break; - case 6: + // Release resource before ending the test. ui_resource_.reset(); EndTest(); break; + case 6: + // Single thread proxy issues extra commits after context lost. + // http://crbug.com/287250 + if (HasImplThread()) + NOTREACHED(); + break; + case 8: + NOTREACHED(); } } - virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { - LayerTreeHostContextTest::DidActivateTreeOnThread(impl); + virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::CommitCompleteOnThread(impl); switch (time_step_) { case 1: - if (layer_tree_host()->settings().impl_side_painting) - EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + // Sequence 1 (continued): + // The first context lost happens before the resources were created, + // and because it resulted in no resources being destroyed, it does not + // trigger resource re-creation. + EXPECT_EQ(1, ui_resource_->resource_create_count); + EXPECT_EQ(0, ui_resource_->lost_resource_count); + // Resource Id on the impl-side has been created. + PostSetNeedsCommitToMainThread(); break; case 3: - if (layer_tree_host()->settings().impl_side_painting) { - EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + // Sequence 2 (continued): + // The previous resource should have been deleted. + EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + if (HasImplThread()) { + // The second resource should have been created. EXPECT_NE(0u, impl->ResourceIdForUIResource(test_id1_)); + } else { + // The extra commit that happens at context lost in the single thread + // proxy changes the timing so that the resource has been destroyed. + // http://crbug.com/287250 + EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id1_)); } + // The second resource called the resource callback once and since the + // context is lost, a "resource lost" callback was also issued. + EXPECT_EQ(2, ui_resource_->resource_create_count); + EXPECT_EQ(1, ui_resource_->lost_resource_count); break; case 5: - if (layer_tree_host()->settings().impl_side_painting) - EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); + // Sequence 3 (continued): + // Expect the resource callback to have been called once. + EXPECT_EQ(1, ui_resource_->resource_create_count); + // No "resource lost" callbacks. + EXPECT_EQ(0, ui_resource_->lost_resource_count); + // The UI resource id should not be valid + EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_)); break; } - ++time_step_; } private: @@ -1824,34 +2076,43 @@ SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostBeforeCommit); // Losing UI resource before the pending trees is activated but after the // commit. Impl-side-painting only. class UIResourceLostBeforeActivateTree : public UIResourceLostTest { - virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { - LayerTreeHostContextTest::CommitCompleteOnThread(impl); - switch (time_step_) { + virtual void StepCompleteOnMainThread(int step) OVERRIDE { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + switch (step) { case 0: ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); PostSetNeedsCommitToMainThread(); break; - case 2: - PostSetNeedsCommitToMainThread(); - break; case 3: test_id_ = ui_resource_->id(); ui_resource_.reset(); PostSetNeedsCommitToMainThread(); break; - case 4: - PostSetNeedsCommitToMainThread(); - break; case 5: + // Release resource before ending the test. + ui_resource_.reset(); EndTest(); break; + case 6: + // Make sure no extra commits happened. + NOTREACHED(); } } - virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + LayerTreeHostContextTest::CommitCompleteOnThread(impl); switch (time_step_) { - case 0: + case 2: + PostSetNeedsCommitToMainThread(); break; + case 4: + PostSetNeedsCommitToMainThread(); + break; + } + } + + virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + switch (time_step_) { case 1: // The resource creation callback has been called. EXPECT_EQ(1, ui_resource_->resource_create_count); @@ -1884,6 +2145,8 @@ class UIResourceLostBeforeActivateTree : public UIResourceLostTest { EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id_)); break; } + + PostStepCompleteToMainThread(); ++time_step_; } @@ -1901,5 +2164,91 @@ TEST_F(UIResourceLostBeforeActivateTree, RunTest(true, true, true); } +// Resources evicted explicitly and by visibility changes. +class UIResourceLostEviction : public UIResourceLostTestSimple { + public: + virtual void StepCompleteOnMainThread(int step) OVERRIDE { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + switch (step) { + case 0: + ui_resource_ = FakeScopedUIResource::Create(layer_tree_host()); + EXPECT_NE(0, ui_resource_->id()); + PostSetNeedsCommitToMainThread(); + break; + case 2: + // Make the tree not visible. + PostSetVisibleToMainThread(false); + break; + case 3: + // Release resource before ending the test. + ui_resource_.reset(); + EndTest(); + break; + case 4: + NOTREACHED(); + } + } + + virtual void DidSetVisibleOnImplTree(LayerTreeHostImpl* impl, + bool visible) OVERRIDE { + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context_provider()->Context3d()); + if (!visible) { + // All resources should have been evicted. + ASSERT_EQ(0u, context->NumTextures()); + EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(2, ui_resource_->resource_create_count); + EXPECT_EQ(1, ui_resource_->lost_resource_count); + // Drawing is disabled both because of the evicted resources and + // because the renderer is not visible. + EXPECT_FALSE(impl->CanDraw()); + // Make the renderer visible again. + PostSetVisibleToMainThread(true); + } + } + + virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) OVERRIDE { + TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>( + impl->output_surface()->context_provider()->Context3d()); + LayerTreeHostContextTest::CommitCompleteOnThread(impl); + switch (time_step_) { + case 1: + // The resource should have been created on LTHI after the commit. + ASSERT_EQ(1u, context->NumTextures()); + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(1, ui_resource_->resource_create_count); + EXPECT_EQ(0, ui_resource_->lost_resource_count); + EXPECT_TRUE(impl->CanDraw()); + // Evict all UI resources. This will trigger a commit. + impl->EvictAllUIResources(); + ASSERT_EQ(0u, context->NumTextures()); + EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(1, ui_resource_->resource_create_count); + EXPECT_EQ(0, ui_resource_->lost_resource_count); + EXPECT_FALSE(impl->CanDraw()); + break; + case 2: + // The resource should have been recreated. + ASSERT_EQ(1u, context->NumTextures()); + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(2, ui_resource_->resource_create_count); + EXPECT_EQ(1, ui_resource_->lost_resource_count); + EXPECT_TRUE(impl->CanDraw()); + break; + case 3: + // The resource should have been recreated after visibility was + // restored. + ASSERT_EQ(1u, context->NumTextures()); + EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id())); + EXPECT_EQ(3, ui_resource_->resource_create_count); + EXPECT_EQ(2, ui_resource_->lost_resource_count); + EXPECT_TRUE(impl->CanDraw()); + break; + } + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostEviction); + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_damage.cc b/chromium/cc/trees/layer_tree_host_unittest_damage.cc index 805c9275c14..64c7d4b7c30 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_damage.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_damage.cc @@ -4,9 +4,15 @@ #include "cc/trees/layer_tree_host.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/time/time.h" #include "cc/test/fake_content_layer.h" #include "cc/test/fake_content_layer_client.h" -#include "cc/test/fake_scrollbar_layer.h" +#include "cc/test/fake_painted_scrollbar_layer.h" +#include "cc/test/fake_picture_layer.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/damage_tracker.h" #include "cc/trees/layer_tree_impl.h" @@ -17,6 +23,225 @@ namespace { // These tests deal with damage tracking. class LayerTreeHostDamageTest : public LayerTreeTest {}; +// Changing visibility alone does not cause drawing. +class LayerTreeHostDamageTestSetVisibleDoesNotDraw + : public LayerTreeHostDamageTest { + virtual void BeginTest() OVERRIDE { + step_ = 0; + PostSetNeedsCommitToMainThread(); + } + + virtual void SetupTree() OVERRIDE { + // Viewport is 10x10. + scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_); + root->SetBounds(gfx::Size(10, 10)); + + layer_tree_host()->SetRootLayer(root); + LayerTreeHostDamageTest::SetupTree(); + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + EXPECT_TRUE(result); + + RenderSurfaceImpl* root_surface = + impl->active_tree()->root_layer()->render_surface(); + gfx::RectF root_damage = + root_surface->damage_tracker()->current_damage_rect(); + + switch (step_) { + case 0: + // The first frame has full damage. + EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString()); + + // No evictions when we become not-visible. + impl->SetMemoryPolicy(ManagedMemoryPolicy( + 1000 * 1000 * 1000, + ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING, + 1000 * 1000 * 1000, + ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING, + ManagedMemoryPolicy::kDefaultNumResourcesLimit)); + + PostSetVisibleToMainThread(false); + break; + case 1: + // The compositor has been set not-visible. + EXPECT_FALSE(impl->visible()); + // This frame not visible, so not drawn. + NOTREACHED(); + break; + case 2: + // The compositor has been set visible again. + EXPECT_TRUE(impl->visible()); + // But it still does not draw. + NOTREACHED(); + break; + case 3: + // Finally we force a draw, but it will have no damage. + EXPECT_EQ(gfx::RectF().ToString(), root_damage.ToString()); + EndTest(); + break; + case 4: + NOTREACHED(); + } + return result; + } + + virtual void DidSetVisibleOnImplTree(LayerTreeHostImpl* impl, + bool visible) OVERRIDE { + if (!visible) { + EXPECT_EQ(0, step_); + PostSetVisibleToMainThread(true); + } else { + EXPECT_EQ(1, step_); + + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&LayerTreeHostDamageTestSetVisibleDoesNotDraw::Redraw, + base::Unretained(this), + impl), + base::TimeDelta::FromMilliseconds(10)); + } + ++step_; + } + + void Redraw(LayerTreeHostImpl* impl) { + EXPECT_EQ(2, step_); + impl->SetNeedsRedraw(); + ++step_; + } + + virtual void AfterTest() OVERRIDE {} + + int step_; + FakeContentLayerClient client_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestSetVisibleDoesNotDraw); + +// LayerTreeHost::SetNeedsRedraw should damage the whole viewport. +class LayerTreeHostDamageTestSetNeedsRedraw + : public LayerTreeHostDamageTest { + virtual void SetupTree() OVERRIDE { + // Viewport is 10x10. + scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_); + root->SetBounds(gfx::Size(10, 10)); + + layer_tree_host()->SetRootLayer(root); + LayerTreeHostDamageTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + draw_count_ = 0; + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 1: + layer_tree_host()->SetNeedsRedraw(); + break; + } + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + EXPECT_TRUE(result); + + RenderSurfaceImpl* root_surface = + impl->active_tree()->root_layer()->render_surface(); + gfx::RectF root_damage = + root_surface->damage_tracker()->current_damage_rect(); + + switch (draw_count_) { + case 0: + // The first frame has full damage. + EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString()); + break; + case 1: + // The second frame has full damage. + EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString()); + EndTest(); + break; + case 2: + NOTREACHED(); + } + + ++draw_count_; + return result; + } + + virtual void AfterTest() OVERRIDE {} + + int draw_count_; + FakeContentLayerClient client_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestSetNeedsRedraw); + +// LayerTreeHost::SetViewportSize should damage the whole viewport. +class LayerTreeHostDamageTestSetViewportSize + : public LayerTreeHostDamageTest { + virtual void SetupTree() OVERRIDE { + // Viewport is 10x10. + scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_); + root->SetBounds(gfx::Size(10, 10)); + + layer_tree_host()->SetRootLayer(root); + LayerTreeHostDamageTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + draw_count_ = 0; + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 1: + layer_tree_host()->SetViewportSize(gfx::Size(15, 15)); + break; + } + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + EXPECT_TRUE(result); + + RenderSurfaceImpl* root_surface = + impl->active_tree()->root_layer()->render_surface(); + gfx::RectF root_damage = + root_surface->damage_tracker()->current_damage_rect(); + + switch (draw_count_) { + case 0: + // The first frame has full damage. + EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString()); + break; + case 1: + // The second frame has full damage. + EXPECT_EQ(gfx::RectF(15.f, 15.f).ToString(), root_damage.ToString()); + EndTest(); + break; + case 2: + NOTREACHED(); + } + + ++draw_count_; + return result; + } + + virtual void AfterTest() OVERRIDE {} + + int draw_count_; + FakeContentLayerClient client_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestSetViewportSize); + class LayerTreeHostDamageTestNoDamageDoesNotSwap : public LayerTreeHostDamageTest { virtual void BeginTest() OVERRIDE { @@ -286,16 +511,10 @@ class LayerTreeHostDamageTestForcedFullDamage : public LayerTreeHostDamageTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestForcedFullDamage); -class LayerTreeHostDamageTestScrollbarDoesDamage - : public LayerTreeHostDamageTest { - virtual void BeginTest() OVERRIDE { - did_swaps_ = 0; - PostSetNeedsCommitToMainThread(); - } - +class LayerTreeHostScrollbarDamageTest : public LayerTreeHostDamageTest { virtual void SetupTree() OVERRIDE { scoped_refptr<Layer> root_layer = Layer::Create(); - root_layer->SetBounds(gfx::Size(400,400)); + root_layer->SetBounds(gfx::Size(400, 400)); layer_tree_host()->SetRootLayer(root_layer); scoped_refptr<Layer> content_layer = FakeContentLayer::Create(&client_); @@ -306,7 +525,7 @@ class LayerTreeHostDamageTestScrollbarDoesDamage root_layer->AddChild(content_layer); scoped_refptr<Layer> scrollbar_layer = - FakeScrollbarLayer::Create(false, true, content_layer->id()); + FakePaintedScrollbarLayer::Create(false, true, content_layer->id()); scrollbar_layer->SetPosition(gfx::Point(300, 300)); scrollbar_layer->SetBounds(gfx::Size(10, 100)); root_layer->AddChild(scrollbar_layer); @@ -320,6 +539,17 @@ class LayerTreeHostDamageTestScrollbarDoesDamage LayerTreeHostDamageTest::SetupTree(); } + private: + FakeContentLayerClient client_; +}; + +class LayerTreeHostDamageTestScrollbarDoesDamage + : public LayerTreeHostScrollbarDamageTest { + virtual void BeginTest() OVERRIDE { + did_swaps_ = 0; + PostSetNeedsCommitToMainThread(); + } + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, LayerTreeHostImpl::FrameData* frame_data, bool result) OVERRIDE { @@ -334,16 +564,16 @@ class LayerTreeHostDamageTestScrollbarDoesDamage // The first frame has damage, so we should draw and swap. break; case 1: - // The second frame should damage the scrollbars. - EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100))); + // The second frame should not damage the scrollbars. + EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100))); break; case 2: // The third frame should damage the scrollbars. EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100))); break; case 3: - // The fourth frame should not damage the scrollbars. - EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100))); + // The fourth frame should damage the scrollbars. + EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100))); EndTest(); break; } @@ -358,34 +588,204 @@ class LayerTreeHostDamageTestScrollbarDoesDamage LayerImpl* scroll_layer = root->children()[0]; switch (did_swaps_) { case 1: - host_impl->ScrollBegin(gfx::Point(1,1), InputHandler::Wheel); - EXPECT_TRUE(host_impl->ScrollBy(gfx::Point(), - gfx::Vector2dF(10.0, 10.0))); + // Test that modifying the position of the content layer (not + // scrolling) won't damage the scrollbar. + scroll_layer->SetPosition(gfx::Point(1, 1)); + scroll_layer->SetScrollOffset(scroll_layer->scroll_offset()); + host_impl->SetNeedsRedraw(); break; case 2: - scroll_layer->SetMaxScrollOffset(gfx::Vector2d(60, 100)); + scroll_layer->ScrollBy(gfx::Vector2dF(10.f, 10.f)); host_impl->SetNeedsRedraw(); break; case 3: - // Test that modifying the position of the content layer (not - // scrolling) won't damage the scrollbar. - scroll_layer->SetPosition(gfx::Point(1,1)); - scroll_layer->SetScrollOffset(scroll_layer->scroll_offset()); + scroll_layer->SetMaxScrollOffset(gfx::Vector2d(60, 100)); host_impl->SetNeedsRedraw(); break; } - } virtual void AfterTest() OVERRIDE { EXPECT_EQ(4, did_swaps_); } - FakeContentLayerClient client_; int did_swaps_; }; MULTI_THREAD_TEST_F(LayerTreeHostDamageTestScrollbarDoesDamage); +class DISABLED_LayerTreeHostDamageTestScrollbarCommitDoesNoDamage + : public LayerTreeHostScrollbarDamageTest { + virtual void BeginTest() OVERRIDE { + did_swaps_ = 0; + PostSetNeedsCommitToMainThread(); + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + EXPECT_TRUE(result); + RenderSurfaceImpl* root_surface = + host_impl->active_tree()->root_layer()->render_surface(); + gfx::RectF root_damage = + root_surface->damage_tracker()->current_damage_rect(); + root_damage.Intersect(root_surface->content_rect()); + int frame = host_impl->active_tree()->source_frame_number(); + switch (did_swaps_) { + case 0: + // The first frame has damage, so we should draw and swap. + EXPECT_EQ(0, frame); + break; + case 1: + // The second frame has scrolled, so the scrollbar should be damaged. + EXPECT_EQ(0, frame); + EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100))); + break; + case 2: + // The third frame (after the commit) has no changes, so it shouldn't. + EXPECT_EQ(1, frame); + EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100))); + break; + default: + NOTREACHED(); + break; + } + return result; + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + ++did_swaps_; + EXPECT_TRUE(result); + LayerImpl* root = host_impl->active_tree()->root_layer(); + LayerImpl* scroll_layer = root->children()[0]; + switch (did_swaps_) { + case 1: + // Scroll on the thread. This should damage the scrollbar for the + // next draw on the thread. + scroll_layer->ScrollBy(gfx::Vector2dF(10.f, 10.f)); + host_impl->SetNeedsRedraw(); + break; + case 2: + // Forcibly send the scroll to the main thread. + PostSetNeedsCommitToMainThread(); + break; + case 3: + // First swap after second commit. + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } + + virtual void AfterTest() OVERRIDE { + EXPECT_EQ(3, did_swaps_); + } + + int did_swaps_; +}; + +MULTI_THREAD_TEST_F( + DISABLED_LayerTreeHostDamageTestScrollbarCommitDoesNoDamage); + +class LayerTreeHostDamageTestVisibleTilesStillTriggerDraws + : public LayerTreeHostDamageTest { + + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->impl_side_painting = true; + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void SetupTree() OVERRIDE { + scoped_refptr<FakePictureLayer> root = FakePictureLayer::Create(&client_); + root->SetBounds(gfx::Size(500, 500)); + layer_tree_host()->SetRootLayer(root); + LayerTreeHostDamageTest::SetupTree(); + + swap_count_ = 0; + prepare_to_draw_count_ = 0; + update_visible_tile_count_ = 0; + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + EXPECT_TRUE(result); + prepare_to_draw_count_++; + switch (prepare_to_draw_count_) { + case 1: + // Detect that we have an incomplete tile, during the first frame. + // The first frame should have damage. + frame_data->contains_incomplete_tile = true; + DCHECK(!frame_data->has_no_damage); + break; + case 2: + // Make a no-damage frame. We early out and can't detect + // incomplete tiles, even if they still exist. + frame_data->contains_incomplete_tile = false; + frame_data->has_no_damage = true; + break; + case 3: + // Trigger the last swap for the completed tile. + frame_data->contains_incomplete_tile = false; + frame_data->has_no_damage = false; + EndTest(); + break; + default: + NOTREACHED(); + break; + } + + return result; + } + + virtual void UpdateVisibleTilesOnThread( + LayerTreeHostImpl* host_impl) OVERRIDE { + // Simulate creating some visible tiles (that trigger prepare-to-draws). + // The first we make into a no-damage-frame during prepare-to-draw (see + // above). This is to ensure we still get UpdateVisibleTiles calls after + // a no-damage or aborted frame. + update_visible_tile_count_++; + switch (update_visible_tile_count_) { + case 3: + case 6: + host_impl->DidInitializeVisibleTileForTesting(); + break; + case 7: + NOTREACHED(); + break; + } + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool didSwap) OVERRIDE { + if (!didSwap) + return; + ++swap_count_; + } + + virtual void AfterTest() OVERRIDE { + // We should keep getting update-visible-tiles calls + // until we report there are no more incomplete-tiles. + EXPECT_EQ(update_visible_tile_count_, 6); + // First frame, plus two triggered by DidInitializeVisibleTile() + EXPECT_EQ(prepare_to_draw_count_, 3); + // First swap, plus final swap (contained damage). + EXPECT_EQ(swap_count_, 2); + } + + FakeContentLayerClient client_; + int swap_count_; + int prepare_to_draw_count_; + int update_visible_tile_count_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostDamageTestVisibleTilesStillTriggerDraws); + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_delegated.cc b/chromium/cc/trees/layer_tree_host_unittest_delegated.cc index d48931328af..012f6f82fdf 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_delegated.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_delegated.cc @@ -4,25 +4,73 @@ #include "cc/trees/layer_tree_host.h" +#include <algorithm> + #include "base/bind.h" +#include "base/location.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/time/time.h" #include "cc/layers/delegated_renderer_layer.h" #include "cc/layers/delegated_renderer_layer_client.h" #include "cc/layers/delegated_renderer_layer_impl.h" #include "cc/output/compositor_frame.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/delegated_frame_data.h" +#include "cc/quads/render_pass_draw_quad.h" #include "cc/quads/shared_quad_state.h" #include "cc/quads/texture_draw_quad.h" +#include "cc/resources/returned_resource.h" #include "cc/test/fake_delegated_renderer_layer.h" #include "cc/test/fake_delegated_renderer_layer_impl.h" #include "cc/test/fake_output_surface.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/layer_tree_impl.h" #include "gpu/GLES2/gl2extchromium.h" +#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" namespace cc { namespace { +bool ReturnedResourceLower(const ReturnedResource& a, + const ReturnedResource& b) { + return a.id < b.id; +} + +// Tests if the list of resources matches an expectation, modulo the order. +bool ResourcesMatch(ReturnedResourceArray actual, + unsigned* expected, + size_t expected_count) { + std::sort(actual.begin(), actual.end(), ReturnedResourceLower); + std::sort(expected, expected + expected_count); + size_t actual_index = 0; + + // for each element of the expected array, count off one of the actual array + // (after checking it matches). + for (size_t expected_index = 0; expected_index < expected_count; + ++expected_index) { + EXPECT_LT(actual_index, actual.size()); + if (actual_index >= actual.size()) + return false; + EXPECT_EQ(actual[actual_index].id, expected[expected_index]); + if (actual[actual_index].id != expected[expected_index]) + return false; + EXPECT_GT(actual[actual_index].count, 0); + if (actual[actual_index].count <= 0) { + return false; + } else { + --actual[actual_index].count; + if (actual[actual_index].count == 0) + ++actual_index; + } + } + EXPECT_EQ(actual_index, actual.size()); + return actual_index == actual.size(); +} + +#define EXPECT_RESOURCES(expected, actual) \ + EXPECT_TRUE(ResourcesMatch(actual, expected, arraysize(expected))); + // These tests deal with delegated renderer layers. class LayerTreeHostDelegatedTest : public LayerTreeTest { protected: @@ -108,6 +156,39 @@ class LayerTreeHostDelegatedTest : public LayerTreeTest { frame->render_pass_list[0]->quad_list.push_back(quad.PassAs<DrawQuad>()); } + void AddRenderPass(DelegatedFrameData* frame, + RenderPass::Id id, + gfx::Rect output_rect, + gfx::Rect damage_rect, + const FilterOperations& filters, + const FilterOperations& background_filters) { + for (size_t i = 0; i < frame->render_pass_list.size(); ++i) + DCHECK(id != frame->render_pass_list[i]->id); + + scoped_ptr<RenderPass> pass(RenderPass::Create()); + pass->SetNew(id, + output_rect, + damage_rect, + gfx::Transform()); + frame->render_pass_list.push_back(pass.Pass()); + + scoped_ptr<SharedQuadState> sqs = SharedQuadState::Create(); + scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create(); + + quad->SetNew(sqs.get(), + output_rect, + id, + false, // is_replica + 0, // mask_resource_id + damage_rect, + gfx::Rect(0, 0, 1, 1), // mask_uv_rect + filters, + skia::RefPtr<SkImageFilter>(), + background_filters); + frame->render_pass_list[0]->shared_quad_state_list.push_back(sqs.Pass()); + frame->render_pass_list[0]->quad_list.push_back(quad.PassAs<DrawQuad>()); + } + scoped_ptr<DelegatedFrameData> CreateEmptyFrameData() { scoped_ptr<DelegatedFrameData> frame(new DelegatedFrameData); return frame.Pass(); @@ -158,7 +239,8 @@ class LayerTreeHostDelegatedTest : public LayerTreeTest { CompositorFrameAck ack; for (size_t i = 0; i < resources_to_return.size(); ++i) output_surface()->ReturnResource(resources_to_return[i], &ack); - host_impl->OnSwapBuffersComplete(&ack); + host_impl->ReclaimResources(&ack); + host_impl->OnSwapBuffersComplete(); } }; @@ -246,8 +328,8 @@ class LayerTreeHostDelegatedTestCreateChildId FakeDelegatedRendererLayerImpl* delegated_impl = static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); - WebKit::WebGraphicsContext3D* context = - host_impl->resource_provider()->GraphicsContext3D(); + ContextProvider* context_provider = + host_impl->output_surface()->context_provider(); ++num_activates_; switch (num_activates_) { @@ -255,8 +337,9 @@ class LayerTreeHostDelegatedTestCreateChildId EXPECT_TRUE(delegated_impl->ChildId()); EXPECT_FALSE(did_reset_child_id_); - context->loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB, - GL_INNOCENT_CONTEXT_RESET_ARB); + context_provider->Context3d()->loseContextCHROMIUM( + GL_GUILTY_CONTEXT_RESET_ARB, + GL_INNOCENT_CONTEXT_RESET_ARB); break; case 3: EXPECT_TRUE(delegated_impl->ChildId()); @@ -291,6 +374,131 @@ class LayerTreeHostDelegatedTestCreateChildId SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestCreateChildId); +class LayerTreeHostDelegatedTestOffscreenContext_NoFilters + : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { + protected: + virtual void BeginTest() OVERRIDE { + scoped_ptr<DelegatedFrameData> frame = + CreateFrameData(gfx::Rect(0, 0, 1, 1), + gfx::Rect(0, 0, 1, 1)); + delegated_->SetFrameData(frame.Pass()); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + EXPECT_FALSE(host_impl->offscreen_context_provider()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostDelegatedTestOffscreenContext_NoFilters); + +class LayerTreeHostDelegatedTestOffscreenContext_Filters + : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { + protected: + virtual void BeginTest() OVERRIDE { + scoped_ptr<DelegatedFrameData> frame = + CreateFrameData(gfx::Rect(0, 0, 1, 1), + gfx::Rect(0, 0, 1, 1)); + + FilterOperations filters; + filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f)); + AddRenderPass(frame.get(), + RenderPass::Id(2, 1), + gfx::Rect(0, 0, 1, 1), + gfx::Rect(0, 0, 1, 1), + filters, + FilterOperations()); + delegated_->SetFrameData(frame.Pass()); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + bool expect_context = !delegating_renderer(); + EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostDelegatedTestOffscreenContext_Filters); + +class LayerTreeHostDelegatedTestOffscreenContext_BackgroundFilters + : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { + protected: + virtual void BeginTest() OVERRIDE { + scoped_ptr<DelegatedFrameData> frame = + CreateFrameData(gfx::Rect(0, 0, 1, 1), + gfx::Rect(0, 0, 1, 1)); + + FilterOperations filters; + filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f)); + AddRenderPass(frame.get(), + RenderPass::Id(2, 1), + gfx::Rect(0, 0, 1, 1), + gfx::Rect(0, 0, 1, 1), + FilterOperations(), + filters); + delegated_->SetFrameData(frame.Pass()); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + bool expect_context = !delegating_renderer(); + EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostDelegatedTestOffscreenContext_BackgroundFilters); + +class LayerTreeHostDelegatedTestOffscreenContext_Filters_AddedToTree + : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { + protected: + virtual void BeginTest() OVERRIDE { + scoped_ptr<DelegatedFrameData> frame = + CreateFrameData(gfx::Rect(0, 0, 1, 1), + gfx::Rect(0, 0, 1, 1)); + + FilterOperations filters; + filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f)); + AddRenderPass(frame.get(), + RenderPass::Id(2, 1), + gfx::Rect(0, 0, 1, 1), + gfx::Rect(0, 0, 1, 1), + filters, + FilterOperations()); + + delegated_->RemoveFromParent(); + delegated_->SetFrameData(frame.Pass()); + layer_tree_host()->root_layer()->AddChild(delegated_); + + PostSetNeedsCommitToMainThread(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + bool expect_context = !delegating_renderer(); + EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostDelegatedTestOffscreenContext_Filters_AddedToTree); + class LayerTreeHostDelegatedTestLayerUsesFrameDamage : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { public: @@ -377,6 +585,28 @@ class LayerTreeHostDelegatedTestLayerUsesFrameDamage // Should create zero damage. layer_tree_host()->SetNeedsCommit(); break; + case 16: + // Moving the layer out of the tree and back in will damage the whole + // impl layer. + delegated_->RemoveFromParent(); + layer_tree_host()->root_layer()->AddChild(delegated_); + break; + case 17: + // Make a larger frame with lots of damage. Then a frame smaller than + // the first frame's damage. The entire layer should be damaged, but + // nothing more. + delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 10, 10), + gfx::Rect(0, 0, 10, 10))); + delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 5, 5), + gfx::Rect(1, 1, 2, 2))); + break; + case 18: + // Make a frame with lots of damage. Then replace it with an empty + // frame. The entire layer should be damaged, but nothing more. + delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 10, 10), + gfx::Rect(0, 0, 10, 10))); + delegated_->SetFrameData(CreateEmptyFrameData()); + break; } first_draw_for_source_frame_ = true; } @@ -462,6 +692,18 @@ class LayerTreeHostDelegatedTestLayerUsesFrameDamage case 15: EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(), damage_rect.ToString()); + break; + case 16: + EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), + damage_rect.ToString()); + break; + case 17: + EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), + damage_rect.ToString()); + break; + case 18: + EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(), + damage_rect.ToString()); EndTest(); break; } @@ -492,10 +734,20 @@ class LayerTreeHostDelegatedTestMergeResources scoped_ptr<DelegatedFrameData> frame2 = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame2.get(), 999); + AddTransferableResource(frame2.get(), 999); AddTextureQuad(frame2.get(), 555); AddTransferableResource(frame2.get(), 555); delegated_->SetFrameData(frame2.Pass()); + // The resource 999 from frame1 is returned since it is still on the main + // thread. + ReturnedResourceArray returned_resources; + delegated_->TakeUnusedResourcesForChildCompositor(&returned_resources); + { + unsigned expected[] = {999}; + EXPECT_RESOURCES(expected, returned_resources); + } + PostSetNeedsCommitToMainThread(); } @@ -514,8 +766,8 @@ class LayerTreeHostDelegatedTestMergeResources EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); EndTest(); } @@ -585,7 +837,7 @@ class LayerTreeHostDelegatedTestReturnUnusedResources virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -599,11 +851,6 @@ class LayerTreeHostDelegatedTestReturnUnusedResources delegated_->SetFrameData(frame.Pass()); break; case 2: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 3: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); @@ -611,47 +858,42 @@ class LayerTreeHostDelegatedTestReturnUnusedResources // Keep using 999 but stop using 555. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); + AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 444); AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; - case 4: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 5: + case 3: // 555 is no longer in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(1u, resources.size()); - EXPECT_EQ(555u, resources[0].id); + { + unsigned expected[] = {555}; + EXPECT_RESOURCES(expected, resources); + } // Stop using any resources. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); delegated_->SetFrameData(frame.Pass()); break; - case 6: + case 4: // Postpone collecting resources for a frame. They should still be there // the next frame. layer_tree_host()->SetNeedsCommit(); return; - case 7: - // 444 and 999 are no longer in use. + case 5: + // 444 and 999 are no longer in use. We sent two refs to 999, so we + // should get two back. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(2u, resources.size()); - if (resources[0].id == 999) { - EXPECT_EQ(999u, resources[0].id); - EXPECT_EQ(444u, resources[1].id); - } else { - EXPECT_EQ(444u, resources[0].id); - EXPECT_EQ(999u, resources[1].id); + { + unsigned expected[] = {444, 999, 999}; + EXPECT_RESOURCES(expected, resources); } EndTest(); break; } // Resource are never immediately released. - TransferableResourceArray empty_resources; + ReturnedResourceArray empty_resources; delegated_->TakeUnusedResourcesForChildCompositor(&empty_resources); EXPECT_TRUE(empty_resources.empty()); } @@ -676,7 +918,7 @@ class LayerTreeHostDelegatedTestReusedResources virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -692,11 +934,6 @@ class LayerTreeHostDelegatedTestReusedResources delegated_->SetFrameData(frame.Pass()); break; case 2: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 3: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); @@ -704,6 +941,7 @@ class LayerTreeHostDelegatedTestReusedResources // Keep using 999 but stop using 555 and 444. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); + AddTransferableResource(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); // Resource are not immediately released. @@ -713,19 +951,19 @@ class LayerTreeHostDelegatedTestReusedResources // Now using 555 and 444 again, but not 999. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); + AddTransferableResource(frame.get(), 555); AddTextureQuad(frame.get(), 444); + AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); break; - case 4: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 5: - // The 999 resource is the only unused one. + case 3: + // The 999 resource is the only unused one. Two references were sent, so + // two should be returned. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(1u, resources.size()); - EXPECT_EQ(999u, resources[0].id); + { + unsigned expected[] = {999, 999}; + EXPECT_RESOURCES(expected, resources); + } EndTest(); break; } @@ -750,7 +988,7 @@ class LayerTreeHostDelegatedTestFrameBeforeAck virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -766,11 +1004,6 @@ class LayerTreeHostDelegatedTestFrameBeforeAck delegated_->SetFrameData(frame.Pass()); break; case 2: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 3: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); @@ -778,6 +1011,7 @@ class LayerTreeHostDelegatedTestFrameBeforeAck // Keep using 999 but stop using 555 and 444. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); + AddTransferableResource(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); // Resource are not immediately released. @@ -786,24 +1020,15 @@ class LayerTreeHostDelegatedTestFrameBeforeAck // The parent compositor (this one) does a commit. break; - case 4: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 5: + case 3: delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(2u, resources.size()); - if (resources[0].id == 555) { - EXPECT_EQ(555u, resources[0].id); - EXPECT_EQ(444u, resources[1].id); - } else { - EXPECT_EQ(444u, resources[0].id); - EXPECT_EQ(555u, resources[1].id); + { + unsigned expected[] = {444, 555}; + EXPECT_RESOURCES(expected, resources); } - // The child compositor sends a frame before receiving an for the - // second frame. It uses 999, 444, and 555 again. + // The child compositor sends a frame referring to resources not in the + // frame. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); AddTextureQuad(frame.get(), 555); @@ -814,7 +1039,7 @@ class LayerTreeHostDelegatedTestFrameBeforeAck } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { - if (host_impl->active_tree()->source_frame_number() != 5) + if (host_impl->active_tree()->source_frame_number() != 3) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); @@ -832,7 +1057,7 @@ class LayerTreeHostDelegatedTestFrameBeforeAck EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(1u, pass->quad_list.size()); @@ -862,7 +1087,7 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -878,11 +1103,6 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources delegated_->SetFrameData(frame.Pass()); break; case 2: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 3: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); @@ -890,6 +1110,7 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources // Keep using 999 but stop using 555 and 444. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); + AddTransferableResource(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); // Resource are not immediately released. @@ -898,31 +1119,28 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources // The parent compositor (this one) does a commit. break; - case 4: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 5: + case 3: // The child compositor sends a frame before taking resources back // from the previous commit. This frame makes use of the resources 555 // and 444, which were just released during commit. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); + AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); + AddTransferableResource(frame.get(), 555); AddTextureQuad(frame.get(), 444); + AddTransferableResource(frame.get(), 444); delegated_->SetFrameData(frame.Pass()); - // The resources are used by the new frame so are not returned. + // The resources are used by the new frame but are returned anyway since + // we passed them again. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(0u, resources.size()); + { + unsigned expected[] = {444, 555}; + EXPECT_RESOURCES(expected, resources); + } break; - case 6: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 7: + case 4: delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); EndTest(); @@ -931,7 +1149,7 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { - if (host_impl->active_tree()->source_frame_number() != 5) + if (host_impl->active_tree()->source_frame_number() != 3) return; LayerImpl* root_impl = host_impl->active_tree()->root_layer(); @@ -950,9 +1168,9 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources EXPECT_EQ(1u, map.count(444)); EXPECT_EQ(3u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(444)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); + EXPECT_EQ(1u, delegated_impl->Resources().count(444)); const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(3u, pass->quad_list.size()); @@ -987,7 +1205,7 @@ class LayerTreeHostDelegatedTestBadFrame virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -1001,11 +1219,6 @@ class LayerTreeHostDelegatedTestBadFrame delegated_->SetFrameData(frame.Pass()); break; case 2: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 3: // All of the resources are in use. delegated_->TakeUnusedResourcesForChildCompositor(&resources); EXPECT_EQ(0u, resources.size()); @@ -1024,32 +1237,26 @@ class LayerTreeHostDelegatedTestBadFrame // The parent compositor (this one) does a commit. break; - case 4: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 5: + case 3: // The bad frame's resource is given back to the child compositor. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(1u, resources.size()); - EXPECT_EQ(444u, resources[0].id); + { + unsigned expected[] = {444}; + EXPECT_RESOURCES(expected, resources); + } // Now send a good frame with 999 again. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); delegated_->SetFrameData(frame.Pass()); break; - case 6: - // Retrieve unused resources to the main thread. - // TODO(danakj): Shouldn't need to commit to get resources. - layer_tree_host()->SetNeedsCommit(); - return; - case 7: + case 4: // The unused 555 from the last good frame is now released. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(1u, resources.size()); - EXPECT_EQ(555u, resources[0].id); + { + unsigned expected[] = {555}; + EXPECT_RESOURCES(expected, resources); + } EndTest(); break; @@ -1080,8 +1287,8 @@ class LayerTreeHostDelegatedTestBadFrame EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(2u, pass->quad_list.size()); @@ -1093,15 +1300,15 @@ class LayerTreeHostDelegatedTestBadFrame EXPECT_EQ(map.find(555)->second, quad2->resource_id); break; } - case 3: { + case 2: { // We only keep resources from the last valid frame. EXPECT_EQ(2u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); // The bad frame is dropped though, we still have the frame with 999 and // 555 in it. @@ -1115,20 +1322,13 @@ class LayerTreeHostDelegatedTestBadFrame EXPECT_EQ(map.find(555)->second, quad2->resource_id); break; } - case 5: - // Resources given to our parent compositor will be returned now, but - // the DelegatedRendererLayerImpl doesn't know about it until the next - // commit. - // TODO(danakj): Shouldn't need a commit to return resources to the - // DelegatedRendererLayerImpl or to the main thread. - break; - case 6: { + case 3: { // We have the new good frame with just 999 in it. EXPECT_EQ(1u, map.size()); EXPECT_EQ(1u, map.count(999)); EXPECT_EQ(1u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0]; EXPECT_EQ(1u, pass->quad_list.size()); @@ -1154,7 +1354,7 @@ class LayerTreeHostDelegatedTestUnnamedResource virtual void DidCommit() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -1169,8 +1369,10 @@ class LayerTreeHostDelegatedTestUnnamedResource case 2: // The unused resource should be returned. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(1u, resources.size()); - EXPECT_EQ(999u, resources[0].id); + { + unsigned expected[] = {999}; + EXPECT_RESOURCES(expected, resources); + } EndTest(); break; @@ -1194,7 +1396,7 @@ class LayerTreeHostDelegatedTestUnnamedResource EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); } virtual void AfterTest() OVERRIDE {} @@ -1209,9 +1411,9 @@ class LayerTreeHostDelegatedTestDontLeakResource PostSetNeedsCommitToMainThread(); } - virtual void DidCommit() OVERRIDE { + virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -1227,14 +1429,29 @@ class LayerTreeHostDelegatedTestDontLeakResource // But then we immediately stop using 999. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); + AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 2: - // The unused resource should be returned. + // The unused resources should be returned. 555 is still used, but it's + // returned once to account for the first frame. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(1u, resources.size()); - EXPECT_EQ(999u, resources[0].id); - + { + unsigned expected[] = {555, 999}; + EXPECT_RESOURCES(expected, resources); + } + // Send a frame with no resources in it. + frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); + delegated_->SetFrameData(frame.Pass()); + break; + case 3: + // The now unused resource 555 should be returned. + resources.clear(); + delegated_->TakeUnusedResourcesForChildCompositor(&resources); + { + unsigned expected[] = {555}; + EXPECT_RESOURCES(expected, resources); + } EndTest(); break; } @@ -1257,7 +1474,12 @@ class LayerTreeHostDelegatedTestDontLeakResource EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + ReturnUnusedResourcesFromParent(host_impl); } virtual void AfterTest() OVERRIDE {} @@ -1270,7 +1492,7 @@ class LayerTreeHostDelegatedTestResourceSentToParent public: virtual void DidCommitAndDrawFrame() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -1291,6 +1513,7 @@ class LayerTreeHostDelegatedTestResourceSentToParent // it present. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); + AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 3: @@ -1299,18 +1522,46 @@ class LayerTreeHostDelegatedTestResourceSentToParent EXPECT_EQ(0u, resources.size()); // The impl side will get back the resource at some point. - // TODO(danakj): The test should work without this. - layer_tree_host()->SetNeedsCommit(); + ImplThreadTaskRunner()->PostTask(FROM_HERE, + receive_resource_on_thread_); break; - case 4: - // 999 was returned from the grandparent and could be released. - delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(1u, resources.size()); - EXPECT_EQ(999u, resources[0].id); + } + } - EndTest(); - break; + void ReceiveResourceOnThread(LayerTreeHostImpl* host_impl) { + LayerImpl* root_impl = host_impl->active_tree()->root_layer(); + FakeDelegatedRendererLayerImpl* delegated_impl = + static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]); + + const ResourceProvider::ResourceIdMap& map = + host_impl->resource_provider()->GetChildToParentMap( + delegated_impl->ChildId()); + + // Receive 999 back from the grandparent. + CompositorFrameAck ack; + output_surface()->ReturnResource(map.find(999)->second, &ack); + host_impl->ReclaimResources(&ack); + host_impl->OnSwapBuffersComplete(); + + // And then it should be released by the DelegatedRendererLayer. + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&LayerTreeHostDelegatedTestResourceSentToParent:: + DidReceiveResourceOnMainThread, + base::Unretained(this))); + } + + void DidReceiveResourceOnMainThread() { + ReturnedResourceArray resources; + + // 999 was returned from the grandparent and could be released. + delegated_->TakeUnusedResourcesForChildCompositor(&resources); + { + unsigned expected[] = {999}; + EXPECT_RESOURCES(expected, resources); } + + EndTest(); } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { @@ -1332,8 +1583,8 @@ class LayerTreeHostDelegatedTestResourceSentToParent EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); // The 999 resource will be sent to a grandparent compositor. break; @@ -1345,12 +1596,13 @@ class LayerTreeHostDelegatedTestResourceSentToParent // 999 is in the parent, so not held by delegated renderer layer. EXPECT_EQ(1u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); - // Receive 999 back from the grandparent. - CompositorFrameAck ack; - output_surface()->ReturnResource(map.find(999)->second, &ack); - host_impl->OnSwapBuffersComplete(&ack); + receive_resource_on_thread_ = + base::Bind(&LayerTreeHostDelegatedTestResourceSentToParent:: + ReceiveResourceOnThread, + base::Unretained(this), + host_impl); break; } case 3: @@ -1366,7 +1618,7 @@ class LayerTreeHostDelegatedTestResourceSentToParent virtual void AfterTest() OVERRIDE {} - TransferableResource resource_in_grandparent; + base::Closure receive_resource_on_thread_; }; SINGLE_AND_MULTI_THREAD_DELEGATING_RENDERER_TEST_F( @@ -1383,7 +1635,7 @@ class LayerTreeHostDelegatedTestCommitWithoutTake virtual void DidCommit() OVERRIDE { scoped_ptr<DelegatedFrameData> frame; - TransferableResourceArray resources; + ReturnedResourceArray resources; int next_source_frame_number = layer_tree_host()->source_frame_number(); switch (next_source_frame_number) { @@ -1404,20 +1656,40 @@ class LayerTreeHostDelegatedTestCommitWithoutTake // Stop using 999 and 444 in this frame and commit. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 555); + AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); + // 999 and 444 will be returned for frame 1, but not 555 since it's in + // the current frame. break; case 3: // Don't take resources here, but set a new frame that uses 999 again. frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); AddTextureQuad(frame.get(), 999); + AddTransferableResource(frame.get(), 999); AddTextureQuad(frame.get(), 555); + AddTransferableResource(frame.get(), 555); delegated_->SetFrameData(frame.Pass()); break; case 4: - // 999 and 555 are in use, but 444 should be returned now. + // 555 from frame 1 and 2 isn't returned since it's still in use. 999 + // from frame 1 is returned though. delegated_->TakeUnusedResourcesForChildCompositor(&resources); - EXPECT_EQ(1u, resources.size()); - EXPECT_EQ(444u, resources[0].id); + { + unsigned expected[] = {444, 999}; + EXPECT_RESOURCES(expected, resources); + } + + frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); + delegated_->SetFrameData(frame.Pass()); + // 555 will be returned 3 times for frames 1 2 and 3, and 999 will be + // returned once for frame 3. + break; + case 5: + delegated_->TakeUnusedResourcesForChildCompositor(&resources); + { + unsigned expected[] = {555, 555, 555, 999}; + EXPECT_RESOURCES(expected, resources); + } EndTest(); break; @@ -1444,16 +1716,16 @@ class LayerTreeHostDelegatedTestCommitWithoutTake EXPECT_EQ(1u, map.count(444)); EXPECT_EQ(3u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(444)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); + EXPECT_EQ(1u, delegated_impl->Resources().count(444)); break; case 2: EXPECT_EQ(1u, map.size()); EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(1u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); break; case 3: EXPECT_EQ(2u, map.size()); @@ -1461,8 +1733,8 @@ class LayerTreeHostDelegatedTestCommitWithoutTake EXPECT_EQ(1u, map.count(555)); EXPECT_EQ(2u, delegated_impl->Resources().size()); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second)); - EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second)); + EXPECT_EQ(1u, delegated_impl->Resources().count(999)); + EXPECT_EQ(1u, delegated_impl->Resources().count(555)); } } @@ -1471,5 +1743,107 @@ class LayerTreeHostDelegatedTestCommitWithoutTake SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestCommitWithoutTake); +class DelegatedFrameIsActivatedDuringCommit + : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer { + protected: + DelegatedFrameIsActivatedDuringCommit() + : wait_thread_("WAIT"), + wait_event_(false, false) { + wait_thread_.Start(); + } + + virtual void BeginTest() OVERRIDE { + activate_count_ = 0; + + scoped_ptr<DelegatedFrameData> frame = + CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); + AddTextureQuad(frame.get(), 999); + AddTransferableResource(frame.get(), 999); + delegated_->SetFrameData(frame.Pass()); + + PostSetNeedsCommitToMainThread(); + } + + virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // Slow down activation so the main thread DidCommit() will run if + // not blocked. + wait_thread_.message_loop()->PostDelayedTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&wait_event_)), + base::TimeDelta::FromMilliseconds(10)); + wait_event_.Wait(); + + base::AutoLock lock(activate_lock_); + ++activate_count_; + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // The main thread is awake now, and will run DidCommit() immediately. + // Run DidActivate() afterwards by posting it now. + proxy()->MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&DelegatedFrameIsActivatedDuringCommit::DidActivate, + base::Unretained(this))); + } + + void DidActivate() { + base::AutoLock lock(activate_lock_); + switch (activate_count_) { + case 1: { + // The first frame has been activated. Set a new frame, and + // expect the next commit to finish *after* it is activated. + scoped_ptr<DelegatedFrameData> frame = + CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1)); + AddTextureQuad(frame.get(), 555); + AddTransferableResource(frame.get(), 555); + delegated_->SetFrameData(frame.Pass()); + // So this commit number should complete after the second activate. + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + break; + } + case 2: + // The second frame has been activated. Remove the layer from + // the tree to cause another commit/activation. The commit should + // finish *after* the layer is removed from the active tree. + delegated_->RemoveFromParent(); + // So this commit number should complete after the third activate. + EXPECT_EQ(2, layer_tree_host()->source_frame_number()); + break; + case 3: + EndTest(); + break; + } + } + + virtual void DidCommit() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 2: { + // The activate for the 2nd frame should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(2, activate_count_); + break; + } + case 3: { + // The activate to remove the layer should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(3, activate_count_); + break; + } + } + } + + + virtual void AfterTest() OVERRIDE {} + + base::Thread wait_thread_; + base::WaitableEvent wait_event_; + base::Lock activate_lock_; + int activate_count_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + DelegatedFrameIsActivatedDuringCommit); + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index 303e92496b4..ab87be1b5c5 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -722,8 +722,7 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest { main_thread_scroll_(40, 5), impl_thread_scroll1_(2, -1), impl_thread_scroll2_(-3, 10), - num_scrolls_(0), - can_activate_(true) {} + num_scrolls_(0) {} virtual void BeginTest() OVERRIDE { layer_tree_host()->root_layer()->SetScrollable(true); @@ -748,15 +747,6 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest { } } - virtual bool CanActivatePendingTree(LayerTreeHostImpl* impl) OVERRIDE { - return can_activate_; - } - - virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* impl) - OVERRIDE { - return can_activate_; - } - virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { // We force a second draw here of the first commit before activating // the second commit. @@ -774,7 +764,7 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest { switch (impl->active_tree()->source_frame_number()) { case 0: if (!impl->pending_tree()) { - can_activate_ = false; + impl->BlockNotifyReadyToActivateForTesting(true); EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d()); root->ScrollBy(impl_thread_scroll1_); @@ -786,7 +776,7 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest { // CommitCompleteOnThread will trigger this function again // and cause us to take the else clause. } else { - can_activate_ = true; + impl->BlockNotifyReadyToActivateForTesting(false); ASSERT_TRUE(pending_root); EXPECT_EQ(impl->pending_tree()->source_frame_number(), 1); @@ -828,11 +818,118 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest { gfx::Vector2d impl_thread_scroll1_; gfx::Vector2d impl_thread_scroll2_; int num_scrolls_; - bool can_activate_; }; MULTI_THREAD_TEST_F(ImplSidePaintingScrollTestSimple); +// This test makes sure that layers pick up scrolls that occur between +// beginning a commit and finishing a commit (aka scroll deltas not +// included in sent scroll delta) still apply to layers that don't +// push properties. +class ImplSidePaintingScrollTestImplOnlyScroll + : public ImplSidePaintingScrollTest { + public: + ImplSidePaintingScrollTestImplOnlyScroll() + : initial_scroll_(20, 10), impl_thread_scroll_(-2, 3) {} + + virtual void BeginTest() OVERRIDE { + layer_tree_host()->root_layer()->SetScrollable(true); + layer_tree_host()->root_layer()->SetMaxScrollOffset( + gfx::Vector2d(100, 100)); + layer_tree_host()->root_layer()->SetScrollOffset(initial_scroll_); + PostSetNeedsCommitToMainThread(); + } + + virtual void WillCommit() OVERRIDE { + Layer* root = layer_tree_host()->root_layer(); + switch (layer_tree_host()->source_frame_number()) { + case 0: + EXPECT_TRUE(root->needs_push_properties()); + break; + case 1: + // Even if this layer doesn't need push properties, it should + // still pick up scrolls that happen on the active layer during + // commit. + EXPECT_FALSE(root->needs_push_properties()); + break; + } + } + + virtual void BeginCommitOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // Scroll after the 2nd commit has started. + if (impl->active_tree()->source_frame_number() == 0) { + LayerImpl* active_root = impl->active_tree()->root_layer(); + ASSERT_TRUE(active_root); + active_root->ScrollBy(impl_thread_scroll_); + } + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // We force a second draw here of the first commit before activating + // the second commit. + LayerImpl* active_root = impl->active_tree()->root_layer(); + LayerImpl* pending_root = impl->pending_tree()->root_layer(); + + ASSERT_TRUE(pending_root); + switch (impl->pending_tree()->source_frame_number()) { + case 0: + EXPECT_VECTOR_EQ(pending_root->scroll_offset(), initial_scroll_); + EXPECT_VECTOR_EQ(pending_root->ScrollDelta(), gfx::Vector2d()); + EXPECT_VECTOR_EQ(pending_root->sent_scroll_delta(), gfx::Vector2d()); + EXPECT_FALSE(active_root); + break; + case 1: + // Even though the scroll happened during the commit, both layers + // should have the appropriate scroll delta. + EXPECT_VECTOR_EQ(pending_root->scroll_offset(), initial_scroll_); + EXPECT_VECTOR_EQ(pending_root->ScrollDelta(), impl_thread_scroll_); + EXPECT_VECTOR_EQ(pending_root->sent_scroll_delta(), gfx::Vector2d()); + ASSERT_TRUE(active_root); + EXPECT_VECTOR_EQ(active_root->scroll_offset(), initial_scroll_); + EXPECT_VECTOR_EQ(active_root->ScrollDelta(), impl_thread_scroll_); + EXPECT_VECTOR_EQ(active_root->sent_scroll_delta(), gfx::Vector2d()); + break; + case 2: + // On the next commit, this delta should have been sent and applied. + EXPECT_VECTOR_EQ(pending_root->scroll_offset(), + initial_scroll_ + impl_thread_scroll_); + EXPECT_VECTOR_EQ(pending_root->ScrollDelta(), gfx::Vector2d()); + EXPECT_VECTOR_EQ(pending_root->sent_scroll_delta(), gfx::Vector2d()); + EndTest(); + break; + } + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { + ImplSidePaintingScrollTest::DrawLayersOnThread(impl); + + LayerImpl* root = impl->active_tree()->root_layer(); + + switch (impl->active_tree()->source_frame_number()) { + case 0: + EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_); + EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d()); + EXPECT_VECTOR_EQ(root->sent_scroll_delta(), gfx::Vector2d()); + PostSetNeedsCommitToMainThread(); + break; + case 1: + EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_); + EXPECT_VECTOR_EQ(root->ScrollDelta(), impl_thread_scroll_); + EXPECT_VECTOR_EQ(root->sent_scroll_delta(), gfx::Vector2d()); + PostSetNeedsCommitToMainThread(); + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + gfx::Vector2d initial_scroll_; + gfx::Vector2d impl_thread_scroll_; +}; + +MULTI_THREAD_TEST_F(ImplSidePaintingScrollTestImplOnlyScroll); + class LayerTreeHostScrollTestScrollZeroMaxScrollOffset : public LayerTreeHostScrollTest { public: diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index 70ad6de88fa..280bdad8ee1 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -9,8 +9,9 @@ #include "cc/animation/scrollbar_animation_controller.h" #include "cc/debug/traced_value.h" #include "cc/layers/heads_up_display_layer_impl.h" +#include "cc/layers/layer.h" #include "cc/layers/render_surface_impl.h" -#include "cc/layers/scrollbar_layer_impl.h" +#include "cc/layers/scrollbar_layer_impl_base.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_host_impl.h" #include "ui/gfx/size_conversions.h" @@ -27,6 +28,9 @@ LayerTreeImpl::LayerTreeImpl(LayerTreeHostImpl* layer_tree_host_impl) root_layer_scroll_offset_delegate_(NULL), background_color_(0), has_transparent_background_(false), + page_scale_layer_(NULL), + inner_viewport_scroll_layer_(NULL), + outer_viewport_scroll_layer_(NULL), page_scale_factor_(1), page_scale_delta_(1), sent_page_scale_delta_(1), @@ -115,6 +119,13 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { target_tree->page_scale_delta() / target_tree->sent_page_scale_delta()); target_tree->set_sent_page_scale_delta(1); + if (settings().use_pinch_virtual_viewport) { + target_tree->SetViewportLayersFromIds( + page_scale_layer_->id(), + inner_viewport_scroll_layer_->id(), + outer_viewport_scroll_layer_ ? outer_viewport_scroll_layer_->id() + : Layer::INVALID_ID); + } // This should match the property synchronization in // LayerTreeHost::finishCommitOnImplThread(). target_tree->set_source_frame_number(source_frame_number()); @@ -207,7 +218,7 @@ void LayerTreeImpl::SetPageScaleDelta(float delta) { } gfx::SizeF LayerTreeImpl::ScrollableViewportSize() const { - return gfx::ScaleSize(layer_tree_host_impl_->VisibleViewportSize(), + return gfx::ScaleSize(layer_tree_host_impl_->UnscaledScrollableViewportSize(), 1.0f / total_page_scale_factor()); } @@ -226,11 +237,11 @@ void LayerTreeImpl::UpdateMaxScrollOffset() { root_scroll_layer_->SetMaxScrollOffset(gfx::ToFlooredVector2d(max_scroll)); } -static void ApplySentScrollDeltasOn(LayerImpl* layer) { - layer->ApplySentScrollDeltas(); +static void ApplySentScrollDeltasFromAbortedCommitTo(LayerImpl* layer) { + layer->ApplySentScrollDeltasFromAbortedCommit(); } -void LayerTreeImpl::ApplySentScrollAndScaleDeltas() { +void LayerTreeImpl::ApplySentScrollAndScaleDeltasFromAbortedCommit() { DCHECK(IsActiveTree()); page_scale_factor_ *= sent_page_scale_delta_; @@ -241,9 +252,48 @@ void LayerTreeImpl::ApplySentScrollAndScaleDeltas() { return; LayerTreeHostCommon::CallFunctionForSubtree( - root_layer(), base::Bind(&ApplySentScrollDeltasOn)); + root_layer(), base::Bind(&ApplySentScrollDeltasFromAbortedCommitTo)); +} + +static void ApplyScrollDeltasSinceBeginFrameTo(LayerImpl* layer) { + layer->ApplyScrollDeltasSinceBeginFrame(); +} + +void LayerTreeImpl::ApplyScrollDeltasSinceBeginFrame() { + DCHECK(IsPendingTree()); + if (!root_layer()) + return; + + LayerTreeHostCommon::CallFunctionForSubtree( + root_layer(), base::Bind(&ApplyScrollDeltasSinceBeginFrameTo)); +} + +void LayerTreeImpl::SetViewportLayersFromIds( + int page_scale_layer_id, + int inner_viewport_scroll_layer_id, + int outer_viewport_scroll_layer_id) { + page_scale_layer_ = LayerById(page_scale_layer_id); + DCHECK(page_scale_layer_); + + inner_viewport_scroll_layer_ = + LayerById(inner_viewport_scroll_layer_id); + DCHECK(inner_viewport_scroll_layer_); + + outer_viewport_scroll_layer_ = + LayerById(outer_viewport_scroll_layer_id); + DCHECK(outer_viewport_scroll_layer_ || + outer_viewport_scroll_layer_id == Layer::INVALID_ID); } +void LayerTreeImpl::ClearViewportLayers() { + page_scale_layer_ = NULL; + inner_viewport_scroll_layer_ = NULL; + outer_viewport_scroll_layer_ = NULL; +} + +// TODO(wjmaclean) This needs to go away, and be replaced with a single core +// of login that works for both scrollbar layer types. This is already planned +// as part of the larger pinch-zoom re-factoring viewport. void LayerTreeImpl::UpdateSolidColorScrollbars() { DCHECK(settings().solid_color_scrollbars); @@ -256,14 +306,17 @@ void LayerTreeImpl::UpdateSolidColorScrollbars() { ScrollableViewportSize()); float vertical_adjust = 0.0f; if (RootContainerLayer()) - vertical_adjust = layer_tree_host_impl_->VisibleViewportSize().height() - - RootContainerLayer()->bounds().height(); - if (ScrollbarLayerImpl* horiz = root_scroll->horizontal_scrollbar_layer()) { + vertical_adjust = + layer_tree_host_impl_->UnscaledScrollableViewportSize().height() - + RootContainerLayer()->bounds().height(); + if (ScrollbarLayerImplBase* horiz = + root_scroll->horizontal_scrollbar_layer()) { horiz->SetVerticalAdjust(vertical_adjust); horiz->SetVisibleToTotalLengthRatio( scrollable_viewport.width() / ScrollableSize().width()); } - if (ScrollbarLayerImpl* vertical = root_scroll->vertical_scrollbar_layer()) { + if (ScrollbarLayerImplBase* vertical = + root_scroll->vertical_scrollbar_layer()) { vertical->SetVerticalAdjust(vertical_adjust); vertical->SetVisibleToTotalLengthRatio( scrollable_viewport.height() / ScrollableSize().height()); @@ -297,13 +350,15 @@ void LayerTreeImpl::UpdateDrawProperties() { IsActiveTree(), "SourceFrameNumber", source_frame_number_); + LayerImpl* page_scale_layer = + page_scale_layer_ ? page_scale_layer_ : RootContainerLayer(); LayerTreeHostCommon::CalcDrawPropsImplInputs inputs( root_layer(), - layer_tree_host_impl_->DeviceViewport().size(), - layer_tree_host_impl_->DeviceTransform(), + DrawViewportSize(), + layer_tree_host_impl_->DrawTransform(), device_scale_factor(), total_page_scale_factor(), - root_scroll_layer_ ? root_scroll_layer_->parent() : NULL, + page_scale_layer, MaxTextureSize(), settings().can_use_lcd_text, settings().layer_transforms_should_scale_layer_contents, @@ -408,6 +463,10 @@ const RendererCapabilities& LayerTreeImpl::GetRendererCapabilities() const { return layer_tree_host_impl_->GetRendererCapabilities(); } +ContextProvider* LayerTreeImpl::context_provider() const { + return output_surface()->context_provider(); +} + OutputSurface* LayerTreeImpl::output_surface() const { return layer_tree_host_impl_->output_surface(); } @@ -432,6 +491,10 @@ MemoryHistory* LayerTreeImpl::memory_history() const { return layer_tree_host_impl_->memory_history(); } +bool LayerTreeImpl::device_viewport_valid_for_tile_management() const { + return layer_tree_host_impl_->device_viewport_valid_for_tile_management(); +} + bool LayerTreeImpl::IsActiveTree() const { return layer_tree_host_impl_->active_tree() == this; } @@ -482,6 +545,14 @@ void LayerTreeImpl::SetNeedsCommit() { layer_tree_host_impl_->SetNeedsCommit(); } +gfx::Size LayerTreeImpl::DrawViewportSize() const { + return layer_tree_host_impl_->DrawViewportSize(); +} + +void LayerTreeImpl::StartScrollbarAnimation() { + layer_tree_host_impl_->StartScrollbarAnimation(); +} + void LayerTreeImpl::SetNeedsRedraw() { layer_tree_host_impl_->SetNeedsRedraw(); } @@ -494,10 +565,6 @@ float LayerTreeImpl::device_scale_factor() const { return layer_tree_host_impl_->device_scale_factor(); } -gfx::Size LayerTreeImpl::device_viewport_size() const { - return layer_tree_host_impl_->device_viewport_size(); -} - DebugRectHistory* LayerTreeImpl::debug_rect_history() const { return layer_tree_host_impl_->debug_rect_history(); } @@ -571,8 +638,8 @@ void LayerTreeImpl::ClearLatencyInfo() { latency_info_.Clear(); } -void LayerTreeImpl::WillModifyTilePriorities() { - layer_tree_host_impl_->SetNeedsManageTiles(); +void LayerTreeImpl::DidModifyTilePriorities() { + layer_tree_host_impl_->DidModifyTilePriorities(); } void LayerTreeImpl::set_ui_resource_request_queue( @@ -590,18 +657,23 @@ void LayerTreeImpl::ProcessUIResourceRequestQueue() { UIResourceRequest req = ui_resource_request_queue_.front(); ui_resource_request_queue_.pop_front(); - switch (req.type) { + switch (req.GetType()) { case UIResourceRequest::UIResourceCreate: - layer_tree_host_impl_->CreateUIResource(req.id, req.bitmap); + layer_tree_host_impl_->CreateUIResource(req.GetId(), req.GetBitmap()); break; case UIResourceRequest::UIResourceDelete: - layer_tree_host_impl_->DeleteUIResource(req.id); + layer_tree_host_impl_->DeleteUIResource(req.GetId()); break; - default: + case UIResourceRequest::UIResourceInvalidRequest: NOTREACHED(); break; } } + + // If all UI resource evictions were not recreated by processing this queue, + // then another commit is required. + if (layer_tree_host_impl_->EvictedUIResourcesExist()) + layer_tree_host_impl_->SetNeedsCommit(); } void LayerTreeImpl::AddLayerWithCopyOutputRequest(LayerImpl* layer) { diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index 4f1afbe3cc7..2ae33df2ba2 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -14,7 +14,7 @@ #include "cc/layers/layer_impl.h" #include "cc/trees/layer_tree_host.h" #include "cc/resources/ui_resource_client.h" -#include "ui/base/latency_info.h" +#include "ui/events/latency_info.h" #if defined(COMPILER_GCC) namespace BASE_HASH_NAMESPACE { @@ -29,6 +29,7 @@ struct hash<cc::LayerImpl*> { namespace cc { +class ContextProvider; class DebugRectHistory; class FrameRateCounter; class HeadsUpDisplayLayerImpl; @@ -42,6 +43,7 @@ class PaintTimeCounter; class Proxy; class ResourceProvider; class TileManager; +class UIResourceRequest; struct RendererCapabilities; typedef std::list<UIResourceRequest> UIResourceRequestQueue; @@ -58,12 +60,14 @@ class CC_EXPORT LayerTreeImpl { // --------------------------------------------------------------------------- const LayerTreeSettings& settings() const; const RendererCapabilities& GetRendererCapabilities() const; + ContextProvider* context_provider() const; OutputSurface* output_surface() const; ResourceProvider* resource_provider() const; TileManager* tile_manager() const; FrameRateCounter* frame_rate_counter() const; PaintTimeCounter* paint_time_counter() const; MemoryHistory* memory_history() const; + bool device_viewport_valid_for_tile_management() const; bool IsActiveTree() const; bool IsPendingTree() const; bool IsRecycleTree() const; @@ -75,6 +79,8 @@ class CC_EXPORT LayerTreeImpl { base::Time CurrentFrameTime() const; base::TimeTicks CurrentPhysicalTimeTicks() const; void SetNeedsCommit(); + gfx::Size DrawViewportSize() const; + void StartScrollbarAnimation(); // Tree specific methods exposed to layer-impl tree. // --------------------------------------------------------------------------- @@ -84,7 +90,6 @@ class CC_EXPORT LayerTreeImpl { // trivial accessors in a followup patch. const LayerTreeDebugState& debug_state() const; float device_scale_factor() const; - gfx::Size device_viewport_size() const; DebugRectHistory* debug_rect_history() const; scoped_ptr<base::Value> AsValue() const; @@ -114,7 +119,12 @@ class CC_EXPORT LayerTreeImpl { void FindRootScrollLayer(); void UpdateMaxScrollOffset(); - void ApplySentScrollAndScaleDeltas(); + void SetViewportLayersFromIds(int page_scale_layer_id, + int inner_viewport_scroll_layer_id, + int outer_viewport_scroll_layer_id); + void ClearViewportLayers(); + void ApplySentScrollAndScaleDeltasFromAbortedCommit(); + void ApplyScrollDeltasSinceBeginFrame(); SkColor background_color() const { return background_color_; } void set_background_color(SkColor color) { background_color_ = color; } @@ -196,7 +206,7 @@ class CC_EXPORT LayerTreeImpl { const ui::LatencyInfo& GetLatencyInfo(); void ClearLatencyInfo(); - void WillModifyTilePriorities(); + void DidModifyTilePriorities(); ResourceProvider::ResourceId ResourceIdForUIResource(UIResourceId uid) const; void ProcessUIResourceRequestQueue(); @@ -222,6 +232,10 @@ class CC_EXPORT LayerTreeImpl { SkColor background_color_; bool has_transparent_background_; + LayerImpl* page_scale_layer_; + LayerImpl* inner_viewport_scroll_layer_; + LayerImpl* outer_viewport_scroll_layer_; + float page_scale_factor_; float page_scale_delta_; float sent_page_scale_delta_; diff --git a/chromium/cc/trees/layer_tree_settings.cc b/chromium/cc/trees/layer_tree_settings.cc index 62fd261ef82..8e8e35d5faa 100644 --- a/chromium/cc/trees/layer_tree_settings.cc +++ b/chromium/cc/trees/layer_tree_settings.cc @@ -17,6 +17,7 @@ LayerTreeSettings::LayerTreeSettings() allow_antialiasing(true), throttle_frame_production(true), begin_frame_scheduling_enabled(false), + deadline_scheduling_enabled(false), using_synchronous_renderer_compositor(false), per_tile_painting_enabled(false), partial_swap_enabled(false), @@ -26,15 +27,15 @@ LayerTreeSettings::LayerTreeSettings() show_overdraw_in_tracing(false), can_use_lcd_text(true), should_clear_root_render_pass(true), - use_linear_fade_scrollbar_animator(false), + scrollbar_animator(NoAnimator), scrollbar_linear_fade_delay_ms(300), scrollbar_linear_fade_length_ms(300), solid_color_scrollbars(false), solid_color_scrollbar_color(SK_ColorWHITE), - solid_color_scrollbar_thickness_dip(-1), calculate_top_controls_position(false), use_memory_management(true), timeout_and_draw_when_animation_checkerboards(true), + maximum_number_of_failed_draws_before_draw_is_forced_(3), layer_transforms_should_scale_layer_contents(false), minimum_contents_scale(0.0625f), low_res_contents_scale_factor(0.125f), @@ -56,8 +57,8 @@ LayerTreeSettings::LayerTreeSettings() force_direct_layer_drawing(false), strict_layer_property_change_checking(false), use_map_image(false), - compositor_name("ChromiumCompositor"), - ignore_root_layer_flings(false) { + ignore_root_layer_flings(false), + use_rgba_4444_textures(false) { // TODO(danakj): Renable surface caching when we can do it more realiably. // crbug.com/170713 cache_render_pass_contents = false; diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h index d3a71cf8ed7..aa8cdd9860f 100644 --- a/chromium/cc/trees/layer_tree_settings.h +++ b/chromium/cc/trees/layer_tree_settings.h @@ -5,8 +5,6 @@ #ifndef CC_TREES_LAYER_TREE_SETTINGS_H_ #define CC_TREES_LAYER_TREE_SETTINGS_H_ -#include <string> - #include "base/basictypes.h" #include "cc/base/cc_export.h" #include "cc/debug/layer_tree_debug_state.h" @@ -24,6 +22,7 @@ class CC_EXPORT LayerTreeSettings { bool allow_antialiasing; bool throttle_frame_production; bool begin_frame_scheduling_enabled; + bool deadline_scheduling_enabled; bool using_synchronous_renderer_compositor; bool per_tile_painting_enabled; bool partial_swap_enabled; @@ -33,15 +32,21 @@ class CC_EXPORT LayerTreeSettings { bool show_overdraw_in_tracing; bool can_use_lcd_text; bool should_clear_root_render_pass; - bool use_linear_fade_scrollbar_animator; + + enum ScrollbarAnimator { + NoAnimator, + LinearFade, + Thinning, + }; + ScrollbarAnimator scrollbar_animator; int scrollbar_linear_fade_delay_ms; int scrollbar_linear_fade_length_ms; bool solid_color_scrollbars; SkColor solid_color_scrollbar_color; - int solid_color_scrollbar_thickness_dip; bool calculate_top_controls_position; bool use_memory_management; bool timeout_and_draw_when_animation_checkerboards; + int maximum_number_of_failed_draws_before_draw_is_forced_; bool layer_transforms_should_scale_layer_contents; float minimum_contents_scale; float low_res_contents_scale_factor; @@ -62,8 +67,8 @@ class CC_EXPORT LayerTreeSettings { bool force_direct_layer_drawing; // With Skia GPU backend. bool strict_layer_property_change_checking; bool use_map_image; - std::string compositor_name; bool ignore_root_layer_flings; + bool use_rgba_4444_textures; LayerTreeDebugState initial_debug_state; }; diff --git a/chromium/cc/trees/proxy.cc b/chromium/cc/trees/proxy.cc index c8dacafb234..9315f31f56a 100644 --- a/chromium/cc/trees/proxy.cc +++ b/chromium/cc/trees/proxy.cc @@ -77,8 +77,8 @@ Proxy::~Proxy() { DCHECK(IsMainThread()); } -std::string Proxy::SchedulerStateAsStringForTesting() { - return ""; +scoped_ptr<base::Value> Proxy::SchedulerStateAsValueForTesting() { + return make_scoped_ptr(base::Value::CreateNullValue()); } } // namespace cc diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h index c5f9d555711..84844c89497 100644 --- a/chromium/cc/trees/proxy.h +++ b/chromium/cc/trees/proxy.h @@ -69,6 +69,7 @@ class CC_EXPORT Proxy { virtual void SetNeedsUpdateLayers() = 0; virtual void SetNeedsCommit() = 0; virtual void SetNeedsRedraw(gfx::Rect damage_rect) = 0; + virtual void SetNextCommitWaitsForActivation() = 0; virtual void NotifyInputThrottledUntilCommit() = 0; @@ -98,7 +99,7 @@ class CC_EXPORT Proxy { // Testing hooks virtual bool CommitPendingForTesting() = 0; - virtual std::string SchedulerStateAsStringForTesting(); + virtual scoped_ptr<base::Value> SchedulerStateAsValueForTesting(); protected: explicit Proxy( diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index 3a26a079ec6..85db94c6902 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -11,6 +11,7 @@ #include "cc/quads/draw_quad.h" #include "cc/resources/prioritized_resource_manager.h" #include "cc/resources/resource_update_controller.h" +#include "cc/trees/blocking_task_runner.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" @@ -69,10 +70,7 @@ bool SingleThreadProxy::CompositeAndReadback(void* pixels, gfx::Rect rect) { if (layer_tree_host_impl_->IsContextLost()) return false; - - layer_tree_host_impl_->SwapBuffers(frame); } - DidSwapFrame(); return true; } @@ -100,7 +98,7 @@ void SingleThreadProxy::SetVisible(bool visible) { layer_tree_host_impl_->SetVisible(visible); // Changing visibility could change ShouldComposite(). - layer_tree_host_impl_->UpdateBackgroundAnimateTicking(!ShouldComposite()); + UpdateBackgroundAnimateTicking(); } void SingleThreadProxy::CreateAndInitializeOutputSurface() { @@ -127,7 +125,7 @@ void SingleThreadProxy::CreateAndInitializeOutputSurface() { } { - DebugScopedSetMainThreadBlocked mainThreadBlocked(this); + DebugScopedSetMainThreadBlocked main_thread_blocked(this); DebugScopedSetImplThread impl(this); layer_tree_host_->DeleteContentsTexturesOnImplThread( layer_tree_host_impl_->resource_provider()); @@ -143,12 +141,13 @@ void SingleThreadProxy::CreateAndInitializeOutputSurface() { if (initialized) { renderer_capabilities_for_main_thread_ = layer_tree_host_impl_->GetRendererCapabilities(); - - layer_tree_host_impl_->resource_provider()-> - set_offscreen_context_provider(offscreen_context_provider); } else if (offscreen_context_provider.get()) { offscreen_context_provider->VerifyContexts(); + offscreen_context_provider = NULL; } + + layer_tree_host_impl_->SetOffscreenContextProvider( + offscreen_context_provider); } OnOutputSurfaceInitializeAttempted(initialized); @@ -183,9 +182,14 @@ void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) { DCHECK(Proxy::IsMainThread()); // Commit immediately. { - DebugScopedSetMainThreadBlocked mainThreadBlocked(this); + DebugScopedSetMainThreadBlocked main_thread_blocked(this); DebugScopedSetImplThread impl(this); + // This CapturePostTasks should be destroyed before CommitComplete() is + // called since that goes out to the embedder, and we want the embedder + // to receive its callbacks before that. + BlockingTaskRunner::CapturePostTasks blocked; + RenderingStatsInstrumentation* stats_instrumentation = layer_tree_host_->rendering_stats_instrumentation(); base::TimeTicks start_time = stats_instrumentation->StartRecording(); @@ -206,6 +210,9 @@ void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) { layer_tree_host_impl_->resource_provider()); update_controller->Finalize(); + if (layer_tree_host_impl_->EvictedUIResourcesExist()) + layer_tree_host_->RecreateUIResources(); + layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get()); layer_tree_host_impl_->CommitComplete(); @@ -221,6 +228,8 @@ void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) { base::TimeDelta duration = stats_instrumentation->EndRecording(start_time); stats_instrumentation->AddCommit(duration); + stats_instrumentation->IssueTraceEventForMainThreadStats(); + stats_instrumentation->AccumulateAndClearMainThreadStats(); } layer_tree_host_->CommitComplete(); next_frame_is_newly_committed_frame_ = true; @@ -235,9 +244,8 @@ void SingleThreadProxy::SetNeedsRedraw(gfx::Rect damage_rect) { SetNeedsRedrawRectOnImplThread(damage_rect); } -void SingleThreadProxy::OnHasPendingTreeStateChanged(bool have_pending_tree) { - // Thread-only feature. - NOTREACHED(); +void SingleThreadProxy::SetNextCommitWaitsForActivation() { + // There is no activation here other than commit. So do nothing. } void SingleThreadProxy::SetDeferCommits(bool defer_commits) { @@ -255,7 +263,7 @@ void SingleThreadProxy::Stop() { TRACE_EVENT0("cc", "SingleThreadProxy::stop"); DCHECK(Proxy::IsMainThread()); { - DebugScopedSetMainThreadBlocked mainThreadBlocked(this); + DebugScopedSetMainThreadBlocked main_thread_blocked(this); DebugScopedSetImplThread impl(this); layer_tree_host_->DeleteContentsTexturesOnImplThread( @@ -267,13 +275,23 @@ void SingleThreadProxy::Stop() { void SingleThreadProxy::OnCanDrawStateChanged(bool can_draw) { DCHECK(Proxy::IsImplThread()); - layer_tree_host_impl_->UpdateBackgroundAnimateTicking(!ShouldComposite()); + UpdateBackgroundAnimateTicking(); +} + +void SingleThreadProxy::NotifyReadyToActivate() { + // Thread-only feature. + NOTREACHED(); } void SingleThreadProxy::SetNeedsRedrawOnImplThread() { layer_tree_host_->ScheduleComposite(); } +void SingleThreadProxy::SetNeedsManageTilesOnImplThread() { + // Thread-only/Impl-side-painting-only feature. + NOTREACHED(); +} + void SingleThreadProxy::SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) { // TODO(brianderson): Once we move render_widget scheduling into this class, // we can treat redraw requests more efficiently than CommitAndRedraw @@ -332,13 +350,6 @@ void SingleThreadProxy::SendManagedMemoryStats() { bool SingleThreadProxy::IsInsideDraw() { return inside_draw_; } -void SingleThreadProxy::DidTryInitializeRendererOnImplThread( - bool success, - scoped_refptr<ContextProvider> offscreen_context_provider) { - NOTREACHED() - << "This is only used on threaded compositing with impl-side painting"; -} - void SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() { // Cause a commit so we can notice the lost context. SetNeedsCommitOnImplThread(); @@ -354,7 +365,21 @@ void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time) { device_viewport_damage_rect, false, // for_readback &frame)) { - layer_tree_host_impl_->SwapBuffers(frame); + { + DebugScopedSetMainThreadBlocked main_thread_blocked(this); + DebugScopedSetImplThread impl(this); + + // This CapturePostTasks should be destroyed before + // DidCommitAndDrawFrame() is called since that goes out to the embedder, + // and we want the embedder to receive its callbacks before that. + // NOTE: This maintains consistent ordering with the ThreadProxy since + // the DidCommitAndDrawFrame() must be post-tasked from the impl thread + // there as the main thread is not blocked, so any posted tasks inside + // the swap buffers will execute first. + BlockingTaskRunner::CapturePostTasks blocked; + + layer_tree_host_impl_->SwapBuffers(frame); + } DidSwapFrame(); } } @@ -407,12 +432,15 @@ bool SingleThreadProxy::CommitAndComposite( if (layer_tree_host_->contents_texture_manager()) { layer_tree_host_->contents_texture_manager() ->UnlinkAndClearEvictedBackings(); + layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( + layer_tree_host_impl_->memory_allocation_limit_bytes()); + layer_tree_host_->contents_texture_manager()->SetExternalPriorityCutoff( + layer_tree_host_impl_->memory_allocation_priority_cutoff()); } scoped_ptr<ResourceUpdateQueue> queue = make_scoped_ptr(new ResourceUpdateQueue); - layer_tree_host_->UpdateLayers( - queue.get(), layer_tree_host_impl_->memory_allocation_limit_bytes()); + layer_tree_host_->UpdateLayers(queue.get()); layer_tree_host_->WillCommit(); DoCommit(queue.Pass()); @@ -431,6 +459,12 @@ bool SingleThreadProxy::ShouldComposite() const { layer_tree_host_impl_->CanDraw(); } +void SingleThreadProxy::UpdateBackgroundAnimateTicking() { + DCHECK(Proxy::IsImplThread()); + layer_tree_host_impl_->UpdateBackgroundAnimateTicking( + !ShouldComposite() && layer_tree_host_impl_->active_tree()->root_layer()); +} + bool SingleThreadProxy::DoComposite( scoped_refptr<cc::ContextProvider> offscreen_context_provider, base::TimeTicks frame_begin_time, @@ -444,8 +478,8 @@ bool SingleThreadProxy::DoComposite( DebugScopedSetImplThread impl(this); base::AutoReset<bool> mark_inside(&inside_draw_, true); - layer_tree_host_impl_->resource_provider()-> - set_offscreen_context_provider(offscreen_context_provider); + layer_tree_host_impl_->SetOffscreenContextProvider( + offscreen_context_provider); bool can_do_readback = layer_tree_host_impl_->renderer()->CanReadPixels(); @@ -454,14 +488,14 @@ bool SingleThreadProxy::DoComposite( // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on // CanDraw() as well. if (!ShouldComposite() || (for_readback && !can_do_readback)) { - layer_tree_host_impl_->UpdateBackgroundAnimateTicking(true); + UpdateBackgroundAnimateTicking(); return false; } layer_tree_host_impl_->Animate( layer_tree_host_impl_->CurrentFrameTimeTicks(), layer_tree_host_impl_->CurrentFrameTime()); - layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false); + UpdateBackgroundAnimateTicking(); layer_tree_host_impl_->PrepareToDraw(frame, device_viewport_damage_rect); layer_tree_host_impl_->DrawLayers(frame, frame_begin_time); @@ -475,8 +509,8 @@ bool SingleThreadProxy::DoComposite( } if (lost_output_surface) { - cc::ContextProvider* offscreen_contexts = layer_tree_host_impl_-> - resource_provider()->offscreen_context_provider(); + cc::ContextProvider* offscreen_contexts = + layer_tree_host_impl_->offscreen_context_provider(); if (offscreen_contexts) offscreen_contexts->VerifyContexts(); layer_tree_host_->DidLoseOutputSurface(); diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h index 8e9438ccbef..474fb1f1101 100644 --- a/chromium/cc/trees/single_thread_proxy.h +++ b/chromium/cc/trees/single_thread_proxy.h @@ -35,6 +35,7 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient { virtual void SetNeedsUpdateLayers() OVERRIDE; virtual void SetNeedsCommit() OVERRIDE; virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE; + virtual void SetNextCommitWaitsForActivation() OVERRIDE; virtual void NotifyInputThrottledUntilCommit() OVERRIDE {} virtual void SetDeferCommits(bool defer_commits) OVERRIDE; virtual bool CommitRequested() const OVERRIDE; @@ -48,17 +49,15 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient { virtual bool CommitPendingForTesting() OVERRIDE; // LayerTreeHostImplClient implementation - virtual void DidTryInitializeRendererOnImplThread( - bool success, - scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE; - virtual void OnHasPendingTreeStateChanged(bool have_pending_tree) OVERRIDE; + virtual void NotifyReadyToActivate() OVERRIDE; virtual void SetNeedsRedrawOnImplThread() OVERRIDE; virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect dirty_rect) OVERRIDE; + virtual void SetNeedsManageTilesOnImplThread() OVERRIDE; virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE; virtual void SetNeedsCommitOnImplThread() OVERRIDE; virtual void PostAnimationEventsToMainThreadOnImplThread( @@ -96,6 +95,7 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient { void DidSwapFrame(); bool ShouldComposite() const; + void UpdateBackgroundAnimateTicking(); // Accessed on main thread only. LayerTreeHost* layer_tree_host_; diff --git a/chromium/cc/trees/thread_proxy.cc b/chromium/cc/trees/thread_proxy.cc index a0bd4bf5a7e..b5dff0e2bcb 100644 --- a/chromium/cc/trees/thread_proxy.cc +++ b/chromium/cc/trees/thread_proxy.cc @@ -18,6 +18,7 @@ #include "cc/scheduler/delay_based_time_source.h" #include "cc/scheduler/frame_rate_controller.h" #include "cc/scheduler/scheduler.h" +#include "cc/trees/blocking_task_runner.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" @@ -52,7 +53,7 @@ struct ThreadProxy::CommitPendingRequest { struct ThreadProxy::SchedulerStateRequest { CompletionEvent completion; - std::string state; + scoped_ptr<base::Value> state; }; scoped_ptr<Proxy> ThreadProxy::Create( @@ -75,6 +76,8 @@ ThreadProxy::ThreadProxy( textures_acquired_(true), in_composite_and_readback_(false), manage_tiles_pending_(false), + commit_waits_for_activation_(false), + inside_commit_(false), weak_factory_on_impl_thread_(this), weak_factory_(this), begin_frame_sent_to_main_thread_completion_event_on_impl_thread_(NULL), @@ -123,19 +126,27 @@ bool ThreadProxy::CompositeAndReadback(void* pixels, gfx::Rect rect) { return false; } - // Perform a synchronous commit. + // Perform a synchronous commit with an associated readback. + ReadbackRequest request; + request.rect = rect; + request.pixels = pixels; { DebugScopedSetMainThreadBlocked main_thread_blocked(this); CompletionEvent begin_frame_sent_to_main_thread_completion; - Proxy::ImplThreadTaskRunner()->PostTask( - FROM_HERE, - base::Bind(&ThreadProxy::ForceCommitOnImplThread, - impl_thread_weak_ptr_, - &begin_frame_sent_to_main_thread_completion)); + Proxy::ImplThreadTaskRunner() + ->PostTask(FROM_HERE, + base::Bind(&ThreadProxy::ForceCommitForReadbackOnImplThread, + impl_thread_weak_ptr_, + &begin_frame_sent_to_main_thread_completion, + &request)); begin_frame_sent_to_main_thread_completion.Wait(); } in_composite_and_readback_ = true; + // This is the forced commit. + // Note: The Impl thread also queues a separate BeginFrameOnMainThread on the + // main thread, which will be called after this CompositeAndReadback + // completes, to replace the forced commit. BeginFrameOnMainThread(scoped_ptr<BeginFrameAndCommitState>()); in_composite_and_readback_ = false; @@ -143,48 +154,35 @@ bool ThreadProxy::CompositeAndReadback(void* pixels, gfx::Rect rect) { // that it made. can_cancel_commit_ = false; - // Perform a synchronous readback. - ReadbackRequest request; - request.rect = rect; - request.pixels = pixels; - { - DebugScopedSetMainThreadBlocked main_thread_blocked(this); - Proxy::ImplThreadTaskRunner()->PostTask( - FROM_HERE, - base::Bind(&ThreadProxy::RequestReadbackOnImplThread, - impl_thread_weak_ptr_, - &request)); - request.completion.Wait(); - } + request.completion.Wait(); return request.success; } -void ThreadProxy::ForceCommitOnImplThread(CompletionEvent* completion) { - TRACE_EVENT0("cc", "ThreadProxy::ForceCommitOnImplThread"); +void ThreadProxy::ForceCommitForReadbackOnImplThread( + CompletionEvent* begin_frame_sent_completion, + ReadbackRequest* request) { + TRACE_EVENT0("cc", "ThreadProxy::ForceCommitForReadbackOnImplThread"); DCHECK(IsImplThread()); DCHECK(!begin_frame_sent_to_main_thread_completion_event_on_impl_thread_); - - scheduler_on_impl_thread_->SetNeedsForcedCommit(); - if (scheduler_on_impl_thread_->CommitPending()) { - completion->Signal(); - return; - } - - begin_frame_sent_to_main_thread_completion_event_on_impl_thread_ = completion; -} - -void ThreadProxy::RequestReadbackOnImplThread(ReadbackRequest* request) { - DCHECK(Proxy::IsImplThread()); DCHECK(!readback_request_on_impl_thread_); + if (!layer_tree_host_impl_) { + begin_frame_sent_completion->Signal(); request->success = false; request->completion.Signal(); return; } readback_request_on_impl_thread_ = request; - scheduler_on_impl_thread_->SetNeedsRedraw(); - scheduler_on_impl_thread_->SetNeedsForcedRedraw(); + + scheduler_on_impl_thread_->SetNeedsForcedCommitForReadback(); + if (scheduler_on_impl_thread_->CommitPending()) { + begin_frame_sent_completion->Signal(); + return; + } + + begin_frame_sent_to_main_thread_completion_event_on_impl_thread_ = + begin_frame_sent_completion; } void ThreadProxy::FinishAllRendering() { @@ -223,6 +221,7 @@ void ThreadProxy::SetLayerTreeHostClientReadyOnImplThread() { void ThreadProxy::SetVisible(bool visible) { TRACE_EVENT0("cc", "ThreadProxy::SetVisible"); DebugScopedSetMainThreadBlocked main_thread_blocked(this); + CompletionEvent completion; Proxy::ImplThreadTaskRunner()->PostTask( FROM_HERE, @@ -238,11 +237,16 @@ void ThreadProxy::SetVisibleOnImplThread(CompletionEvent* completion, TRACE_EVENT0("cc", "ThreadProxy::SetVisibleOnImplThread"); layer_tree_host_impl_->SetVisible(visible); scheduler_on_impl_thread_->SetVisible(visible); - layer_tree_host_impl_->UpdateBackgroundAnimateTicking( - !scheduler_on_impl_thread_->WillDrawIfNeeded()); + UpdateBackgroundAnimateTicking(); completion->Signal(); } +void ThreadProxy::UpdateBackgroundAnimateTicking() { + layer_tree_host_impl_->UpdateBackgroundAnimateTicking( + !scheduler_on_impl_thread_->WillDrawIfNeeded() && + layer_tree_host_impl_->active_tree()->root_layer()); +} + void ThreadProxy::DoCreateAndInitializeOutputSurface() { TRACE_EVENT0("cc", "ThreadProxy::DoCreateAndInitializeOutputSurface"); DCHECK(IsMainThread()); @@ -363,10 +367,7 @@ void ThreadProxy::SetNeedsCommit() { void ThreadProxy::DidLoseOutputSurfaceOnImplThread() { DCHECK(IsImplThread()); TRACE_EVENT0("cc", "ThreadProxy::DidLoseOutputSurfaceOnImplThread"); - Proxy::ImplThreadTaskRunner()->PostTask( - FROM_HERE, - base::Bind(&ThreadProxy::CheckOutputSurfaceStatusOnImplThread, - impl_thread_weak_ptr_)); + CheckOutputSurfaceStatusOnImplThread(); } void ThreadProxy::CheckOutputSurfaceStatusOnImplThread() { @@ -374,8 +375,8 @@ void ThreadProxy::CheckOutputSurfaceStatusOnImplThread() { TRACE_EVENT0("cc", "ThreadProxy::CheckOutputSurfaceStatusOnImplThread"); if (!layer_tree_host_impl_->IsContextLost()) return; - if (cc::ContextProvider* offscreen_contexts = layer_tree_host_impl_ - ->resource_provider()->offscreen_context_provider()) + if (cc::ContextProvider* offscreen_contexts = + layer_tree_host_impl_->offscreen_context_provider()) offscreen_contexts->VerifyContexts(); scheduler_on_impl_thread_->DidLoseOutputSurface(); } @@ -393,28 +394,35 @@ void ThreadProxy::SetNeedsBeginFrameOnImplThread(bool enable) { TRACE_EVENT1("cc", "ThreadProxy::SetNeedsBeginFrameOnImplThread", "enable", enable); layer_tree_host_impl_->SetNeedsBeginFrame(enable); + UpdateBackgroundAnimateTicking(); } void ThreadProxy::BeginFrameOnImplThread(const BeginFrameArgs& args) { DCHECK(IsImplThread()); TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnImplThread"); + + // Sample the frame time now. This time will be used for updating animations + // when we draw. + layer_tree_host_impl_->CurrentFrameTimeTicks(); + scheduler_on_impl_thread_->BeginFrame(args); } +void ThreadProxy::DidBeginFrameDeadlineOnImplThread() { + layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame(); +} + void ThreadProxy::OnCanDrawStateChanged(bool can_draw) { DCHECK(IsImplThread()); TRACE_EVENT1( "cc", "ThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw); scheduler_on_impl_thread_->SetCanDraw(can_draw); - layer_tree_host_impl_->UpdateBackgroundAnimateTicking( - !scheduler_on_impl_thread_->WillDrawIfNeeded()); + UpdateBackgroundAnimateTicking(); } -void ThreadProxy::OnHasPendingTreeStateChanged(bool has_pending_tree) { - DCHECK(IsImplThread()); - TRACE_EVENT1("cc", "ThreadProxy::OnHasPendingTreeStateChanged", - "has_pending_tree", has_pending_tree); - scheduler_on_impl_thread_->SetHasPendingTree(has_pending_tree); +void ThreadProxy::NotifyReadyToActivate() { + TRACE_EVENT0("cc", "ThreadProxy::NotifyReadyToActivate"); + scheduler_on_impl_thread_->NotifyReadyToActivate(); } void ThreadProxy::SetNeedsCommitOnImplThread() { @@ -502,6 +510,12 @@ void ThreadProxy::SetNeedsRedraw(gfx::Rect damage_rect) { damage_rect)); } +void ThreadProxy::SetNextCommitWaitsForActivation() { + DCHECK(IsMainThread()); + DCHECK(!inside_commit_); + commit_waits_for_activation_ = true; +} + void ThreadProxy::SetDeferCommits(bool defer_commits) { DCHECK(IsMainThread()); DCHECK_NE(defer_commits_, defer_commits); @@ -531,16 +545,29 @@ void ThreadProxy::SetNeedsRedrawOnImplThread() { scheduler_on_impl_thread_->SetNeedsRedraw(); } +void ThreadProxy::SetNeedsManageTilesOnImplThread() { + DCHECK(IsImplThread()); + TRACE_EVENT0("cc", "ThreadProxy::SetNeedsManageTilesOnImplThread"); + scheduler_on_impl_thread_->SetNeedsManageTiles(); +} + void ThreadProxy::SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) { DCHECK(IsImplThread()); layer_tree_host_impl_->SetViewportDamage(damage_rect); SetNeedsRedrawOnImplThread(); } -void ThreadProxy::DidSwapUseIncompleteTileOnImplThread() { +void ThreadProxy::SetSwapUsedIncompleteTileOnImplThread( + bool used_incomplete_tile) { DCHECK(IsImplThread()); - TRACE_EVENT0("cc", "ThreadProxy::DidSwapUseIncompleteTileOnImplThread"); - scheduler_on_impl_thread_->DidSwapUseIncompleteTile(); + if (used_incomplete_tile) { + TRACE_EVENT_INSTANT0( + "cc", + "ThreadProxy::SetSwapUsedIncompleteTileOnImplThread", + TRACE_EVENT_SCOPE_THREAD); + } + scheduler_on_impl_thread_->SetSwapUsedIncompleteTile( + used_incomplete_tile); } void ThreadProxy::DidInitializeVisibleTileOnImplThread() { @@ -584,6 +611,7 @@ void ThreadProxy::Start(scoped_ptr<OutputSurface> first_output_surface) { DCHECK(IsMainThread()); DCHECK(Proxy::HasImplThread()); DCHECK(first_output_surface); + // Create LayerTreeHostImpl. DebugScopedSetMainThreadBlocked main_thread_blocked(this); CompletionEvent completion; @@ -678,6 +706,10 @@ void ThreadProxy::ScheduledActionSendBeginFrameToMainThread() { } begin_frame_state->memory_allocation_limit_bytes = layer_tree_host_impl_->memory_allocation_limit_bytes(); + begin_frame_state->memory_allocation_priority_cutoff = + layer_tree_host_impl_->memory_allocation_priority_cutoff(); + begin_frame_state->evicted_ui_resources = + layer_tree_host_impl_->EvictedUIResourcesExist(); Proxy::MainThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&ThreadProxy::BeginFrameOnMainThread, @@ -695,6 +727,7 @@ void ThreadProxy::BeginFrameOnMainThread( scoped_ptr<BeginFrameAndCommitState> begin_frame_state) { TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnMainThread"); DCHECK(IsMainThread()); + if (!layer_tree_host_) return; @@ -749,8 +782,22 @@ void ThreadProxy::BeginFrameOnMainThread( if (layer_tree_host_->contents_texture_manager()) { layer_tree_host_->contents_texture_manager()-> UnlinkAndClearEvictedBackings(); + + if (begin_frame_state) { + layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes( + begin_frame_state->memory_allocation_limit_bytes); + layer_tree_host_->contents_texture_manager()->SetExternalPriorityCutoff( + begin_frame_state->memory_allocation_priority_cutoff); + } } + // Recreate all UI resources if there were evicted UI resources when the impl + // thread initiated the commit. + bool evicted_ui_resources = + begin_frame_state ? begin_frame_state->evicted_ui_resources : false; + if (evicted_ui_resources) + layer_tree_host_->RecreateUIResources(); + layer_tree_host_->Layout(); // Clear the commit flag after updating animations and layout here --- objects @@ -759,15 +806,15 @@ void ThreadProxy::BeginFrameOnMainThread( commit_requested_ = false; commit_request_sent_to_impl_thread_ = false; bool can_cancel_this_commit = - can_cancel_commit_ && !in_composite_and_readback_; + can_cancel_commit_ && + !in_composite_and_readback_ && + !evicted_ui_resources; can_cancel_commit_ = true; scoped_ptr<ResourceUpdateQueue> queue = make_scoped_ptr(new ResourceUpdateQueue); - bool updated = layer_tree_host_->UpdateLayers( - queue.get(), - begin_frame_state ? begin_frame_state->memory_allocation_limit_bytes - : 0u); + + bool updated = layer_tree_host_->UpdateLayers(queue.get()); // Once single buffered layers are committed, they cannot be modified until // they are drawn by the impl thread. @@ -820,6 +867,11 @@ void ThreadProxy::BeginFrameOnMainThread( DebugScopedSetMainThreadBlocked main_thread_blocked(this); + // This CapturePostTasks should be destroyed before CommitComplete() is + // called since that goes out to the embedder, and we want the embedder + // to receive its callbacks before that. + BlockingTaskRunner::CapturePostTasks blocked; + RenderingStatsInstrumentation* stats_instrumentation = layer_tree_host_->rendering_stats_instrumentation(); base::TimeTicks start_time = stats_instrumentation->StartRecording(); @@ -836,6 +888,8 @@ void ThreadProxy::BeginFrameOnMainThread( base::TimeDelta duration = stats_instrumentation->EndRecording(start_time); stats_instrumentation->AddCommit(duration); + stats_instrumentation->IssueTraceEventForMainThreadStats(); + stats_instrumentation->AccumulateAndClearMainThreadStats(); } layer_tree_host_->CommitComplete(); @@ -862,8 +916,8 @@ void ThreadProxy::StartCommitOnImplThread( if (offscreen_context_provider.get()) offscreen_context_provider->BindToCurrentThread(); - layer_tree_host_impl_->resource_provider()-> - set_offscreen_context_provider(offscreen_context_provider); + layer_tree_host_impl_->SetOffscreenContextProvider( + offscreen_context_provider); if (layer_tree_host_->contents_texture_manager()) { if (layer_tree_host_->contents_texture_manager()-> @@ -902,7 +956,8 @@ void ThreadProxy::BeginFrameAbortedByMainThreadOnImplThread(bool did_handle) { // by the main thread, so the active tree needs to be updated as if these sent // values were applied and committed. if (did_handle) { - layer_tree_host_impl_->active_tree()->ApplySentScrollAndScaleDeltas(); + layer_tree_host_impl_->active_tree() + ->ApplySentScrollAndScaleDeltasFromAbortedCommit(); layer_tree_host_impl_->active_tree()->ResetContentsTexturesPurged(); SetInputThrottledUntilCommitOnImplThread(false); } @@ -919,21 +974,21 @@ void ThreadProxy::ScheduledActionCommit() { current_resource_update_controller_on_impl_thread_->Finalize(); current_resource_update_controller_on_impl_thread_.reset(); + inside_commit_ = true; layer_tree_host_impl_->BeginCommit(); layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get()); layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get()); layer_tree_host_impl_->CommitComplete(); + inside_commit_ = false; SetInputThrottledUntilCommitOnImplThread(false); - layer_tree_host_impl_->UpdateBackgroundAnimateTicking( - !scheduler_on_impl_thread_->WillDrawIfNeeded()); + UpdateBackgroundAnimateTicking(); next_frame_is_newly_committed_frame_on_impl_thread_ = true; if (layer_tree_host_->settings().impl_side_painting && - layer_tree_host_->BlocksPendingCommit() && - layer_tree_host_impl_->pending_tree()) { + commit_waits_for_activation_) { // For some layer types in impl-side painting, the commit is held until // the pending tree is activated. It's also possible that the // pending tree has already activated if there was no work to be done. @@ -946,6 +1001,8 @@ void ThreadProxy::ScheduledActionCommit() { commit_completion_event_on_impl_thread_ = NULL; } + commit_waits_for_activation_ = false; + commit_complete_time_ = base::TimeTicks::HighResNow(); begin_frame_to_commit_duration_history_.InsertSample( commit_complete_time_ - begin_frame_sent_to_main_thread_time_); @@ -960,28 +1017,29 @@ void ThreadProxy::ScheduledActionUpdateVisibleTiles() { layer_tree_host_impl_->UpdateVisibleTiles(); } -void ThreadProxy::ScheduledActionActivatePendingTreeIfNeeded() { +void ThreadProxy::ScheduledActionActivatePendingTree() { DCHECK(IsImplThread()); - TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionActivatePendingTreeIfNeeded"); - layer_tree_host_impl_->ActivatePendingTreeIfNeeded(); + TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionActivatePendingTree"); + layer_tree_host_impl_->ActivatePendingTree(); } void ThreadProxy::ScheduledActionBeginOutputSurfaceCreation() { DCHECK(IsImplThread()); + TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionBeginOutputSurfaceCreation"); Proxy::MainThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&ThreadProxy::CreateAndInitializeOutputSurface, main_thread_weak_ptr_)); } -ScheduledActionDrawAndSwapResult -ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) { - TRACE_EVENT1( - "cc", "ThreadProxy::ScheduledActionDrawAndSwap", "forced", forced_draw); - - ScheduledActionDrawAndSwapResult result; +DrawSwapReadbackResult ThreadProxy::DrawSwapReadbackInternal( + bool forced_draw, + bool swap_requested, + bool readback_requested) { + DrawSwapReadbackResult result; result.did_draw = false; result.did_swap = false; + result.did_readback = false; DCHECK(IsImplThread()); DCHECK(layer_tree_host_impl_.get()); if (!layer_tree_host_impl_) @@ -991,22 +1049,19 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) { if (!layer_tree_host_impl_->renderer()) return result; + base::TimeTicks start_time = base::TimeTicks::HighResNow(); + base::TimeDelta draw_duration_estimate = DrawDurationEstimate(); + base::AutoReset<bool> mark_inside(&inside_draw_, true); + + // Advance our animations. base::TimeTicks monotonic_time = layer_tree_host_impl_->CurrentFrameTimeTicks(); base::Time wall_clock_time = layer_tree_host_impl_->CurrentFrameTime(); // TODO(enne): This should probably happen post-animate. - if (layer_tree_host_impl_->pending_tree()) { - layer_tree_host_impl_->ActivatePendingTreeIfNeeded(); - if (layer_tree_host_impl_->pending_tree()) - layer_tree_host_impl_->pending_tree()->UpdateDrawProperties(); - } + if (layer_tree_host_impl_->pending_tree()) + layer_tree_host_impl_->pending_tree()->UpdateDrawProperties(); layer_tree_host_impl_->Animate(monotonic_time, wall_clock_time); - layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false); - - base::TimeTicks start_time = base::TimeTicks::HighResNow(); - base::TimeDelta draw_duration_estimate = DrawDurationEstimate(); - base::AutoReset<bool> mark_inside(&inside_draw_, true); // This method is called on a forced draw, regardless of whether we are able // to produce a frame, as the calling site on main thread is blocked until its @@ -1019,12 +1074,12 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) { // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on // CanDraw() as well. - bool drawing_for_readback = !!readback_request_on_impl_thread_; + bool drawing_for_readback = + readback_requested && !!readback_request_on_impl_thread_; bool can_do_readback = layer_tree_host_impl_->renderer()->CanReadPixels(); LayerTreeHostImpl::FrameData frame; bool draw_frame = false; - bool start_ready_animations = true; if (layer_tree_host_impl_->CanDraw() && (!drawing_for_readback || can_do_readback)) { @@ -1033,13 +1088,9 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) { if (drawing_for_readback) readback_rect = readback_request_on_impl_thread_->rect; - // Do not start animations if we skip drawing the frame to avoid - // checkerboarding. if (layer_tree_host_impl_->PrepareToDraw(&frame, readback_rect) || forced_draw) draw_frame = true; - else - start_ready_animations = false; } if (draw_frame) { @@ -1050,24 +1101,30 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) { } layer_tree_host_impl_->DidDrawAllLayers(frame); + bool start_ready_animations = draw_frame; layer_tree_host_impl_->UpdateAnimationState(start_ready_animations); // Check for a pending CompositeAndReadback. - if (readback_request_on_impl_thread_) { - readback_request_on_impl_thread_->success = false; - if (draw_frame) { + if (drawing_for_readback) { + DCHECK(!swap_requested); + result.did_readback = false; + if (draw_frame && !layer_tree_host_impl_->IsContextLost()) { layer_tree_host_impl_->Readback(readback_request_on_impl_thread_->pixels, readback_request_on_impl_thread_->rect); - readback_request_on_impl_thread_->success = - !layer_tree_host_impl_->IsContextLost(); + result.did_readback = true; } + readback_request_on_impl_thread_->success = result.did_readback; readback_request_on_impl_thread_->completion.Signal(); readback_request_on_impl_thread_ = NULL; } else if (draw_frame) { + DCHECK(swap_requested); result.did_swap = layer_tree_host_impl_->SwapBuffers(frame); - if (frame.contains_incomplete_tile) - DidSwapUseIncompleteTileOnImplThread(); + // We don't know if we have incomplete tiles if we didn't actually swap. + if (result.did_swap) { + DCHECK(!frame.has_no_damage); + SetSwapUsedIncompleteTileOnImplThread(frame.contains_incomplete_tile); + } } // Tell the main thread that the the newly-commited frame was drawn. @@ -1106,12 +1163,6 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) { 50); } - // Update the tile state after drawing. This prevents manage tiles from - // being in the critical path for getting things on screen, but still - // makes sure that tile state is updated on a semi-regular basis. - if (layer_tree_host_impl_->settings().impl_side_painting) - layer_tree_host_impl_->ManageTiles(); - return result; } @@ -1156,21 +1207,43 @@ void ThreadProxy::ScheduledActionAcquireLayerTexturesForMainThread() { texture_acquisition_completion_event_on_impl_thread_ = NULL; } -ScheduledActionDrawAndSwapResult -ThreadProxy::ScheduledActionDrawAndSwapIfPossible() { - return ScheduledActionDrawAndSwapInternal(false); +void ThreadProxy::ScheduledActionManageTiles() { + TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionManageTiles"); + DCHECK(layer_tree_host_impl_->settings().impl_side_painting); + layer_tree_host_impl_->ManageTiles(); +} + +DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndSwapIfPossible() { + TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionDrawAndSwap"); + bool forced_draw = false; + bool swap_requested = true; + bool readback_requested = false; + return DrawSwapReadbackInternal( + forced_draw, swap_requested, readback_requested); +} + +DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndSwapForced() { + TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionDrawAndSwapForced"); + bool forced_draw = true; + bool swap_requested = true; + bool readback_requested = false; + return DrawSwapReadbackInternal( + forced_draw, swap_requested, readback_requested); } -ScheduledActionDrawAndSwapResult -ThreadProxy::ScheduledActionDrawAndSwapForced() { - return ScheduledActionDrawAndSwapInternal(true); +DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndReadback() { + TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionDrawAndReadback"); + bool forced_draw = true; + bool swap_requested = false; + bool readback_requested = true; + return DrawSwapReadbackInternal( + forced_draw, swap_requested, readback_requested); } void ThreadProxy::DidAnticipatedDrawTimeChange(base::TimeTicks time) { if (current_resource_update_controller_on_impl_thread_) - current_resource_update_controller_on_impl_thread_ - ->PerformMoreUpdates(time); - layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame(); + current_resource_update_controller_on_impl_thread_->PerformMoreUpdates( + time); } base::TimeDelta ThreadProxy::DrawDurationEstimate() { @@ -1191,6 +1264,14 @@ base::TimeDelta ThreadProxy::CommitToActivateDurationEstimate() { kCommitAndActivationDurationEstimationPercentile); } +void ThreadProxy::PostBeginFrameDeadline(const base::Closure& closure, + base::TimeTicks deadline) { + base::TimeDelta delta = deadline - base::TimeTicks::Now(); + if (delta <= base::TimeDelta()) + delta = base::TimeDelta(); + Proxy::ImplThreadTaskRunner()->PostDelayedTask(FROM_HERE, closure, delta); +} + void ThreadProxy::ReadyToFinalizeTextureUpdates() { DCHECK(IsImplThread()); scheduler_on_impl_thread_->FinishCommit(); @@ -1261,9 +1342,13 @@ void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) { layer_tree_host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this); const LayerTreeSettings& settings = layer_tree_host_->settings(); SchedulerSettings scheduler_settings; + scheduler_settings.deadline_scheduling_enabled = + settings.deadline_scheduling_enabled; scheduler_settings.impl_side_painting = settings.impl_side_painting; scheduler_settings.timeout_and_draw_when_animation_checkerboards = settings.timeout_and_draw_when_animation_checkerboards; + scheduler_settings.maximum_number_of_failed_draws_before_draw_is_forced_ = + settings.maximum_number_of_failed_draws_before_draw_is_forced_; scheduler_settings.using_synchronous_renderer_compositor = settings.using_synchronous_renderer_compositor; scheduler_settings.throttle_frame_production = @@ -1295,30 +1380,18 @@ void ThreadProxy::InitializeOutputSurfaceOnImplThread( if (*success) { *capabilities = layer_tree_host_impl_->GetRendererCapabilities(); scheduler_on_impl_thread_->DidCreateAndInitializeOutputSurface(); + } else if (offscreen_context_provider.get()) { + if (offscreen_context_provider->BindToCurrentThread()) + offscreen_context_provider->VerifyContexts(); + offscreen_context_provider = NULL; } - DidTryInitializeRendererOnImplThread(*success, offscreen_context_provider); + layer_tree_host_impl_->SetOffscreenContextProvider( + offscreen_context_provider); completion->Signal(); } -void ThreadProxy::DidTryInitializeRendererOnImplThread( - bool success, - scoped_refptr<ContextProvider> offscreen_context_provider) { - DCHECK(IsImplThread()); - DCHECK(!inside_draw_); - - if (offscreen_context_provider.get()) - offscreen_context_provider->BindToCurrentThread(); - - if (success) { - layer_tree_host_impl_->resource_provider()-> - set_offscreen_context_provider(offscreen_context_provider); - } else if (offscreen_context_provider.get()) { - offscreen_context_provider->VerifyContexts(); - } -} - void ThreadProxy::FinishGLOnImplThread(CompletionEvent* completion) { TRACE_EVENT0("cc", "ThreadProxy::FinishGLOnImplThread"); DCHECK(IsImplThread()); @@ -1332,6 +1405,7 @@ void ThreadProxy::LayerTreeHostClosedOnImplThread(CompletionEvent* completion) { DCHECK(IsImplThread()); layer_tree_host_->DeleteContentsTexturesOnImplThread( layer_tree_host_impl_->resource_provider()); + current_resource_update_controller_on_impl_thread_.reset(); layer_tree_host_impl_->SetNeedsBeginFrame(false); scheduler_on_impl_thread_.reset(); layer_tree_host_impl_.reset(); @@ -1344,7 +1418,9 @@ size_t ThreadProxy::MaxPartialTextureUpdates() const { } ThreadProxy::BeginFrameAndCommitState::BeginFrameAndCommitState() - : memory_allocation_limit_bytes(0) {} + : memory_allocation_limit_bytes(0), + memory_allocation_priority_cutoff(0), + evicted_ui_resources(false) {} ThreadProxy::BeginFrameAndCommitState::~BeginFrameAndCommitState() {} @@ -1398,27 +1474,27 @@ void ThreadProxy::CommitPendingOnImplThreadForTesting( request->completion.Signal(); } -std::string ThreadProxy::SchedulerStateAsStringForTesting() { +scoped_ptr<base::Value> ThreadProxy::SchedulerStateAsValueForTesting() { if (IsImplThread()) - return scheduler_on_impl_thread_->StateAsStringForTesting(); + return scheduler_on_impl_thread_->StateAsValue().Pass(); SchedulerStateRequest scheduler_state_request; { DebugScopedSetMainThreadBlocked main_thread_blocked(this); Proxy::ImplThreadTaskRunner()->PostTask( FROM_HERE, - base::Bind(&ThreadProxy::SchedulerStateAsStringOnImplThreadForTesting, + base::Bind(&ThreadProxy::SchedulerStateAsValueOnImplThreadForTesting, impl_thread_weak_ptr_, &scheduler_state_request)); scheduler_state_request.completion.Wait(); } - return scheduler_state_request.state; + return scheduler_state_request.state.Pass(); } -void ThreadProxy::SchedulerStateAsStringOnImplThreadForTesting( +void ThreadProxy::SchedulerStateAsValueOnImplThreadForTesting( SchedulerStateRequest* request) { DCHECK(IsImplThread()); - request->state = scheduler_on_impl_thread_->StateAsStringForTesting(); + request->state = scheduler_on_impl_thread_->StateAsValue(); request->completion.Signal(); } @@ -1449,10 +1525,13 @@ void ThreadProxy::RenewTreePriority() { // evicted resources or there is an invalid viewport size. if (layer_tree_host_impl_->active_tree()->ContentsTexturesPurged() || layer_tree_host_impl_->active_tree()->ViewportSizeInvalid() || + layer_tree_host_impl_->EvictedUIResourcesExist() || input_throttled_until_commit_) priority = NEW_CONTENT_TAKES_PRIORITY; layer_tree_host_impl_->SetTreePriority(priority); + scheduler_on_impl_thread_->SetSmoothnessTakesPriority( + priority == SMOOTHNESS_TAKES_PRIORITY); // Notify the the client of this compositor via the output surface. // TODO(epenner): Route this to compositor-thread instead of output-surface @@ -1512,6 +1591,8 @@ void ThreadProxy::DidActivatePendingTree() { completion_event_for_commit_held_on_tree_activation_ = NULL; } + UpdateBackgroundAnimateTicking(); + commit_to_activate_duration_history_.InsertSample( base::TimeTicks::HighResNow() - commit_complete_time_); } diff --git a/chromium/cc/trees/thread_proxy.h b/chromium/cc/trees/thread_proxy.h index 1f5f29eec6c..78a59613d39 100644 --- a/chromium/cc/trees/thread_proxy.h +++ b/chromium/cc/trees/thread_proxy.h @@ -52,6 +52,7 @@ class ThreadProxy : public Proxy, virtual void SetNeedsUpdateLayers() OVERRIDE; virtual void SetNeedsCommit() OVERRIDE; virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE; + virtual void SetNextCommitWaitsForActivation() OVERRIDE; virtual void NotifyInputThrottledUntilCommit() OVERRIDE; virtual void SetDeferCommits(bool defer_commits) OVERRIDE; virtual bool CommitRequested() const OVERRIDE; @@ -63,19 +64,18 @@ class ThreadProxy : public Proxy, virtual void ForceSerializeOnSwapBuffers() OVERRIDE; virtual scoped_ptr<base::Value> AsValue() const OVERRIDE; virtual bool CommitPendingForTesting() OVERRIDE; - virtual std::string SchedulerStateAsStringForTesting() OVERRIDE; + virtual scoped_ptr<base::Value> SchedulerStateAsValueForTesting() OVERRIDE; // LayerTreeHostImplClient implementation - virtual void DidTryInitializeRendererOnImplThread( - bool success, - scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE; virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) OVERRIDE; + virtual void DidBeginFrameDeadlineOnImplThread() OVERRIDE; virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE; - virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE; + virtual void NotifyReadyToActivate() OVERRIDE; virtual void SetNeedsRedrawOnImplThread() OVERRIDE; virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect dirty_rect) OVERRIDE; + virtual void SetNeedsManageTilesOnImplThread() OVERRIDE; virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE; virtual void SetNeedsCommitOnImplThread() OVERRIDE; virtual void PostAnimationEventsToMainThreadOnImplThread( @@ -95,19 +95,22 @@ class ThreadProxy : public Proxy, // SchedulerClient implementation virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE; virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE; - virtual ScheduledActionDrawAndSwapResult - ScheduledActionDrawAndSwapIfPossible() OVERRIDE; - virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced() + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible() OVERRIDE; + virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE; + virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() OVERRIDE; virtual void ScheduledActionCommit() OVERRIDE; virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE; - virtual void ScheduledActionActivatePendingTreeIfNeeded() OVERRIDE; + virtual void ScheduledActionActivatePendingTree() OVERRIDE; virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE; virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE; + virtual void ScheduledActionManageTiles() OVERRIDE; virtual void DidAnticipatedDrawTimeChange(base::TimeTicks time) OVERRIDE; virtual base::TimeDelta DrawDurationEstimate() OVERRIDE; virtual base::TimeDelta BeginFrameToCommitDurationEstimate() OVERRIDE; virtual base::TimeDelta CommitToActivateDurationEstimate() OVERRIDE; + virtual void PostBeginFrameDeadline(const base::Closure& closure, + base::TimeTicks deadline) OVERRIDE; // ResourceUpdateControllerClient implementation virtual void ReadyToFinalizeTextureUpdates() OVERRIDE; @@ -123,6 +126,8 @@ class ThreadProxy : public Proxy, base::TimeTicks monotonic_frame_begin_time; scoped_ptr<ScrollAndScaleSet> scroll_info; size_t memory_allocation_limit_bytes; + int memory_allocation_priority_cutoff; + bool evicted_ui_resources; }; // Called on main thread. @@ -144,7 +149,9 @@ class ThreadProxy : public Proxy, struct CommitPendingRequest; struct SchedulerStateRequest; - void ForceCommitOnImplThread(CompletionEvent* completion); + void ForceCommitForReadbackOnImplThread( + CompletionEvent* begin_frame_sent_completion, + ReadbackRequest* request); void StartCommitOnImplThread( CompletionEvent* completion, ResourceUpdateQueue* queue, @@ -155,6 +162,7 @@ class ThreadProxy : public Proxy, void InitializeImplOnImplThread(CompletionEvent* completion); void SetLayerTreeHostClientReadyOnImplThread(); void SetVisibleOnImplThread(CompletionEvent* completion, bool visible); + void UpdateBackgroundAnimateTicking(); void HasInitializedOutputSurfaceOnImplThread( CompletionEvent* completion, bool* has_initialized_output_surface); @@ -168,17 +176,18 @@ class ThreadProxy : public Proxy, void LayerTreeHostClosedOnImplThread(CompletionEvent* completion); void AcquireLayerTexturesForMainThreadOnImplThread( CompletionEvent* completion); - ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapInternal( - bool forced_draw); + DrawSwapReadbackResult DrawSwapReadbackInternal(bool forced_draw, + bool swap_requested, + bool readback_requested); void ForceSerializeOnSwapBuffersOnImplThread(CompletionEvent* completion); void CheckOutputSurfaceStatusOnImplThread(); void CommitPendingOnImplThreadForTesting(CommitPendingRequest* request); - void SchedulerStateAsStringOnImplThreadForTesting( + void SchedulerStateAsValueOnImplThreadForTesting( SchedulerStateRequest* request); void AsValueOnImplThread(CompletionEvent* completion, base::DictionaryValue* state) const; void RenewTreePriorityOnImplThread(); - void DidSwapUseIncompleteTileOnImplThread(); + void SetSwapUsedIncompleteTileOnImplThread(bool used_incomplete_tile); void StartScrollbarAnimationOnImplThread(); void MainThreadHasStoppedFlingingOnImplThread(); void SetInputThrottledUntilCommitOnImplThread(bool is_throttled); @@ -206,6 +215,10 @@ class ThreadProxy : public Proxy, // anything else. scoped_ptr<OutputSurface> first_output_surface_; + // Accessed on the main thread, or when main thread is blocked. + bool commit_waits_for_activation_; + bool inside_commit_; + base::WeakPtrFactory<ThreadProxy> weak_factory_on_impl_thread_; base::WeakPtr<ThreadProxy> main_thread_weak_ptr_; diff --git a/chromium/cc/trees/tree_synchronizer.cc b/chromium/cc/trees/tree_synchronizer.cc index f307257ff05..48a9d5bd966 100644 --- a/chromium/cc/trees/tree_synchronizer.cc +++ b/chromium/cc/trees/tree_synchronizer.cc @@ -5,18 +5,19 @@ #include "cc/trees/tree_synchronizer.h" #include "base/containers/hash_tables.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "base/debug/trace_event.h" #include "base/logging.h" #include "cc/animation/scrollbar_animation_controller.h" #include "cc/input/scrollbar.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" -#include "cc/layers/scrollbar_layer.h" -#include "cc/layers/scrollbar_layer_impl.h" +#include "cc/layers/scrollbar_layer_impl_base.h" +#include "cc/layers/scrollbar_layer_interface.h" namespace cc { -typedef ScopedPtrHashMap<int, LayerImpl> ScopedPtrLayerImplMap; +typedef base::ScopedPtrHashMap<int, LayerImpl> ScopedPtrLayerImplMap; typedef base::hash_map<int, LayerImpl*> RawPtrLayerImplMap; void CollectExistingLayerImplRecursive(ScopedPtrLayerImplMap* old_layers, @@ -154,18 +155,19 @@ void UpdateScrollbarLayerPointersRecursiveInternal( return; RawPtrLayerImplMap::const_iterator iter = - new_layers->find(scrollbar_layer->id()); - ScrollbarLayerImpl* scrollbar_layer_impl = - iter != new_layers->end() ? static_cast<ScrollbarLayerImpl*>(iter->second) - : NULL; - iter = new_layers->find(scrollbar_layer->scroll_layer_id()); + new_layers->find(layer->id()); + ScrollbarLayerImplBase* scrollbar_layer_impl = + iter != new_layers->end() + ? static_cast<ScrollbarLayerImplBase*>(iter->second) + : NULL; + iter = new_layers->find(scrollbar_layer->ScrollLayerId()); LayerImpl* scroll_layer_impl = iter != new_layers->end() ? iter->second : NULL; DCHECK(scrollbar_layer_impl); DCHECK(scroll_layer_impl); - if (scrollbar_layer->Orientation() == HORIZONTAL) + if (scrollbar_layer->orientation() == HORIZONTAL) scroll_layer_impl->SetHorizontalScrollbarLayer(scrollbar_layer_impl); else scroll_layer_impl->SetVerticalScrollbarLayer(scrollbar_layer_impl); @@ -173,14 +175,15 @@ void UpdateScrollbarLayerPointersRecursiveInternal( void UpdateScrollbarLayerPointersRecursive(const RawPtrLayerImplMap* new_layers, Layer* layer) { - UpdateScrollbarLayerPointersRecursiveInternal<Layer, ScrollbarLayer>( + UpdateScrollbarLayerPointersRecursiveInternal<Layer, ScrollbarLayerInterface>( new_layers, layer); } void UpdateScrollbarLayerPointersRecursive(const RawPtrLayerImplMap* new_layers, LayerImpl* layer) { - UpdateScrollbarLayerPointersRecursiveInternal<LayerImpl, ScrollbarLayerImpl>( - new_layers, layer); + UpdateScrollbarLayerPointersRecursiveInternal< + LayerImpl, + ScrollbarLayerImplBase>(new_layers, layer); } // static diff --git a/chromium/cc/trees/tree_synchronizer.h b/chromium/cc/trees/tree_synchronizer.h index 40fa69d2b57..de48cee26f0 100644 --- a/chromium/cc/trees/tree_synchronizer.h +++ b/chromium/cc/trees/tree_synchronizer.h @@ -8,7 +8,6 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" -#include "cc/base/scoped_ptr_hash_map.h" namespace cc { diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc index f1b6d32df27..4e0c89fca5c 100644 --- a/chromium/cc/trees/tree_synchronizer_unittest.cc +++ b/chromium/cc/trees/tree_synchronizer_unittest.cc @@ -5,6 +5,7 @@ #include "cc/trees/tree_synchronizer.h" #include <algorithm> +#include <set> #include <vector> #include "cc/animation/layer_animation_controller.h" @@ -124,6 +125,56 @@ void ExpectTreesAreIdentical(Layer* layer, ASSERT_EQ(layer_children.size(), layer_impl_children.size()); + const std::set<Layer*>* layer_scroll_children = layer->scroll_children(); + const std::set<LayerImpl*>* layer_impl_scroll_children = + layer_impl->scroll_children(); + + ASSERT_EQ(!!layer_scroll_children, !!layer_impl_scroll_children); + + if (layer_scroll_children) { + ASSERT_EQ( + layer_scroll_children->size(), + layer_impl_scroll_children->size()); + } + + const Layer* layer_scroll_parent = layer->scroll_parent(); + const LayerImpl* layer_impl_scroll_parent = layer_impl->scroll_parent(); + + ASSERT_EQ(!!layer_scroll_parent, !!layer_impl_scroll_parent); + + if (layer_scroll_parent) { + ASSERT_EQ(layer_scroll_parent->id(), layer_impl_scroll_parent->id()); + ASSERT_TRUE(layer_scroll_parent->scroll_children()->find(layer) != + layer_scroll_parent->scroll_children()->end()); + ASSERT_TRUE(layer_impl_scroll_parent->scroll_children()->find(layer_impl) != + layer_impl_scroll_parent->scroll_children()->end()); + } + + const std::set<Layer*>* layer_clip_children = layer->clip_children(); + const std::set<LayerImpl*>* layer_impl_clip_children = + layer_impl->clip_children(); + + ASSERT_EQ(!!layer_clip_children, !!layer_impl_clip_children); + + if (layer_clip_children) + ASSERT_EQ(layer_clip_children->size(), layer_impl_clip_children->size()); + + const Layer* layer_clip_parent = layer->clip_parent(); + const LayerImpl* layer_impl_clip_parent = layer_impl->clip_parent(); + + ASSERT_EQ(!!layer_clip_parent, !!layer_impl_clip_parent); + + if (layer_clip_parent) { + const std::set<LayerImpl*>* clip_children_impl = + layer_impl_clip_parent->clip_children(); + const std::set<Layer*>* clip_children = + layer_clip_parent->clip_children(); + ASSERT_EQ(layer_clip_parent->id(), layer_impl_clip_parent->id()); + ASSERT_TRUE(clip_children->find(layer) != clip_children->end()); + ASSERT_TRUE(clip_children_impl->find(layer_impl) != + clip_children_impl->end()); + } + for (size_t i = 0; i < layer_children.size(); ++i) { ExpectTreesAreIdentical( layer_children[i].get(), layer_impl_children[i], tree_impl); @@ -531,5 +582,167 @@ TEST_F(TreeSynchronizerTest, SynchronizeAnimations) { layer_tree_root->layer_animation_controller())->SynchronizedAnimations()); } +TEST_F(TreeSynchronizerTest, SynchronizeScrollParent) { + LayerTreeSettings settings; + FakeProxy proxy; + DebugScopedSetImplThread impl(&proxy); + FakeRenderingStatsInstrumentation stats_instrumentation; + scoped_ptr<LayerTreeHostImpl> host_impl = + LayerTreeHostImpl::Create(settings, + NULL, + &proxy, + &stats_instrumentation); + + scoped_refptr<Layer> layer_tree_root = Layer::Create(); + scoped_refptr<Layer> scroll_parent = Layer::Create(); + layer_tree_root->AddChild(scroll_parent); + layer_tree_root->AddChild(Layer::Create()); + layer_tree_root->AddChild(Layer::Create()); + + host_->SetRootLayer(layer_tree_root); + + // First child is the second and third child's scroll parent. + layer_tree_root->children()[1]->SetScrollParent(scroll_parent); + layer_tree_root->children()[2]->SetScrollParent(scroll_parent); + + scoped_ptr<LayerImpl> layer_impl_tree_root = + TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), + scoped_ptr<LayerImpl>(), + host_impl->active_tree()); + TreeSynchronizer::PushProperties(layer_tree_root.get(), + layer_impl_tree_root.get()); + ExpectTreesAreIdentical(layer_tree_root.get(), + layer_impl_tree_root.get(), + host_impl->active_tree()); + + // Remove the first scroll child. + layer_tree_root->children()[1]->RemoveFromParent(); + layer_impl_tree_root = + TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), + layer_impl_tree_root.Pass(), + host_impl->active_tree()); + TreeSynchronizer::PushProperties(layer_tree_root.get(), + layer_impl_tree_root.get()); + ExpectTreesAreIdentical(layer_tree_root.get(), + layer_impl_tree_root.get(), + host_impl->active_tree()); + + // Add an additional scroll layer. + scoped_refptr<Layer> additional_scroll_child = Layer::Create(); + layer_tree_root->AddChild(additional_scroll_child); + additional_scroll_child->SetScrollParent(scroll_parent); + layer_impl_tree_root = + TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), + layer_impl_tree_root.Pass(), + host_impl->active_tree()); + TreeSynchronizer::PushProperties(layer_tree_root.get(), + layer_impl_tree_root.get()); + ExpectTreesAreIdentical(layer_tree_root.get(), + layer_impl_tree_root.get(), + host_impl->active_tree()); + + // Remove the scroll parent. + scroll_parent->RemoveFromParent(); + scroll_parent = NULL; + layer_impl_tree_root = + TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), + layer_impl_tree_root.Pass(), + host_impl->active_tree()); + TreeSynchronizer::PushProperties(layer_tree_root.get(), + layer_impl_tree_root.get()); + ExpectTreesAreIdentical(layer_tree_root.get(), + layer_impl_tree_root.get(), + host_impl->active_tree()); + + // The scroll children should have been unhooked. + EXPECT_EQ(2u, layer_tree_root->children().size()); + EXPECT_FALSE(layer_tree_root->children()[0]->scroll_parent()); + EXPECT_FALSE(layer_tree_root->children()[1]->scroll_parent()); +} + +TEST_F(TreeSynchronizerTest, SynchronizeClipParent) { + LayerTreeSettings settings; + FakeProxy proxy; + DebugScopedSetImplThread impl(&proxy); + FakeRenderingStatsInstrumentation stats_instrumentation; + scoped_ptr<LayerTreeHostImpl> host_impl = + LayerTreeHostImpl::Create(settings, + NULL, + &proxy, + &stats_instrumentation); + + scoped_refptr<Layer> layer_tree_root = Layer::Create(); + scoped_refptr<Layer> clip_parent = Layer::Create(); + scoped_refptr<Layer> intervening = Layer::Create(); + scoped_refptr<Layer> clip_child1 = Layer::Create(); + scoped_refptr<Layer> clip_child2 = Layer::Create(); + layer_tree_root->AddChild(clip_parent); + clip_parent->AddChild(intervening); + intervening->AddChild(clip_child1); + intervening->AddChild(clip_child2); + + host_->SetRootLayer(layer_tree_root); + + // First child is the second and third child's scroll parent. + clip_child1->SetClipParent(clip_parent); + clip_child2->SetClipParent(clip_parent); + + scoped_ptr<LayerImpl> layer_impl_tree_root = + TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), + scoped_ptr<LayerImpl>(), + host_impl->active_tree()); + TreeSynchronizer::PushProperties(layer_tree_root.get(), + layer_impl_tree_root.get()); + ExpectTreesAreIdentical(layer_tree_root.get(), + layer_impl_tree_root.get(), + host_impl->active_tree()); + + // Remove the first clip child. + clip_child1->RemoveFromParent(); + clip_child1 = NULL; + + layer_impl_tree_root = + TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), + layer_impl_tree_root.Pass(), + host_impl->active_tree()); + TreeSynchronizer::PushProperties(layer_tree_root.get(), + layer_impl_tree_root.get()); + ExpectTreesAreIdentical(layer_tree_root.get(), + layer_impl_tree_root.get(), + host_impl->active_tree()); + + // Add an additional clip child. + scoped_refptr<Layer> additional_clip_child = Layer::Create(); + intervening->AddChild(additional_clip_child); + additional_clip_child->SetClipParent(clip_parent); + layer_impl_tree_root = + TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), + layer_impl_tree_root.Pass(), + host_impl->active_tree()); + TreeSynchronizer::PushProperties(layer_tree_root.get(), + layer_impl_tree_root.get()); + ExpectTreesAreIdentical(layer_tree_root.get(), + layer_impl_tree_root.get(), + host_impl->active_tree()); + + // Remove the nearest clipping ancestor. + clip_parent->RemoveFromParent(); + clip_parent = NULL; + layer_impl_tree_root = + TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), + layer_impl_tree_root.Pass(), + host_impl->active_tree()); + TreeSynchronizer::PushProperties(layer_tree_root.get(), + layer_impl_tree_root.get()); + ExpectTreesAreIdentical(layer_tree_root.get(), + layer_impl_tree_root.get(), + host_impl->active_tree()); + + // The clip children should have been unhooked. + EXPECT_EQ(2u, intervening->children().size()); + EXPECT_FALSE(clip_child2->clip_parent()); + EXPECT_FALSE(additional_clip_child->clip_parent()); +} + } // namespace } // namespace cc |