1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <VectorDrawable.h>
18 #include <gtest/gtest.h>
19
20 #include "AnimationContext.h"
21 #include "DamageAccumulator.h"
22 #include "IContextFactory.h"
23 #include "pipeline/skia/GLFunctorDrawable.h"
24 #include "pipeline/skia/SkiaDisplayList.h"
25 #include "renderthread/CanvasContext.h"
26 #include "tests/common/TestContext.h"
27 #include "tests/common/TestUtils.h"
28
29 using namespace android;
30 using namespace android::uirenderer;
31 using namespace android::uirenderer::renderthread;
32 using namespace android::uirenderer::skiapipeline;
33
TEST(SkiaDisplayList,create)34 TEST(SkiaDisplayList, create) {
35 SkiaDisplayList skiaDL;
36 ASSERT_TRUE(skiaDL.isEmpty());
37 ASSERT_FALSE(skiaDL.mProjectionReceiver);
38 }
39
TEST(SkiaDisplayList,reset)40 TEST(SkiaDisplayList, reset) {
41 std::unique_ptr<SkiaDisplayList> skiaDL;
42 {
43 SkiaRecordingCanvas canvas{nullptr, 1, 1};
44 canvas.drawColor(0, SkBlendMode::kSrc);
45 skiaDL.reset(canvas.finishRecording());
46 }
47
48 SkCanvas dummyCanvas;
49 RenderNodeDrawable drawable(nullptr, &dummyCanvas);
50 skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
51 GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
52 skiaDL->mChildFunctors.push_back(&functorDrawable);
53 skiaDL->mMutableImages.push_back(nullptr);
54 skiaDL->appendVD(nullptr);
55 skiaDL->mProjectionReceiver = &drawable;
56
57 ASSERT_FALSE(skiaDL->mChildNodes.empty());
58 ASSERT_FALSE(skiaDL->mChildFunctors.empty());
59 ASSERT_FALSE(skiaDL->mMutableImages.empty());
60 ASSERT_TRUE(skiaDL->hasVectorDrawables());
61 ASSERT_FALSE(skiaDL->isEmpty());
62 ASSERT_TRUE(skiaDL->mProjectionReceiver);
63
64 skiaDL->reset();
65
66 ASSERT_TRUE(skiaDL->mChildNodes.empty());
67 ASSERT_TRUE(skiaDL->mChildFunctors.empty());
68 ASSERT_TRUE(skiaDL->mMutableImages.empty());
69 ASSERT_FALSE(skiaDL->hasVectorDrawables());
70 ASSERT_TRUE(skiaDL->isEmpty());
71 ASSERT_FALSE(skiaDL->mProjectionReceiver);
72 }
73
TEST(SkiaDisplayList,reuseDisplayList)74 TEST(SkiaDisplayList, reuseDisplayList) {
75 sp<RenderNode> renderNode = new RenderNode();
76 std::unique_ptr<SkiaDisplayList> availableList;
77
78 // no list has been attached so it should return a nullptr
79 availableList = renderNode->detachAvailableList();
80 ASSERT_EQ(availableList.get(), nullptr);
81
82 // attach a displayList for reuse
83 SkiaDisplayList skiaDL;
84 ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
85
86 // detach the list that you just attempted to reuse
87 availableList = renderNode->detachAvailableList();
88 ASSERT_EQ(availableList.get(), &skiaDL);
89 availableList.release(); // prevents an invalid free since our DL is stack allocated
90
91 // after detaching there should return no available list
92 availableList = renderNode->detachAvailableList();
93 ASSERT_EQ(availableList.get(), nullptr);
94 }
95
TEST(SkiaDisplayList,syncContexts)96 TEST(SkiaDisplayList, syncContexts) {
97 SkiaDisplayList skiaDL;
98
99 SkCanvas dummyCanvas;
100 TestUtils::MockFunctor functor;
101 GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
102 skiaDL.mChildFunctors.push_back(&functorDrawable);
103
104 int functor2 = WebViewFunctor_create(
105 nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
106 auto& counts = TestUtils::countsForFunctor(functor2);
107 skiaDL.mChildFunctors.push_back(
108 skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
109 WebViewFunctor_release(functor2);
110
111 SkRect bounds = SkRect::MakeWH(200, 200);
112 VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
113 vectorDrawable.mutateStagingProperties()->setBounds(bounds);
114 skiaDL.appendVD(&vectorDrawable);
115
116 // ensure that the functor and vectorDrawable are properly synced
117 TestUtils::runOnRenderThread([&](auto&) {
118 skiaDL.syncContents(WebViewSyncData{
119 .applyForceDark = false,
120 });
121 });
122
123 EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
124 EXPECT_EQ(counts.sync, 1);
125 EXPECT_EQ(counts.destroyed, 0);
126 EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
127
128 skiaDL.reset();
129 TestUtils::runOnRenderThread([](auto&) {
130 // Fence
131 });
132 EXPECT_EQ(counts.destroyed, 1);
133 }
134
135 class ContextFactory : public IContextFactory {
136 public:
createAnimationContext(renderthread::TimeLord & clock)137 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
138 return new AnimationContext(clock);
139 }
140 };
141
RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList,prepareListAndChildren)142 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
143 auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
144 ContextFactory contextFactory;
145 std::unique_ptr<CanvasContext> canvasContext(
146 CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
147 TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
148 DamageAccumulator damageAccumulator;
149 info.damageAccumulator = &damageAccumulator;
150
151 SkiaDisplayList skiaDL;
152
153 // The VectorDrawableRoot needs to have bounds on screen (and therefore not
154 // empty) in order to have PropertyChangeWillBeConsumed set.
155 const auto bounds = SkRect::MakeIWH(100, 100);
156
157 // prepare with a clean VD
158 VectorDrawableRoot cleanVD(new VectorDrawable::Group());
159 cleanVD.mutateProperties()->setBounds(bounds);
160 skiaDL.appendVD(&cleanVD);
161 cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit
162
163 ASSERT_FALSE(cleanVD.isDirty());
164 ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
165 TestUtils::MockTreeObserver observer;
166 ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false,
167 [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
168 ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
169
170 // prepare again this time adding a dirty VD
171 VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
172 dirtyVD.mutateProperties()->setBounds(bounds);
173 skiaDL.appendVD(&dirtyVD);
174
175 ASSERT_TRUE(dirtyVD.isDirty());
176 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
177 ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false,
178 [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
179 ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
180
181 // prepare again this time adding a RenderNode and a callback
182 sp<RenderNode> renderNode = new RenderNode();
183 TreeInfo* infoPtr = &info;
184 SkCanvas dummyCanvas;
185 skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
186 bool hasRun = false;
187 ASSERT_TRUE(skiaDL.prepareListAndChildren(
188 observer, info, false,
189 [&hasRun, renderNode, infoPtr](RenderNode* n, TreeObserver& observer, TreeInfo& i,
190 bool r) {
191 hasRun = true;
192 ASSERT_EQ(renderNode.get(), n);
193 ASSERT_EQ(infoPtr, &i);
194 ASSERT_FALSE(r);
195 }));
196 ASSERT_TRUE(hasRun);
197
198 canvasContext->destroy();
199 }
200
RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList,prepareListAndChildren_vdOffscreen)201 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
202 auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
203 ContextFactory contextFactory;
204 std::unique_ptr<CanvasContext> canvasContext(
205 CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
206
207 // Set up a Surface so that we can position the VectorDrawable offscreen.
208 test::TestContext testContext;
209 testContext.setRenderOffscreen(true);
210 auto surface = testContext.surface();
211 int width = ANativeWindow_getWidth(surface.get());
212 int height = ANativeWindow_getHeight(surface.get());
213 canvasContext->setSurface(surface.get());
214
215 TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
216 DamageAccumulator damageAccumulator;
217 info.damageAccumulator = &damageAccumulator;
218
219 // The VectorDrawableRoot needs to have bounds on screen (and therefore not
220 // empty) in order to have PropertyChangeWillBeConsumed set.
221 const auto bounds = SkRect::MakeIWH(100, 100);
222
223 for (const SkRect b : {bounds.makeOffset(width, 0),
224 bounds.makeOffset(0, height),
225 bounds.makeOffset(-bounds.width(), 0),
226 bounds.makeOffset(0, -bounds.height())}) {
227 SkiaDisplayList skiaDL;
228 VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
229 dirtyVD.mutateProperties()->setBounds(b);
230 skiaDL.appendVD(&dirtyVD);
231
232 ASSERT_TRUE(dirtyVD.isDirty());
233 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
234
235 TestUtils::MockTreeObserver observer;
236 ASSERT_FALSE(skiaDL.prepareListAndChildren(
237 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
238 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
239 }
240
241 // The DamageAccumulator's transform can also result in the
242 // VectorDrawableRoot being offscreen.
243 for (const SkISize translate : { SkISize{width, 0},
244 SkISize{0, height},
245 SkISize{-width, 0},
246 SkISize{0, -height}}) {
247 Matrix4 mat4;
248 mat4.translate(translate.fWidth, translate.fHeight);
249 damageAccumulator.pushTransform(&mat4);
250
251 SkiaDisplayList skiaDL;
252 VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
253 dirtyVD.mutateProperties()->setBounds(bounds);
254 skiaDL.appendVD(&dirtyVD);
255
256 ASSERT_TRUE(dirtyVD.isDirty());
257 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
258
259 TestUtils::MockTreeObserver observer;
260 ASSERT_FALSE(skiaDL.prepareListAndChildren(
261 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
262 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
263 damageAccumulator.popTransform();
264 }
265
266 // Another way to be offscreen: a matrix from the draw call.
267 for (const SkMatrix translate : { SkMatrix::MakeTrans(width, 0),
268 SkMatrix::MakeTrans(0, height),
269 SkMatrix::MakeTrans(-width, 0),
270 SkMatrix::MakeTrans(0, -height)}) {
271 SkiaDisplayList skiaDL;
272 VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
273 dirtyVD.mutateProperties()->setBounds(bounds);
274 skiaDL.appendVD(&dirtyVD, translate);
275
276 ASSERT_TRUE(dirtyVD.isDirty());
277 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
278
279 TestUtils::MockTreeObserver observer;
280 ASSERT_FALSE(skiaDL.prepareListAndChildren(
281 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
282 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
283 }
284
285 // Verify that the matrices are combined in the right order.
286 {
287 // Rotate and then translate, so the VD is offscreen.
288 Matrix4 mat4;
289 mat4.loadRotate(180);
290 damageAccumulator.pushTransform(&mat4);
291
292 SkiaDisplayList skiaDL;
293 VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
294 dirtyVD.mutateProperties()->setBounds(bounds);
295 SkMatrix translate = SkMatrix::MakeTrans(50, 50);
296 skiaDL.appendVD(&dirtyVD, translate);
297
298 ASSERT_TRUE(dirtyVD.isDirty());
299 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
300
301 TestUtils::MockTreeObserver observer;
302 ASSERT_FALSE(skiaDL.prepareListAndChildren(
303 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
304 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
305 damageAccumulator.popTransform();
306 }
307 {
308 // Switch the order of rotate and translate, so it is on screen.
309 Matrix4 mat4;
310 mat4.translate(50, 50);
311 damageAccumulator.pushTransform(&mat4);
312
313 SkiaDisplayList skiaDL;
314 VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
315 dirtyVD.mutateProperties()->setBounds(bounds);
316 SkMatrix rotate;
317 rotate.setRotate(180);
318 skiaDL.appendVD(&dirtyVD, rotate);
319
320 ASSERT_TRUE(dirtyVD.isDirty());
321 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
322
323 TestUtils::MockTreeObserver observer;
324 ASSERT_TRUE(skiaDL.prepareListAndChildren(
325 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
326 ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
327 damageAccumulator.popTransform();
328 }
329 {
330 // An AVD that is larger than the screen.
331 SkiaDisplayList skiaDL;
332 VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
333 dirtyVD.mutateProperties()->setBounds(SkRect::MakeLTRB(-1, -1, width + 1, height + 1));
334 skiaDL.appendVD(&dirtyVD);
335
336 ASSERT_TRUE(dirtyVD.isDirty());
337 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
338
339 TestUtils::MockTreeObserver observer;
340 ASSERT_TRUE(skiaDL.prepareListAndChildren(
341 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
342 ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
343 }
344 {
345 // An AVD whose bounds are not a rectangle after applying a matrix.
346 SkiaDisplayList skiaDL;
347 VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
348 dirtyVD.mutateProperties()->setBounds(bounds);
349 SkMatrix mat;
350 mat.setRotate(45, 50, 50);
351 skiaDL.appendVD(&dirtyVD, mat);
352
353 ASSERT_TRUE(dirtyVD.isDirty());
354 ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
355
356 TestUtils::MockTreeObserver observer;
357 ASSERT_TRUE(skiaDL.prepareListAndChildren(
358 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
359 ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
360 }
361 }
362
TEST(SkiaDisplayList,updateChildren)363 TEST(SkiaDisplayList, updateChildren) {
364 SkiaDisplayList skiaDL;
365
366 sp<RenderNode> renderNode = new RenderNode();
367 SkCanvas dummyCanvas;
368 skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
369 skiaDL.updateChildren([renderNode](RenderNode* n) { ASSERT_EQ(renderNode.get(), n); });
370 }
371