1 /*
2  * Copyright (C) 2014 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 <cutils/log.h>
18 #include <gui/Surface.h>
19 #include <ui/PixelFormat.h>
20 
21 #include <AnimationContext.h>
22 #include <DisplayListCanvas.h>
23 #include <RenderNode.h>
24 #include <renderthread/RenderProxy.h>
25 #include <renderthread/RenderTask.h>
26 
27 #include "TestContext.h"
28 
29 #include <stdio.h>
30 #include <unistd.h>
31 
32 using namespace android;
33 using namespace android::uirenderer;
34 using namespace android::uirenderer::renderthread;
35 using namespace android::uirenderer::test;
36 
37 class ContextFactory : public IContextFactory {
38 public:
createAnimationContext(renderthread::TimeLord & clock)39     virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
40         return new AnimationContext(clock);
41     }
42 };
43 
startRecording(RenderNode * node)44 static DisplayListCanvas* startRecording(RenderNode* node) {
45     DisplayListCanvas* renderer = new DisplayListCanvas();
46     renderer->setViewport(node->stagingProperties().getWidth(),
47             node->stagingProperties().getHeight());
48     renderer->prepare();
49     return renderer;
50 }
51 
endRecording(DisplayListCanvas * renderer,RenderNode * node)52 static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
53     renderer->finish();
54     node->setStagingDisplayList(renderer->finishRecording());
55     delete renderer;
56 }
57 
58 class TreeContentAnimation {
59 public:
~TreeContentAnimation()60     virtual ~TreeContentAnimation() {}
61     int frameCount = 150;
getFrameCount()62     virtual int getFrameCount() { return frameCount; }
setFrameCount(int fc)63     virtual void setFrameCount(int fc) {
64         if (fc > 0) {
65             frameCount = fc;
66         }
67     }
68     virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
69     virtual void doFrame(int frameNr) = 0;
70 
71     template <class T>
run(int frameCount)72     static void run(int frameCount) {
73         T animation;
74         animation.setFrameCount(frameCount);
75 
76         TestContext testContext;
77 
78         // create the native surface
79         const int width = gDisplay.w;
80         const int height = gDisplay.h;
81         sp<Surface> surface = testContext.surface();
82 
83         RenderNode* rootNode = new RenderNode();
84         rootNode->incStrong(nullptr);
85         rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
86         rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
87         rootNode->mutateStagingProperties().setClipToBounds(false);
88         rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
89 
90         ContextFactory factory;
91         std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
92         proxy->loadSystemProperties();
93         proxy->initialize(surface);
94         float lightX = width / 2.0;
95         proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
96         proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
97 
98         android::uirenderer::Rect DUMMY;
99 
100         DisplayListCanvas* renderer = startRecording(rootNode);
101         animation.createContent(width, height, renderer);
102         endRecording(renderer, rootNode);
103 
104         // Do a few cold runs then reset the stats so that the caches are all hot
105         for (int i = 0; i < 3; i++) {
106             testContext.waitForVsync();
107             proxy->syncAndDrawFrame();
108         }
109         proxy->resetProfileInfo();
110 
111         for (int i = 0; i < animation.getFrameCount(); i++) {
112             testContext.waitForVsync();
113 
114             ATRACE_NAME("UI-Draw Frame");
115             nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
116             UiFrameInfoBuilder(proxy->frameInfo())
117                     .setVsync(vsync, vsync);
118             animation.doFrame(i);
119             proxy->syncAndDrawFrame();
120         }
121 
122         proxy->dumpProfileInfo(STDOUT_FILENO, 0);
123         rootNode->decStrong(nullptr);
124     }
125 };
126 
127 class ShadowGridAnimation : public TreeContentAnimation {
128 public:
129     std::vector< sp<RenderNode> > cards;
createContent(int width,int height,DisplayListCanvas * renderer)130     void createContent(int width, int height, DisplayListCanvas* renderer) override {
131         renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
132         renderer->insertReorderBarrier(true);
133 
134         for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
135             for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
136                 sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
137                 renderer->drawRenderNode(card.get());
138                 cards.push_back(card);
139             }
140         }
141 
142         renderer->insertReorderBarrier(false);
143     }
doFrame(int frameNr)144     void doFrame(int frameNr) override {
145         int curFrame = frameNr % 150;
146         for (size_t ci = 0; ci < cards.size(); ci++) {
147             cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
148             cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
149             cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
150         }
151     }
152 private:
createCard(int x,int y,int width,int height)153     sp<RenderNode> createCard(int x, int y, int width, int height) {
154         sp<RenderNode> node = new RenderNode();
155         node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
156         node->mutateStagingProperties().setElevation(dp(16));
157         node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
158         node->mutateStagingProperties().mutableOutline().setShouldClip(true);
159         node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
160 
161         DisplayListCanvas* renderer = startRecording(node.get());
162         renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
163         endRecording(renderer, node.get());
164         return node;
165     }
166 };
167 
168 class ShadowGrid2Animation : public TreeContentAnimation {
169 public:
170     std::vector< sp<RenderNode> > cards;
createContent(int width,int height,DisplayListCanvas * renderer)171     void createContent(int width, int height, DisplayListCanvas* renderer) override {
172         renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
173         renderer->insertReorderBarrier(true);
174 
175         for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
176             for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
177                 sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
178                 renderer->drawRenderNode(card.get());
179                 cards.push_back(card);
180             }
181         }
182 
183         renderer->insertReorderBarrier(false);
184     }
doFrame(int frameNr)185     void doFrame(int frameNr) override {
186         int curFrame = frameNr % 150;
187         for (size_t ci = 0; ci < cards.size(); ci++) {
188             cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
189             cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
190             cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
191         }
192     }
193 private:
createCard(int x,int y,int width,int height)194     sp<RenderNode> createCard(int x, int y, int width, int height) {
195         sp<RenderNode> node = new RenderNode();
196         node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
197         node->mutateStagingProperties().setElevation(dp(16));
198         node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
199         node->mutateStagingProperties().mutableOutline().setShouldClip(true);
200         node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
201 
202         DisplayListCanvas* renderer = startRecording(node.get());
203         renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
204         endRecording(renderer, node.get());
205         return node;
206     }
207 };
208 
209 class RectGridAnimation : public TreeContentAnimation {
210 public:
211     sp<RenderNode> card;
createContent(int width,int height,DisplayListCanvas * renderer)212     void createContent(int width, int height, DisplayListCanvas* renderer) override {
213         renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
214         renderer->insertReorderBarrier(true);
215 
216         card = createCard(40, 40, 200, 200);
217         renderer->drawRenderNode(card.get());
218 
219         renderer->insertReorderBarrier(false);
220     }
doFrame(int frameNr)221     void doFrame(int frameNr) override {
222         int curFrame = frameNr % 150;
223         card->mutateStagingProperties().setTranslationX(curFrame);
224         card->mutateStagingProperties().setTranslationY(curFrame);
225         card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
226     }
227 private:
createCard(int x,int y,int width,int height)228     sp<RenderNode> createCard(int x, int y, int width, int height) {
229         sp<RenderNode> node = new RenderNode();
230         node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
231         node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
232 
233         DisplayListCanvas* renderer = startRecording(node.get());
234         renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
235 
236         float rects[width * height];
237         int index = 0;
238         for (int xOffset = 0; xOffset < width; xOffset+=2) {
239             for (int yOffset = 0; yOffset < height; yOffset+=2) {
240                 rects[index++] = xOffset;
241                 rects[index++] = yOffset;
242                 rects[index++] = xOffset + 1;
243                 rects[index++] = yOffset + 1;
244             }
245         }
246         int count = width * height;
247 
248         SkPaint paint;
249         paint.setColor(0xff00ffff);
250         renderer->drawRects(rects, count, &paint);
251 
252         endRecording(renderer, node.get());
253         return node;
254     }
255 };
256 
257 class OvalAnimation : public TreeContentAnimation {
258 public:
259     sp<RenderNode> card;
createContent(int width,int height,DisplayListCanvas * renderer)260     void createContent(int width, int height, DisplayListCanvas* renderer) override {
261         renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
262         renderer->insertReorderBarrier(true);
263 
264         card = createCard(40, 40, 400, 400);
265         renderer->drawRenderNode(card.get());
266 
267         renderer->insertReorderBarrier(false);
268     }
269 
doFrame(int frameNr)270     void doFrame(int frameNr) override {
271         int curFrame = frameNr % 150;
272         card->mutateStagingProperties().setTranslationX(curFrame);
273         card->mutateStagingProperties().setTranslationY(curFrame);
274         card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
275     }
276 private:
createCard(int x,int y,int width,int height)277     sp<RenderNode> createCard(int x, int y, int width, int height) {
278         sp<RenderNode> node = new RenderNode();
279         node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
280         node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
281 
282         DisplayListCanvas* renderer = startRecording(node.get());
283 
284         SkPaint paint;
285         paint.setAntiAlias(true);
286         paint.setColor(0xFF000000);
287         renderer->drawOval(0, 0, width, height, paint);
288 
289         endRecording(renderer, node.get());
290         return node;
291     }
292 };
293 
294 struct cstr_cmp {
operator ()cstr_cmp295     bool operator()(const char *a, const char *b) const {
296         return std::strcmp(a, b) < 0;
297     }
298 };
299 
300 typedef void (*testProc)(int);
301 
302 std::map<const char*, testProc, cstr_cmp> gTestMap {
303     {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
304     {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
305     {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
306     {"oval", TreeContentAnimation::run<OvalAnimation> },
307 };
308 
main(int argc,char * argv[])309 int main(int argc, char* argv[]) {
310     const char* testName = argc > 1 ? argv[1] : "shadowgrid";
311     testProc proc = gTestMap[testName];
312     if(!proc) {
313         printf("Error: couldn't find test %s\n", testName);
314         return 1;
315     }
316     int loopCount = 1;
317     if (argc > 2) {
318         loopCount = atoi(argv[2]);
319         if (!loopCount) {
320             printf("Invalid loop count!\n");
321             return 1;
322         }
323     }
324     int frameCount = 150;
325     if (argc > 3) {
326         frameCount = atoi(argv[3]);
327         if (frameCount < 1) {
328             printf("Invalid frame count!\n");
329             return 1;
330         }
331     }
332     if (loopCount < 0) {
333         loopCount = INT_MAX;
334     }
335     for (int i = 0; i < loopCount; i++) {
336         proc(frameCount);
337     }
338     printf("Success!\n");
339     return 0;
340 }
341