Commit 3039f172 authored by Xida Chen's avatar Xida Chen Committed by Commit Bot

Detect canvas invalidation during document lifecycle update

As discussed in this doc:
https://docs.google.com/document/d/1zCIs8zBE7SPIS_OTvTuLkLbEgSfh-RxF0cYSCNAm-Ko/edit?ts=5f4ea439#heading=h.7o3hpjjptc4v

We would apply some heuristic to detect js-driven animation, and
canvas invalidation is one of them. This CL detects that, it contains
a unit test to verify the correctness.

Bug: 1111392
Change-Id: I7f37077b1a0da3c85301bf6034fde96898a84534
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2393068
Commit-Queue: Xida Chen <xidachen@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808141}
parent 99559af3
...@@ -127,6 +127,12 @@ void AnimationHost::RemoveAnimationTimeline( ...@@ -127,6 +127,12 @@ void AnimationHost::RemoveAnimationTimeline(
SetNeedsPushProperties(); SetNeedsPushProperties();
} }
void AnimationHost::SetHasCanvasInvalidation(bool has_canvas_invalidation) {
// TODO(crbug.com/1111392): send this value to LayerTreeHostImpl for
// constructing a canvas frame sequence tracker.
has_canvas_invalidation_ = has_canvas_invalidation;
}
void AnimationHost::UpdateRegisteredElementIds(ElementListType changed_list) { void AnimationHost::UpdateRegisteredElementIds(ElementListType changed_list) {
for (auto map_entry : element_to_animations_map_) { for (auto map_entry : element_to_animations_map_) {
if (mutator_host_client()->IsElementInPropertyTrees(map_entry.first, if (mutator_host_client()->IsElementInPropertyTrees(map_entry.first,
......
...@@ -221,6 +221,8 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, ...@@ -221,6 +221,8 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost,
bool current_frame_had_raf, bool current_frame_had_raf,
bool next_frame_has_pending_raf); bool next_frame_has_pending_raf);
void SetHasCanvasInvalidation(bool has_canvas_invalidation);
private: private:
explicit AnimationHost(ThreadInstance thread_instance); explicit AnimationHost(ThreadInstance thread_instance);
...@@ -272,6 +274,7 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, ...@@ -272,6 +274,7 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost,
size_t main_thread_animations_count_ = 0; size_t main_thread_animations_count_ = 0;
bool current_frame_had_raf_ = false; bool current_frame_had_raf_ = false;
bool next_frame_has_pending_raf_ = false; bool next_frame_has_pending_raf_ = false;
bool has_canvas_invalidation_ = false;
PendingThroughputTrackerInfos pending_throughput_tracker_infos_; PendingThroughputTrackerInfos pending_throughput_tracker_infos_;
......
...@@ -2797,6 +2797,8 @@ void LocalFrameView::RunPaintLifecyclePhase() { ...@@ -2797,6 +2797,8 @@ void LocalFrameView::RunPaintLifecyclePhase() {
} }
} }
} }
if (GetPage())
GetPage()->Animator().ReportFrameAnimations(GetCompositorAnimationHost());
} }
bool LocalFrameView::RunAccessibilityLifecyclePhase( bool LocalFrameView::RunAccessibilityLifecyclePhase(
......
...@@ -471,6 +471,9 @@ bool HTMLCanvasElement::IsWebGLBlocked() const { ...@@ -471,6 +471,9 @@ bool HTMLCanvasElement::IsWebGLBlocked() const {
void HTMLCanvasElement::DidDraw(const FloatRect& rect) { void HTMLCanvasElement::DidDraw(const FloatRect& rect) {
if (rect.IsEmpty()) if (rect.IsEmpty())
return; return;
if (GetLayoutObject() && GetLayoutObject()->PreviousVisibilityVisible() &&
GetDocument().GetPage())
GetDocument().GetPage()->Animator().SetHasCanvasInvalidation(true);
canvas_is_clear_ = false; canvas_is_clear_ = false;
if (GetLayoutObject() && !LowLatencyEnabled()) if (GetLayoutObject() && !LowLatencyEnabled())
GetLayoutObject()->SetShouldCheckForPaintInvalidation(); GetLayoutObject()->SetShouldCheckForPaintInvalidation();
......
...@@ -9,7 +9,11 @@ ...@@ -9,7 +9,11 @@
namespace blink { namespace blink {
using HTMLCanvasElementTest = RenderingTest; class HTMLCanvasElementTest : public RenderingTest {
public:
HTMLCanvasElementTest()
: RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
};
TEST_F(HTMLCanvasElementTest, CreateLayerUpdatesCompositing) { TEST_F(HTMLCanvasElementTest, CreateLayerUpdatesCompositing) {
// Enable script so that the canvas will create a LayoutHTMLCanvas. // Enable script so that the canvas will create a LayoutHTMLCanvas.
...@@ -29,4 +33,86 @@ TEST_F(HTMLCanvasElementTest, CreateLayerUpdatesCompositing) { ...@@ -29,4 +33,86 @@ TEST_F(HTMLCanvasElementTest, CreateLayerUpdatesCompositing) {
EXPECT_EQ(CompositingReason::kCanvas, layer->DirectCompositingReasons()); EXPECT_EQ(CompositingReason::kCanvas, layer->DirectCompositingReasons());
} }
TEST_F(HTMLCanvasElementTest, CanvasInvalidation) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<canvas id='canvas' width='10px' height='10px'></canvas>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
)JS");
GetDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
RunDocumentLifecycle();
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
}
TEST_F(HTMLCanvasElementTest, CanvasNotInvalidatedOnFirstFrameInDOM) {
GetDocument().GetSettings()->SetScriptEnabled(true);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
)JS");
GetDocument().body()->appendChild(script);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
}
TEST_F(HTMLCanvasElementTest, CanvasNotInvalidatedOnFirstPaint) {
GetDocument().GetSettings()->SetScriptEnabled(true);
SetBodyInnerHTML("<canvas id='canvas' style='display:none'></canvas>");
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
RunDocumentLifecycle();
auto* script = GetDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var canvas = document.getElementById('canvas');
canvas.style.display = 'block';
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
)JS");
GetDocument().body()->appendChild(script);
EXPECT_FALSE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
}
TEST_F(HTMLCanvasElementTest, CanvasInvalidationInFrame) {
SetBodyInnerHTML(R"HTML(
<iframe id='iframe'></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<canvas id='canvas' width='10px' height='10px'></canvas>
)HTML");
GetDocument().GetSettings()->SetScriptEnabled(true);
ChildDocument().GetSettings()->SetScriptEnabled(true);
EXPECT_FALSE(
ChildDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
RunDocumentLifecycle();
auto* script = ChildDocument().CreateRawElement(html_names::kScriptTag);
script->setTextContent(R"JS(
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 10, 10);
)JS");
ChildDocument().body()->appendChild(script);
EXPECT_TRUE(
GetDocument().GetPage()->Animator().has_canvas_invalidation_for_test());
}
} // namespace blink } // namespace blink
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/page/page_animator.h" #include "third_party/blink/renderer/core/page/page_animator.h"
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "cc/animation/animation_host.h"
#include "third_party/blink/renderer/core/animation/document_animations.h" #include "third_party/blink/renderer/core/animation/document_animations.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h"
...@@ -120,6 +121,16 @@ void PageAnimator::PostAnimate() { ...@@ -120,6 +121,16 @@ void PageAnimator::PostAnimate() {
Clock().SetAllowedToDynamicallyUpdateTime(true); Clock().SetAllowedToDynamicallyUpdateTime(true);
} }
void PageAnimator::SetHasCanvasInvalidation(bool has_canvas_invalidation) {
has_canvas_invalidation_ = has_canvas_invalidation;
}
void PageAnimator::ReportFrameAnimations(cc::AnimationHost* animation_host) {
if (animation_host)
animation_host->SetHasCanvasInvalidation(has_canvas_invalidation_);
has_canvas_invalidation_ = false;
}
void PageAnimator::SetSuppressFrameRequestsWorkaroundFor704763Only( void PageAnimator::SetSuppressFrameRequestsWorkaroundFor704763Only(
bool suppress_frame_requests) { bool suppress_frame_requests) {
// If we are enabling the suppression and it was already enabled then we must // If we are enabling the suppression and it was already enabled then we must
......
...@@ -12,6 +12,10 @@ ...@@ -12,6 +12,10 @@
#include "third_party/blink/renderer/core/dom/document_lifecycle.h" #include "third_party/blink/renderer/core/dom/document_lifecycle.h"
#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/handle.h"
namespace cc {
class AnimationHost;
}
namespace blink { namespace blink {
class LocalFrame; class LocalFrame;
...@@ -45,6 +49,11 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> { ...@@ -45,6 +49,11 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> {
DocumentUpdateReason reason); DocumentUpdateReason reason);
AnimationClock& Clock() { return animation_clock_; } AnimationClock& Clock() { return animation_clock_; }
HeapVector<Member<Animation>> GetAnimations(const TreeScope&); HeapVector<Member<Animation>> GetAnimations(const TreeScope&);
void SetHasCanvasInvalidation(bool has_canvas_invalidation);
bool has_canvas_invalidation_for_test() const {
return has_canvas_invalidation_;
}
void ReportFrameAnimations(cc::AnimationHost* animation_host);
private: private:
Member<Page> page_; Member<Page> page_;
...@@ -52,6 +61,8 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> { ...@@ -52,6 +61,8 @@ class CORE_EXPORT PageAnimator final : public GarbageCollected<PageAnimator> {
bool updating_layout_and_style_for_painting_; bool updating_layout_and_style_for_painting_;
bool suppress_frame_requests_workaround_for704763_only_ = false; bool suppress_frame_requests_workaround_for704763_only_ = false;
AnimationClock animation_clock_; AnimationClock animation_clock_;
// True if the current main frame has canvas invalidation.
bool has_canvas_invalidation_ = false;
}; };
} // namespace blink } // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment