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
32using namespace android;
33using namespace android::uirenderer;
34using namespace android::uirenderer::renderthread;
35using namespace android::uirenderer::test;
36
37class ContextFactory : public IContextFactory {
38public:
39    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
40        return new AnimationContext(clock);
41    }
42};
43
44static 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
52static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
53    renderer->finish();
54    node->setStagingDisplayList(renderer->finishRecording());
55    delete renderer;
56}
57
58class TreeContentAnimation {
59public:
60    virtual ~TreeContentAnimation() {}
61    int frameCount = 150;
62    virtual int getFrameCount() { return frameCount; }
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>
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
127class ShadowGridAnimation : public TreeContentAnimation {
128public:
129    std::vector< sp<RenderNode> > cards;
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    }
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    }
152private:
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
168class ShadowGrid2Animation : public TreeContentAnimation {
169public:
170    std::vector< sp<RenderNode> > cards;
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    }
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    }
193private:
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
209class RectGridAnimation : public TreeContentAnimation {
210public:
211    sp<RenderNode> card;
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    }
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    }
227private:
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
257class OvalAnimation : public TreeContentAnimation {
258public:
259    sp<RenderNode> card;
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
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    }
276private:
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
294struct cstr_cmp {
295    bool operator()(const char *a, const char *b) const {
296        return std::strcmp(a, b) < 0;
297    }
298};
299
300typedef void (*testProc)(int);
301
302std::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
309int 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