Commit 3f5c5524 authored by Yi Gu's avatar Yi Gu Committed by Commit Bot

Composite elements with worklet animations

Elements with worklet animations should be composited to run on cc.
Currently the workaround is asking developers to force promoting the
elements by adding will-change property.

This patch is to composite the affected elements automatically based on
their properties.

Bug: 776533
Change-Id: I8265e27b5aa3c136a622f8f35b83f770c87b58c6
Reviewed-on: https://chromium-review.googlesource.com/930128
Commit-Queue: Yi Gu <yigu@chromium.org>
Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Reviewed-by: default avatarMajid Valipour <majidvp@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#538888}
parent 300365f1
......@@ -4,11 +4,6 @@
width: 100px;
height: 100px;
background-color: #00ff00;
/*
* Force compositing.
* TODO(majidvp): Should not be needed when http://crbug.com/776533 is fixed.
*/
will-change: transform;
}
#covered {
......@@ -21,11 +16,7 @@
overflow: auto;
height: 100px;
width: 100px;
/*
* Force compositing.
* TODO(crbug.com/776533): Shouldn't be needed - we should fallback to main if scroller isn't composited.
*/
will-change: transform;
will-change: transform; /* force compositing */
}
#contents {
......
......@@ -4,11 +4,6 @@
width: 100px;
height: 100px;
background-color: #00ff00;
/*
* Force compositing.
* TODO(majidvp): Should not be needed when http://crbug.com/776533 is fixed.
*/
will-change: transform, opacity;
}
#covered {
......
......@@ -34,6 +34,25 @@
namespace blink {
namespace {
void UpdateAnimationFlagsForEffect(const KeyframeEffectReadOnly& effect,
ComputedStyle& style) {
if (effect.Affects(PropertyHandle(GetCSSPropertyOpacity())))
style.SetHasCurrentOpacityAnimation(true);
if (effect.Affects(PropertyHandle(GetCSSPropertyTransform())) ||
effect.Affects(PropertyHandle(GetCSSPropertyRotate())) ||
effect.Affects(PropertyHandle(GetCSSPropertyScale())) ||
effect.Affects(PropertyHandle(GetCSSPropertyTranslate())))
style.SetHasCurrentTransformAnimation(true);
if (effect.Affects(PropertyHandle(GetCSSPropertyFilter())))
style.SetHasCurrentFilterAnimation(true);
if (effect.Affects(PropertyHandle(GetCSSPropertyBackdropFilter())))
style.SetHasCurrentBackdropFilterAnimation(true);
}
} // namespace
ElementAnimations::ElementAnimations() : animation_style_change_(false) {}
ElementAnimations::~ElementAnimations() = default;
......@@ -46,19 +65,17 @@ void ElementAnimations::UpdateAnimationFlags(ComputedStyle& style) {
DCHECK(animation.effect()->IsKeyframeEffectReadOnly());
const KeyframeEffectReadOnly& effect =
*ToKeyframeEffectReadOnly(animation.effect());
if (effect.IsCurrent()) {
if (effect.Affects(PropertyHandle(GetCSSPropertyOpacity())))
style.SetHasCurrentOpacityAnimation(true);
if (effect.Affects(PropertyHandle(GetCSSPropertyTransform())) ||
effect.Affects(PropertyHandle(GetCSSPropertyRotate())) ||
effect.Affects(PropertyHandle(GetCSSPropertyScale())) ||
effect.Affects(PropertyHandle(GetCSSPropertyTranslate())))
style.SetHasCurrentTransformAnimation(true);
if (effect.Affects(PropertyHandle(GetCSSPropertyFilter())))
style.SetHasCurrentFilterAnimation(true);
if (effect.Affects(PropertyHandle(GetCSSPropertyBackdropFilter())))
style.SetHasCurrentBackdropFilterAnimation(true);
}
if (!effect.IsCurrent())
continue;
UpdateAnimationFlagsForEffect(effect, style);
}
for (const auto& entry : worklet_animations_) {
const KeyframeEffectReadOnly& effect = *entry->GetEffect();
// TODO(majidvp): we should check the effect's phase before updating the
// style once the timing of effect is ready to use.
// https://crbug.com/814851.
UpdateAnimationFlagsForEffect(effect, style);
}
if (style.HasCurrentOpacityAnimation()) {
......@@ -92,6 +109,7 @@ void ElementAnimations::Trace(blink::Visitor* visitor) {
visitor->Trace(css_animations_);
visitor->Trace(effect_stack_);
visitor->Trace(animations_);
visitor->Trace(worklet_animations_);
}
const ComputedStyle* ElementAnimations::BaseComputedStyle() const {
......
......@@ -34,6 +34,7 @@
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "core/animation/EffectStack.h"
#include "core/animation/WorkletAnimationBase.h"
#include "core/animation/css/CSSAnimations.h"
#include "platform/wtf/HashCountedSet.h"
#include "platform/wtf/HashMap.h"
......@@ -43,8 +44,10 @@ namespace blink {
class CSSAnimations;
using AnimationCountedSet = HeapHashCountedSet<WeakMember<Animation>>;
using WorkletAnimationSet = HeapHashSet<WeakMember<WorkletAnimationBase>>;
class ElementAnimations : public GarbageCollectedFinalized<ElementAnimations> {
class CORE_EXPORT ElementAnimations
: public GarbageCollectedFinalized<ElementAnimations> {
public:
ElementAnimations();
~ElementAnimations();
......@@ -62,6 +65,8 @@ class ElementAnimations : public GarbageCollectedFinalized<ElementAnimations> {
// Animations which have effects targeting this element.
AnimationCountedSet& Animations() { return animations_; }
// Worklet Animations which have effects targeting this element.
WorkletAnimationSet& GetWorkletAnimations() { return worklet_animations_; }
bool IsEmpty() const {
return effect_stack_.IsEmpty() && css_animations_.IsEmpty() &&
......@@ -87,6 +92,7 @@ class ElementAnimations : public GarbageCollectedFinalized<ElementAnimations> {
EffectStack effect_stack_;
CSSAnimations css_animations_;
AnimationCountedSet animations_;
WorkletAnimationSet worklet_animations_;
bool animation_style_change_;
scoped_refptr<ComputedStyle> base_computed_style_;
......
......@@ -12,6 +12,7 @@
namespace blink {
class Document;
class KeyframeEffectReadOnly;
class CORE_EXPORT WorkletAnimationBase : public ScriptWrappable {
public:
......@@ -27,6 +28,7 @@ class CORE_EXPORT WorkletAnimationBase : public ScriptWrappable {
virtual bool StartOnCompositor(String* failure_message) = 0;
virtual Document* GetDocument() const = 0;
virtual KeyframeEffectReadOnly* GetEffect() const = 0;
};
} // namespace blink
......
......@@ -240,6 +240,7 @@ jumbo_source_set("unit_tests") {
"accessibility/testing/AccessibilityTest.h",
"animationworklet/AnimationWorkletGlobalScopeTest.cpp",
"animationworklet/AnimationWorkletThreadTest.cpp",
"animationworklet/WorkletAnimationTest.cpp",
"background_fetch/BackgroundFetchManagerTest.cpp",
"cachestorage/CacheTest.cpp",
"canvas/canvas2d/CanvasRenderingContext2DAPITest.cpp",
......
......@@ -198,18 +198,36 @@ String WorkletAnimation::playState() {
void WorkletAnimation::play() {
DCHECK(IsMainThread());
if (play_state_ != Animation::kPending) {
document_->GetWorkletAnimationController().AttachAnimation(*this);
play_state_ = Animation::kPending;
}
if (play_state_ == Animation::kPending)
return;
document_->GetWorkletAnimationController().AttachAnimation(*this);
play_state_ = Animation::kPending;
KeyframeEffectReadOnly* target_effect = effects_.at(0);
Element* target = target_effect->target();
if (!target)
return;
target->EnsureElementAnimations().GetWorkletAnimations().insert(this);
// TODO(majidvp): This should be removed once worklet animation correctly
// updates its effect timing. https://crbug.com/814851.
target->SetNeedsAnimationStyleRecalc();
}
void WorkletAnimation::cancel() {
DCHECK(IsMainThread());
if (play_state_ != Animation::kIdle) {
document_->GetWorkletAnimationController().DetachAnimation(*this);
play_state_ = Animation::kIdle;
}
if (play_state_ == Animation::kIdle)
return;
document_->GetWorkletAnimationController().DetachAnimation(*this);
play_state_ = Animation::kIdle;
KeyframeEffectReadOnly* target_effect = effects_.at(0);
Element* target = target_effect->target();
if (!target)
return;
target->EnsureElementAnimations().GetWorkletAnimations().erase(this);
// TODO(majidvp): This should be removed once worklet animation correctly
// updates its effect timing. https://crbug.com/814851.
target->SetNeedsAnimationStyleRecalc();
}
bool WorkletAnimation::StartOnCompositor(String* failure_message) {
......@@ -261,9 +279,6 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) {
document_->Timeline().CompositorTimeline())
compositor_timeline->AnimationAttached(*this);
// TODO(smcgruer): Creating a WorkletAnimation should be a hint to blink
// compositing that the animated element should be promoted. Otherwise this
// fails. http://crbug.com/776533
CompositorAnimations::AttachCompositedLayers(target,
compositor_animation_.get());
......
......@@ -71,6 +71,7 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
const DocumentTimelineOrScrollTimeline& Timeline() { return timeline_; }
const scoped_refptr<SerializedScriptValue> Options() { return options_; }
KeyframeEffectReadOnly* GetEffect() const override { return effects_.at(0); }
void Trace(blink::Visitor*) override;
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/animationworklet/WorkletAnimation.h"
#include "bindings/modules/v8/animation_effect_read_only_or_animation_effect_read_only_sequence.h"
#include "core/animation/ElementAnimations.h"
#include "core/animation/KeyframeEffect.h"
#include "core/animation/KeyframeEffectModel.h"
#include "core/dom/Document.h"
#include "core/layout/LayoutTestHelper.h"
#include "core/testing/DummyPageHolder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
KeyframeEffectModelBase* CreateEffectModel() {
StringKeyframeVector frames_mixed_properties;
scoped_refptr<StringKeyframe> keyframe = StringKeyframe::Create();
keyframe->SetOffset(0);
keyframe->SetCSSPropertyValue(CSSPropertyOpacity, "0",
SecureContextMode::kInsecureContext, nullptr);
frames_mixed_properties.push_back(std::move(keyframe));
keyframe = StringKeyframe::Create();
keyframe->SetOffset(1);
keyframe->SetCSSPropertyValue(CSSPropertyOpacity, "1",
SecureContextMode::kInsecureContext, nullptr);
frames_mixed_properties.push_back(std::move(keyframe));
return StringKeyframeEffectModel::Create(frames_mixed_properties);
}
KeyframeEffect* CreateKeyframeEffect(Element* element) {
Timing timing;
timing.iteration_duration = 30;
timing.playback_rate = 1;
return KeyframeEffect::Create(element, CreateEffectModel(), timing);
}
WorkletAnimation* CreateWorkletAnimation(Element* element) {
AnimationEffectReadOnlyOrAnimationEffectReadOnlySequence effects;
AnimationEffectReadOnly* effect = CreateKeyframeEffect(element);
effects.SetAnimationEffectReadOnly(effect);
DocumentTimelineOrScrollTimeline timeline;
scoped_refptr<SerializedScriptValue> options;
DummyExceptionStateForTesting exception_state;
return WorkletAnimation::Create("WorkletAnimation", effects, timeline,
std::move(options), exception_state);
}
} // namespace
class WorkletAnimationTest : public RenderingTest {
public:
WorkletAnimationTest()
: RenderingTest(SingleChildLocalFrameClient::Create()) {}
void SetUp() override {
RenderingTest::SetUp();
element_ = GetDocument().CreateElementForBinding("test");
worklet_animation_ = CreateWorkletAnimation(element_);
}
Persistent<Element> element_;
Persistent<WorkletAnimation> worklet_animation_;
};
TEST_F(WorkletAnimationTest, WorkletAnimationInElementAnimations) {
worklet_animation_->play();
EXPECT_EQ(1u,
element_->EnsureElementAnimations().GetWorkletAnimations().size());
worklet_animation_->cancel();
EXPECT_EQ(0u,
element_->EnsureElementAnimations().GetWorkletAnimations().size());
}
TEST_F(WorkletAnimationTest, StyleHasCurrentAnimation) {
scoped_refptr<ComputedStyle> style =
GetDocument().EnsureStyleResolver().StyleForElement(element_).get();
EXPECT_EQ(false, style->HasCurrentOpacityAnimation());
worklet_animation_->play();
element_->EnsureElementAnimations().UpdateAnimationFlags(*style);
EXPECT_EQ(true, style->HasCurrentOpacityAnimation());
}
} // 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