blob: a59261c14fc5197c1cb9ebd87ac33f9280c24ccd [file] [log] [blame]
John Recke702c9c2015-10-07 10:26:02 -07001/*
2 * Copyright (C) 2015 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 "Benchmark.h"
28#include "TestContext.h"
29
30#include "protos/hwui.pb.h"
31
32#include <stdio.h>
33#include <unistd.h>
34#include <getopt.h>
35#include <vector>
36
37using namespace android;
38using namespace android::uirenderer;
39using namespace android::uirenderer::renderthread;
40using namespace android::uirenderer::test;
41
42class ContextFactory : public IContextFactory {
43public:
44 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
45 return new AnimationContext(clock);
46 }
47};
48
49static DisplayListCanvas* startRecording(RenderNode* node) {
50 DisplayListCanvas* renderer = new DisplayListCanvas(
51 node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
52 return renderer;
53}
54
55static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
56 node->setStagingDisplayList(renderer->finishRecording());
57 delete renderer;
58}
59
60class TreeContentAnimation {
61public:
62 virtual ~TreeContentAnimation() {}
63 int frameCount = 150;
64 virtual int getFrameCount() { return frameCount; }
65 virtual void setFrameCount(int fc) {
66 if (fc > 0) {
67 frameCount = fc;
68 }
69 }
70 virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
71 virtual void doFrame(int frameNr) = 0;
72
73 template <class T>
74 static void run(const BenchmarkOptions& opts) {
75 // Switch to the real display
76 gDisplay = getBuiltInDisplay();
77
78 T animation;
79 animation.setFrameCount(opts.count);
80
81 TestContext testContext;
82
83 // create the native surface
84 const int width = gDisplay.w;
85 const int height = gDisplay.h;
86 sp<Surface> surface = testContext.surface();
87
88 RenderNode* rootNode = new RenderNode();
89 rootNode->incStrong(nullptr);
90 rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
91 rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
92 rootNode->mutateStagingProperties().setClipToBounds(false);
93 rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
94
95 ContextFactory factory;
96 std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
97 proxy->loadSystemProperties();
98 proxy->initialize(surface);
99 float lightX = width / 2.0;
100 proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
101 proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
102
103 android::uirenderer::Rect DUMMY;
104
105 DisplayListCanvas* renderer = startRecording(rootNode);
106 animation.createContent(width, height, renderer);
107 endRecording(renderer, rootNode);
108
109 // Do a few cold runs then reset the stats so that the caches are all hot
110 for (int i = 0; i < 3; i++) {
111 testContext.waitForVsync();
112 proxy->syncAndDrawFrame();
113 }
114 proxy->resetProfileInfo();
115
116 for (int i = 0; i < animation.getFrameCount(); i++) {
117 testContext.waitForVsync();
118
119 ATRACE_NAME("UI-Draw Frame");
120 nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
121 UiFrameInfoBuilder(proxy->frameInfo())
122 .setVsync(vsync, vsync);
123 animation.doFrame(i);
124 proxy->syncAndDrawFrame();
125 }
126
127 proxy->dumpProfileInfo(STDOUT_FILENO, 0);
128 rootNode->decStrong(nullptr);
129 }
130};
131
132class ShadowGridAnimation : public TreeContentAnimation {
133public:
134 std::vector< sp<RenderNode> > cards;
135 void createContent(int width, int height, DisplayListCanvas* renderer) override {
136 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
137 renderer->insertReorderBarrier(true);
138
139 for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
140 for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
141 sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
142 renderer->drawRenderNode(card.get());
143 cards.push_back(card);
144 }
145 }
146
147 renderer->insertReorderBarrier(false);
148 }
149 void doFrame(int frameNr) override {
150 int curFrame = frameNr % 150;
151 for (size_t ci = 0; ci < cards.size(); ci++) {
152 cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
153 cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
154 cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
155 }
156 }
157private:
158 sp<RenderNode> createCard(int x, int y, int width, int height) {
159 sp<RenderNode> node = new RenderNode();
160 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
161 node->mutateStagingProperties().setElevation(dp(16));
162 node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
163 node->mutateStagingProperties().mutableOutline().setShouldClip(true);
164 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
165
166 DisplayListCanvas* renderer = startRecording(node.get());
167 renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
168 endRecording(renderer, node.get());
169 return node;
170 }
171};
172static Benchmark _ShadowGrid(BenchmarkInfo{
173 "shadowgrid",
174 "A grid of rounded rects that cast a shadow. Simplified scenario of an "
175 "Android TV-style launcher interface. High CPU/GPU load.",
176 TreeContentAnimation::run<ShadowGridAnimation>
177});
178
179class ShadowGrid2Animation : public TreeContentAnimation {
180public:
181 std::vector< sp<RenderNode> > cards;
182 void createContent(int width, int height, DisplayListCanvas* renderer) override {
183 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
184 renderer->insertReorderBarrier(true);
185
186 for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
187 for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
188 sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
189 renderer->drawRenderNode(card.get());
190 cards.push_back(card);
191 }
192 }
193
194 renderer->insertReorderBarrier(false);
195 }
196 void doFrame(int frameNr) override {
197 int curFrame = frameNr % 150;
198 for (size_t ci = 0; ci < cards.size(); ci++) {
199 cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
200 cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
201 cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
202 }
203 }
204private:
205 sp<RenderNode> createCard(int x, int y, int width, int height) {
206 sp<RenderNode> node = new RenderNode();
207 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
208 node->mutateStagingProperties().setElevation(dp(16));
209 node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
210 node->mutateStagingProperties().mutableOutline().setShouldClip(true);
211 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
212
213 DisplayListCanvas* renderer = startRecording(node.get());
214 renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
215 endRecording(renderer, node.get());
216 return node;
217 }
218};
219static Benchmark _ShadowGrid2(BenchmarkInfo{
220 "shadowgrid2",
221 "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
222 "variant of shadowgrid. Very high CPU load, high GPU load.",
223 TreeContentAnimation::run<ShadowGrid2Animation>
224});
225
226class RectGridAnimation : public TreeContentAnimation {
227public:
228 sp<RenderNode> card;
229 void createContent(int width, int height, DisplayListCanvas* renderer) override {
230 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
231 renderer->insertReorderBarrier(true);
232
233 card = createCard(40, 40, 200, 200);
234 renderer->drawRenderNode(card.get());
235
236 renderer->insertReorderBarrier(false);
237 }
238 void doFrame(int frameNr) override {
239 int curFrame = frameNr % 150;
240 card->mutateStagingProperties().setTranslationX(curFrame);
241 card->mutateStagingProperties().setTranslationY(curFrame);
242 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
243 }
244private:
245 sp<RenderNode> createCard(int x, int y, int width, int height) {
246 sp<RenderNode> node = new RenderNode();
247 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
248 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
249
250 DisplayListCanvas* renderer = startRecording(node.get());
251 renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
252
253 SkRegion region;
254 for (int xOffset = 0; xOffset < width; xOffset+=2) {
255 for (int yOffset = 0; yOffset < height; yOffset+=2) {
256 region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
257 }
258 }
259
260 SkPaint paint;
261 paint.setColor(0xff00ffff);
262 renderer->drawRegion(region, paint);
263
264 endRecording(renderer, node.get());
265 return node;
266 }
267};
268static Benchmark _RectGrid(BenchmarkInfo{
269 "rectgrid",
270 "A dense grid of 1x1 rects that should visually look like a single rect. "
271 "Low CPU/GPU load.",
272 TreeContentAnimation::run<RectGridAnimation>
273});
274
275class OvalAnimation : public TreeContentAnimation {
276public:
277 sp<RenderNode> card;
278 void createContent(int width, int height, DisplayListCanvas* renderer) override {
279 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
280 renderer->insertReorderBarrier(true);
281
282 card = createCard(40, 40, 400, 400);
283 renderer->drawRenderNode(card.get());
284
285 renderer->insertReorderBarrier(false);
286 }
287
288 void doFrame(int frameNr) override {
289 int curFrame = frameNr % 150;
290 card->mutateStagingProperties().setTranslationX(curFrame);
291 card->mutateStagingProperties().setTranslationY(curFrame);
292 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
293 }
294private:
295 sp<RenderNode> createCard(int x, int y, int width, int height) {
296 sp<RenderNode> node = new RenderNode();
297 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
298 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
299
300 DisplayListCanvas* renderer = startRecording(node.get());
301
302 SkPaint paint;
303 paint.setAntiAlias(true);
304 paint.setColor(0xFF000000);
305 renderer->drawOval(0, 0, width, height, paint);
306
307 endRecording(renderer, node.get());
308 return node;
309 }
310};
311static Benchmark _Oval(BenchmarkInfo{
312 "oval",
313 "Draws 1 oval.",
314 TreeContentAnimation::run<OvalAnimation>
315});
316
317class PartialDamageTest : public TreeContentAnimation {
318public:
319 std::vector< sp<RenderNode> > cards;
320 void createContent(int width, int height, DisplayListCanvas* renderer) override {
321 static SkColor COLORS[] = {
322 0xFFF44336,
323 0xFF9C27B0,
324 0xFF2196F3,
325 0xFF4CAF50,
326 };
327
328 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
329
330 for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
331 for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
332 sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
333 COLORS[static_cast<int>((y / dp(116))) % 4]);
334 renderer->drawRenderNode(card.get());
335 cards.push_back(card);
336 }
337 }
338 }
339 void doFrame(int frameNr) override {
340 int curFrame = frameNr % 150;
341 cards[0]->mutateStagingProperties().setTranslationX(curFrame);
342 cards[0]->mutateStagingProperties().setTranslationY(curFrame);
343 cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
344
345 DisplayListCanvas* renderer = startRecording(cards[0].get());
346 renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
347 SkXfermode::kSrcOver_Mode);
348 endRecording(renderer, cards[0].get());
349 }
350
351 static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
352 int startA = (start >> 24) & 0xff;
353 int startR = (start >> 16) & 0xff;
354 int startG = (start >> 8) & 0xff;
355 int startB = start & 0xff;
356
357 int endA = (end >> 24) & 0xff;
358 int endR = (end >> 16) & 0xff;
359 int endG = (end >> 8) & 0xff;
360 int endB = end & 0xff;
361
362 return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
363 (int)((startR + (int)(fraction * (endR - startR))) << 16) |
364 (int)((startG + (int)(fraction * (endG - startG))) << 8) |
365 (int)((startB + (int)(fraction * (endB - startB))));
366 }
367private:
368 sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
369 sp<RenderNode> node = new RenderNode();
370 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
371 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
372
373 DisplayListCanvas* renderer = startRecording(node.get());
374 renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
375 endRecording(renderer, node.get());
376 return node;
377 }
378};
379static Benchmark _PartialDamage(BenchmarkInfo{
380 "partialdamage",
381 "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
382 "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
383 "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
384 TreeContentAnimation::run<PartialDamageTest>
385});