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 = canvas.finishRecording();
46     }
47 
48     SkCanvas dummyCanvas;
49     RenderNodeDrawable drawable(nullptr, &dummyCanvas);
50     skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
51     int functor1 = TestUtils::createMockFunctor();
52     GLFunctorDrawable functorDrawable{functor1, &dummyCanvas};
53     WebViewFunctor_release(functor1);
54     skiaDL->mChildFunctors.push_back(&functorDrawable);
55     skiaDL->mMutableImages.push_back(nullptr);
56     skiaDL->appendVD(nullptr);
57     skiaDL->mProjectionReceiver = &drawable;
58 
59     ASSERT_FALSE(skiaDL->mChildNodes.empty());
60     ASSERT_FALSE(skiaDL->mChildFunctors.empty());
61     ASSERT_FALSE(skiaDL->mMutableImages.empty());
62     ASSERT_TRUE(skiaDL->hasVectorDrawables());
63     ASSERT_FALSE(skiaDL->isEmpty());
64     ASSERT_TRUE(skiaDL->mProjectionReceiver);
65 
66     skiaDL->reset();
67 
68     ASSERT_TRUE(skiaDL->mChildNodes.empty());
69     ASSERT_TRUE(skiaDL->mChildFunctors.empty());
70     ASSERT_TRUE(skiaDL->mMutableImages.empty());
71     ASSERT_FALSE(skiaDL->hasVectorDrawables());
72     ASSERT_TRUE(skiaDL->isEmpty());
73     ASSERT_FALSE(skiaDL->mProjectionReceiver);
74 }
75 
TEST(SkiaDisplayList,reuseDisplayList)76 TEST(SkiaDisplayList, reuseDisplayList) {
77     sp<RenderNode> renderNode = new RenderNode();
78     std::unique_ptr<SkiaDisplayList> availableList;
79 
80     // no list has been attached so it should return a nullptr
81     availableList = renderNode->detachAvailableList();
82     ASSERT_EQ(availableList.get(), nullptr);
83 
84     // attach a displayList for reuse
85     SkiaDisplayList skiaDL;
86     ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get()));
87 
88     // detach the list that you just attempted to reuse
89     availableList = renderNode->detachAvailableList();
90     ASSERT_EQ(availableList.get(), &skiaDL);
91     availableList.release();  // prevents an invalid free since our DL is stack allocated
92 
93     // after detaching there should return no available list
94     availableList = renderNode->detachAvailableList();
95     ASSERT_EQ(availableList.get(), nullptr);
96 }
97 
TEST(SkiaDisplayList,syncContexts)98 TEST(SkiaDisplayList, syncContexts) {
99     SkiaDisplayList skiaDL;
100 
101     SkCanvas dummyCanvas;
102 
103     int functor1 = TestUtils::createMockFunctor();
104     auto& counts = TestUtils::countsForFunctor(functor1);
105     skiaDL.mChildFunctors.push_back(
106             skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas));
107     WebViewFunctor_release(functor1);
108 
109     SkRect bounds = SkRect::MakeWH(200, 200);
110     VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
111     vectorDrawable.mutateStagingProperties()->setBounds(bounds);
112     skiaDL.appendVD(&vectorDrawable);
113 
114     // ensure that the functor and vectorDrawable are properly synced
115     TestUtils::runOnRenderThread([&](auto&) {
116         skiaDL.syncContents(WebViewSyncData{
117                 .applyForceDark = false,
118         });
119     });
120 
121     EXPECT_EQ(counts.sync, 1);
122     EXPECT_EQ(counts.destroyed, 0);
123     EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
124 
125     skiaDL.reset();
126     TestUtils::runOnRenderThread([](auto&) {
127         // Fence
128     });
129     EXPECT_EQ(counts.destroyed, 1);
130 }
131 
TEST(SkiaDisplayList,recordMutableBitmap)132 TEST(SkiaDisplayList, recordMutableBitmap) {
133     SkiaRecordingCanvas canvas{nullptr, 100, 100};
134     auto bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::Make(
135             10, 20, SkColorType::kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType));
136     EXPECT_FALSE(bitmap->isImmutable());
137     canvas.drawBitmap(*bitmap, 0, 0, nullptr);
138     auto displayList = canvas.finishRecording();
139     ASSERT_EQ(1, displayList->mMutableImages.size());
140     EXPECT_EQ(10, displayList->mMutableImages[0]->width());
141     EXPECT_EQ(20, displayList->mMutableImages[0]->height());
142 }
143 
TEST(SkiaDisplayList,recordMutableBitmapInShader)144 TEST(SkiaDisplayList, recordMutableBitmapInShader) {
145     SkiaRecordingCanvas canvas{nullptr, 100, 100};
146     auto bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::Make(
147             10, 20, SkColorType::kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType));
148     EXPECT_FALSE(bitmap->isImmutable());
149     SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
150     Paint paint;
151     paint.setShader(bitmap->makeImage()->makeShader(sampling));
152     canvas.drawPaint(paint);
153     auto displayList = canvas.finishRecording();
154     ASSERT_EQ(1, displayList->mMutableImages.size());
155     EXPECT_EQ(10, displayList->mMutableImages[0]->width());
156     EXPECT_EQ(20, displayList->mMutableImages[0]->height());
157 }
158 
159 class ContextFactory : public IContextFactory {
160 public:
createAnimationContext(renderthread::TimeLord & clock)161     virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
162         return new AnimationContext(clock);
163     }
164 };
165 
RENDERTHREAD_TEST(SkiaDisplayList,prepareListAndChildren)166 RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
167     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
168     ContextFactory contextFactory;
169     std::unique_ptr<CanvasContext> canvasContext(
170             CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
171     TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
172     DamageAccumulator damageAccumulator;
173     info.damageAccumulator = &damageAccumulator;
174 
175     SkiaDisplayList skiaDL;
176 
177     // The VectorDrawableRoot needs to have bounds on screen (and therefore not
178     // empty) in order to have PropertyChangeWillBeConsumed set.
179     const auto bounds = SkRect::MakeIWH(100, 100);
180 
181     // prepare with a clean VD
182     VectorDrawableRoot cleanVD(new VectorDrawable::Group());
183     cleanVD.mutateProperties()->setBounds(bounds);
184     skiaDL.appendVD(&cleanVD);
185     cleanVD.getBitmapUpdateIfDirty();  // this clears the dirty bit
186 
187     ASSERT_FALSE(cleanVD.isDirty());
188     ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
189     TestUtils::MockTreeObserver observer;
190     ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false,
191                                                [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
192     ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
193 
194     // prepare again this time adding a dirty VD
195     VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
196     dirtyVD.mutateProperties()->setBounds(bounds);
197     skiaDL.appendVD(&dirtyVD);
198 
199     ASSERT_TRUE(dirtyVD.isDirty());
200     ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
201     ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false,
202                                               [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
203     ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
204 
205     // prepare again this time adding a RenderNode and a callback
206     sp<RenderNode> renderNode = new RenderNode();
207     TreeInfo* infoPtr = &info;
208     SkCanvas dummyCanvas;
209     skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
210     bool hasRun = false;
211     ASSERT_TRUE(skiaDL.prepareListAndChildren(
212             observer, info, false,
213             [&hasRun, renderNode, infoPtr](RenderNode* n, TreeObserver& observer, TreeInfo& i,
214                                            bool r) {
215                 hasRun = true;
216                 ASSERT_EQ(renderNode.get(), n);
217                 ASSERT_EQ(infoPtr, &i);
218                 ASSERT_FALSE(r);
219             }));
220     ASSERT_TRUE(hasRun);
221 
222     canvasContext->destroy();
223 }
224 
RENDERTHREAD_TEST(SkiaDisplayList,prepareListAndChildren_vdOffscreen)225 RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
226     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
227     ContextFactory contextFactory;
228     std::unique_ptr<CanvasContext> canvasContext(
229             CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
230 
231     // Set up a Surface so that we can position the VectorDrawable offscreen.
232     test::TestContext testContext;
233     testContext.setRenderOffscreen(true);
234     auto surface = testContext.surface();
235     int width = ANativeWindow_getWidth(surface.get());
236     int height = ANativeWindow_getHeight(surface.get());
237     canvasContext->setSurface(surface.get());
238 
239     TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
240     DamageAccumulator damageAccumulator;
241     info.damageAccumulator = &damageAccumulator;
242 
243     // The VectorDrawableRoot needs to have bounds on screen (and therefore not
244     // empty) in order to have PropertyChangeWillBeConsumed set.
245     const auto bounds = SkRect::MakeIWH(100, 100);
246 
247     for (const SkRect b : {bounds.makeOffset(width, 0),
248                            bounds.makeOffset(0, height),
249                            bounds.makeOffset(-bounds.width(), 0),
250                            bounds.makeOffset(0, -bounds.height())}) {
251         SkiaDisplayList skiaDL;
252         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
253         dirtyVD.mutateProperties()->setBounds(b);
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     }
264 
265     // The DamageAccumulator's transform can also result in the
266     // VectorDrawableRoot being offscreen.
267     for (const SkISize translate : { SkISize{width, 0},
268                                      SkISize{0, height},
269                                      SkISize{-width, 0},
270                                      SkISize{0, -height}}) {
271         Matrix4 mat4;
272         mat4.translate(translate.fWidth, translate.fHeight);
273         damageAccumulator.pushTransform(&mat4);
274 
275         SkiaDisplayList skiaDL;
276         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
277         dirtyVD.mutateProperties()->setBounds(bounds);
278         skiaDL.appendVD(&dirtyVD);
279 
280         ASSERT_TRUE(dirtyVD.isDirty());
281         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
282 
283         TestUtils::MockTreeObserver observer;
284         ASSERT_FALSE(skiaDL.prepareListAndChildren(
285                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
286         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
287         damageAccumulator.popTransform();
288     }
289 
290     // Another way to be offscreen: a matrix from the draw call.
291     for (const SkMatrix translate : { SkMatrix::Translate(width, 0),
292                                       SkMatrix::Translate(0, height),
293                                       SkMatrix::Translate(-width, 0),
294                                       SkMatrix::Translate(0, -height)}) {
295         SkiaDisplayList skiaDL;
296         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
297         dirtyVD.mutateProperties()->setBounds(bounds);
298         skiaDL.appendVD(&dirtyVD, translate);
299 
300         ASSERT_TRUE(dirtyVD.isDirty());
301         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
302 
303         TestUtils::MockTreeObserver observer;
304         ASSERT_FALSE(skiaDL.prepareListAndChildren(
305                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
306         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
307     }
308 
309     // Verify that the matrices are combined in the right order.
310     {
311         // Rotate and then translate, so the VD is offscreen.
312         Matrix4 mat4;
313         mat4.loadRotate(180);
314         damageAccumulator.pushTransform(&mat4);
315 
316         SkiaDisplayList skiaDL;
317         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
318         dirtyVD.mutateProperties()->setBounds(bounds);
319         SkMatrix translate = SkMatrix::Translate(50, 50);
320         skiaDL.appendVD(&dirtyVD, translate);
321 
322         ASSERT_TRUE(dirtyVD.isDirty());
323         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
324 
325         TestUtils::MockTreeObserver observer;
326         ASSERT_FALSE(skiaDL.prepareListAndChildren(
327                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
328         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
329         damageAccumulator.popTransform();
330     }
331     {
332         // Switch the order of rotate and translate, so it is on screen.
333         Matrix4 mat4;
334         mat4.translate(50, 50);
335         damageAccumulator.pushTransform(&mat4);
336 
337         SkiaDisplayList skiaDL;
338         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
339         dirtyVD.mutateProperties()->setBounds(bounds);
340         SkMatrix rotate;
341         rotate.setRotate(180);
342         skiaDL.appendVD(&dirtyVD, rotate);
343 
344         ASSERT_TRUE(dirtyVD.isDirty());
345         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
346 
347         TestUtils::MockTreeObserver observer;
348         ASSERT_TRUE(skiaDL.prepareListAndChildren(
349                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
350         ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
351         damageAccumulator.popTransform();
352     }
353     {
354         // An AVD that is larger than the screen.
355         SkiaDisplayList skiaDL;
356         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
357         dirtyVD.mutateProperties()->setBounds(SkRect::MakeLTRB(-1, -1, width + 1, height + 1));
358         skiaDL.appendVD(&dirtyVD);
359 
360         ASSERT_TRUE(dirtyVD.isDirty());
361         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
362 
363         TestUtils::MockTreeObserver observer;
364         ASSERT_TRUE(skiaDL.prepareListAndChildren(
365                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
366         ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
367     }
368     {
369         // An AVD whose bounds are not a rectangle after applying a matrix.
370         SkiaDisplayList skiaDL;
371         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
372         dirtyVD.mutateProperties()->setBounds(bounds);
373         SkMatrix mat;
374         mat.setRotate(45, 50, 50);
375         skiaDL.appendVD(&dirtyVD, mat);
376 
377         ASSERT_TRUE(dirtyVD.isDirty());
378         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
379 
380         TestUtils::MockTreeObserver observer;
381         ASSERT_TRUE(skiaDL.prepareListAndChildren(
382                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
383         ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
384     }
385 }
386 
TEST(SkiaDisplayList,updateChildren)387 TEST(SkiaDisplayList, updateChildren) {
388     SkiaDisplayList skiaDL;
389 
390     sp<RenderNode> renderNode = new RenderNode();
391     SkCanvas dummyCanvas;
392     skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
393     skiaDL.updateChildren([renderNode](RenderNode* n) { ASSERT_EQ(renderNode.get(), n); });
394 }
395