1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h"
6 
7 #include "base/bind.h"
8 #include "base/run_loop.h"
9 #include "cc/paint/paint_worklet_job.h"
10 #include "testing/gmock/include/gmock/gmock.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "third_party/blink/public/platform/platform.h"
13 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
14 #include "third_party/blink/renderer/platform/scheduler/public/thread_type.h"
15 
16 using ::testing::_;
17 using ::testing::NiceMock;
18 using ::testing::Return;
19 
20 namespace blink {
21 namespace {
22 // We need a thread (or multiple threads) for the (mock) worklets to run on.
CreateTestThread(const char * name)23 std::unique_ptr<Thread> CreateTestThread(const char* name) {
24   return Platform::Current()->CreateThread(
25       ThreadCreationParams(ThreadType::kTestThread).SetThreadNameForTest(name));
26 }
27 
28 class PaintWorkletPaintDispatcherAsyncTest : public ::testing::Test {
29  public:
CreateTestCompleteCallback()30   PlatformPaintWorkletLayerPainter::DoneCallback CreateTestCompleteCallback() {
31     return base::BindOnce(
32         &PaintWorkletPaintDispatcherAsyncTest::VerifyResultAndFinish,
33         base::Unretained(this));
34   }
35 
36   // Allows a test to block on |VerifyResultAndFinish| being called. If a
37   // PaintWorkletPaintDispatcherAsyncTest test times out, it likely means the
38   // callback created by |CreateTestCompleteCallback| was never posted by the
39   // worklet thread.
WaitForTestCompletion()40   void WaitForTestCompletion() { run_loop_.Run(); }
41 
42  private:
VerifyResultAndFinish(cc::PaintWorkletJobMap results)43   void VerifyResultAndFinish(cc::PaintWorkletJobMap results) {
44     run_loop_.Quit();
45   }
46 
47   base::RunLoop run_loop_;
48 };
49 
50 class MockPaintWorkletPainter
51     : public GarbageCollected<MockPaintWorkletPainter>,
52       public PaintWorkletPainter {
53   USING_GARBAGE_COLLECTED_MIXIN(MockPaintWorkletPainter);
54 
55  public:
MockPaintWorkletPainter(int worklet_id)56   MockPaintWorkletPainter(int worklet_id) {
57     ON_CALL(*this, GetWorkletId).WillByDefault(Return(worklet_id));
58   }
59   ~MockPaintWorkletPainter() = default;
60 
61   MOCK_CONST_METHOD0(GetWorkletId, int());
62   MOCK_METHOD2(
63       Paint,
64       sk_sp<PaintRecord>(const cc::PaintWorkletInput*,
65                          const cc::PaintWorkletJob::AnimatedPropertyValues&));
66 };
67 
68 class MockPaintWorkletInput : public cc::PaintWorkletInput {
69  public:
MockPaintWorkletInput(int worklet_id)70   explicit MockPaintWorkletInput(int worklet_id) {
71     ON_CALL(*this, WorkletId).WillByDefault(Return(worklet_id));
72   }
73   ~MockPaintWorkletInput() = default;
74 
75   MOCK_CONST_METHOD0(GetSize, gfx::SizeF());
76   MOCK_CONST_METHOD0(WorkletId, int());
77   MOCK_CONST_METHOD0(GetPropertyKeys,
78                      const std::vector<PaintWorkletInput::PropertyKey>&());
79 };
80 
AddPaintWorkletInputToMap(cc::PaintWorkletJobMap & map,int worklet_id)81 cc::PaintWorkletInput* AddPaintWorkletInputToMap(cc::PaintWorkletJobMap& map,
82                                                  int worklet_id) {
83   if (!map.contains(worklet_id))
84     map[worklet_id] = base::MakeRefCounted<cc::PaintWorkletJobVector>();
85   auto input = base::MakeRefCounted<MockPaintWorkletInput>(worklet_id);
86   MockPaintWorkletInput* input_ptr = input.get();
87   cc::PaintWorkletJob::AnimatedPropertyValues animated_property_values;
88   map[worklet_id]->data.emplace_back(/*layer_id=*/1, std::move(input),
89                                      animated_property_values);
90   return input_ptr;
91 }
92 }  // namespace
93 
TEST_F(PaintWorkletPaintDispatcherAsyncTest,DispatchedWorkletIsPainted)94 TEST_F(PaintWorkletPaintDispatcherAsyncTest, DispatchedWorkletIsPainted) {
95   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
96 
97   const int worklet_id = 4;
98   MockPaintWorkletPainter* mock_painter =
99       MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(worklet_id);
100   std::unique_ptr<Thread> worklet_thread = CreateTestThread("WorkletThread");
101   dispatcher->RegisterPaintWorkletPainter(mock_painter,
102                                           worklet_thread->GetTaskRunner());
103 
104   cc::PaintWorkletJobMap job_map;
105   Vector<cc::PaintWorkletInput*> inputs = {
106       AddPaintWorkletInputToMap(job_map, worklet_id),
107       AddPaintWorkletInputToMap(job_map, worklet_id),
108       AddPaintWorkletInputToMap(job_map, worklet_id),
109   };
110 
111   // The input jobs match the registered painter, so we should see a series of
112   // calls to Paint() with the appropriate PaintWorkletInputs.
113   for (cc::PaintWorkletInput* input : inputs)
114     EXPECT_CALL(*mock_painter, Paint(input, _)).Times(1);
115   dispatcher->DispatchWorklets(job_map, CreateTestCompleteCallback());
116 
117   WaitForTestCompletion();
118 }
119 
TEST_F(PaintWorkletPaintDispatcherAsyncTest,DispatchCompletesWithNoPainters)120 TEST_F(PaintWorkletPaintDispatcherAsyncTest, DispatchCompletesWithNoPainters) {
121   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
122 
123   cc::PaintWorkletJobMap job_map;
124   AddPaintWorkletInputToMap(job_map, /*worklet_id=*/2);
125   AddPaintWorkletInputToMap(job_map, /*worklet_id=*/2);
126   AddPaintWorkletInputToMap(job_map, /*worklet_id=*/5);
127 
128   // There are no painters to dispatch to, matching or otherwise, but the
129   // callback should still be called so this test passes if it doesn't hang on
130   // WaitForTestCompletion.
131   dispatcher->DispatchWorklets(job_map, CreateTestCompleteCallback());
132 
133   WaitForTestCompletion();
134 }
135 
TEST_F(PaintWorkletPaintDispatcherAsyncTest,DispatchHandlesEmptyInput)136 TEST_F(PaintWorkletPaintDispatcherAsyncTest, DispatchHandlesEmptyInput) {
137   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
138 
139   const int worklet_id = 4;
140   auto* mock_painter =
141       MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(worklet_id);
142   std::unique_ptr<Thread> worklet_thread = CreateTestThread("WorkletThread");
143   dispatcher->RegisterPaintWorkletPainter(mock_painter,
144                                           worklet_thread->GetTaskRunner());
145 
146   cc::PaintWorkletJobMap job_map;
147 
148   // The input job map is empty, so we should see no calls to Paint but the
149   // callback should still be called.
150   EXPECT_CALL(*mock_painter, Paint(_, _)).Times(0);
151   dispatcher->DispatchWorklets(job_map, CreateTestCompleteCallback());
152 
153   WaitForTestCompletion();
154 }
155 
TEST_F(PaintWorkletPaintDispatcherAsyncTest,DispatchSelectsCorrectPainter)156 TEST_F(PaintWorkletPaintDispatcherAsyncTest, DispatchSelectsCorrectPainter) {
157   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
158 
159   const int first_worklet_id = 2;
160   auto* first_mock_painter =
161       MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(first_worklet_id);
162   std::unique_ptr<Thread> first_thread = CreateTestThread("WorkletThread1");
163   dispatcher->RegisterPaintWorkletPainter(first_mock_painter,
164                                           first_thread->GetTaskRunner());
165 
166   const int second_worklet_id = 3;
167   auto* second_mock_painter =
168       MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(
169           second_worklet_id);
170   std::unique_ptr<Thread> second_thread = CreateTestThread("WorkletThread2");
171   dispatcher->RegisterPaintWorkletPainter(second_mock_painter,
172                                           second_thread->GetTaskRunner());
173 
174   cc::PaintWorkletJobMap job_map;
175   Vector<cc::PaintWorkletInput*> inputs{
176       AddPaintWorkletInputToMap(job_map, second_worklet_id),
177       AddPaintWorkletInputToMap(job_map, second_worklet_id),
178   };
179 
180   // Paint should only be called on the correct painter, with our input.
181   EXPECT_CALL(*first_mock_painter, Paint(_, _)).Times(0);
182   for (cc::PaintWorkletInput* input : inputs) {
183     EXPECT_CALL(*second_mock_painter, Paint(input, _)).Times(1);
184   }
185   dispatcher->DispatchWorklets(job_map, CreateTestCompleteCallback());
186 
187   WaitForTestCompletion();
188 }
189 
TEST_F(PaintWorkletPaintDispatcherAsyncTest,DispatchIgnoresNonMatchingInput)190 TEST_F(PaintWorkletPaintDispatcherAsyncTest, DispatchIgnoresNonMatchingInput) {
191   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
192 
193   const int worklet_id = 2;
194   auto* mock_painter =
195       MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(worklet_id);
196   std::unique_ptr<Thread> worklet_thread = CreateTestThread("WorkletThread");
197   dispatcher->RegisterPaintWorkletPainter(mock_painter,
198                                           worklet_thread->GetTaskRunner());
199 
200   cc::PaintWorkletJobMap job_map;
201   const int non_registered_worklet_id = 3;
202   cc::PaintWorkletInput* matching_input =
203       AddPaintWorkletInputToMap(job_map, worklet_id);
204   AddPaintWorkletInputToMap(job_map, non_registered_worklet_id);
205 
206   // Only one job matches, so our painter should only be called once, and the
207   // callback should still be called.
208   EXPECT_CALL(*mock_painter, Paint(matching_input, _)).Times(1);
209   dispatcher->DispatchWorklets(job_map, CreateTestCompleteCallback());
210 
211   WaitForTestCompletion();
212 }
213 
TEST_F(PaintWorkletPaintDispatcherAsyncTest,DispatchCorrectlyAssignsInputsToMultiplePainters)214 TEST_F(PaintWorkletPaintDispatcherAsyncTest,
215        DispatchCorrectlyAssignsInputsToMultiplePainters) {
216   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
217 
218   const int first_worklet_id = 5;
219   auto* first_mock_painter =
220       MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(first_worklet_id);
221   std::unique_ptr<Thread> first_thread = CreateTestThread("WorkletThread1");
222   dispatcher->RegisterPaintWorkletPainter(first_mock_painter,
223                                           first_thread->GetTaskRunner());
224 
225   const int second_worklet_id = 1;
226   auto* second_mock_painter =
227       MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(
228           second_worklet_id);
229   std::unique_ptr<Thread> second_thread = CreateTestThread("WorkletThread2");
230   dispatcher->RegisterPaintWorkletPainter(second_mock_painter,
231                                           second_thread->GetTaskRunner());
232 
233   cc::PaintWorkletJobMap job_map;
234   cc::PaintWorkletInput* first_input =
235       AddPaintWorkletInputToMap(job_map, first_worklet_id);
236   cc::PaintWorkletInput* second_input =
237       AddPaintWorkletInputToMap(job_map, second_worklet_id);
238 
239   // Both painters should be called with the correct inputs.
240   EXPECT_CALL(*first_mock_painter, Paint(first_input, _)).Times(1);
241   EXPECT_CALL(*second_mock_painter, Paint(second_input, _)).Times(1);
242   dispatcher->DispatchWorklets(job_map, CreateTestCompleteCallback());
243 
244   WaitForTestCompletion();
245 }
246 
TEST_F(PaintWorkletPaintDispatcherAsyncTest,HasOngoingDispatchIsTrackedCorrectly)247 TEST_F(PaintWorkletPaintDispatcherAsyncTest,
248        HasOngoingDispatchIsTrackedCorrectly) {
249   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
250 
251   const int first_worklet_id = 2;
252   auto* first_mock_painter =
253       MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(first_worklet_id);
254   std::unique_ptr<Thread> first_thread = CreateTestThread("WorkletThread1");
255   dispatcher->RegisterPaintWorkletPainter(first_mock_painter,
256                                           first_thread->GetTaskRunner());
257 
258   // Nothing going on; no dispatch.
259   EXPECT_FALSE(dispatcher->HasOngoingDispatch());
260 
261   cc::PaintWorkletJobMap job_map;
262   AddPaintWorkletInputToMap(job_map, first_worklet_id);
263 
264   dispatcher->DispatchWorklets(job_map, CreateTestCompleteCallback());
265   EXPECT_TRUE(dispatcher->HasOngoingDispatch());
266 
267   WaitForTestCompletion();
268   EXPECT_FALSE(dispatcher->HasOngoingDispatch());
269 }
270 
271 }  // namespace blink
272