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