Commit b305a615 authored by Kevin Ellis's avatar Kevin Ellis Committed by Commit Bot

Implement asynchronous mutation of animation worklets.

* Adds MutateAynchronously method to the mutator dispatcher.
* Adds tests for ansynchronous mutation.
* Stubs in API for notification of mutation state (pending/complete).

Note async mutations are not used outside of testing with the patch pending completion of the notification plumbing in a separate CL.

Bug: 791280
Change-Id: I91545306c71899945f797a641831257247849b86
Reviewed-on: https://chromium-review.googlesource.com/c/1409600Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Reviewed-by: default avatarMajid Valipour <majidvp@chromium.org>
Commit-Queue: Kevin Ellis <kevers@chromium.org>
Cr-Commit-Position: refs/heads/master@{#625199}
parent eda64695
...@@ -139,7 +139,8 @@ void WorkletAnimationController::MutateAnimations() { ...@@ -139,7 +139,8 @@ void WorkletAnimationController::MutateAnimations() {
if (!main_thread_mutator_client_) if (!main_thread_mutator_client_)
return; return;
main_thread_mutator_client_->Mutator()->Mutate(CollectAnimationStates()); main_thread_mutator_client_->Mutator()->MutateSynchronously(
CollectAnimationStates());
} }
std::unique_ptr<AnimationWorkletDispatcherInput> std::unique_ptr<AnimationWorkletDispatcherInput>
......
...@@ -58,6 +58,8 @@ class CORE_EXPORT WorkletAnimationController ...@@ -58,6 +58,8 @@ class CORE_EXPORT WorkletAnimationController
void SetMutationUpdate( void SetMutationUpdate(
std::unique_ptr<AnimationWorkletOutput> output) override; std::unique_ptr<AnimationWorkletOutput> output) override;
void NotifyAnimationsPending() override {}
void NotifyAnimationsReady() override {}
void SynchronizeAnimatorName(const String& animator_name) override; void SynchronizeAnimatorName(const String& animator_name) override;
// Returns true if the animator with given name is registered in // Returns true if the animator with given name is registered in
......
...@@ -15,7 +15,11 @@ class PLATFORM_EXPORT AnimationWorkletMutatorDispatcher { ...@@ -15,7 +15,11 @@ class PLATFORM_EXPORT AnimationWorkletMutatorDispatcher {
virtual ~AnimationWorkletMutatorDispatcher() = default; virtual ~AnimationWorkletMutatorDispatcher() = default;
// Run the animation frame callbacks from all connected AnimationWorklets. // Run the animation frame callbacks from all connected AnimationWorklets.
virtual void Mutate(std::unique_ptr<AnimationWorkletDispatcherInput>) = 0; virtual void MutateSynchronously(
std::unique_ptr<AnimationWorkletDispatcherInput>) = 0;
virtual void MutateAsynchronously(
std::unique_ptr<AnimationWorkletDispatcherInput>) = 0;
// Returns true if Mutate may do something if called 'now'. // Returns true if Mutate may do something if called 'now'.
virtual bool HasMutators() = 0; virtual bool HasMutators() = 0;
}; };
......
...@@ -19,6 +19,15 @@ ...@@ -19,6 +19,15 @@
namespace blink { namespace blink {
namespace {
int next_async_mutation_id = 0;
int GetNextAsyncMutationId() {
return next_async_mutation_id++;
}
} // end namespace
// Wrap output vector in a thread safe and ref-counted object since it is // Wrap output vector in a thread safe and ref-counted object since it is
// accessed from animation worklet threads and its lifetime must be guaranteed // accessed from animation worklet threads and its lifetime must be guaranteed
// to outlive the mutation update cycle. // to outlive the mutation update cycle.
...@@ -84,13 +93,14 @@ AnimationWorkletMutatorDispatcherImpl::CreateMainThreadClient( ...@@ -84,13 +93,14 @@ AnimationWorkletMutatorDispatcherImpl::CreateMainThreadClient(
return CreateClient<MainThreadMutatorClient>(weak_interface, queue, true); return CreateClient<MainThreadMutatorClient>(weak_interface, queue, true);
} }
void AnimationWorkletMutatorDispatcherImpl::Mutate( void AnimationWorkletMutatorDispatcherImpl::MutateSynchronously(
std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input) { std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input) {
TRACE_EVENT0("cc", "AnimationWorkletMutatorDispatcherImpl::mutate"); TRACE_EVENT0("cc", "AnimationWorkletMutatorDispatcherImpl::mutate");
if (mutator_map_.IsEmpty() || !mutator_input) if (mutator_map_.IsEmpty() || !mutator_input)
return; return;
base::ElapsedTimer timer; base::ElapsedTimer timer;
DCHECK(client_); DCHECK(client_);
DCHECK(host_queue_->BelongsToCurrentThread());
DCHECK(mutator_input_map_.IsEmpty()); DCHECK(mutator_input_map_.IsEmpty());
DCHECK(outputs_->get().IsEmpty()); DCHECK(outputs_->get().IsEmpty());
...@@ -104,15 +114,7 @@ void AnimationWorkletMutatorDispatcherImpl::Mutate( ...@@ -104,15 +114,7 @@ void AnimationWorkletMutatorDispatcherImpl::Mutate(
RequestMutations(std::move(on_done)); RequestMutations(std::move(on_done));
event.Wait(); event.Wait();
for (auto& output : outputs_->get()) { ApplyMutationsOnHostThread();
// Animator that has no input does not produce any output.
if (!output)
continue;
client_->SetMutationUpdate(std::move(output));
}
mutator_input_map_.clear();
outputs_->get().clear();
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Animation.AnimationWorklet.Dispatcher.SynchronousMutateDuration", "Animation.AnimationWorklet.Dispatcher.SynchronousMutateDuration",
...@@ -120,6 +122,58 @@ void AnimationWorkletMutatorDispatcherImpl::Mutate( ...@@ -120,6 +122,58 @@ void AnimationWorkletMutatorDispatcherImpl::Mutate(
base::TimeDelta::FromMilliseconds(100), 50); base::TimeDelta::FromMilliseconds(100), 50);
} }
void AnimationWorkletMutatorDispatcherImpl::MutateAsynchronously(
std::unique_ptr<AnimationWorkletDispatcherInput> mutator_input) {
if (mutator_map_.IsEmpty() || !mutator_input)
return;
DCHECK(client_);
DCHECK(host_queue_->BelongsToCurrentThread());
if (!mutator_input_map_.IsEmpty()) {
// Still running mutations from a previous frame. Skip this frame to avoid
// lagging behind.
// TODO(kevers): Consider queuing pending mutation cycle. A pending tree
// mutation should likely be queued an active tree mutation cycle is still
// running.
return;
}
mutator_input_map_ = CreateInputMap(*mutator_input);
if (mutator_input_map_.IsEmpty())
return;
int next_async_mutation_id = GetNextAsyncMutationId();
TRACE_EVENT_ASYNC_BEGIN0("cc",
"AnimationWorkletMutatorDispatcherImpl::MutateAsync",
next_async_mutation_id);
WTF::CrossThreadClosure on_done = CrossThreadBind(
[](scoped_refptr<base::SingleThreadTaskRunner> host_queue,
base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> dispatcher,
int next_async_mutation_id) {
PostCrossThreadTask(
*host_queue, FROM_HERE,
CrossThreadBind(
&AnimationWorkletMutatorDispatcherImpl::AsyncMutationsDone,
dispatcher, next_async_mutation_id));
},
host_queue_, weak_factory_.GetWeakPtr(), next_async_mutation_id);
client_->NotifyAnimationsPending();
RequestMutations(std::move(on_done));
}
void AnimationWorkletMutatorDispatcherImpl::AsyncMutationsDone(
int async_mutation_id) {
DCHECK(client_);
DCHECK(host_queue_->BelongsToCurrentThread());
ApplyMutationsOnHostThread();
client_->NotifyAnimationsReady();
TRACE_EVENT_ASYNC_END0("cc",
"AnimationWorkletMutatorDispatcherImpl::MutateAsync",
async_mutation_id);
// TODO(kevers): Add UMA metric to track the asynchronous mutate duration.
}
void AnimationWorkletMutatorDispatcherImpl::RegisterAnimationWorkletMutator( void AnimationWorkletMutatorDispatcherImpl::RegisterAnimationWorkletMutator(
CrossThreadPersistent<AnimationWorkletMutator> mutator, CrossThreadPersistent<AnimationWorkletMutator> mutator,
scoped_refptr<base::SingleThreadTaskRunner> mutator_runner) { scoped_refptr<base::SingleThreadTaskRunner> mutator_runner) {
...@@ -171,6 +225,7 @@ AnimationWorkletMutatorDispatcherImpl::CreateInputMap( ...@@ -171,6 +225,7 @@ AnimationWorkletMutatorDispatcherImpl::CreateInputMap(
void AnimationWorkletMutatorDispatcherImpl::RequestMutations( void AnimationWorkletMutatorDispatcherImpl::RequestMutations(
WTF::CrossThreadClosure done_callback) { WTF::CrossThreadClosure done_callback) {
DCHECK(client_);
DCHECK(outputs_->get().IsEmpty()); DCHECK(outputs_->get().IsEmpty());
int num_requests = mutator_map_.size(); int num_requests = mutator_map_.size();
...@@ -214,4 +269,15 @@ void AnimationWorkletMutatorDispatcherImpl::RequestMutations( ...@@ -214,4 +269,15 @@ void AnimationWorkletMutatorDispatcherImpl::RequestMutations(
} }
} }
void AnimationWorkletMutatorDispatcherImpl::ApplyMutationsOnHostThread() {
DCHECK(client_);
DCHECK(host_queue_->BelongsToCurrentThread());
for (auto& output : outputs_->get()) {
if (output)
client_->SetMutationUpdate(std::move(output));
}
mutator_input_map_.clear();
outputs_->get().clear();
}
} // namespace blink } // namespace blink
...@@ -48,7 +48,12 @@ class PLATFORM_EXPORT AnimationWorkletMutatorDispatcherImpl final ...@@ -48,7 +48,12 @@ class PLATFORM_EXPORT AnimationWorkletMutatorDispatcherImpl final
~AnimationWorkletMutatorDispatcherImpl() override; ~AnimationWorkletMutatorDispatcherImpl() override;
// AnimationWorkletMutatorDispatcher implementation. // AnimationWorkletMutatorDispatcher implementation.
void Mutate(std::unique_ptr<AnimationWorkletDispatcherInput>) override; void MutateSynchronously(
std::unique_ptr<AnimationWorkletDispatcherInput>) override;
void MutateAsynchronously(
std::unique_ptr<AnimationWorkletDispatcherInput>) override;
// TODO(majidvp): Remove when timeline inputs are known. // TODO(majidvp): Remove when timeline inputs are known.
bool HasMutators() override; bool HasMutators() override;
...@@ -65,6 +70,8 @@ class PLATFORM_EXPORT AnimationWorkletMutatorDispatcherImpl final ...@@ -65,6 +70,8 @@ class PLATFORM_EXPORT AnimationWorkletMutatorDispatcherImpl final
void SynchronizeAnimatorName(const String& animator_name); void SynchronizeAnimatorName(const String& animator_name);
MutatorClient* client() { return client_; }
private: private:
class OutputVectorRef; class OutputVectorRef;
...@@ -81,6 +88,10 @@ class PLATFORM_EXPORT AnimationWorkletMutatorDispatcherImpl final ...@@ -81,6 +88,10 @@ class PLATFORM_EXPORT AnimationWorkletMutatorDispatcherImpl final
// associated with the last mutation to complete. // associated with the last mutation to complete.
void RequestMutations(WTF::CrossThreadClosure done_callback); void RequestMutations(WTF::CrossThreadClosure done_callback);
void AsyncMutationsDone(int async_mutation_id);
void ApplyMutationsOnHostThread();
// The AnimationWorkletProxyClients are also owned by the WorkerClients // The AnimationWorkletProxyClients are also owned by the WorkerClients
// dictionary. // dictionary.
AnimationWorkletMutatorToTaskRunnerMap mutator_map_; AnimationWorkletMutatorToTaskRunnerMap mutator_map_;
......
...@@ -9,11 +9,15 @@ ...@@ -9,11 +9,15 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_thread_type.h" #include "third_party/blink/public/platform/web_thread_type.h"
#include "third_party/blink/renderer/platform/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h" #include "third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h"
#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.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/heap/handle.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h" #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/waitable_event.h"
#include <memory> #include <memory>
...@@ -21,6 +25,7 @@ using ::testing::_; ...@@ -21,6 +25,7 @@ using ::testing::_;
using ::testing::AtLeast; using ::testing::AtLeast;
using ::testing::Mock; using ::testing::Mock;
using ::testing::Return; using ::testing::Return;
using ::testing::Sequence;
using ::testing::StrictMock; using ::testing::StrictMock;
using ::testing::Truly; using ::testing::Truly;
...@@ -64,7 +69,7 @@ class MockCompositorMutatorClient : public CompositorMutatorClient { ...@@ -64,7 +69,7 @@ class MockCompositorMutatorClient : public CompositorMutatorClient {
public: public:
MockCompositorMutatorClient( MockCompositorMutatorClient(
std::unique_ptr<AnimationWorkletMutatorDispatcherImpl> mutator) std::unique_ptr<AnimationWorkletMutatorDispatcherImpl> mutator)
: CompositorMutatorClient(std::move(mutator)) {} : CompositorMutatorClient(std::move(mutator)), done_event_(nullptr) {}
~MockCompositorMutatorClient() override {} ~MockCompositorMutatorClient() override {}
// gmock cannot mock methods with move-only args so we forward it to ourself. // gmock cannot mock methods with move-only args so we forward it to ourself.
void SetMutationUpdate( void SetMutationUpdate(
...@@ -72,8 +77,26 @@ class MockCompositorMutatorClient : public CompositorMutatorClient { ...@@ -72,8 +77,26 @@ class MockCompositorMutatorClient : public CompositorMutatorClient {
SetMutationUpdateRef(output_state.get()); SetMutationUpdateRef(output_state.get());
} }
MOCK_METHOD0(NotifyAnimationsPending, void());
void NotifyAnimationsReady() override {
NotifyAnimationsReadyRef();
if (done_event_) {
done_event_->Signal();
}
}
MOCK_METHOD0(NotifyAnimationsReadyRef, void());
void SignalWhenComplete(WaitableEvent* done_event) {
done_event_ = done_event;
}
MOCK_METHOD1(SetMutationUpdateRef, MOCK_METHOD1(SetMutationUpdateRef,
void(cc::MutatorOutputState* output_state)); void(cc::MutatorOutputState* output_state));
private:
WaitableEvent* done_event_; // not owned.
}; };
class AnimationWorkletMutatorDispatcherImplTest : public ::testing::Test { class AnimationWorkletMutatorDispatcherImplTest : public ::testing::Test {
...@@ -130,8 +153,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest, ...@@ -130,8 +153,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest,
.Times(1) .Times(1)
.WillOnce(Return(new AnimationWorkletOutput())); .WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1); EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
mutator_->MutateSynchronously(CreateTestMutatorInput());
mutator_->Mutate(CreateTestMutatorInput());
} }
TEST_F(AnimationWorkletMutatorDispatcherImplTest, TEST_F(AnimationWorkletMutatorDispatcherImplTest,
...@@ -156,7 +178,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest, ...@@ -156,7 +178,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest,
auto input = std::make_unique<AnimationWorkletDispatcherInput>(); auto input = std::make_unique<AnimationWorkletDispatcherInput>();
input->Add(std::move(state2)); input->Add(std::move(state2));
mutator_->Mutate(std::move(input)); mutator_->MutateSynchronously(std::move(input));
} }
TEST_F(AnimationWorkletMutatorDispatcherImplTest, TEST_F(AnimationWorkletMutatorDispatcherImplTest,
...@@ -164,7 +186,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest, ...@@ -164,7 +186,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest,
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0); EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
std::unique_ptr<AnimationWorkletDispatcherInput> input = std::unique_ptr<AnimationWorkletDispatcherInput> input =
std::make_unique<AnimationWorkletDispatcherInput>(); std::make_unique<AnimationWorkletDispatcherInput>();
mutator_->Mutate(std::move(input)); mutator_->MutateSynchronously(std::move(input));
} }
TEST_F(AnimationWorkletMutatorDispatcherImplTest, TEST_F(AnimationWorkletMutatorDispatcherImplTest,
...@@ -177,12 +199,13 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest, ...@@ -177,12 +199,13 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest,
mutator_->RegisterAnimationWorkletMutator(first_mutator, mutator_->RegisterAnimationWorkletMutator(first_mutator,
first_thread->GetTaskRunner()); first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId()) EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1)) .Times(AtLeast(1))
.WillRepeatedly(Return(11)); .WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_)).Times(1).WillOnce(Return(nullptr)); EXPECT_CALL(*first_mutator, MutateRef(_)).Times(1).WillOnce(Return(nullptr));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0); EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
mutator_->Mutate(CreateTestMutatorInput()); mutator_->MutateSynchronously(CreateTestMutatorInput());
} }
TEST_F(AnimationWorkletMutatorDispatcherImplTest, TEST_F(AnimationWorkletMutatorDispatcherImplTest,
...@@ -195,6 +218,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest, ...@@ -195,6 +218,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest,
mutator_->RegisterAnimationWorkletMutator(first_mutator, mutator_->RegisterAnimationWorkletMutator(first_mutator,
first_thread->GetTaskRunner()); first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId()) EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1)) .Times(AtLeast(1))
.WillRepeatedly(Return(11)); .WillRepeatedly(Return(11));
...@@ -202,7 +226,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest, ...@@ -202,7 +226,7 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest,
.Times(1) .Times(1)
.WillOnce(Return(new AnimationWorkletOutput())); .WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1); EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
mutator_->Mutate(CreateTestMutatorInput()); mutator_->MutateSynchronously(CreateTestMutatorInput());
// The above call blocks on mutator threads running their tasks so we can // The above call blocks on mutator threads running their tasks so we can
// safely verify here. // safely verify here.
...@@ -210,10 +234,12 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest, ...@@ -210,10 +234,12 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest,
// Ensure mutator is not invoked after unregistration. // Ensure mutator is not invoked after unregistration.
EXPECT_CALL(*first_mutator, MutateRef(_)).Times(0); EXPECT_CALL(*first_mutator, MutateRef(_)).Times(0);
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(0);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0); EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(0);
mutator_->UnregisterAnimationWorkletMutator(first_mutator); mutator_->UnregisterAnimationWorkletMutator(first_mutator);
mutator_->Mutate(CreateTestMutatorInput()); mutator_->MutateSynchronously(CreateTestMutatorInput());
Mock::VerifyAndClearExpectations(client_.get()); Mock::VerifyAndClearExpectations(client_.get());
} }
...@@ -244,9 +270,8 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest, ...@@ -244,9 +270,8 @@ TEST_F(AnimationWorkletMutatorDispatcherImplTest,
EXPECT_CALL(*second_mutator, MutateRef(_)) EXPECT_CALL(*second_mutator, MutateRef(_))
.Times(1) .Times(1)
.WillOnce(Return(new AnimationWorkletOutput())); .WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2); EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
mutator_->Mutate(CreateTestMutatorInput()); mutator_->MutateSynchronously(CreateTestMutatorInput());
} }
TEST_F( TEST_F(
...@@ -280,13 +305,13 @@ TEST_F( ...@@ -280,13 +305,13 @@ TEST_F(
.Times(1) .Times(1)
.WillOnce(Return(new AnimationWorkletOutput())); .WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2); EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
mutator_->Mutate(CreateTestMutatorInput()); mutator_->MutateSynchronously(CreateTestMutatorInput());
// The above call blocks on mutator threads running their tasks so we can // The above call blocks on mutator threads running their tasks so we can
// safely verify here. // safely verify here.
Mock::VerifyAndClearExpectations(client_.get()); Mock::VerifyAndClearExpectations(client_.get());
// Ensure mutator is not invoked after unregistration. // Ensure first_mutator is not invoked after unregistration.
mutator_->UnregisterAnimationWorkletMutator(first_mutator); mutator_->UnregisterAnimationWorkletMutator(first_mutator);
EXPECT_CALL(*first_mutator, GetWorkletId()).Times(0); EXPECT_CALL(*first_mutator, GetWorkletId()).Times(0);
...@@ -298,8 +323,331 @@ TEST_F( ...@@ -298,8 +323,331 @@ TEST_F(
.Times(1) .Times(1)
.WillOnce(Return(new AnimationWorkletOutput())); .WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1); EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
mutator_->Mutate(CreateTestMutatorInput()); mutator_->MutateSynchronously(CreateTestMutatorInput());
Mock::VerifyAndClearExpectations(client_.get());
}
// -----------------------------------------------------------------------
// Asynchronous version of tests.
// Callback wrapping portion of the async test that is required to run on the
// compositor thread.
using MutateAsyncCallback = WTF::CrossThreadFunction<void()>;
using MutatorDispatcherRef =
scoped_refptr<AnimationWorkletMutatorDispatcherImpl>;
class AnimationWorkletMutatorDispatcherImplAsyncTest
: public AnimationWorkletMutatorDispatcherImplTest {
public:
void SetUp() override {
if (!Thread::CompositorThread()) {
Thread::CreateAndSetCompositorThread();
}
AnimationWorkletMutatorDispatcherImplTest::SetUp();
}
// Call this version of mutate and wait if expecting a mutate completion
// notification from the client.
void CallMutateAndWaitForClientCompletion(
MutateAsyncCallback mutate_callback) {
WaitableEvent done_event;
client_->SignalWhenComplete(&done_event);
PostCrossThreadTask(*Thread::CompositorThread()->GetTaskRunner(), FROM_HERE,
std::move(mutate_callback));
done_event.Wait();
}
// Call this version of mutate and wait if there is no expectation of client
// notifications. There are no notificaitons if the mutate call is a no-op
// such as when there are no inputs.
void CallMutateAndWaitForCallbackCompletion(
MutateAsyncCallback mutate_callback) {
WaitableEvent done_event;
PostCrossThreadTask(
*Thread::CompositorThread()->GetTaskRunner(), FROM_HERE,
CrossThreadBind(
[](MutateAsyncCallback mutate_callback, WaitableEvent* done_event) {
mutate_callback.Run();
done_event->Signal();
},
WTF::Passed(std::move(mutate_callback)),
WTF::CrossThreadUnretained(&done_event)));
done_event.Wait();
}
};
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
RegisteredAnimatorShouldOnlyReceiveInputForItself) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
// Call MutateAsynchronously from the compositor thread.
MutateAsyncCallback mutate_callback = CrossThreadBind(
[](std::unique_ptr<Thread> first_thread,
MockAnimationWorkletMutator* first_mutator,
AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
async_test->mutator_->RegisterAnimationWorkletMutator(
first_mutator, first_thread->GetTaskRunner());
async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
},
WTF::Passed(std::move(first_thread)),
WrapCrossThreadWeakPersistent(first_mutator),
WTF::CrossThreadUnretained(this));
Sequence s;
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1).InSequence(s);
EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
RegisteredAnimatorShouldNotBeMutatedWhenNoInput) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
// Call MutateAsynchronously from the compositor thread.
MutateAsyncCallback mutate_callback = CrossThreadBind(
[](std::unique_ptr<Thread> first_thread,
MockAnimationWorkletMutator* first_mutator,
AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
async_test->mutator_->RegisterAnimationWorkletMutator(
first_mutator, first_thread->GetTaskRunner());
AnimationWorkletInput::AddAndUpdateState state2{
{22, 2}, "test2", 5000, nullptr, 1};
auto input = std::make_unique<AnimationWorkletDispatcherInput>();
input->Add(std::move(state2));
async_test->mutator_->MutateAsynchronously(std::move(input));
},
WTF::Passed(std::move(first_thread)),
WrapCrossThreadWeakPersistent(first_mutator),
WTF::CrossThreadUnretained(this));
// The start of the mutation process will be synchronous. If a pending
// notification is not received by the time the callback returns, it will not
// be triggered later.
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(0);
CallMutateAndWaitForCallbackCompletion(std::move(mutate_callback));
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateIsNotInvokedWithNoRegisteredAnimators) {
// Call MutateAsynchronously from the compositor thread.
MutateAsyncCallback mutate_callback = CrossThreadBind(
[](AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
std::unique_ptr<AnimationWorkletDispatcherInput> input =
std::make_unique<AnimationWorkletDispatcherInput>();
async_test->mutator_->MutateAsynchronously(std::move(input));
},
WTF::CrossThreadUnretained(this));
// The start of the mutation process will be synchronous. If a pending
// notification is not received by the time the callback returns, it will not
// be triggered later.
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(0);
CallMutateAndWaitForCallbackCompletion(std::move(mutate_callback));
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateIsNotInvokedWithNullOutput) {
// Create a thread to run mutator tasks.
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
// Call MutateAsynchronously from the compositor thread.
MutateAsyncCallback mutate_callback = CrossThreadBind(
[](std::unique_ptr<Thread> first_thread,
MockAnimationWorkletMutator* first_mutator,
AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
async_test->mutator_->RegisterAnimationWorkletMutator(
first_mutator, first_thread->GetTaskRunner());
async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
},
WTF::Passed(std::move(first_thread)),
WrapCrossThreadWeakPersistent(first_mutator),
WTF::CrossThreadUnretained(this));
Sequence s;
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_)).Times(1).WillOnce(Return(nullptr));
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateIsInvokedCorrectlyWithSingleRegisteredAnimator) {
// Create a thread to run mutator tasks.
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
// Call MutateAsynchronously from the compositor thread.
MutateAsyncCallback mutate_callback = CrossThreadBind(
[](std::unique_ptr<Thread> first_thread,
MockAnimationWorkletMutator* first_mutator,
AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
async_test->mutator_->RegisterAnimationWorkletMutator(
first_mutator, first_thread->GetTaskRunner());
async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
},
WTF::Passed(std::move(first_thread)),
WrapCrossThreadWeakPersistent(first_mutator),
WTF::CrossThreadUnretained(this));
Sequence s;
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1).InSequence(s);
EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
// Above call blocks until complete signal is received.
Mock::VerifyAndClearExpectations(client_.get()); Mock::VerifyAndClearExpectations(client_.get());
// Call MutateAsynchronously from the compositor thread.
MutateAsyncCallback mutate_callback2 = CrossThreadBind(
[](MockAnimationWorkletMutator* first_mutator,
AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
// Ensure mutator is not invoked after unregistration.
async_test->mutator_->UnregisterAnimationWorkletMutator(first_mutator);
async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
},
WrapCrossThreadWeakPersistent(first_mutator),
WTF::CrossThreadUnretained(this));
// The start of the mutation process will be synchronous. If a pending
// notification is not received by the time the callback returns, it will not
// be triggered later.
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(0);
CallMutateAndWaitForCallbackCompletion(std::move(mutate_callback2));
Mock::VerifyAndClearExpectations(client_.get());
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateInvokedCorrectlyWithTwoRegisteredAnimatorsOnSameThread) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
MockAnimationWorkletMutator* second_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
// Call MutateAsynchronously from the compositor thread.
MutateAsyncCallback mutate_callback = CrossThreadBind(
[](std::unique_ptr<Thread> first_thread,
MockAnimationWorkletMutator* first_mutator,
MockAnimationWorkletMutator* second_mutator,
AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
async_test->mutator_->RegisterAnimationWorkletMutator(
first_mutator, first_thread->GetTaskRunner());
async_test->mutator_->RegisterAnimationWorkletMutator(
second_mutator, first_thread->GetTaskRunner());
async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
},
WTF::Passed(std::move(first_thread)),
WrapCrossThreadWeakPersistent(first_mutator),
WrapCrossThreadWeakPersistent(second_mutator),
WTF::CrossThreadUnretained(this));
Sequence s;
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*second_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(22));
EXPECT_CALL(*second_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2).InSequence(s);
EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
}
TEST_F(
AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateInvokedCorrectlyWithTwoRegisteredAnimatorsOnDifferentThreads) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
std::unique_ptr<Thread> second_thread = CreateThread("SecondAnimationThread");
MockAnimationWorkletMutator* second_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
second_thread->GetTaskRunner());
// Call MutateAsynchronously from the compositor thread.
MutateAsyncCallback mutate_callback = CrossThreadBind(
[](std::unique_ptr<Thread> first_thread,
std::unique_ptr<Thread> second_thread,
MockAnimationWorkletMutator* first_mutator,
MockAnimationWorkletMutator* second_mutator,
AnimationWorkletMutatorDispatcherImplAsyncTest* async_test) {
async_test->mutator_->RegisterAnimationWorkletMutator(
first_mutator, first_thread->GetTaskRunner());
async_test->mutator_->RegisterAnimationWorkletMutator(
second_mutator, second_thread->GetTaskRunner());
async_test->mutator_->MutateAsynchronously(CreateTestMutatorInput());
},
WTF::Passed(std::move(first_thread)),
WTF::Passed(std::move(second_thread)),
WrapCrossThreadWeakPersistent(first_mutator),
WrapCrossThreadWeakPersistent(second_mutator),
WTF::CrossThreadUnretained(this));
Sequence s;
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*second_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(22));
EXPECT_CALL(*second_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, NotifyAnimationsPending()).Times(1).InSequence(s);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2).InSequence(s);
EXPECT_CALL(*client_, NotifyAnimationsReadyRef()).Times(1).InSequence(s);
CallMutateAndWaitForClientCompletion(std::move(mutate_callback));
} }
} // namespace } // namespace
......
...@@ -26,7 +26,9 @@ CompositorMutatorClient::~CompositorMutatorClient() { ...@@ -26,7 +26,9 @@ CompositorMutatorClient::~CompositorMutatorClient() {
void CompositorMutatorClient::Mutate( void CompositorMutatorClient::Mutate(
std::unique_ptr<cc::MutatorInputState> input_state) { std::unique_ptr<cc::MutatorInputState> input_state) {
TRACE_EVENT0("cc", "CompositorMutatorClient::Mutate"); TRACE_EVENT0("cc", "CompositorMutatorClient::Mutate");
mutator_->Mutate(std::move(input_state)); // TODO(http://crbug.com/791280): Switch to asynchronous once plumbing is
// complete.
mutator_->MutateSynchronously(std::move(input_state));
} }
void CompositorMutatorClient::SetMutationUpdate( void CompositorMutatorClient::SetMutationUpdate(
......
...@@ -23,6 +23,9 @@ class PLATFORM_EXPORT CompositorMutatorClient : public cc::LayerTreeMutator, ...@@ -23,6 +23,9 @@ class PLATFORM_EXPORT CompositorMutatorClient : public cc::LayerTreeMutator,
void SynchronizeAnimatorName(const String& animator_name) override {} void SynchronizeAnimatorName(const String& animator_name) override {}
void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>) override; void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>) override;
// TODO(http://crbug.com/791280): Plumb notifications through to cc scheduler.
void NotifyAnimationsPending() override {}
void NotifyAnimationsReady() override {}
// cc::LayerTreeMutator // cc::LayerTreeMutator
void SetClient(cc::LayerTreeMutatorClient*) override; void SetClient(cc::LayerTreeMutatorClient*) override;
......
...@@ -22,6 +22,8 @@ class PLATFORM_EXPORT MainThreadMutatorClient : public MutatorClient { ...@@ -22,6 +22,8 @@ class PLATFORM_EXPORT MainThreadMutatorClient : public MutatorClient {
void SynchronizeAnimatorName(const String& animator_name) override; void SynchronizeAnimatorName(const String& animator_name) override;
void SetMutationUpdate(std::unique_ptr<AnimationWorkletOutput>) override; void SetMutationUpdate(std::unique_ptr<AnimationWorkletOutput>) override;
void NotifyAnimationsPending() override {}
void NotifyAnimationsReady() override {}
void SetDelegate(MutatorClient* client); void SetDelegate(MutatorClient* client);
AnimationWorkletMutatorDispatcherImpl* Mutator() { return mutator_.get(); } AnimationWorkletMutatorDispatcherImpl* Mutator() { return mutator_.get(); }
......
...@@ -17,6 +17,10 @@ class PLATFORM_EXPORT MutatorClient { ...@@ -17,6 +17,10 @@ class PLATFORM_EXPORT MutatorClient {
virtual void SynchronizeAnimatorName(const String& animator_name) = 0; virtual void SynchronizeAnimatorName(const String& animator_name) = 0;
virtual void SetMutationUpdate(std::unique_ptr<AnimationWorkletOutput>) = 0; virtual void SetMutationUpdate(std::unique_ptr<AnimationWorkletOutput>) = 0;
virtual void NotifyAnimationsPending() = 0;
virtual void NotifyAnimationsReady() = 0;
}; };
} // namespace blink } // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment