Commit 9bbcad05 authored by Yi Gu's avatar Yi Gu Committed by Commit Bot

Make worklet animation stateful

Currently the sync mechanism between cc and worklet is stateless meaning
that it sends a new copy of the world every sync cycle. This has the
benefit of keeping things very simple initially but it is not
necessarily very efficient.

This patch makes worklet animation stateful and adds the ability to
mutate only a subset of animations.

Bug: 843255
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I18e5d64d0145394fad739a70fa7d0e91bb0e4588
Reviewed-on: https://chromium-review.googlesource.com/1092413
Commit-Queue: Yi Gu <yigu@chromium.org>
Reviewed-by: default avatarMajid Valipour <majidvp@chromium.org>
Reviewed-by: default avatarStephen McGruer <smcgruer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567367}
parent 12c4b5fa
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "cc/animation/animation_events.h" #include "cc/animation/animation_events.h"
#include "cc/animation/animation_host.h" #include "cc/animation/animation_host.h"
#include "cc/animation/animation_timeline.h" #include "cc/animation/animation_timeline.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/scroll_offset_animation_curve.h" #include "cc/animation/scroll_offset_animation_curve.h"
#include "cc/animation/transform_operations.h" #include "cc/animation/transform_operations.h"
#include "cc/trees/property_animation_state.h" #include "cc/trees/property_animation_state.h"
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include "cc/animation/animation_curve.h" #include "cc/animation/animation_curve.h"
#include "cc/animation/animation_export.h" #include "cc/animation/animation_export.h"
#include "cc/animation/element_animations.h" #include "cc/animation/element_animations.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/keyframe_model.h" #include "cc/animation/keyframe_model.h"
#include "cc/trees/element_id.h" #include "cc/trees/element_id.h"
...@@ -24,6 +23,7 @@ class AnimationDelegate; ...@@ -24,6 +23,7 @@ class AnimationDelegate;
class AnimationEvents; class AnimationEvents;
class AnimationHost; class AnimationHost;
class AnimationTimeline; class AnimationTimeline;
class KeyframeEffect;
struct AnimationEvent; struct AnimationEvent;
// An Animation is responsible for managing animating properties for a set of // An Animation is responsible for managing animating properties for a set of
......
...@@ -362,10 +362,9 @@ std::unique_ptr<MutatorInputState> AnimationHost::CollectWorkletAnimationsState( ...@@ -362,10 +362,9 @@ std::unique_ptr<MutatorInputState> AnimationHost::CollectWorkletAnimationsState(
if (!animation->IsWorkletAnimation()) if (!animation->IsWorkletAnimation())
continue; continue;
MutatorInputState::AnimationState state = ToWorkletAnimation(animation.get())
ToWorkletAnimation(animation.get()) ->UpdateInputState(result.get(), monotonic_time, scroll_tree,
->GetInputState(monotonic_time, scroll_tree, is_active_tree); is_active_tree);
result->animations.push_back(std::move(state));
} }
return result; return result;
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
namespace cc { namespace cc {
class Animation; class Animation;
class KeyframeModel;
struct PropertyAnimationState; struct PropertyAnimationState;
typedef size_t KeyframeEffectId; typedef size_t KeyframeEffectId;
......
...@@ -44,7 +44,7 @@ class CC_ANIMATION_EXPORT SingleKeyframeEffectAnimation : public Animation { ...@@ -44,7 +44,7 @@ class CC_ANIMATION_EXPORT SingleKeyframeEffectAnimation : public Animation {
KeyframeEffect* keyframe_effect() const; KeyframeEffect* keyframe_effect() const;
void AddKeyframeModel(std::unique_ptr<KeyframeModel> keyframe_model); void AddKeyframeModel(std::unique_ptr<KeyframeModel> keyframe_model);
void PauseKeyframeModel(int keyframe_model_id, double time_offset); void PauseKeyframeModel(int keyframe_model_id, double time_offset);
void RemoveKeyframeModel(int keyframe_model_id); virtual void RemoveKeyframeModel(int keyframe_model_id);
void AbortKeyframeModel(int keyframe_model_id); void AbortKeyframeModel(int keyframe_model_id);
bool NotifyKeyframeModelFinishedForTesting( bool NotifyKeyframeModelFinishedForTesting(
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "cc/animation/worklet_animation.h" #include "cc/animation/worklet_animation.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/scroll_timeline.h" #include "cc/animation/scroll_timeline.h"
#include "cc/trees/animation_options.h" #include "cc/trees/animation_options.h"
...@@ -21,6 +22,7 @@ WorkletAnimation::WorkletAnimation( ...@@ -21,6 +22,7 @@ WorkletAnimation::WorkletAnimation(
options_(std::move(options)), options_(std::move(options)),
start_time_(base::nullopt), start_time_(base::nullopt),
last_current_time_(base::nullopt), last_current_time_(base::nullopt),
state_(State::PENDING),
is_impl_instance_(is_controlling_instance) {} is_impl_instance_(is_controlling_instance) {}
WorkletAnimation::~WorkletAnimation() = default; WorkletAnimation::~WorkletAnimation() = default;
...@@ -67,10 +69,10 @@ void WorkletAnimation::Tick(base::TimeTicks monotonic_time) { ...@@ -67,10 +69,10 @@ void WorkletAnimation::Tick(base::TimeTicks monotonic_time) {
keyframe_effect()->Tick(monotonic_time); keyframe_effect()->Tick(monotonic_time);
} }
MutatorInputState::AnimationState WorkletAnimation::GetInputState( void WorkletAnimation::UpdateInputState(MutatorInputState* input_state,
base::TimeTicks monotonic_time, base::TimeTicks monotonic_time,
const ScrollTree& scroll_tree, const ScrollTree& scroll_tree,
bool is_active_tree) { bool is_active_tree) {
// Record the monotonic time to be the start time first time state is // Record the monotonic time to be the start time first time state is
// generated. This time is used as the origin for computing the current time. // generated. This time is used as the origin for computing the current time.
if (!start_time_.has_value()) if (!start_time_.has_value())
...@@ -79,7 +81,19 @@ MutatorInputState::AnimationState WorkletAnimation::GetInputState( ...@@ -79,7 +81,19 @@ MutatorInputState::AnimationState WorkletAnimation::GetInputState(
double current_time = double current_time =
CurrentTime(monotonic_time, scroll_tree, is_active_tree); CurrentTime(monotonic_time, scroll_tree, is_active_tree);
last_current_time_ = current_time; last_current_time_ = current_time;
return {id(), name(), current_time, CloneOptions()}; switch (state_) {
case State::PENDING:
input_state->added_and_updated_animations.push_back(
{id(), name(), current_time, CloneOptions()});
state_ = State::RUNNING;
break;
case State::RUNNING:
input_state->updated_animations.push_back({id(), current_time});
break;
case State::REMOVED:
input_state->removed_animations.push_back(id());
break;
}
} }
void WorkletAnimation::SetOutputState( void WorkletAnimation::SetOutputState(
...@@ -126,6 +140,11 @@ void WorkletAnimation::PromoteScrollTimelinePendingToActive() { ...@@ -126,6 +140,11 @@ void WorkletAnimation::PromoteScrollTimelinePendingToActive() {
scroll_timeline_->PromoteScrollTimelinePendingToActive(); scroll_timeline_->PromoteScrollTimelinePendingToActive();
} }
void WorkletAnimation::RemoveKeyframeModel(int keyframe_model_id) {
state_ = State::REMOVED;
SingleKeyframeEffectAnimation::RemoveKeyframeModel(keyframe_model_id);
}
bool WorkletAnimation::IsWorkletAnimation() const { bool WorkletAnimation::IsWorkletAnimation() const {
return true; return true;
} }
......
...@@ -8,8 +8,9 @@ ...@@ -8,8 +8,9 @@
#include "base/optional.h" #include "base/optional.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "cc/animation/animation_export.h" #include "cc/animation/animation_export.h"
#include "cc/animation/keyframe_effect.h" #include "cc/animation/animation_host.h"
#include "cc/animation/single_keyframe_effect_animation.h" #include "cc/animation/single_keyframe_effect_animation.h"
#include "cc/trees/property_tree.h"
namespace cc { namespace cc {
...@@ -22,6 +23,7 @@ class ScrollTimeline; ...@@ -22,6 +23,7 @@ class ScrollTimeline;
class CC_ANIMATION_EXPORT WorkletAnimation final class CC_ANIMATION_EXPORT WorkletAnimation final
: public SingleKeyframeEffectAnimation { : public SingleKeyframeEffectAnimation {
public: public:
enum class State { PENDING, RUNNING, REMOVED };
WorkletAnimation(int id, WorkletAnimation(int id,
const std::string& name, const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<ScrollTimeline> scroll_timeline,
...@@ -43,10 +45,10 @@ class CC_ANIMATION_EXPORT WorkletAnimation final ...@@ -43,10 +45,10 @@ class CC_ANIMATION_EXPORT WorkletAnimation final
void Tick(base::TimeTicks monotonic_time) override; void Tick(base::TimeTicks monotonic_time) override;
MutatorInputState::AnimationState GetInputState( void UpdateInputState(MutatorInputState* input_state,
base::TimeTicks monotonic_time, base::TimeTicks monotonic_time,
const ScrollTree& scroll_tree, const ScrollTree& scroll_tree,
bool is_active_tree); bool is_active_tree);
void SetOutputState(const MutatorOutputState::AnimationState& state); void SetOutputState(const MutatorOutputState::AnimationState& state);
void PushPropertiesTo(Animation* animation_impl) override; void PushPropertiesTo(Animation* animation_impl) override;
...@@ -66,6 +68,8 @@ class CC_ANIMATION_EXPORT WorkletAnimation final ...@@ -66,6 +68,8 @@ class CC_ANIMATION_EXPORT WorkletAnimation final
// require updating the ElementId for the ScrollTimeline scroll source. // require updating the ElementId for the ScrollTimeline scroll source.
void PromoteScrollTimelinePendingToActive() override; void PromoteScrollTimelinePendingToActive() override;
void RemoveKeyframeModel(int keyframe_model_id) override;
private: private:
~WorkletAnimation() override; ~WorkletAnimation() override;
...@@ -96,6 +100,8 @@ class CC_ANIMATION_EXPORT WorkletAnimation final ...@@ -96,6 +100,8 @@ class CC_ANIMATION_EXPORT WorkletAnimation final
base::Optional<base::TimeTicks> start_time_; base::Optional<base::TimeTicks> start_time_;
base::Optional<double> last_current_time_; base::Optional<double> last_current_time_;
State state_;
bool is_impl_instance_; bool is_impl_instance_;
}; };
......
...@@ -153,9 +153,11 @@ TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) { ...@@ -153,9 +153,11 @@ TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) {
worklet_animation_id_, "test_name", std::move(scroll_timeline), nullptr); worklet_animation_id_, "test_name", std::move(scroll_timeline), nullptr);
ScrollTree scroll_tree; ScrollTree scroll_tree;
MutatorInputState::AnimationState state = worklet_animation->GetInputState( std::unique_ptr<MutatorInputState> state =
base::TimeTicks::Now(), scroll_tree, true); std::make_unique<MutatorInputState>();
EXPECT_EQ(1234, state.current_time); worklet_animation->UpdateInputState(state.get(), base::TimeTicks::Now(),
scroll_tree, true);
EXPECT_EQ(1234, state->added_and_updated_animations[0].current_time);
} }
TEST_F(WorkletAnimationTest, TEST_F(WorkletAnimationTest,
...@@ -171,17 +173,23 @@ TEST_F(WorkletAnimationTest, ...@@ -171,17 +173,23 @@ TEST_F(WorkletAnimationTest,
base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 246.8); base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 246.8);
ScrollTree scroll_tree; ScrollTree scroll_tree;
MutatorInputState::AnimationState first_state = std::unique_ptr<MutatorInputState> first_state =
worklet_animation->GetInputState(first_ticks, scroll_tree, true); std::make_unique<MutatorInputState>();
worklet_animation->UpdateInputState(first_state.get(), first_ticks,
scroll_tree, true);
// First state request sets the start time and thus current time should be 0. // First state request sets the start time and thus current time should be 0.
EXPECT_EQ(0, first_state.current_time); EXPECT_EQ(0, first_state->added_and_updated_animations[0].current_time);
MutatorInputState::AnimationState second_state = std::unique_ptr<MutatorInputState> second_state =
worklet_animation->GetInputState(second_ticks, scroll_tree, true); std::make_unique<MutatorInputState>();
EXPECT_EQ(123.4, second_state.current_time); worklet_animation->UpdateInputState(second_state.get(), second_ticks,
scroll_tree, true);
EXPECT_EQ(123.4, second_state->updated_animations[0].current_time);
// Should always offset from start time. // Should always offset from start time.
MutatorInputState::AnimationState third_state = std::unique_ptr<MutatorInputState> third_state =
worklet_animation->GetInputState(third_ticks, scroll_tree, true); std::make_unique<MutatorInputState>();
EXPECT_EQ(246.8, third_state.current_time); worklet_animation->UpdateInputState(third_state.get(), third_ticks,
scroll_tree, true);
EXPECT_EQ(246.8, third_state->updated_animations[0].current_time);
} }
TEST_F(WorkletAnimationTest, NeedsUpdateCorrectlyReflectsInputTimeChange) { TEST_F(WorkletAnimationTest, NeedsUpdateCorrectlyReflectsInputTimeChange) {
...@@ -194,9 +202,12 @@ TEST_F(WorkletAnimationTest, NeedsUpdateCorrectlyReflectsInputTimeChange) { ...@@ -194,9 +202,12 @@ TEST_F(WorkletAnimationTest, NeedsUpdateCorrectlyReflectsInputTimeChange) {
base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 123.4); base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 123.4);
ScrollTree scroll_tree; ScrollTree scroll_tree;
std::unique_ptr<MutatorInputState> state =
std::make_unique<MutatorInputState>();
// First time should always be true. // First time should always be true.
EXPECT_TRUE(worklet_animation->NeedsUpdate(first_ticks, scroll_tree, true)); EXPECT_TRUE(worklet_animation->NeedsUpdate(first_ticks, scroll_tree, true));
worklet_animation->GetInputState(first_ticks, scroll_tree, true); worklet_animation->UpdateInputState(state.get(), first_ticks, scroll_tree,
true);
// Should be false if time is not different from last GetState. // Should be false if time is not different from last GetState.
EXPECT_FALSE(worklet_animation->NeedsUpdate(first_ticks, scroll_tree, true)); EXPECT_FALSE(worklet_animation->NeedsUpdate(first_ticks, scroll_tree, true));
// Should be true when input time is different. // Should be true when input time is different.
...@@ -205,6 +216,74 @@ TEST_F(WorkletAnimationTest, NeedsUpdateCorrectlyReflectsInputTimeChange) { ...@@ -205,6 +216,74 @@ TEST_F(WorkletAnimationTest, NeedsUpdateCorrectlyReflectsInputTimeChange) {
EXPECT_TRUE(worklet_animation->NeedsUpdate(second_ticks, scroll_tree, true)); EXPECT_TRUE(worklet_animation->NeedsUpdate(second_ticks, scroll_tree, true));
} }
// This test verifies that worklet animation state is properly updated.
TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
AttachWorkletAnimation();
MockLayerTreeMutator* mock_mutator = new NiceMock<MockLayerTreeMutator>();
host_impl_->SetLayerTreeMutator(
base::WrapUnique<LayerTreeMutator>(mock_mutator));
ON_CALL(*mock_mutator, HasAnimators()).WillByDefault(Return(true));
const float start_opacity = .7f;
const float end_opacity = .3f;
const double duration = 1.;
int keyframe_model_id = AddOpacityTransitionToAnimation(
worklet_animation_.get(), duration, start_opacity, end_opacity, true);
ScrollTree scroll_tree;
std::unique_ptr<MutatorEvents> events = host_->CreateEvents();
std::unique_ptr<MutatorInputState> state =
std::make_unique<MutatorInputState>();
host_->PushPropertiesTo(host_impl_);
host_impl_->ActivateAnimations();
KeyframeModel* keyframe_model =
worklet_animation_impl_->GetKeyframeModel(TargetProperty::OPACITY);
ASSERT_TRUE(keyframe_model);
base::TimeTicks time;
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 1u);
EXPECT_EQ("test_name", state->added_and_updated_animations[0].name);
EXPECT_EQ(state->updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations.size(), 0u);
// The state of WorkletAnimation is updated to RUNNING after calling
// UpdateInputState above.
state.reset(new MutatorInputState());
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->updated_animations.size(), 1u);
EXPECT_EQ(state->removed_animations.size(), 0u);
// Operating on individual KeyframeModel doesn't affect the state of
// WorkletAnimation.
keyframe_model->SetRunState(KeyframeModel::FINISHED, time);
state.reset(new MutatorInputState());
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->updated_animations.size(), 1u);
EXPECT_EQ(state->removed_animations.size(), 0u);
// WorkletAnimation sets state to REMOVED when JavaScript fires cancel() which
// leads to RemoveKeyframeModel.
worklet_animation_impl_->RemoveKeyframeModel(keyframe_model_id);
host_impl_->UpdateAnimationState(true, events.get());
state.reset(new MutatorInputState());
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations.size(), 1u);
EXPECT_EQ(state->removed_animations[0], worklet_animation_id_);
}
} // namespace } // namespace
} // namespace cc } // namespace cc
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
namespace cc { namespace cc {
MutatorInputState::AnimationState::AnimationState( MutatorInputState::AddAndUpdateState::AddAndUpdateState(
int animation_id, int animation_id,
std::string name, std::string name,
double current_time, double current_time,
...@@ -15,10 +15,9 @@ MutatorInputState::AnimationState::AnimationState( ...@@ -15,10 +15,9 @@ MutatorInputState::AnimationState::AnimationState(
name(name), name(name),
current_time(current_time), current_time(current_time),
options(std::move(options)) {} options(std::move(options)) {}
MutatorInputState::AddAndUpdateState::AddAndUpdateState(AddAndUpdateState&&) =
MutatorInputState::AnimationState::AnimationState(AnimationState&& state) =
default; default;
MutatorInputState::AnimationState::~AnimationState() = default; MutatorInputState::AddAndUpdateState::~AddAndUpdateState() = default;
MutatorInputState::MutatorInputState() = default; MutatorInputState::MutatorInputState() = default;
MutatorInputState::~MutatorInputState() = default; MutatorInputState::~MutatorInputState() = default;
......
...@@ -16,32 +16,35 @@ ...@@ -16,32 +16,35 @@
namespace cc { namespace cc {
// TODO(majidvp): Currently the sync mechanism between cc and worklet is
// stateless meaning that it sends a new copy of the world every sync cycle.
// This has the benefit of keeping things very simple but we should revisit this
// and only send data relevant to particular phase of animator lifecycle e.g.,
// name and options dictionary are used just for construction.
struct CC_EXPORT MutatorInputState { struct CC_EXPORT MutatorInputState {
struct CC_EXPORT AnimationState { struct CC_EXPORT AddAndUpdateState {
int animation_id = 0; int animation_id;
// Name associated with worklet animation. // Name associated with worklet animation.
std::string name; std::string name;
// Worklet animation's current time, from its associated timeline. // Worklet animation's current time, from its associated timeline.
double current_time;
std::unique_ptr<AnimationOptions> options;
AddAndUpdateState(int animation_id,
std::string name,
double current_time,
std::unique_ptr<AnimationOptions> options);
AddAndUpdateState(AddAndUpdateState&&);
~AddAndUpdateState();
};
struct CC_EXPORT UpdateState {
int animation_id = 0;
// Worklet animation's current time, from its associated timeline.
double current_time = 0; double current_time = 0;
std::unique_ptr<AnimationOptions> options = nullptr;
AnimationState(int animation_id,
std::string name,
double current_time,
std::unique_ptr<AnimationOptions> options = nullptr);
AnimationState(AnimationState&&);
~AnimationState();
}; };
MutatorInputState(); MutatorInputState();
~MutatorInputState(); ~MutatorInputState();
std::vector<AnimationState> animations; std::vector<int> removed_animations;
std::vector<AddAndUpdateState> added_and_updated_animations;
std::vector<UpdateState> updated_animations;
DISALLOW_COPY_AND_ASSIGN(MutatorInputState); DISALLOW_COPY_AND_ASSIGN(MutatorInputState);
}; };
......
...@@ -19,37 +19,17 @@ namespace blink { ...@@ -19,37 +19,17 @@ namespace blink {
namespace { namespace {
// Once this goes out of scope it clears any animators that have not been void UpdateAnimation(Animator* animator,
// animated. ScriptState* script_state,
class ScopedAnimatorsSweeper { int id,
STACK_ALLOCATED(); double current_time,
CompositorMutatorOutputState* result) {
public: CompositorMutatorOutputState::AnimationState animation_output;
using AnimatorMap = HeapHashMap<int, TraceWrapperMember<Animator>>; if (animator->Animate(script_state, current_time, &animation_output)) {
explicit ScopedAnimatorsSweeper(AnimatorMap& animators) animation_output.animation_id = id;
: animators_(animators) { result->animations.push_back(std::move(animation_output));
for (const auto& entry : animators_) {
Animator* animator = entry.value;
animator->clear_did_animate();
}
} }
~ScopedAnimatorsSweeper() { }
// Clear any animator that has not been animated.
// TODO(majidvp): Reconsider this once we add specific entry to mutator
// input that explicitly inform us that an animator is deleted.
Vector<int> to_be_removed;
for (const auto& entry : animators_) {
int id = entry.key;
Animator* animator = entry.value;
if (!animator->did_animate())
to_be_removed.push_back(id);
}
animators_.RemoveAll(to_be_removed);
}
private:
AnimatorMap& animators_;
};
} // namespace } // namespace
...@@ -84,19 +64,15 @@ void AnimationWorkletGlobalScope::Dispose() { ...@@ -84,19 +64,15 @@ void AnimationWorkletGlobalScope::Dispose() {
ThreadedWorkletGlobalScope::Dispose(); ThreadedWorkletGlobalScope::Dispose();
} }
Animator* AnimationWorkletGlobalScope::GetOrCreateAnimatorFor( Animator* AnimationWorkletGlobalScope::CreateAnimatorFor(
int animation_id, int animation_id,
const String& name, const String& name,
WorkletAnimationOptions* options) { WorkletAnimationOptions* options) {
Animator* animator = animators_.at(animation_id); DCHECK(!animators_.at(animation_id));
if (!animator) { Animator* animator = CreateInstance(name, options);
// This is a new animation so we should create an animator for it. if (!animator)
animator = CreateInstance(name, options); return nullptr;
if (!animator) animators_.Set(animation_id, animator);
return nullptr;
animators_.Set(animation_id, animator);
}
return animator; return animator;
} }
...@@ -106,37 +82,42 @@ AnimationWorkletGlobalScope::Mutate( ...@@ -106,37 +82,42 @@ AnimationWorkletGlobalScope::Mutate(
const CompositorMutatorInputState& mutator_input) { const CompositorMutatorInputState& mutator_input) {
DCHECK(IsContextThread()); DCHECK(IsContextThread());
// Clean any animator that is not updated
ScopedAnimatorsSweeper sweeper(animators_);
ScriptState* script_state = ScriptController()->GetScriptState(); ScriptState* script_state = ScriptController()->GetScriptState();
ScriptState::Scope scope(script_state); ScriptState::Scope scope(script_state);
std::unique_ptr<CompositorMutatorOutputState> result = std::unique_ptr<CompositorMutatorOutputState> result =
std::make_unique<CompositorMutatorOutputState>(); std::make_unique<CompositorMutatorOutputState>();
for (const CompositorMutatorInputState::AnimationState& animation_input : for (const auto& id : mutator_input.removed_animations)
mutator_input.animations) { animators_.erase(id);
int id = animation_input.animation_id;
const String name = String::FromUTF8(animation_input.name.data(), for (const auto& animation : mutator_input.added_and_updated_animations) {
animation_input.name.size()); int id = animation.animation_id;
DCHECK(!animators_.at(id));
const String name =
String::FromUTF8(animation.name.data(), animation.name.size());
// Down casting to blink type to access the serialized value. // Down casting to blink type to access the serialized value.
WorkletAnimationOptions* options = WorkletAnimationOptions* options =
static_cast<WorkletAnimationOptions*>(animation_input.options.get()); static_cast<WorkletAnimationOptions*>(animation.options.get());
Animator* animator = CreateAnimatorFor(id, name, options);
if (!animator)
continue;
UpdateAnimation(animator, script_state, id, animation.current_time,
result.get());
}
Animator* animator = GetOrCreateAnimatorFor(id, name, options); for (const auto& animation : mutator_input.updated_animations) {
// TODO(majidvp): This means there is an animatorName for which int id = animation.animation_id;
// definition was not registered. We should handle this case gracefully. Animator* animator = animators_.at(id);
// http://crbug.com/776017 // We don't try to create an animator if there isn't any.
if (!animator) if (!animator)
continue; continue;
CompositorMutatorOutputState::AnimationState animation_output; UpdateAnimation(animator, script_state, id, animation.current_time,
if (animator->Animate(script_state, animation_input, &animation_output)) { result.get());
animation_output.animation_id = id;
result->animations.push_back(std::move(animation_output));
}
} }
return result; return result;
......
...@@ -61,9 +61,9 @@ class MODULES_EXPORT AnimationWorkletGlobalScope ...@@ -61,9 +61,9 @@ class MODULES_EXPORT AnimationWorkletGlobalScope
void RegisterWithProxyClientIfNeeded(); void RegisterWithProxyClientIfNeeded();
Animator* CreateInstance(const String& name, Animator* CreateInstance(const String& name,
WorkletAnimationOptions* options); WorkletAnimationOptions* options);
Animator* GetOrCreateAnimatorFor(int animation_id, Animator* CreateAnimatorFor(int animation_id,
const String& name, const String& name,
WorkletAnimationOptions* options); WorkletAnimationOptions* options);
typedef HeapHashMap<String, TraceWrapperMember<AnimatorDefinition>> typedef HeapHashMap<String, TraceWrapperMember<AnimatorDefinition>>
DefinitionMap; DefinitionMap;
DefinitionMap animator_definitions_; DefinitionMap animator_definitions_;
......
...@@ -223,7 +223,7 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase { ...@@ -223,7 +223,7 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
// Passing a new input state with a new animation id should cause the // Passing a new input state with a new animation id should cause the
// worklet to create and animate an animator. // worklet to create and animate an animator.
CompositorMutatorInputState state; CompositorMutatorInputState state;
state.animations.emplace_back(1, "test", 5000, nullptr); state.added_and_updated_animations.emplace_back(1, "test", 5000, nullptr);
std::unique_ptr<CompositorMutatorOutputState> output = std::unique_ptr<CompositorMutatorOutputState> output =
global_scope->Mutate(state); global_scope->Mutate(state);
...@@ -271,7 +271,7 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase { ...@@ -271,7 +271,7 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
// Passing a new input state with a new animation id should cause the // Passing a new input state with a new animation id should cause the
// worklet to create and animate an animator. // worklet to create and animate an animator.
CompositorMutatorInputState state; CompositorMutatorInputState state;
state.animations.emplace_back(1, "test", 5000, nullptr); state.added_and_updated_animations.emplace_back(1, "test", 5000, nullptr);
std::unique_ptr<CompositorMutatorOutputState> output = std::unique_ptr<CompositorMutatorOutputState> output =
global_scope->Mutate(state); global_scope->Mutate(state);
...@@ -281,12 +281,104 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase { ...@@ -281,12 +281,104 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
EXPECT_EQ(output->animations[0].local_time, EXPECT_EQ(output->animations[0].local_time,
WTF::TimeDelta::FromMillisecondsD(123)); WTF::TimeDelta::FromMillisecondsD(123));
// Passing a new empty input state should cause the worklet to remove the waitable_event->Signal();
// previously constructed animator. }
CompositorMutatorInputState empty_state;
output = global_scope->Mutate(empty_state); // This test verifies that an animator instance is not created if
EXPECT_TRUE(output); // MutatorInputState does not have an animation in
EXPECT_EQ(output->animations.size(), 0ul); // added_and_updated_animations.
void RunAnimatorInstanceCreationTestOnWorklet(WorkerThread* thread,
WaitableEvent* waitable_event) {
AnimationWorkletGlobalScope* global_scope =
static_cast<AnimationWorkletGlobalScope*>(thread->GlobalScope());
ASSERT_TRUE(global_scope);
ASSERT_TRUE(global_scope->IsAnimationWorkletGlobalScope());
ScriptState* script_state =
global_scope->ScriptController()->GetScriptState();
ASSERT_TRUE(script_state);
v8::Isolate* isolate = script_state->GetIsolate();
ASSERT_TRUE(isolate);
EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
ScriptState::Scope scope(script_state);
global_scope->ScriptController()->Evaluate(ScriptSourceCode(
R"JS(
registerAnimator('test', class {
animate (currentTime, effect) {
effect.localTime = 123;
}
});
)JS"));
int animation_id = 1;
CompositorMutatorInputState state;
state.updated_animations.push_back({animation_id, 5000});
EXPECT_EQ(state.added_and_updated_animations.size(), 0u);
EXPECT_EQ(state.updated_animations.size(), 1u);
global_scope->Mutate(state);
EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
state.removed_animations.push_back(animation_id);
EXPECT_EQ(state.added_and_updated_animations.size(), 0u);
EXPECT_EQ(state.removed_animations.size(), 1u);
global_scope->Mutate(state);
EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
state.added_and_updated_animations.push_back(
{animation_id, "test", 5000, nullptr});
EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
global_scope->Mutate(state);
EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 1u);
waitable_event->Signal();
}
// This test verifies that an animator instance is created and removed
// properly.
void RunAnimatorInstanceUpdateTestOnWorklet(WorkerThread* thread,
WaitableEvent* waitable_event) {
AnimationWorkletGlobalScope* global_scope =
static_cast<AnimationWorkletGlobalScope*>(thread->GlobalScope());
ASSERT_TRUE(global_scope);
ASSERT_TRUE(global_scope->IsAnimationWorkletGlobalScope());
ScriptState* script_state =
global_scope->ScriptController()->GetScriptState();
ASSERT_TRUE(script_state);
v8::Isolate* isolate = script_state->GetIsolate();
ASSERT_TRUE(isolate);
EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
ScriptState::Scope scope(script_state);
global_scope->ScriptController()->Evaluate(ScriptSourceCode(
R"JS(
registerAnimator('test', class {
animate (currentTime, effect) {
effect.localTime = 123;
}
});
)JS"));
int animation_id = 1;
CompositorMutatorInputState state;
state.added_and_updated_animations.push_back(
{animation_id, "test", 5000, nullptr});
EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
global_scope->Mutate(state);
EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 1u);
state.added_and_updated_animations.clear();
state.updated_animations.push_back({animation_id, 6000});
EXPECT_EQ(state.added_and_updated_animations.size(), 0u);
EXPECT_EQ(state.updated_animations.size(), 1u);
global_scope->Mutate(state);
EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 1u);
state.updated_animations.clear();
state.removed_animations.push_back(animation_id);
EXPECT_EQ(state.updated_animations.size(), 0u);
EXPECT_EQ(state.removed_animations.size(), 1u);
global_scope->Mutate(state);
EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u); EXPECT_EQ(global_scope->GetAnimatorsSizeForTest(), 0u);
waitable_event->Signal(); waitable_event->Signal();
...@@ -329,6 +421,16 @@ TEST_F(AnimationWorkletGlobalScopeTest, AnimationOutput) { ...@@ -329,6 +421,16 @@ TEST_F(AnimationWorkletGlobalScopeTest, AnimationOutput) {
&AnimationWorkletGlobalScopeTest::RunAnimateOutputTestOnWorklet); &AnimationWorkletGlobalScopeTest::RunAnimateOutputTestOnWorklet);
} }
TEST_F(AnimationWorkletGlobalScopeTest, AnimatorInstanceCreation) {
RunTestOnWorkletThread(&AnimationWorkletGlobalScopeTest::
RunAnimatorInstanceCreationTestOnWorklet);
}
TEST_F(AnimationWorkletGlobalScopeTest, AnimatorInstanceUpdate) {
RunTestOnWorkletThread(
&AnimationWorkletGlobalScopeTest::RunAnimatorInstanceUpdateTestOnWorklet);
}
TEST_F(AnimationWorkletGlobalScopeTest, TEST_F(AnimationWorkletGlobalScopeTest,
ShouldRegisterItselfAfterFirstAnimatorRegistration) { ShouldRegisterItselfAfterFirstAnimatorRegistration) {
MockAnimationWorkletProxyClient* proxy_client = MockAnimationWorkletProxyClient* proxy_client =
......
...@@ -29,10 +29,8 @@ void Animator::Trace(blink::Visitor* visitor) { ...@@ -29,10 +29,8 @@ void Animator::Trace(blink::Visitor* visitor) {
} }
bool Animator::Animate(ScriptState* script_state, bool Animator::Animate(ScriptState* script_state,
const CompositorMutatorInputState::AnimationState& input, double current_time,
CompositorMutatorOutputState::AnimationState* output) { CompositorMutatorOutputState::AnimationState* output) {
did_animate_ = true;
v8::Isolate* isolate = script_state->GetIsolate(); v8::Isolate* isolate = script_state->GetIsolate();
v8::Local<v8::Object> instance = instance_.NewLocal(isolate); v8::Local<v8::Object> instance = instance_.NewLocal(isolate);
...@@ -51,7 +49,7 @@ bool Animator::Animate(ScriptState* script_state, ...@@ -51,7 +49,7 @@ bool Animator::Animate(ScriptState* script_state,
ToV8(effect_, script_state->GetContext()->Global(), isolate); ToV8(effect_, script_state->GetContext()->Global(), isolate);
v8::Local<v8::Value> v8_current_time = v8::Local<v8::Value> v8_current_time =
ToV8(input.current_time, script_state->GetContext()->Global(), isolate); ToV8(current_time, script_state->GetContext()->Global(), isolate);
v8::Local<v8::Value> argv[] = {v8_current_time, v8_effect}; v8::Local<v8::Value> argv[] = {v8_current_time, v8_effect};
......
...@@ -34,19 +34,15 @@ class Animator final : public GarbageCollectedFinalized<Animator>, ...@@ -34,19 +34,15 @@ class Animator final : public GarbageCollectedFinalized<Animator>,
// latest state coming from |AnimationHost| as input and fills // latest state coming from |AnimationHost| as input and fills
// the output state with new updates. // the output state with new updates.
bool Animate(ScriptState*, bool Animate(ScriptState*,
const CompositorMutatorInputState::AnimationState&, double current_time,
CompositorMutatorOutputState::AnimationState*); CompositorMutatorOutputState::AnimationState*);
bool did_animate() const { return did_animate_; }
void clear_did_animate() { did_animate_ = false; }
private: private:
// This object keeps the definition object, and animator instance alive. // This object keeps the definition object, and animator instance alive.
// It participates in wrapper tracing as it holds onto V8 wrappers. // It participates in wrapper tracing as it holds onto V8 wrappers.
TraceWrapperMember<AnimatorDefinition> definition_; TraceWrapperMember<AnimatorDefinition> definition_;
TraceWrapperV8Reference<v8::Object> instance_; TraceWrapperV8Reference<v8::Object> instance_;
bool did_animate_ = false;
Member<EffectProxy> effect_; Member<EffectProxy> effect_;
}; };
......
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