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 <gtest/gtest.h>
18#include <VectorDrawable.h>
19
20#include "AnimationContext.h"
21#include "DamageAccumulator.h"
22#include "IContextFactory.h"
23#include "pipeline/skia/SkiaDisplayList.h"
24#include "renderthread/CanvasContext.h"
25#include "tests/common/TestUtils.h"
26
27using namespace android;
28using namespace android::uirenderer;
29using namespace android::uirenderer::renderthread;
30using namespace android::uirenderer::skiapipeline;
31
32TEST(SkiaDisplayList, create) {
33    SkiaDisplayList skiaDL;
34    ASSERT_TRUE(skiaDL.isEmpty());
35    ASSERT_FALSE(skiaDL.mProjectionReceiver);
36}
37
38TEST(SkiaDisplayList, reset) {
39    SkiaDisplayList skiaDL;
40
41    SkCanvas dummyCanvas;
42    RenderNodeDrawable drawable(nullptr, &dummyCanvas);
43    skiaDL.mChildNodes.emplace_back(nullptr, &dummyCanvas);
44    skiaDL.mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
45    skiaDL.mMutableImages.push_back(nullptr);
46    skiaDL.mVectorDrawables.push_back(nullptr);
47    skiaDL.mDisplayList.drawAnnotation(SkRect::MakeWH(200, 200), "testAnnotation", nullptr);
48    skiaDL.mProjectionReceiver = &drawable;
49
50    ASSERT_FALSE(skiaDL.mChildNodes.empty());
51    ASSERT_FALSE(skiaDL.mChildFunctors.empty());
52    ASSERT_FALSE(skiaDL.mMutableImages.empty());
53    ASSERT_FALSE(skiaDL.mVectorDrawables.empty());
54    ASSERT_FALSE(skiaDL.isEmpty());
55    ASSERT_TRUE(skiaDL.mProjectionReceiver);
56
57    skiaDL.reset();
58
59    ASSERT_TRUE(skiaDL.mChildNodes.empty());
60    ASSERT_TRUE(skiaDL.mChildFunctors.empty());
61    ASSERT_TRUE(skiaDL.mMutableImages.empty());
62    ASSERT_TRUE(skiaDL.mVectorDrawables.empty());
63    ASSERT_TRUE(skiaDL.isEmpty());
64    ASSERT_FALSE(skiaDL.mProjectionReceiver);
65}
66
67TEST(SkiaDisplayList, reuseDisplayList) {
68    sp<RenderNode> renderNode = new RenderNode();
69    std::unique_ptr<SkiaDisplayList> availableList;
70
71    // no list has been attached so it should return a nullptr
72    availableList = renderNode->detachAvailableList();
73    ASSERT_EQ(availableList.get(), nullptr);
74
75    // attach a displayList for reuse
76    SkiaDisplayList skiaDL;
77    ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
78
79    // detach the list that you just attempted to reuse
80    availableList = renderNode->detachAvailableList();
81    ASSERT_EQ(availableList.get(), &skiaDL);
82    availableList.release(); // prevents an invalid free since our DL is stack allocated
83
84    // after detaching there should return no available list
85    availableList = renderNode->detachAvailableList();
86    ASSERT_EQ(availableList.get(), nullptr);
87}
88
89TEST(SkiaDisplayList, syncContexts) {
90    SkiaDisplayList skiaDL;
91
92    SkCanvas dummyCanvas;
93    TestUtils::MockFunctor functor;
94    skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
95
96    SkRect bounds = SkRect::MakeWH(200, 200);
97    VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
98    vectorDrawable.mutateStagingProperties()->setBounds(bounds);
99    skiaDL.mVectorDrawables.push_back(&vectorDrawable);
100
101    // ensure that the functor and vectorDrawable are properly synced
102    skiaDL.syncContents();
103
104    ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
105    ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
106}
107
108class ContextFactory : public IContextFactory {
109public:
110    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
111        return new AnimationContext(clock);
112    }
113};
114
115RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
116    auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
117    ContextFactory contextFactory;
118    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
119            renderThread, false, rootNode.get(), &contextFactory));
120    TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
121    DamageAccumulator damageAccumulator;
122    info.damageAccumulator = &damageAccumulator;
123
124    SkiaDisplayList skiaDL;
125
126    // prepare with a clean VD
127    VectorDrawableRoot cleanVD(new VectorDrawable::Group());
128    skiaDL.mVectorDrawables.push_back(&cleanVD);
129    cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit
130
131    ASSERT_FALSE(cleanVD.isDirty());
132    ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
133    TestUtils::MockTreeObserver observer;
134    ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false,
135            [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
136    ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed());
137
138    // prepare again this time adding a dirty VD
139    VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
140    skiaDL.mVectorDrawables.push_back(&dirtyVD);
141
142    ASSERT_TRUE(dirtyVD.isDirty());
143    ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
144    ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false,
145            [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
146    ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
147
148    // prepare again this time adding a RenderNode and a callback
149    sp<RenderNode> renderNode = new RenderNode();
150    TreeInfo* infoPtr = &info;
151    SkCanvas dummyCanvas;
152    skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
153    bool hasRun = false;
154    ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false,
155            [&hasRun, renderNode, infoPtr](RenderNode* n, TreeObserver& observer, TreeInfo& i, bool r) {
156        hasRun = true;
157        ASSERT_EQ(renderNode.get(), n);
158        ASSERT_EQ(infoPtr, &i);
159        ASSERT_FALSE(r);
160    }));
161    ASSERT_TRUE(hasRun);
162
163    canvasContext->destroy();
164}
165
166TEST(SkiaDisplayList, updateChildren) {
167    SkiaDisplayList skiaDL;
168
169    sp<RenderNode> renderNode = new RenderNode();
170    SkCanvas dummyCanvas;
171    skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
172    skiaDL.updateChildren([renderNode](RenderNode* n) {
173        ASSERT_EQ(renderNode.get(), n);
174    });
175}
176