Commit 8eb8112e authored by Majid Valipour's avatar Majid Valipour Committed by Commit Bot

[animation-worklet] worklet animation updates cc when timing changes

Introduce WorkletAnimation::UpdateCompositingState which is resposible
to update cc side when necessary (e.g., during start or when there is
a change while running).

WorkletAnimation now notifies its controller that it has been invalidated
once its timing has changed. This ensures it gets a change to update its
compositing state on the next commit cycle.

While animation is running, the state update is done by canceling and
starting the keyframe effect on compositor side. This ensures the
animation instance continues to live (with the same id) and thus
keep any associated state while also updating its keyframes and timings.

Other minor changes:
- Use GetEffect() to access the main effect for the animation.
- Refactor start logic.

Tests:
- virtual/threaded/fast/animationworklet/worklet-animation-set-timing.html test timing
   updates are reflected in cc.
- virtual/threaded/fast/animationworklet/worklet-animation-set-keyframes.html test keyframe
   updates are reflected in cc.

Bug: 833846
Change-Id: Icab984331a0584d9bd1eb38d24aef2f657070970
Reviewed-on: https://chromium-review.googlesource.com/1020239
Commit-Queue: Majid Valipour <majidvp@chromium.org>
Reviewed-by: default avatarMajid Valipour <majidvp@chromium.org>
Reviewed-by: default avatarStephen McGruer <smcgruer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#554776}
parent 61966fae
<!DOCTYPE html>
<style>
.box {
width: 100px;
height: 100px;
transform: translateX(100px);
background-color: #00ff00;
opacity: 0.8;
will-change: transform;
}
</style>
<div class="box"></div>
<!DOCTYPE html>
<style>
#box {
width: 100px;
height: 100px;
background-color: #00ff00;
}
</style>
<div id="box"></div>
<script id="visual_update" type="text/worklet">
registerAnimator("test_animator", class {
animate(currentTime, effect) {
effect.localTime = 500;
}
});
</script>
<script src="resources/animation-worklet-tests.js"></script>
<script>
if (window.testRunner) {
testRunner.waitUntilDone();
}
runInAnimationWorklet(
document.getElementById('visual_update').textContent
).then(()=>{
const keyframes_before = [
{ transform: 'translateY(0)' },
{ transform: 'translateY(200px)' }
];
const keyframes_after = [
{ transform: 'translateX(0)' },
{ transform: 'translateX(200px)' }
];
const box = document.getElementById('box');
const effect = new KeyframeEffect(box, keyframes_before, {duration: 1000});
const animation = new WorkletAnimation('test_animator', effect, document.timeline, {});
animation.play();
waitTwoAnimationFrames(_ => {
effect.setKeyframes(keyframes_after);
waitTwoAnimationFrames(_ => {
// TODO(crbug.com/829926): The same issue that is affecting
// worklet-animation-cancel test is at play here. Change opacity to force
// a new animation frame and commit to get updated result.
box.style.opacity = 0.8;
waitTwoAnimationFrames(_ => {
if (window.testRunner)
testRunner.notifyDone();
});
});
});
});
</script>
<!DOCTYPE html>
<style>
.box {
width: 100px;
height: 100px;
transform: translateX(50px);
opacity: 0.8;
background-color: #00ff00;
will-change: transform;
}
</style>
<div class="box"></div>
<!DOCTYPE html>
<style>
#box {
width: 100px;
height: 100px;
background-color: #00ff00;
}
</style>
<div id="box"></div>
<script id="visual_update" type="text/worklet">
registerAnimator("test_animator", class {
animate(currentTime, effect) {
effect.localTime = 500;
}
});
</script>
<script src="resources/animation-worklet-tests.js"></script>
<script>
if (window.testRunner)
testRunner.waitUntilDone();
runInAnimationWorklet(
document.getElementById('visual_update').textContent
).then(()=>{
console.log('aw');
const keyframes = [
{ transform: 'translateX(0)' },
{ transform: 'translateX(200px)' }
];
const options_before = {
duration: 1000
};
const options_after = {
duration: 2000
};
const box = document.getElementById('box');
const effect = new KeyframeEffect(box, keyframes, options_before);
const animation = new WorkletAnimation('test_animator', effect, document.timeline, {});
animation.play();
waitTwoAnimationFrames(_ => {
effect.updateTiming(options_after);
waitTwoAnimationFrames(_ => {
box.style.opacity = 0.8;
// TODO(crbug.com/829926): The same issue that is affecting
// worklet-animation-cancel is at play here. Change opacity to force a
// new animation frame and commit to get updated result.
waitTwoAnimationFrames(_ => {
if (window.testRunner)
testRunner.notifyDone();
});
});
});
});
</script>
...@@ -21,14 +21,12 @@ class CORE_EXPORT WorkletAnimationBase : public ScriptWrappable { ...@@ -21,14 +21,12 @@ class CORE_EXPORT WorkletAnimationBase : public ScriptWrappable {
// Asks the animation to update its effect inherited time. // Asks the animation to update its effect inherited time.
virtual void Update(TimingUpdateReason) = 0; virtual void Update(TimingUpdateReason) = 0;
// Attempts to start the animation on the compositor side, returning true if // Updates the animation on the compositor side to reflect the main thread
// it succeeds or false otherwise. If false is returned and failure_message // state.
// was non-null, failure_message may be filled with an error description.
// //
// On a false return it may still be possible to start the animation on the // Returns true if animation is started on compositor and should now start
// compositor later (e.g. if an incompatible property is removed from the // receiving timing update requests.
// element), so the caller should try again next main frame. virtual bool UpdateCompositingState() = 0;
virtual bool StartOnCompositor(String* failure_message) = 0;
virtual Document* GetDocument() const = 0; virtual Document* GetDocument() const = 0;
virtual KeyframeEffect* GetEffect() const = 0; virtual KeyframeEffect* GetEffect() const = 0;
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#include "third_party/blink/renderer/core/animation/worklet_animation_base.h" #include "third_party/blink/renderer/core/animation/worklet_animation_base.h"
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink { namespace blink {
...@@ -32,14 +31,16 @@ void WorkletAnimationController::AttachAnimation( ...@@ -32,14 +31,16 @@ void WorkletAnimationController::AttachAnimation(
void WorkletAnimationController::DetachAnimation( void WorkletAnimationController::DetachAnimation(
WorkletAnimationBase& animation) { WorkletAnimationBase& animation) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
DCHECK(pending_animations_.Contains(&animation) != pending_animations_.erase(&animation);
compositor_animations_.Contains(&animation)); compositor_animations_.erase(&animation);
// If detached animation is in pending list, it has not been started on the }
// compositor yet so it just needs to be removed from pending list.
if (pending_animations_.Contains(&animation)) void WorkletAnimationController::InvalidateAnimation(
pending_animations_.erase(&animation); WorkletAnimationBase& animation) {
else DCHECK(IsMainThread());
compositor_animations_.erase(&animation); pending_animations_.insert(&animation);
if (LocalFrameView* view = animation.GetDocument()->View())
view->ScheduleAnimation();
} }
void WorkletAnimationController::UpdateAnimationCompositingStates() { void WorkletAnimationController::UpdateAnimationCompositingStates() {
...@@ -47,12 +48,8 @@ void WorkletAnimationController::UpdateAnimationCompositingStates() { ...@@ -47,12 +48,8 @@ void WorkletAnimationController::UpdateAnimationCompositingStates() {
HeapHashSet<Member<WorkletAnimationBase>> animations; HeapHashSet<Member<WorkletAnimationBase>> animations;
animations.swap(pending_animations_); animations.swap(pending_animations_);
for (const auto& animation : animations) { for (const auto& animation : animations) {
String failure_message; if (animation->UpdateCompositingState()) {
if (animation->StartOnCompositor(&failure_message)) {
compositor_animations_.insert(animation); compositor_animations_.insert(animation);
} else {
document_->AddConsoleMessage(ConsoleMessage::Create(
kOtherMessageSource, kWarningMessageLevel, failure_message));
} }
} }
} }
......
...@@ -34,6 +34,7 @@ class CORE_EXPORT WorkletAnimationController ...@@ -34,6 +34,7 @@ class CORE_EXPORT WorkletAnimationController
void AttachAnimation(WorkletAnimationBase&); void AttachAnimation(WorkletAnimationBase&);
void DetachAnimation(WorkletAnimationBase&); void DetachAnimation(WorkletAnimationBase&);
void InvalidateAnimation(WorkletAnimationBase&);
void UpdateAnimationCompositingStates(); void UpdateAnimationCompositingStates();
void UpdateAnimationTimings(TimingUpdateReason); void UpdateAnimationTimings(TimingUpdateReason);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "third_party/blink/renderer/core/animation/timing.h" #include "third_party/blink/renderer/core/animation/timing.h"
#include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
...@@ -153,6 +154,22 @@ std::unique_ptr<CompositorScrollTimeline> ToCompositorScrollTimeline( ...@@ -153,6 +154,22 @@ std::unique_ptr<CompositorScrollTimeline> ToCompositorScrollTimeline(
time_range.GetAsDouble()); time_range.GetAsDouble());
} }
void StartEffectOnCompositor(CompositorAnimation* animation,
KeyframeEffect* effect) {
DCHECK(effect);
Element& target = *effect->target();
effect->Model()->SnapshotAllCompositorKeyframes(
target, target.ComputedStyleRef(), target.ParentComputedStyle());
int group = 0;
base::Optional<double> start_time = base::nullopt;
double time_offset = 0;
double playback_rate = 1;
effect->StartAnimationOnCompositor(group, start_time, time_offset,
playback_rate, animation);
}
unsigned NextSequenceNumber() { unsigned NextSequenceNumber() {
// TODO(majidvp): This should actually come from the same source as other // TODO(majidvp): This should actually come from the same source as other
// animation so that they have the correct ordering. // animation so that they have the correct ordering.
...@@ -228,8 +245,7 @@ void WorkletAnimation::play() { ...@@ -228,8 +245,7 @@ void WorkletAnimation::play() {
document_->GetWorkletAnimationController().AttachAnimation(*this); document_->GetWorkletAnimationController().AttachAnimation(*this);
play_state_ = Animation::kPending; play_state_ = Animation::kPending;
KeyframeEffect* target_effect = effects_.at(0); Element* target = GetEffect()->target();
Element* target = target_effect->target();
if (!target) if (!target)
return; return;
target->EnsureElementAnimations().GetWorkletAnimations().insert(this); target->EnsureElementAnimations().GetWorkletAnimations().insert(this);
...@@ -244,15 +260,14 @@ void WorkletAnimation::cancel() { ...@@ -244,15 +260,14 @@ void WorkletAnimation::cancel() {
return; return;
document_->GetWorkletAnimationController().DetachAnimation(*this); document_->GetWorkletAnimationController().DetachAnimation(*this);
KeyframeEffect* target_effect = effects_.at(0);
if (compositor_animation_) { if (compositor_animation_) {
target_effect->CancelAnimationOnCompositor(compositor_animation_.get()); GetEffect()->CancelAnimationOnCompositor(compositor_animation_.get());
DestroyCompositorAnimation(); DestroyCompositorAnimation();
} }
play_state_ = Animation::kIdle; play_state_ = Animation::kIdle;
Element* target = target_effect->target(); Element* target = GetEffect()->target();
if (!target) if (!target)
return; return;
target->EnsureElementAnimations().GetWorkletAnimations().erase(this); target->EnsureElementAnimations().GetWorkletAnimations().erase(this);
...@@ -271,6 +286,10 @@ void WorkletAnimation::UpdateIfNecessary() { ...@@ -271,6 +286,10 @@ void WorkletAnimation::UpdateIfNecessary() {
Update(kTimingUpdateOnDemand); Update(kTimingUpdateOnDemand);
} }
void WorkletAnimation::EffectInvalidated() {
document_->GetWorkletAnimationController().InvalidateAnimation(*this);
}
void WorkletAnimation::Update(TimingUpdateReason reason) { void WorkletAnimation::Update(TimingUpdateReason reason) {
if (play_state_ != Animation::kRunning) if (play_state_ != Animation::kRunning)
return; return;
...@@ -281,9 +300,7 @@ void WorkletAnimation::Update(TimingUpdateReason reason) { ...@@ -281,9 +300,7 @@ void WorkletAnimation::Update(TimingUpdateReason reason) {
// TODO(crbug.com/756359): For now we use 0 as inherited time in but we will // TODO(crbug.com/756359): For now we use 0 as inherited time in but we will
// need to get the inherited time from worklet context. // need to get the inherited time from worklet context.
double inherited_time_seconds = 0; double inherited_time_seconds = 0;
GetEffect()->UpdateInheritedTime(inherited_time_seconds, reason);
KeyframeEffect* target_effect = effects_.at(0);
target_effect->UpdateInheritedTime(inherited_time_seconds, reason);
} }
AnimationTimeline& WorkletAnimation::GetAnimationTimeline() { AnimationTimeline& WorkletAnimation::GetAnimationTimeline() {
...@@ -295,10 +312,28 @@ AnimationTimeline& WorkletAnimation::GetAnimationTimeline() { ...@@ -295,10 +312,28 @@ AnimationTimeline& WorkletAnimation::GetAnimationTimeline() {
return *timeline_.GetAsDocumentTimeline(); return *timeline_.GetAsDocumentTimeline();
} }
bool WorkletAnimation::UpdateCompositingState() {
switch (play_state_) {
case Animation::kPending: {
String failure_message;
if (StartOnCompositor(&failure_message))
return true;
document_->AddConsoleMessage(ConsoleMessage::Create(
kOtherMessageSource, kWarningMessageLevel, failure_message));
return false;
}
case Animation::kRunning: {
UpdateOnCompositor();
return false;
}
default:
return false;
}
}
bool WorkletAnimation::StartOnCompositor(String* failure_message) { bool WorkletAnimation::StartOnCompositor(String* failure_message) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
KeyframeEffect* target_effect = effects_.at(0); Element& target = *GetEffect()->target();
Element& target = *target_effect->target();
// TODO(crbug.com/836393): This should not be possible but it is currently // TODO(crbug.com/836393): This should not be possible but it is currently
// happening and needs to be investigated/fixed. // happening and needs to be investigated/fixed.
...@@ -311,7 +346,7 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) { ...@@ -311,7 +346,7 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) {
// keyframe groups have been created. To ensure this we manually snapshot the // keyframe groups have been created. To ensure this we manually snapshot the
// frames in the target effect. // frames in the target effect.
// TODO(smcgruer): This shouldn't be necessary - Animation doesn't do this. // TODO(smcgruer): This shouldn't be necessary - Animation doesn't do this.
target_effect->Model()->SnapshotAllCompositorKeyframes( GetEffect()->Model()->SnapshotAllCompositorKeyframes(
target, target.ComputedStyleRef(), target.ParentComputedStyle()); target, target.ComputedStyleRef(), target.ParentComputedStyle());
if (!CheckElementComposited(target)) { if (!CheckElementComposited(target)) {
...@@ -330,7 +365,7 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) { ...@@ -330,7 +365,7 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) {
double playback_rate = 1; double playback_rate = 1;
CompositorAnimations::FailureCode failure_code = CompositorAnimations::FailureCode failure_code =
target_effect->CheckCanStartAnimationOnCompositor(playback_rate); GetEffect()->CheckCanStartAnimationOnCompositor(playback_rate);
if (!failure_code.Ok()) { if (!failure_code.Ok()) {
play_state_ = Animation::kIdle; play_state_ = Animation::kIdle;
...@@ -354,14 +389,8 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) { ...@@ -354,14 +389,8 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) {
CompositorAnimations::AttachCompositedLayers(target, CompositorAnimations::AttachCompositedLayers(target,
compositor_animation_.get()); compositor_animation_.get());
base::Optional<double> start_time = base::nullopt;
double time_offset = 0;
int group = 0;
// TODO(smcgruer): We need to start all of the effects, not just the first. // TODO(smcgruer): We need to start all of the effects, not just the first.
effects_.at(0)->StartAnimationOnCompositor(group, start_time, time_offset, StartEffectOnCompositor(compositor_animation_.get(), GetEffect());
playback_rate,
compositor_animation_.get());
play_state_ = Animation::kRunning; play_state_ = Animation::kRunning;
AnimationTimeline& timeline = GetAnimationTimeline(); AnimationTimeline& timeline = GetAnimationTimeline();
...@@ -373,6 +402,14 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) { ...@@ -373,6 +402,14 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) {
return true; return true;
} }
void WorkletAnimation::UpdateOnCompositor() {
// We want to update the keyframe effect on compositor animation without
// destroying the compositor animation instance. This is achieved by
// canceling, and start the blink keyframe effect on compositor.
GetEffect()->CancelAnimationOnCompositor(compositor_animation_.get());
StartEffectOnCompositor(compositor_animation_.get(), GetEffect());
}
void WorkletAnimation::DestroyCompositorAnimation() { void WorkletAnimation::DestroyCompositorAnimation() {
if (compositor_animation_ && compositor_animation_->IsElementAttached()) if (compositor_animation_ && compositor_animation_->IsElementAttached())
compositor_animation_->DetachElement(); compositor_animation_->DetachElement();
...@@ -387,6 +424,11 @@ void WorkletAnimation::DestroyCompositorAnimation() { ...@@ -387,6 +424,11 @@ void WorkletAnimation::DestroyCompositorAnimation() {
} }
} }
KeyframeEffect* WorkletAnimation::GetEffect() const {
DCHECK(effects_.at(0));
return effects_.at(0);
}
void WorkletAnimation::Dispose() { void WorkletAnimation::Dispose() {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
DestroyCompositorAnimation(); DestroyCompositorAnimation();
......
...@@ -65,16 +65,14 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase, ...@@ -65,16 +65,14 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
// is not currently supported by worklet animations. // is not currently supported by worklet animations.
bool EffectSuppressed() const override { return false; } bool EffectSuppressed() const override { return false; }
// TODO(crbug.com/833846): We should update compositor animation when this void EffectInvalidated() override;
// happens.
void EffectInvalidated() override {}
void UpdateIfNecessary() override; void UpdateIfNecessary() override;
Animation* GetAnimation() override { return nullptr; } Animation* GetAnimation() override { return nullptr; }
// WorkletAnimationBase implementation. // WorkletAnimationBase implementation.
void Update(TimingUpdateReason) override; void Update(TimingUpdateReason) override;
bool StartOnCompositor(String* failure_message) override; bool UpdateCompositingState() override;
// CompositorAnimationClient implementation. // CompositorAnimationClient implementation.
CompositorAnimation* GetCompositorAnimation() const override { CompositorAnimation* GetCompositorAnimation() const override {
...@@ -94,7 +92,7 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase, ...@@ -94,7 +92,7 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
const DocumentTimelineOrScrollTimeline& Timeline() { return timeline_; } const DocumentTimelineOrScrollTimeline& Timeline() { return timeline_; }
const scoped_refptr<SerializedScriptValue> Options() { return options_; } const scoped_refptr<SerializedScriptValue> Options() { return options_; }
KeyframeEffect* GetEffect() const override { return effects_.at(0); } KeyframeEffect* GetEffect() const override;
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
...@@ -108,6 +106,14 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase, ...@@ -108,6 +106,14 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
AnimationTimeline& GetAnimationTimeline(); AnimationTimeline& GetAnimationTimeline();
// Attempts to start the animation on the compositor side, returning true if
// it succeeds or false otherwise. If false is returned and failure_message
// was non-null, failure_message may be filled with an error description.
bool StartOnCompositor(String* failure_message);
// Updates a running animation on the compositor side.
void UpdateOnCompositor();
unsigned sequence_number_; unsigned sequence_number_;
const String animator_name_; const String animator_name_;
......
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