Commit c035b5fc authored by Majid Valipour's avatar Majid Valipour Committed by Commit Bot

[animation-worket] Ensure each worklet receives only its own input

Previously we would allow worklet to access and act on all of worklet inputs.
This is bad since it can leak information between different animation
worklet global scopes.

Summary of the fix:
- Introduce WorkletAnimationId which can uniquely identify both the worklet and animation.
- Give each worklet animation its own Id generated using its underlying worklet's scope.
- Plumb this new Id to cc.
- AnimationHost produces input state which is bucketed per scope.
- CompositorMutatorImpl only passes to each compositor animator the input.
  bucket that matches that compositor animator's scope Id.
- For additional safety added DCHECK to verify that each scope is receiving only its
  own input.

TEST: compositor_mutator_impl_test.cc
Bug: 857479

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ib4b8f610ef661bdbf09c4c0252a2b9c4ba04db16
Reviewed-on: https://chromium-review.googlesource.com/1120436Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Commit-Queue: Majid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#572572}
parent 3e326e4a
...@@ -620,12 +620,14 @@ void AnimationHost::SetMutationUpdate( ...@@ -620,12 +620,14 @@ void AnimationHost::SetMutationUpdate(
TRACE_EVENT0("cc", "AnimationHost::SetMutationUpdate"); TRACE_EVENT0("cc", "AnimationHost::SetMutationUpdate");
for (auto& animation_state : output_state->animations) { for (auto& animation_state : output_state->animations) {
int id = animation_state.animation_id; WorkletAnimationId id = animation_state.worklet_animation_id;
// TODO(majidvp): Use a map to make lookup O(1) // TODO(majidvp): Use a map to make lookup O(1)
auto to_update = auto to_update = std::find_if(
std::find_if(ticking_animations_.begin(), ticking_animations_.end(), ticking_animations_.begin(), ticking_animations_.end(), [id](auto& it) {
[id](auto& it) { return it->id() == id; }); return it->IsWorkletAnimation() &&
ToWorkletAnimation(it.get())->worklet_animation_id() == id;
});
if (to_update == ticking_animations_.end()) if (to_update == ticking_animations_.end())
continue; continue;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "cc/animation/worklet_animation.h" #include "cc/animation/worklet_animation.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/animation/keyframe_effect.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"
...@@ -11,12 +12,14 @@ ...@@ -11,12 +12,14 @@
namespace cc { namespace cc {
WorkletAnimation::WorkletAnimation( WorkletAnimation::WorkletAnimation(
int id, int cc_animation_id,
WorkletAnimationId worklet_animation_id,
const std::string& name, const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<ScrollTimeline> scroll_timeline,
std::unique_ptr<AnimationOptions> options, std::unique_ptr<AnimationOptions> options,
bool is_controlling_instance) bool is_controlling_instance)
: SingleKeyframeEffectAnimation(id), : SingleKeyframeEffectAnimation(cc_animation_id),
worklet_animation_id_(worklet_animation_id),
name_(name), name_(name),
scroll_timeline_(std::move(scroll_timeline)), scroll_timeline_(std::move(scroll_timeline)),
options_(std::move(options)), options_(std::move(options)),
...@@ -28,12 +31,13 @@ WorkletAnimation::WorkletAnimation( ...@@ -28,12 +31,13 @@ WorkletAnimation::WorkletAnimation(
WorkletAnimation::~WorkletAnimation() = default; WorkletAnimation::~WorkletAnimation() = default;
scoped_refptr<WorkletAnimation> WorkletAnimation::Create( scoped_refptr<WorkletAnimation> WorkletAnimation::Create(
int id, WorkletAnimationId worklet_animation_id,
const std::string& name, const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<ScrollTimeline> scroll_timeline,
std::unique_ptr<AnimationOptions> options) { std::unique_ptr<AnimationOptions> options) {
return WrapRefCounted(new WorkletAnimation( return WrapRefCounted(new WorkletAnimation(
id, name, std::move(scroll_timeline), std::move(options), false)); AnimationIdProvider::NextAnimationId(), worklet_animation_id, name,
std::move(scroll_timeline), std::move(options), false));
} }
scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const { scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const {
...@@ -41,8 +45,9 @@ scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const { ...@@ -41,8 +45,9 @@ scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const {
if (scroll_timeline_) if (scroll_timeline_)
impl_timeline = scroll_timeline_->CreateImplInstance(); impl_timeline = scroll_timeline_->CreateImplInstance();
return WrapRefCounted(new WorkletAnimation( return WrapRefCounted(new WorkletAnimation(id(), worklet_animation_id_,
id(), name(), std::move(impl_timeline), CloneOptions(), true)); name(), std::move(impl_timeline),
CloneOptions(), true));
} }
void WorkletAnimation::PushPropertiesTo(Animation* animation_impl) { void WorkletAnimation::PushPropertiesTo(Animation* animation_impl) {
...@@ -89,15 +94,15 @@ void WorkletAnimation::UpdateInputState(MutatorInputState* input_state, ...@@ -89,15 +94,15 @@ void WorkletAnimation::UpdateInputState(MutatorInputState* input_state,
switch (state_) { switch (state_) {
case State::PENDING: case State::PENDING:
input_state->added_and_updated_animations.push_back( input_state->Add(
{id(), name(), current_time, CloneOptions()}); {worklet_animation_id(), name(), current_time, CloneOptions()});
state_ = State::RUNNING; state_ = State::RUNNING;
break; break;
case State::RUNNING: case State::RUNNING:
input_state->updated_animations.push_back({id(), current_time}); input_state->Update({worklet_animation_id(), current_time});
break; break;
case State::REMOVED: case State::REMOVED:
input_state->removed_animations.push_back(id()); input_state->Remove(worklet_animation_id());
break; break;
} }
} }
......
...@@ -24,18 +24,20 @@ class CC_ANIMATION_EXPORT WorkletAnimation final ...@@ -24,18 +24,20 @@ class CC_ANIMATION_EXPORT WorkletAnimation final
: public SingleKeyframeEffectAnimation { : public SingleKeyframeEffectAnimation {
public: public:
enum class State { PENDING, RUNNING, REMOVED }; enum class State { PENDING, RUNNING, REMOVED };
WorkletAnimation(int id, WorkletAnimation(int cc_animation_id,
WorkletAnimationId worklet_animation_id,
const std::string& name, const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<ScrollTimeline> scroll_timeline,
std::unique_ptr<AnimationOptions> options, std::unique_ptr<AnimationOptions> options,
bool is_controlling_instance); bool is_controlling_instance);
static scoped_refptr<WorkletAnimation> Create( static scoped_refptr<WorkletAnimation> Create(
int id, WorkletAnimationId worklet_animation_id,
const std::string& name, const std::string& name,
std::unique_ptr<ScrollTimeline> scroll_timeline, std::unique_ptr<ScrollTimeline> scroll_timeline,
std::unique_ptr<AnimationOptions> options); std::unique_ptr<AnimationOptions> options);
scoped_refptr<Animation> CreateImplInstance() const override; scoped_refptr<Animation> CreateImplInstance() const override;
WorkletAnimationId worklet_animation_id() { return worklet_animation_id_; }
const std::string& name() const { return name_; } const std::string& name() const { return name_; }
const ScrollTimeline* scroll_timeline() const { const ScrollTimeline* scroll_timeline() const {
return scroll_timeline_.get(); return scroll_timeline_.get();
...@@ -83,6 +85,7 @@ class CC_ANIMATION_EXPORT WorkletAnimation final ...@@ -83,6 +85,7 @@ class CC_ANIMATION_EXPORT WorkletAnimation final
return options_ ? options_->Clone() : nullptr; return options_ ? options_->Clone() : nullptr;
} }
WorkletAnimationId worklet_animation_id_;
std::string name_; std::string name_;
// The ScrollTimeline associated with the underlying animation. If null, the // The ScrollTimeline associated with the underlying animation. If null, the
......
...@@ -33,20 +33,20 @@ class WorkletAnimationTest : public AnimationTimelinesTest { ...@@ -33,20 +33,20 @@ class WorkletAnimationTest : public AnimationTimelinesTest {
worklet_animation_ = WorkletAnimation::Create( worklet_animation_ = WorkletAnimation::Create(
worklet_animation_id_, "test_name", nullptr, nullptr); worklet_animation_id_, "test_name", nullptr, nullptr);
int cc_id = worklet_animation_->id();
worklet_animation_->AttachElement(element_id_); worklet_animation_->AttachElement(element_id_);
host_->AddAnimationTimeline(timeline_); host_->AddAnimationTimeline(timeline_);
timeline_->AttachAnimation(worklet_animation_); timeline_->AttachAnimation(worklet_animation_);
host_->PushPropertiesTo(host_impl_); host_->PushPropertiesTo(host_impl_);
timeline_impl_ = host_impl_->GetTimelineById(timeline_id_); timeline_impl_ = host_impl_->GetTimelineById(timeline_id_);
worklet_animation_impl_ = ToWorkletAnimation( worklet_animation_impl_ =
timeline_impl_->GetAnimationById(worklet_animation_id_)); ToWorkletAnimation(timeline_impl_->GetAnimationById(cc_id));
} }
scoped_refptr<WorkletAnimation> worklet_animation_; scoped_refptr<WorkletAnimation> worklet_animation_;
scoped_refptr<WorkletAnimation> worklet_animation_impl_; scoped_refptr<WorkletAnimation> worklet_animation_impl_;
int worklet_animation_id_ = 11; WorkletAnimationId worklet_animation_id_{11, 12};
}; };
class MockScrollTimeline : public ScrollTimeline { class MockScrollTimeline : public ScrollTimeline {
...@@ -73,8 +73,7 @@ TEST_F(WorkletAnimationTest, LocalTimeIsUsedWithAnimations) { ...@@ -73,8 +73,7 @@ TEST_F(WorkletAnimationTest, LocalTimeIsUsedWithAnimations) {
host_impl_->ActivateAnimations(); host_impl_->ActivateAnimations();
base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2); base::TimeDelta local_time = base::TimeDelta::FromSecondsD(duration / 2);
worklet_animation_impl_->SetOutputState({worklet_animation_id_, local_time});
worklet_animation_impl_->SetOutputState({0, local_time});
TickAnimationsTransferEvents(base::TimeTicks(), 0u); TickAnimationsTransferEvents(base::TimeTicks(), 0u);
...@@ -157,7 +156,9 @@ TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) { ...@@ -157,7 +156,9 @@ TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) {
std::make_unique<MutatorInputState>(); std::make_unique<MutatorInputState>();
worklet_animation->UpdateInputState(state.get(), base::TimeTicks::Now(), worklet_animation->UpdateInputState(state.get(), base::TimeTicks::Now(),
scroll_tree, true); scroll_tree, true);
EXPECT_EQ(1234, state->added_and_updated_animations[0].current_time); std::unique_ptr<AnimationWorkletInput> input =
state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(1234, input->added_and_updated_animations[0].current_time);
} }
TEST_F(WorkletAnimationTest, TEST_F(WorkletAnimationTest,
...@@ -173,23 +174,25 @@ TEST_F(WorkletAnimationTest, ...@@ -173,23 +174,25 @@ 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;
std::unique_ptr<MutatorInputState> first_state = std::unique_ptr<MutatorInputState> state =
std::make_unique<MutatorInputState>(); std::make_unique<MutatorInputState>();
worklet_animation->UpdateInputState(first_state.get(), first_ticks, worklet_animation->UpdateInputState(state.get(), first_ticks, scroll_tree,
scroll_tree, true); 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->added_and_updated_animations[0].current_time); std::unique_ptr<AnimationWorkletInput> input =
std::unique_ptr<MutatorInputState> second_state = state->TakeWorkletState(worklet_animation_id_.scope_id);
std::make_unique<MutatorInputState>(); EXPECT_EQ(0, input->added_and_updated_animations[0].current_time);
worklet_animation->UpdateInputState(second_state.get(), second_ticks, state.reset(new MutatorInputState);
scroll_tree, true); worklet_animation->UpdateInputState(state.get(), second_ticks, scroll_tree,
EXPECT_EQ(123.4, second_state->updated_animations[0].current_time); true);
input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(123.4, input->updated_animations[0].current_time);
// Should always offset from start time. // Should always offset from start time.
std::unique_ptr<MutatorInputState> third_state = state.reset(new MutatorInputState());
std::make_unique<MutatorInputState>(); worklet_animation->UpdateInputState(state.get(), third_ticks, scroll_tree,
worklet_animation->UpdateInputState(third_state.get(), third_ticks, true);
scroll_tree, true); input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(246.8, third_state->updated_animations[0].current_time); EXPECT_EQ(246.8, input->updated_animations[0].current_time);
} }
// This test verifies that worklet animation state is properly updated. // This test verifies that worklet animation state is properly updated.
...@@ -223,10 +226,12 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) { ...@@ -223,10 +226,12 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
base::TimeTicks time; base::TimeTicks time;
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true); true);
EXPECT_EQ(state->added_and_updated_animations.size(), 1u); std::unique_ptr<AnimationWorkletInput> input =
EXPECT_EQ("test_name", state->added_and_updated_animations[0].name); state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(state->updated_animations.size(), 0u); EXPECT_EQ(input->added_and_updated_animations.size(), 1u);
EXPECT_EQ(state->removed_animations.size(), 0u); EXPECT_EQ("test_name", input->added_and_updated_animations[0].name);
EXPECT_EQ(input->updated_animations.size(), 0u);
EXPECT_EQ(input->removed_animations.size(), 0u);
// The state of WorkletAnimation is updated to RUNNING after calling // The state of WorkletAnimation is updated to RUNNING after calling
// UpdateInputState above. // UpdateInputState above.
...@@ -234,9 +239,10 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) { ...@@ -234,9 +239,10 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
time += base::TimeDelta::FromSecondsD(0.1); time += base::TimeDelta::FromSecondsD(0.1);
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true); true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u); input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(state->updated_animations.size(), 1u); EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations.size(), 0u); EXPECT_EQ(input->updated_animations.size(), 1u);
EXPECT_EQ(input->removed_animations.size(), 0u);
// Operating on individual KeyframeModel doesn't affect the state of // Operating on individual KeyframeModel doesn't affect the state of
// WorkletAnimation. // WorkletAnimation.
...@@ -245,9 +251,10 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) { ...@@ -245,9 +251,10 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
time += base::TimeDelta::FromSecondsD(0.1); time += base::TimeDelta::FromSecondsD(0.1);
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true); true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u); input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(state->updated_animations.size(), 1u); EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations.size(), 0u); EXPECT_EQ(input->updated_animations.size(), 1u);
EXPECT_EQ(input->removed_animations.size(), 0u);
// WorkletAnimation sets state to REMOVED when JavaScript fires cancel() which // WorkletAnimation sets state to REMOVED when JavaScript fires cancel() which
// leads to RemoveKeyframeModels. // leads to RemoveKeyframeModels.
...@@ -256,10 +263,11 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) { ...@@ -256,10 +263,11 @@ TEST_F(WorkletAnimationTest, WorkletAnimationStateTestWithSingleKeyframeModel) {
state.reset(new MutatorInputState()); state.reset(new MutatorInputState());
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true); true);
EXPECT_EQ(state->added_and_updated_animations.size(), 0u); input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(state->updated_animations.size(), 0u); EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations.size(), 1u); EXPECT_EQ(input->updated_animations.size(), 0u);
EXPECT_EQ(state->removed_animations[0], worklet_animation_id_); EXPECT_EQ(input->removed_animations.size(), 1u);
EXPECT_EQ(input->removed_animations[0], worklet_animation_id_);
} }
// This test verifies that worklet animation gets skipped properly. // This test verifies that worklet animation gets skipped properly.
...@@ -289,21 +297,25 @@ TEST_F(WorkletAnimationTest, SkipUnchangedAnimations) { ...@@ -289,21 +297,25 @@ TEST_F(WorkletAnimationTest, SkipUnchangedAnimations) {
base::TimeTicks time; base::TimeTicks time;
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true); true);
EXPECT_EQ(state->added_and_updated_animations.size(), 1u); std::unique_ptr<AnimationWorkletInput> input =
EXPECT_EQ(state->updated_animations.size(), 0u); state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->added_and_updated_animations.size(), 1u);
EXPECT_EQ(input->updated_animations.size(), 0u);
state.reset(new MutatorInputState()); state.reset(new MutatorInputState());
// No update on the input state if input time stays the same. // No update on the input state if input time stays the same.
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true); true);
EXPECT_EQ(state->updated_animations.size(), 0u); input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_FALSE(input);
state.reset(new MutatorInputState()); state.reset(new MutatorInputState());
// Different input time causes the input state to be updated. // Different input time causes the input state to be updated.
time += base::TimeDelta::FromSecondsD(0.1); time += base::TimeDelta::FromSecondsD(0.1);
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true); true);
EXPECT_EQ(state->updated_animations.size(), 1u); input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(input->updated_animations.size(), 1u);
state.reset(new MutatorInputState()); state.reset(new MutatorInputState());
// Input state gets updated when the worklet animation is to be removed even // Input state gets updated when the worklet animation is to be removed even
...@@ -311,8 +323,9 @@ TEST_F(WorkletAnimationTest, SkipUnchangedAnimations) { ...@@ -311,8 +323,9 @@ TEST_F(WorkletAnimationTest, SkipUnchangedAnimations) {
worklet_animation_impl_->RemoveKeyframeModels(); worklet_animation_impl_->RemoveKeyframeModels();
worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree, worklet_animation_impl_->UpdateInputState(state.get(), time, scroll_tree,
true); true);
EXPECT_EQ(state->updated_animations.size(), 0u); input = state->TakeWorkletState(worklet_animation_id_.scope_id);
EXPECT_EQ(state->removed_animations.size(), 1u); EXPECT_EQ(input->updated_animations.size(), 0u);
EXPECT_EQ(input->removed_animations.size(), 1u);
} }
} // namespace } // namespace
......
...@@ -4,25 +4,88 @@ ...@@ -4,25 +4,88 @@
#include "cc/trees/layer_tree_mutator.h" #include "cc/trees/layer_tree_mutator.h"
#include <algorithm>
namespace cc { namespace cc {
MutatorInputState::AddAndUpdateState::AddAndUpdateState( AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState(
int animation_id, WorkletAnimationId worklet_animation_id,
std::string name, std::string name,
double current_time, double current_time,
std::unique_ptr<AnimationOptions> options) std::unique_ptr<AnimationOptions> options)
: animation_id(animation_id), : worklet_animation_id(worklet_animation_id),
name(name), name(name),
current_time(current_time), current_time(current_time),
options(std::move(options)) {} options(std::move(options)) {}
MutatorInputState::AddAndUpdateState::AddAndUpdateState(AddAndUpdateState&&) = AnimationWorkletInput::AddAndUpdateState::AddAndUpdateState(
default; AddAndUpdateState&&) = default;
MutatorInputState::AddAndUpdateState::~AddAndUpdateState() = default; AnimationWorkletInput::AddAndUpdateState::~AddAndUpdateState() = default;
#if DCHECK_IS_ON()
bool AnimationWorkletInput::ValidateScope(int scope_id) const {
return std::all_of(added_and_updated_animations.cbegin(),
added_and_updated_animations.cend(),
[scope_id](auto& it) {
return it.worklet_animation_id.scope_id == scope_id;
}) &&
std::all_of(updated_animations.cbegin(), updated_animations.cend(),
[scope_id](auto& it) {
return it.worklet_animation_id.scope_id == scope_id;
}) &&
std::all_of(removed_animations.cbegin(), removed_animations.cend(),
[scope_id](auto& it) { return it.scope_id == scope_id; });
}
#endif
AnimationWorkletInput::AnimationWorkletInput() = default;
AnimationWorkletInput::~AnimationWorkletInput() = default;
MutatorInputState::MutatorInputState() = default; MutatorInputState::MutatorInputState() = default;
MutatorInputState::~MutatorInputState() = default; MutatorInputState::~MutatorInputState() = default;
MutatorOutputState::MutatorOutputState() = default; bool MutatorInputState::IsEmpty() const {
MutatorOutputState::~MutatorOutputState() = default; // If there is an AnimationWorkletInput entry in the map then that entry is
// guranteed to be non-empty. So checking |inputs_| map emptiness is
// sufficient.
return inputs_.empty();
}
AnimationWorkletInput& MutatorInputState::EnsureWorkletEntry(int id) {
auto it = inputs_.find(id);
if (it == inputs_.end())
it = inputs_.emplace_hint(it, id, new AnimationWorkletInput);
return *it->second;
}
void MutatorInputState::Add(AnimationWorkletInput::AddAndUpdateState&& state) {
AnimationWorkletInput& worklet_input =
EnsureWorkletEntry(state.worklet_animation_id.scope_id);
worklet_input.added_and_updated_animations.push_back(std::move(state));
}
void MutatorInputState::Update(AnimationWorkletInput::UpdateState&& state) {
AnimationWorkletInput& worklet_input =
EnsureWorkletEntry(state.worklet_animation_id.scope_id);
worklet_input.updated_animations.push_back(std::move(state));
}
void MutatorInputState::Remove(WorkletAnimationId worklet_animation_id) {
AnimationWorkletInput& worklet_input =
EnsureWorkletEntry(worklet_animation_id.scope_id);
worklet_input.removed_animations.push_back(worklet_animation_id);
}
std::unique_ptr<AnimationWorkletInput> MutatorInputState::TakeWorkletState(
int scope_id) {
auto it = inputs_.find(scope_id);
if (it == inputs_.end())
return nullptr;
std::unique_ptr<AnimationWorkletInput> result = std::move(it->second);
inputs_.erase(it);
return result;
}
AnimationWorkletOutput::AnimationWorkletOutput() = default;
AnimationWorkletOutput::~AnimationWorkletOutput() = default;
} // namespace cc } // namespace cc
...@@ -12,50 +12,96 @@ ...@@ -12,50 +12,96 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map>
#include <vector> #include <vector>
namespace cc { namespace cc {
struct CC_EXPORT MutatorInputState { struct CC_EXPORT WorkletAnimationId {
// Uniquely identifies the animation worklet with which this animation is
// associated.
int scope_id;
// Uniquely identifies the animation within its animation worklet. Note that
// animation_id is only guaranteed to be unique per animation worklet.
int animation_id;
inline bool operator==(const WorkletAnimationId& rhs) const {
return (this->scope_id == rhs.scope_id) &&
(this->animation_id == rhs.animation_id);
}
};
struct CC_EXPORT AnimationWorkletInput {
struct CC_EXPORT AddAndUpdateState { struct CC_EXPORT AddAndUpdateState {
int animation_id; WorkletAnimationId worklet_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; double current_time;
std::unique_ptr<AnimationOptions> options; std::unique_ptr<AnimationOptions> options;
AddAndUpdateState(int animation_id, AddAndUpdateState(WorkletAnimationId worklet_animation_id,
std::string name, std::string name,
double current_time, double current_time,
std::unique_ptr<AnimationOptions> options); std::unique_ptr<AnimationOptions> options);
AddAndUpdateState(AddAndUpdateState&&); AddAndUpdateState(AddAndUpdateState&&);
~AddAndUpdateState(); ~AddAndUpdateState();
}; };
struct CC_EXPORT UpdateState { struct CC_EXPORT UpdateState {
int animation_id = 0; WorkletAnimationId worklet_animation_id;
// Worklet animation's current time, from its associated timeline. // Worklet animation's current time, from its associated timeline.
double current_time = 0; double current_time = 0;
}; };
// Note: When adding any new fields please also update ValidateScope to
// reflect them if necessary.
std::vector<AddAndUpdateState> added_and_updated_animations;
std::vector<UpdateState> updated_animations;
std::vector<WorkletAnimationId> removed_animations;
AnimationWorkletInput();
~AnimationWorkletInput();
#if DCHECK_IS_ON()
// Verifies all animation states have the expected scope id.
bool ValidateScope(int scope_id) const;
#endif
DISALLOW_COPY_AND_ASSIGN(AnimationWorkletInput);
};
class CC_EXPORT MutatorInputState {
public:
MutatorInputState(); MutatorInputState();
~MutatorInputState(); ~MutatorInputState();
bool IsEmpty() { bool IsEmpty() const;
return added_and_updated_animations.empty() && updated_animations.empty() && void Add(AnimationWorkletInput::AddAndUpdateState&& state);
removed_animations.empty(); void Update(AnimationWorkletInput::UpdateState&& state);
} void Remove(WorkletAnimationId worklet_animation_id);
std::vector<AddAndUpdateState> added_and_updated_animations; // Returns input for animation worklet with the given |scope_id| and nullptr
std::vector<UpdateState> updated_animations; // if there is no input.
std::vector<int> removed_animations; std::unique_ptr<AnimationWorkletInput> TakeWorkletState(int scope_id);
private:
using InputMap =
std::unordered_map<int, std::unique_ptr<AnimationWorkletInput>>;
// Maps a scope id to its associated AnimationWorkletInput instance.
// Only contains scope ids for which there is a non-empty input.
InputMap inputs_;
// Returns iterator pointing to the entry in |inputs_| map whose key is id. It
// inserts a new entry if none exists.
AnimationWorkletInput& EnsureWorkletEntry(int id);
DISALLOW_COPY_AND_ASSIGN(MutatorInputState); DISALLOW_COPY_AND_ASSIGN(MutatorInputState);
}; };
struct CC_EXPORT MutatorOutputState { struct CC_EXPORT AnimationWorkletOutput {
struct CC_EXPORT AnimationState { struct CC_EXPORT AnimationState {
int animation_id = 0; WorkletAnimationId worklet_animation_id;
// The animator effect's local time. // The animator effect's local time.
// TODO(majidvp): This assumes each animator has a single output effect // TODO(majidvp): This assumes each animator has a single output effect
// which does not hold once we state support group effects. // which does not hold once we state support group effects.
...@@ -63,12 +109,16 @@ struct CC_EXPORT MutatorOutputState { ...@@ -63,12 +109,16 @@ struct CC_EXPORT MutatorOutputState {
base::TimeDelta local_time; base::TimeDelta local_time;
}; };
MutatorOutputState(); AnimationWorkletOutput();
~MutatorOutputState(); ~AnimationWorkletOutput();
std::vector<AnimationState> animations; std::vector<AnimationState> animations;
}; };
// LayerTreeMutatorClient processes worklet outputs individually so we can
// define mutator output to be the same as animation worklet output.
using MutatorOutputState = AnimationWorkletOutput;
class LayerTreeMutatorClient { class LayerTreeMutatorClient {
public: public:
// Called when mutator needs to update its output. // Called when mutator needs to update its output.
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/modules/animationworklet/animation_worklet.h" #include "third_party/blink/renderer/modules/animationworklet/animation_worklet.h"
#include "base/atomic_sequence_num.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/dom/animation_worklet_proxy_client.h" #include "third_party/blink/renderer/core/dom/animation_worklet_proxy_client.h"
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
...@@ -13,9 +14,18 @@ ...@@ -13,9 +14,18 @@
#include "third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.h" #include "third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_impl.h"
#include "third_party/blink/renderer/modules/animationworklet/animation_worklet_thread.h" #include "third_party/blink/renderer/modules/animationworklet/animation_worklet_thread.h"
base::AtomicSequenceNumber g_next_worklet_id;
int NextId() {
// Start id from 1. This way it safe to use it as key in hashmap with default
// key traits.
return g_next_worklet_id.GetNext() + 1;
}
namespace blink { namespace blink {
AnimationWorklet::AnimationWorklet(Document* document) : Worklet(document) {} AnimationWorklet::AnimationWorklet(Document* document)
: Worklet(document), scope_id_(NextId()), last_animation_id_(0) {}
AnimationWorklet::~AnimationWorklet() = default; AnimationWorklet::~AnimationWorklet() = default;
...@@ -31,7 +41,7 @@ WorkletGlobalScopeProxy* AnimationWorklet::CreateGlobalScope() { ...@@ -31,7 +41,7 @@ WorkletGlobalScopeProxy* AnimationWorklet::CreateGlobalScope() {
Document* document = ToDocument(GetExecutionContext()); Document* document = ToDocument(GetExecutionContext());
AnimationWorkletProxyClient* proxy_client = AnimationWorkletProxyClient* proxy_client =
AnimationWorkletProxyClientImpl::FromDocument(document); AnimationWorkletProxyClientImpl::FromDocument(document, scope_id_);
WorkerClients* worker_clients = WorkerClients::Create(); WorkerClients* worker_clients = WorkerClients::Create();
ProvideAnimationWorkletProxyClientTo(worker_clients, proxy_client); ProvideAnimationWorkletProxyClientTo(worker_clients, proxy_client);
...@@ -42,6 +52,12 @@ WorkletGlobalScopeProxy* AnimationWorklet::CreateGlobalScope() { ...@@ -42,6 +52,12 @@ WorkletGlobalScopeProxy* AnimationWorklet::CreateGlobalScope() {
return proxy; return proxy;
} }
WorkletAnimationId AnimationWorklet::NextWorkletAnimationId() {
// Id starts from 1. This way it safe to use it as key in hashmap with default
// key traits.
return {.scope_id = scope_id_, .animation_id = ++last_animation_id_};
}
void AnimationWorklet::Trace(blink::Visitor* visitor) { void AnimationWorklet::Trace(blink::Visitor* visitor) {
Worklet::Trace(visitor); Worklet::Trace(visitor);
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/workers/worklet.h" #include "third_party/blink/renderer/core/workers/worklet.h"
#include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/graphics/compositor_animators_state.h"
#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink { namespace blink {
...@@ -25,9 +26,14 @@ class MODULES_EXPORT AnimationWorklet final : public Worklet { ...@@ -25,9 +26,14 @@ class MODULES_EXPORT AnimationWorklet final : public Worklet {
explicit AnimationWorklet(Document*); explicit AnimationWorklet(Document*);
~AnimationWorklet() override; ~AnimationWorklet() override;
WorkletAnimationId NextWorkletAnimationId();
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
private: private:
// Unique id associated with this worklet that is used by cc to identify all
// animations associated it.
int scope_id_;
int last_animation_id_;
// Implements Worklet. // Implements Worklet.
bool NeedsToCreateGlobalScope() final; bool NeedsToCreateGlobalScope() final;
......
...@@ -21,12 +21,12 @@ namespace { ...@@ -21,12 +21,12 @@ namespace {
void UpdateAnimation(Animator* animator, void UpdateAnimation(Animator* animator,
ScriptState* script_state, ScriptState* script_state,
int id, WorkletAnimationId id,
double current_time, double current_time,
CompositorMutatorOutputState* result) { CompositorMutatorOutputState* result) {
CompositorMutatorOutputState::AnimationState animation_output; CompositorMutatorOutputState::AnimationState animation_output;
if (animator->Animate(script_state, current_time, &animation_output)) { if (animator->Animate(script_state, current_time, &animation_output)) {
animation_output.animation_id = id; animation_output.worklet_animation_id = id;
result->animations.push_back(std::move(animation_output)); result->animations.push_back(std::move(animation_output));
} }
} }
...@@ -77,23 +77,22 @@ Animator* AnimationWorkletGlobalScope::CreateAnimatorFor( ...@@ -77,23 +77,22 @@ Animator* AnimationWorkletGlobalScope::CreateAnimatorFor(
return animator; return animator;
} }
std::unique_ptr<CompositorMutatorOutputState> std::unique_ptr<AnimationWorkletOutput> AnimationWorkletGlobalScope::Mutate(
AnimationWorkletGlobalScope::Mutate( const AnimationWorkletInput& mutator_input) {
const CompositorMutatorInputState& mutator_input) {
DCHECK(IsContextThread()); DCHECK(IsContextThread());
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<AnimationWorkletOutput> result =
std::make_unique<CompositorMutatorOutputState>(); std::make_unique<AnimationWorkletOutput>();
for (const auto& id : mutator_input.removed_animations) for (const auto& worklet_animation_id : mutator_input.removed_animations)
animators_.erase(id); animators_.erase(worklet_animation_id.animation_id);
for (const auto& animation : mutator_input.added_and_updated_animations) { for (const auto& animation : mutator_input.added_and_updated_animations) {
int id = animation.animation_id; int id = animation.worklet_animation_id.animation_id;
DCHECK(!animators_.at(id)); DCHECK(!animators_.Contains(id));
const String name = const String name =
String::FromUTF8(animation.name.data(), animation.name.size()); String::FromUTF8(animation.name.data(), animation.name.size());
...@@ -105,19 +104,19 @@ AnimationWorkletGlobalScope::Mutate( ...@@ -105,19 +104,19 @@ AnimationWorkletGlobalScope::Mutate(
if (!animator) if (!animator)
continue; continue;
UpdateAnimation(animator, script_state, id, animation.current_time, UpdateAnimation(animator, script_state, animation.worklet_animation_id,
result.get()); animation.current_time, result.get());
} }
for (const auto& animation : mutator_input.updated_animations) { for (const auto& animation : mutator_input.updated_animations) {
int id = animation.animation_id; int id = animation.worklet_animation_id.animation_id;
Animator* animator = animators_.at(id); Animator* animator = animators_.at(id);
// We don't try to create an animator if there isn't any. // We don't try to create an animator if there isn't any.
if (!animator) if (!animator)
continue; continue;
UpdateAnimation(animator, script_state, id, animation.current_time, UpdateAnimation(animator, script_state, animation.worklet_animation_id,
result.get()); animation.current_time, result.get());
} }
return result; return result;
......
...@@ -42,8 +42,7 @@ class MODULES_EXPORT AnimationWorkletGlobalScope ...@@ -42,8 +42,7 @@ class MODULES_EXPORT AnimationWorkletGlobalScope
bool IsAnimationWorkletGlobalScope() const final { return true; } bool IsAnimationWorkletGlobalScope() const final { return true; }
// Invokes the |animate| function of all of its active animators. // Invokes the |animate| function of all of its active animators.
std::unique_ptr<CompositorMutatorOutputState> Mutate( std::unique_ptr<AnimationWorkletOutput> Mutate(const AnimationWorkletInput&);
const CompositorMutatorInputState&);
// Registers a animator definition with the given name and constructor. // Registers a animator definition with the given name and constructor.
void registerAnimator(const String& name, void registerAnimator(const String& name,
......
...@@ -222,10 +222,12 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase { ...@@ -222,10 +222,12 @@ 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; cc::WorkletAnimationId animation_id = {1, 1};
state.added_and_updated_animations.emplace_back(1, "test", 5000, nullptr); AnimationWorkletInput state;
state.added_and_updated_animations.emplace_back(animation_id, "test", 5000,
nullptr);
std::unique_ptr<CompositorMutatorOutputState> output = std::unique_ptr<AnimationWorkletOutput> output =
global_scope->Mutate(state); global_scope->Mutate(state);
EXPECT_TRUE(output); EXPECT_TRUE(output);
...@@ -270,10 +272,12 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase { ...@@ -270,10 +272,12 @@ 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; cc::WorkletAnimationId animation_id = {1, 1};
state.added_and_updated_animations.emplace_back(1, "test", 5000, nullptr); AnimationWorkletInput state;
state.added_and_updated_animations.emplace_back(animation_id, "test", 5000,
nullptr);
std::unique_ptr<CompositorMutatorOutputState> output = std::unique_ptr<AnimationWorkletOutput> output =
global_scope->Mutate(state); global_scope->Mutate(state);
EXPECT_TRUE(output); EXPECT_TRUE(output);
...@@ -311,8 +315,8 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase { ...@@ -311,8 +315,8 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
}); });
)JS")); )JS"));
int animation_id = 1; cc::WorkletAnimationId animation_id = {1, 1};
CompositorMutatorInputState state; AnimationWorkletInput state;
state.updated_animations.push_back({animation_id, 5000}); state.updated_animations.push_back({animation_id, 5000});
EXPECT_EQ(state.added_and_updated_animations.size(), 0u); EXPECT_EQ(state.added_and_updated_animations.size(), 0u);
EXPECT_EQ(state.updated_animations.size(), 1u); EXPECT_EQ(state.updated_animations.size(), 1u);
...@@ -359,8 +363,8 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase { ...@@ -359,8 +363,8 @@ class AnimationWorkletGlobalScopeTest : public PageTestBase {
}); });
)JS")); )JS"));
int animation_id = 1; cc::WorkletAnimationId animation_id = {1, 1};
CompositorMutatorInputState state; AnimationWorkletInput state;
state.added_and_updated_animations.push_back( state.added_and_updated_animations.push_back(
{animation_id, "test", 5000, nullptr}); {animation_id, "test", 5000, nullptr});
EXPECT_EQ(state.added_and_updated_animations.size(), 1u); EXPECT_EQ(state.added_and_updated_animations.size(), 1u);
......
...@@ -14,9 +14,11 @@ ...@@ -14,9 +14,11 @@
namespace blink { namespace blink {
AnimationWorkletProxyClientImpl::AnimationWorkletProxyClientImpl( AnimationWorkletProxyClientImpl::AnimationWorkletProxyClientImpl(
int scope_id,
base::WeakPtr<CompositorMutatorImpl> mutator, base::WeakPtr<CompositorMutatorImpl> mutator,
scoped_refptr<base::SingleThreadTaskRunner> mutator_runner) scoped_refptr<base::SingleThreadTaskRunner> mutator_runner)
: mutator_(std::move(mutator)), : scope_id_(scope_id),
mutator_(std::move(mutator)),
mutator_runner_(std::move(mutator_runner)), mutator_runner_(std::move(mutator_runner)),
state_(RunState::kUninitialized) { state_(RunState::kUninitialized) {
DCHECK(IsMainThread()); DCHECK(IsMainThread());
...@@ -71,29 +73,36 @@ void AnimationWorkletProxyClientImpl::Dispose() { ...@@ -71,29 +73,36 @@ void AnimationWorkletProxyClientImpl::Dispose() {
state_ = RunState::kDisposed; state_ = RunState::kDisposed;
} }
std::unique_ptr<CompositorMutatorOutputState> std::unique_ptr<AnimationWorkletOutput> AnimationWorkletProxyClientImpl::Mutate(
AnimationWorkletProxyClientImpl::Mutate( std::unique_ptr<AnimationWorkletInput> input) {
const CompositorMutatorInputState& input_state) { DCHECK(input);
#if DCHECK_IS_ON()
DCHECK(input->ValidateScope(scope_id_))
<< "Input has state that does not belong to this global scope: "
<< scope_id_;
#endif
if (!global_scope_) if (!global_scope_)
return nullptr; return nullptr;
auto output_state = global_scope_->Mutate(input_state); auto output = global_scope_->Mutate(*input);
// TODO(petermayo): https://crbug.com/791280 PostCrossThreadTask to supply // TODO(petermayo): https://crbug.com/791280 PostCrossThreadTask to supply
// this rather than return it. // this rather than return it.
return output_state; return output;
} }
// static // static
AnimationWorkletProxyClientImpl* AnimationWorkletProxyClientImpl::FromDocument( AnimationWorkletProxyClientImpl* AnimationWorkletProxyClientImpl::FromDocument(
Document* document) { Document* document,
int scope_id) {
WebLocalFrameImpl* local_frame = WebLocalFrameImpl* local_frame =
WebLocalFrameImpl::FromFrame(document->GetFrame()); WebLocalFrameImpl::FromFrame(document->GetFrame());
scoped_refptr<base::SingleThreadTaskRunner> mutator_queue; scoped_refptr<base::SingleThreadTaskRunner> mutator_queue;
base::WeakPtr<CompositorMutatorImpl> mutator = base::WeakPtr<CompositorMutatorImpl> mutator =
local_frame->LocalRootFrameWidget()->EnsureCompositorMutator( local_frame->LocalRootFrameWidget()->EnsureCompositorMutator(
&mutator_queue); &mutator_queue);
return new AnimationWorkletProxyClientImpl(std::move(mutator), return new AnimationWorkletProxyClientImpl(scope_id, std::move(mutator),
std::move(mutator_queue)); std::move(mutator_queue));
} }
......
...@@ -35,6 +35,7 @@ class MODULES_EXPORT AnimationWorkletProxyClientImpl final ...@@ -35,6 +35,7 @@ class MODULES_EXPORT AnimationWorkletProxyClientImpl final
// This client is hooked to the given |mutatee|, on the given // This client is hooked to the given |mutatee|, on the given
// |mutatee_runner|. // |mutatee_runner|.
explicit AnimationWorkletProxyClientImpl( explicit AnimationWorkletProxyClientImpl(
int scope_id,
base::WeakPtr<CompositorMutatorImpl> mutatee, base::WeakPtr<CompositorMutatorImpl> mutatee,
scoped_refptr<base::SingleThreadTaskRunner> mutatee_runner); scoped_refptr<base::SingleThreadTaskRunner> mutatee_runner);
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
...@@ -45,12 +46,14 @@ class MODULES_EXPORT AnimationWorkletProxyClientImpl final ...@@ -45,12 +46,14 @@ class MODULES_EXPORT AnimationWorkletProxyClientImpl final
// CompositorAnimator: // CompositorAnimator:
// These methods are invoked on the animation worklet thread. // These methods are invoked on the animation worklet thread.
std::unique_ptr<CompositorMutatorOutputState> Mutate( int GetScopeId() const override { return scope_id_; }
const CompositorMutatorInputState&) override; std::unique_ptr<AnimationWorkletOutput> Mutate(
std::unique_ptr<AnimationWorkletInput> input) override;
static AnimationWorkletProxyClientImpl* FromDocument(Document*); static AnimationWorkletProxyClientImpl* FromDocument(Document*, int scope_id);
private: private:
const int scope_id_;
base::WeakPtr<CompositorMutatorImpl> mutator_; base::WeakPtr<CompositorMutatorImpl> mutator_;
scoped_refptr<base::SingleThreadTaskRunner> mutator_runner_; scoped_refptr<base::SingleThreadTaskRunner> mutator_runner_;
......
...@@ -14,13 +14,16 @@ ...@@ -14,13 +14,16 @@
#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/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/inspector/console_message.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/modules/animationworklet/window_animation_worklet.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 {
namespace { namespace {
bool ConvertAnimationEffects( bool ConvertAnimationEffects(
const AnimationEffectOrAnimationEffectSequence& effects, const AnimationEffectOrAnimationEffectSequence& effects,
HeapVector<Member<KeyframeEffect>>& keyframe_effects, HeapVector<Member<KeyframeEffect>>& keyframe_effects,
...@@ -197,21 +200,26 @@ unsigned NextSequenceNumber() { ...@@ -197,21 +200,26 @@ unsigned NextSequenceNumber() {
} // namespace } // namespace
WorkletAnimation* WorkletAnimation::Create( WorkletAnimation* WorkletAnimation::Create(
ExecutionContext* context,
String animator_name, String animator_name,
const AnimationEffectOrAnimationEffectSequence& effects, const AnimationEffectOrAnimationEffectSequence& effects,
ExceptionState& exception_state) { ExceptionState& exception_state) {
return Create(animator_name, effects, DocumentTimelineOrScrollTimeline(), return Create(context, animator_name, effects,
nullptr, exception_state); DocumentTimelineOrScrollTimeline(), nullptr, exception_state);
} }
WorkletAnimation* WorkletAnimation::Create( WorkletAnimation* WorkletAnimation::Create(
ExecutionContext* context,
String animator_name, String animator_name,
const AnimationEffectOrAnimationEffectSequence& effects, const AnimationEffectOrAnimationEffectSequence& effects,
DocumentTimelineOrScrollTimeline timeline, DocumentTimelineOrScrollTimeline timeline,
ExceptionState& exception_state) { ExceptionState& exception_state) {
return Create(animator_name, effects, timeline, nullptr, exception_state); return Create(context, animator_name, effects, timeline, nullptr,
exception_state);
} }
WorkletAnimation* WorkletAnimation::Create( WorkletAnimation* WorkletAnimation::Create(
ExecutionContext* context,
String animator_name, String animator_name,
const AnimationEffectOrAnimationEffectSequence& effects, const AnimationEffectOrAnimationEffectSequence& effects,
DocumentTimelineOrScrollTimeline timeline, DocumentTimelineOrScrollTimeline timeline,
...@@ -240,24 +248,31 @@ WorkletAnimation* WorkletAnimation::Create( ...@@ -240,24 +248,31 @@ WorkletAnimation* WorkletAnimation::Create(
return nullptr; return nullptr;
} }
AnimationWorklet* worklet =
WindowAnimationWorklet::animationWorklet(*context->ExecutingWindow());
WorkletAnimationId id = worklet->NextWorkletAnimationId();
Document& document = keyframe_effects.at(0)->target()->GetDocument(); Document& document = keyframe_effects.at(0)->target()->GetDocument();
AnimationTimeline* animation_timeline = AnimationTimeline* animation_timeline =
ConvertAnimationTimeline(document, timeline); ConvertAnimationTimeline(document, timeline);
WorkletAnimation* animation = WorkletAnimation* animation =
new WorkletAnimation(animator_name, document, keyframe_effects, new WorkletAnimation(id, animator_name, document, keyframe_effects,
animation_timeline, std::move(options)); animation_timeline, std::move(options));
return animation; return animation;
} }
WorkletAnimation::WorkletAnimation( WorkletAnimation::WorkletAnimation(
WorkletAnimationId id,
const String& animator_name, const String& animator_name,
Document& document, Document& document,
const HeapVector<Member<KeyframeEffect>>& effects, const HeapVector<Member<KeyframeEffect>>& effects,
AnimationTimeline* timeline, AnimationTimeline* timeline,
scoped_refptr<SerializedScriptValue> options) scoped_refptr<SerializedScriptValue> options)
: sequence_number_(NextSequenceNumber()), : sequence_number_(NextSequenceNumber()),
id_(id),
animator_name_(animator_name), animator_name_(animator_name),
play_state_(Animation::kIdle), play_state_(Animation::kIdle),
document_(document), document_(document),
...@@ -402,7 +417,7 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) { ...@@ -402,7 +417,7 @@ bool WorkletAnimation::StartOnCompositor(String* failure_message) {
if (!compositor_animation_) { if (!compositor_animation_) {
compositor_animation_ = CompositorAnimation::CreateWorkletAnimation( compositor_animation_ = CompositorAnimation::CreateWorkletAnimation(
animator_name_, ToCompositorScrollTimeline(timeline_), id_, animator_name_, ToCompositorScrollTimeline(timeline_),
std::move(options_)); std::move(options_));
compositor_animation_->SetAnimationDelegate(this); compositor_animation_->SetAnimationDelegate(this);
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "third_party/blink/renderer/platform/animation/compositor_animation.h" #include "third_party/blink/renderer/platform/animation/compositor_animation.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h" #include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h" #include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h"
#include "third_party/blink/renderer/platform/graphics/compositor_animators_state.h"
namespace blink { namespace blink {
...@@ -43,15 +44,18 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase, ...@@ -43,15 +44,18 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
public: public:
static WorkletAnimation* Create( static WorkletAnimation* Create(
ExecutionContext*,
String animator_name, String animator_name,
const AnimationEffectOrAnimationEffectSequence&, const AnimationEffectOrAnimationEffectSequence&,
ExceptionState&); ExceptionState&);
static WorkletAnimation* Create( static WorkletAnimation* Create(
ExecutionContext*,
String animator_name, String animator_name,
const AnimationEffectOrAnimationEffectSequence&, const AnimationEffectOrAnimationEffectSequence&,
DocumentTimelineOrScrollTimeline, DocumentTimelineOrScrollTimeline,
ExceptionState&); ExceptionState&);
static WorkletAnimation* Create( static WorkletAnimation* Create(
ExecutionContext*,
String animator_name, String animator_name,
const AnimationEffectOrAnimationEffectSequence&, const AnimationEffectOrAnimationEffectSequence&,
DocumentTimelineOrScrollTimeline, DocumentTimelineOrScrollTimeline,
...@@ -106,7 +110,8 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase, ...@@ -106,7 +110,8 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
private: private:
WorkletAnimation(const String& animator_name, WorkletAnimation(WorkletAnimationId id,
const String& animator_name,
Document&, Document&,
const HeapVector<Member<KeyframeEffect>>&, const HeapVector<Member<KeyframeEffect>>&,
AnimationTimeline*, AnimationTimeline*,
...@@ -123,6 +128,8 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase, ...@@ -123,6 +128,8 @@ class MODULES_EXPORT WorkletAnimation : public WorkletAnimationBase,
unsigned sequence_number_; unsigned sequence_number_;
WorkletAnimationId id_;
const String animator_name_; const String animator_name_;
Animation::AnimationPlayState play_state_; Animation::AnimationPlayState play_state_;
// Start time in ms. // Start time in ms.
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
optional (DocumentTimeline or ScrollTimeline) timeline, optional (DocumentTimeline or ScrollTimeline) timeline,
optional SerializedScriptValue options), optional SerializedScriptValue options),
RaisesException=Constructor, RaisesException=Constructor,
ConstructorCallWith=ExecutionContext,
MeasureAs=WorkletAnimationConstructor, MeasureAs=WorkletAnimationConstructor,
OriginTrialEnabled=AnimationWorklet OriginTrialEnabled=AnimationWorklet
] interface WorkletAnimation { ] interface WorkletAnimation {
......
...@@ -40,15 +40,17 @@ KeyframeEffect* CreateKeyframeEffect(Element* element) { ...@@ -40,15 +40,17 @@ KeyframeEffect* CreateKeyframeEffect(Element* element) {
return KeyframeEffect::Create(element, CreateEffectModel(), timing); return KeyframeEffect::Create(element, CreateEffectModel(), timing);
} }
WorkletAnimation* CreateWorkletAnimation(Element* element) { WorkletAnimation* CreateWorkletAnimation(ExecutionContext* context,
Element* element) {
AnimationEffectOrAnimationEffectSequence effects; AnimationEffectOrAnimationEffectSequence effects;
AnimationEffect* effect = CreateKeyframeEffect(element); AnimationEffect* effect = CreateKeyframeEffect(element);
effects.SetAnimationEffect(effect); effects.SetAnimationEffect(effect);
DocumentTimelineOrScrollTimeline timeline; DocumentTimelineOrScrollTimeline timeline;
scoped_refptr<SerializedScriptValue> options; scoped_refptr<SerializedScriptValue> options;
DummyExceptionStateForTesting exception_state; DummyExceptionStateForTesting exception_state;
return WorkletAnimation::Create("WorkletAnimation", effects, timeline, return WorkletAnimation::Create(context, "WorkletAnimation", effects,
std::move(options), exception_state); timeline, std::move(options),
exception_state);
} }
} // namespace } // namespace
...@@ -61,7 +63,7 @@ class WorkletAnimationTest : public RenderingTest { ...@@ -61,7 +63,7 @@ class WorkletAnimationTest : public RenderingTest {
void SetUp() override { void SetUp() override {
RenderingTest::SetUp(); RenderingTest::SetUp();
element_ = GetDocument().CreateElementForBinding("test"); element_ = GetDocument().CreateElementForBinding("test");
worklet_animation_ = CreateWorkletAnimation(element_); worklet_animation_ = CreateWorkletAnimation(&GetDocument(), element_);
} }
Persistent<Element> element_; Persistent<Element> element_;
......
...@@ -2200,6 +2200,7 @@ jumbo_source_set("unit_tests") { ...@@ -2200,6 +2200,7 @@ jumbo_source_set("unit_tests") {
"graphics/canvas_color_params_test.cc", "graphics/canvas_color_params_test.cc",
"graphics/canvas_resource_test.cc", "graphics/canvas_resource_test.cc",
"graphics/color_correction_test_utils.cc", "graphics/color_correction_test_utils.cc",
"graphics/compositor_mutator_impl_test.cc",
"graphics/deferred_image_decoder_test.cc", "graphics/deferred_image_decoder_test.cc",
"graphics/gpu/drawing_buffer_software_rendering_test.cc", "graphics/gpu/drawing_buffer_software_rendering_test.cc",
"graphics/graphics_layer_test.cc", "graphics/graphics_layer_test.cc",
......
...@@ -20,12 +20,12 @@ std::unique_ptr<CompositorAnimation> CompositorAnimation::Create() { ...@@ -20,12 +20,12 @@ std::unique_ptr<CompositorAnimation> CompositorAnimation::Create() {
std::unique_ptr<CompositorAnimation> std::unique_ptr<CompositorAnimation>
CompositorAnimation::CreateWorkletAnimation( CompositorAnimation::CreateWorkletAnimation(
cc::WorkletAnimationId worklet_animation_id,
const String& name, const String& name,
std::unique_ptr<CompositorScrollTimeline> scroll_timeline, std::unique_ptr<CompositorScrollTimeline> scroll_timeline,
std::unique_ptr<cc::AnimationOptions> options) { std::unique_ptr<cc::AnimationOptions> options) {
return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create( return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create(
cc::AnimationIdProvider::NextAnimationId(), worklet_animation_id, std::string(name.Ascii().data(), name.length()),
std::string(name.Ascii().data(), name.length()),
std::move(scroll_timeline), std::move(options))); std::move(scroll_timeline), std::move(options)));
} }
......
...@@ -34,6 +34,7 @@ class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate { ...@@ -34,6 +34,7 @@ class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate {
public: public:
static std::unique_ptr<CompositorAnimation> Create(); static std::unique_ptr<CompositorAnimation> Create();
static std::unique_ptr<CompositorAnimation> CreateWorkletAnimation( static std::unique_ptr<CompositorAnimation> CreateWorkletAnimation(
cc::WorkletAnimationId,
const String& name, const String& name,
std::unique_ptr<CompositorScrollTimeline>, std::unique_ptr<CompositorScrollTimeline>,
std::unique_ptr<cc::AnimationOptions>); std::unique_ptr<cc::AnimationOptions>);
......
...@@ -13,9 +13,12 @@ namespace blink { ...@@ -13,9 +13,12 @@ namespace blink {
class PLATFORM_EXPORT CompositorAnimator : public GarbageCollectedMixin { class PLATFORM_EXPORT CompositorAnimator : public GarbageCollectedMixin {
public: public:
virtual ~CompositorAnimator() = default;
virtual int GetScopeId() const = 0;
// Runs the animation frame callback. // Runs the animation frame callback.
virtual std::unique_ptr<CompositorMutatorOutputState> Mutate( virtual std::unique_ptr<AnimationWorkletOutput> Mutate(
const CompositorMutatorInputState&) = 0; std::unique_ptr<AnimationWorkletInput>) = 0;
void Trace(blink::Visitor* visitor) override {} void Trace(blink::Visitor* visitor) override {}
}; };
......
...@@ -9,9 +9,12 @@ ...@@ -9,9 +9,12 @@
namespace blink { namespace blink {
using AnimationWorkletInput = cc::AnimationWorkletInput;
using AnimationWorkletOutput = cc::AnimationWorkletOutput;
using CompositorMutatorInputState = cc::MutatorInputState; using CompositorMutatorInputState = cc::MutatorInputState;
using CompositorMutatorOutputState = cc::MutatorOutputState; using CompositorMutatorOutputState = cc::MutatorOutputState;
using WorkletAnimationId = cc::WorkletAnimationId;
} // namespace blink } // namespace blink
......
...@@ -18,7 +18,7 @@ class PLATFORM_EXPORT CompositorMutatorClient : public cc::LayerTreeMutator { ...@@ -18,7 +18,7 @@ class PLATFORM_EXPORT CompositorMutatorClient : public cc::LayerTreeMutator {
explicit CompositorMutatorClient(std::unique_ptr<CompositorMutatorImpl>); explicit CompositorMutatorClient(std::unique_ptr<CompositorMutatorImpl>);
~CompositorMutatorClient() override; ~CompositorMutatorClient() override;
void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>); virtual void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>);
// cc::LayerTreeMutator // cc::LayerTreeMutator
void SetClient(cc::LayerTreeMutatorClient*) override; void SetClient(cc::LayerTreeMutatorClient*) override;
......
...@@ -59,8 +59,13 @@ void CompositorMutatorImpl::Mutate( ...@@ -59,8 +59,13 @@ void CompositorMutatorImpl::Mutate(
[](const CompositorAnimators* animators, [](const CompositorAnimators* animators,
std::unique_ptr<CompositorMutatorInputState> state, std::unique_ptr<CompositorMutatorInputState> state,
std::unique_ptr<AutoSignal> completion, Outputs* output) { std::unique_ptr<AutoSignal> completion, Outputs* output) {
for (CompositorAnimator* animator : *animators)
output->push_back(animator->Mutate(*state)); for (CompositorAnimator* animator : *animators) {
std::unique_ptr<AnimationWorkletInput> worklet_input =
state->TakeWorkletState(animator->GetScopeId());
if (worklet_input)
output->push_back(animator->Mutate(std::move(worklet_input)));
}
}, },
CrossThreadUnretained(&animators_), WTF::Passed(std::move(state)), CrossThreadUnretained(&animators_), WTF::Passed(std::move(state)),
WTF::Passed(std::make_unique<AutoSignal>(&done)), WTF::Passed(std::make_unique<AutoSignal>(&done)),
......
// 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 "third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h"
#include "base/single_thread_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_thread.h"
#include "third_party/blink/public/platform/web_thread_type.h"
#include "third_party/blink/renderer/platform/graphics/compositor_animator.h"
#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include <memory>
using ::testing::_;
using ::testing::Mock;
using ::testing::StrictMock;
using ::testing::Return;
using ::testing::Truly;
// This test uses actual threads since mutator logic requires it. This means we
// have dependency on Blink platform to create threads.
namespace blink {
namespace {
std::unique_ptr<WebThread> CreateThread(const char* name) {
return Platform::Current()->CreateThread(
WebThreadCreationParams(WebThreadType::kTestThread)
.SetThreadNameForTest(name));
}
class MockCompositorAnimator
: public GarbageCollectedFinalized<MockCompositorAnimator>,
public CompositorAnimator {
USING_GARBAGE_COLLECTED_MIXIN(MockCompositorAnimator);
public:
MockCompositorAnimator(
scoped_refptr<base::SingleThreadTaskRunner> expected_runner)
: expected_runner_(expected_runner) {}
~MockCompositorAnimator() override {}
std::unique_ptr<AnimationWorkletOutput> Mutate(
std::unique_ptr<AnimationWorkletInput> input) override {
return MutateRef(*input);
}
MOCK_CONST_METHOD0(GetScopeId, int());
MOCK_METHOD1(
MutateRef,
std::unique_ptr<AnimationWorkletOutput>(const AnimationWorkletInput&));
scoped_refptr<base::SingleThreadTaskRunner> expected_runner_;
};
class MockCompositorMutatorClient : public CompositorMutatorClient {
public:
MockCompositorMutatorClient(std::unique_ptr<CompositorMutatorImpl> mutator)
: CompositorMutatorClient(std::move(mutator)) {}
~MockCompositorMutatorClient() override {}
// gmock cannot mock methods with move-only args so we forward it to ourself.
void SetMutationUpdate(
std::unique_ptr<cc::MutatorOutputState> output_state) override {
SetMutationUpdateRef(output_state.get());
}
MOCK_METHOD1(SetMutationUpdateRef,
void(cc::MutatorOutputState* output_state));
};
class CompositorMutatorImplTest : public ::testing::Test {
public:
void SetUp() override {
auto mutator = std::make_unique<CompositorMutatorImpl>();
mutator_ = mutator.get();
client_ =
std::make_unique<::testing::StrictMock<MockCompositorMutatorClient>>(
std::move(mutator));
}
void TearDown() override { mutator_ = nullptr; }
std::unique_ptr<::testing::StrictMock<MockCompositorMutatorClient>> client_;
CompositorMutatorImpl* mutator_;
};
bool OnlyIncludesAnimation1(const AnimationWorkletInput& in) {
return in.added_and_updated_animations.size() == 1 &&
in.added_and_updated_animations[0].worklet_animation_id.animation_id ==
1;
}
TEST_F(CompositorMutatorImplTest,
RegisteredAnimatorShouldOnlyReceiveInputForItself) {
std::unique_ptr<WebThread> first_thread = CreateThread("FirstThread");
MockCompositorAnimator* first_animator =
new ::testing::StrictMock<MockCompositorAnimator>(
first_thread->GetTaskRunner());
mutator_->RegisterCompositorAnimator(first_animator,
first_thread->GetTaskRunner());
EXPECT_CALL(*first_animator, GetScopeId()).Times(1).WillOnce(Return(11));
EXPECT_CALL(*first_animator, MutateRef(Truly(OnlyIncludesAnimation1)))
.Times(1);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
AnimationWorkletInput::AddAndUpdateState state1{
{11, 1}, "test1", 5000, nullptr};
AnimationWorkletInput::AddAndUpdateState state2{
{22, 2}, "test2", 5000, nullptr};
auto input = std::make_unique<CompositorMutatorInputState>();
input->Add(std::move(state1));
input->Add(std::move(state2));
mutator_->Mutate(std::move(input));
}
TEST_F(CompositorMutatorImplTest, AnimatorShouldNotBeMutatedWhenNoInput) {
std::unique_ptr<WebThread> first_thread = CreateThread("FirstThread");
MockCompositorAnimator* first_animator =
new ::testing::StrictMock<MockCompositorAnimator>(
first_thread->GetTaskRunner());
mutator_->RegisterCompositorAnimator(first_animator,
first_thread->GetTaskRunner());
EXPECT_CALL(*first_animator, GetScopeId()).Times(1).WillOnce(Return(11));
EXPECT_CALL(*first_animator, MutateRef(_)).Times(0);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
AnimationWorkletInput::AddAndUpdateState state2{
{22, 2}, "test2", 5000, nullptr};
auto input = std::make_unique<CompositorMutatorInputState>();
input->Add(std::move(state2));
mutator_->Mutate(std::move(input));
}
} // namespace
} // 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