blob: 2eefd37561a607b04b4a58218f6bc9021ad214a7 [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>
Chris Craikb565df12015-10-05 13:00:52 -070023#include <RecordingCanvas.h>
John Recke702c9c2015-10-07 10:26:02 -070024#include <RenderNode.h>
25#include <renderthread/RenderProxy.h>
26#include <renderthread/RenderTask.h>
27
28#include "Benchmark.h"
29#include "TestContext.h"
30
31#include "protos/hwui.pb.h"
32
33#include <stdio.h>
34#include <unistd.h>
35#include <getopt.h>
36#include <vector>
37
38using namespace android;
39using namespace android::uirenderer;
40using namespace android::uirenderer::renderthread;
41using namespace android::uirenderer::test;
42
Chris Craikb565df12015-10-05 13:00:52 -070043#if HWUI_NEW_OPS
44typedef RecordingCanvas TestCanvas;
45#else
46typedef DisplayListCanvas TestCanvas;
47#endif
48
49
John Recke702c9c2015-10-07 10:26:02 -070050class ContextFactory : public IContextFactory {
51public:
52 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
53 return new AnimationContext(clock);
54 }
55};
56
Chris Craik818c9fb2015-10-23 14:33:42 -070057static void recordNode(RenderNode& node, std::function<void(TestCanvas&)> contentCallback) {
58 TestCanvas canvas(node.stagingProperties().getWidth(), node.stagingProperties().getHeight());
59 contentCallback(canvas);
60 node.setStagingDisplayList(canvas.finishRecording());
John Recke702c9c2015-10-07 10:26:02 -070061}
62
63class TreeContentAnimation {
64public:
65 virtual ~TreeContentAnimation() {}
66 int frameCount = 150;
67 virtual int getFrameCount() { return frameCount; }
68 virtual void setFrameCount(int fc) {
69 if (fc > 0) {
70 frameCount = fc;
71 }
72 }
Chris Craik818c9fb2015-10-23 14:33:42 -070073 virtual void createContent(int width, int height, TestCanvas* canvas) = 0;
John Recke702c9c2015-10-07 10:26:02 -070074 virtual void doFrame(int frameNr) = 0;
75
76 template <class T>
77 static void run(const BenchmarkOptions& opts) {
78 // Switch to the real display
79 gDisplay = getBuiltInDisplay();
80
81 T animation;
82 animation.setFrameCount(opts.count);
83
84 TestContext testContext;
85
86 // create the native surface
87 const int width = gDisplay.w;
88 const int height = gDisplay.h;
89 sp<Surface> surface = testContext.surface();
90
91 RenderNode* rootNode = new RenderNode();
92 rootNode->incStrong(nullptr);
93 rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
94 rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
95 rootNode->mutateStagingProperties().setClipToBounds(false);
96 rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
97
98 ContextFactory factory;
99 std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
100 proxy->loadSystemProperties();
101 proxy->initialize(surface);
102 float lightX = width / 2.0;
103 proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
104 proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
105
Chris Craik818c9fb2015-10-23 14:33:42 -0700106 recordNode(*rootNode, [&animation, width, height](TestCanvas& canvas) {
107 animation.createContent(width, height, &canvas); //TODO: no&
108 });
John Recke702c9c2015-10-07 10:26:02 -0700109
110 // Do a few cold runs then reset the stats so that the caches are all hot
111 for (int i = 0; i < 3; i++) {
112 testContext.waitForVsync();
113 proxy->syncAndDrawFrame();
114 }
115 proxy->resetProfileInfo();
116
117 for (int i = 0; i < animation.getFrameCount(); i++) {
118 testContext.waitForVsync();
119
120 ATRACE_NAME("UI-Draw Frame");
121 nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
122 UiFrameInfoBuilder(proxy->frameInfo())
123 .setVsync(vsync, vsync);
124 animation.doFrame(i);
125 proxy->syncAndDrawFrame();
126 }
127
128 proxy->dumpProfileInfo(STDOUT_FILENO, 0);
129 rootNode->decStrong(nullptr);
130 }
131};
132
133class ShadowGridAnimation : public TreeContentAnimation {
134public:
135 std::vector< sp<RenderNode> > cards;
Chris Craik818c9fb2015-10-23 14:33:42 -0700136 void createContent(int width, int height, TestCanvas* canvas) override {
137 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
138 canvas->insertReorderBarrier(true);
John Recke702c9c2015-10-07 10:26:02 -0700139
140 for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
141 for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
142 sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
Chris Craik818c9fb2015-10-23 14:33:42 -0700143 canvas->drawRenderNode(card.get());
John Recke702c9c2015-10-07 10:26:02 -0700144 cards.push_back(card);
145 }
146 }
147
Chris Craik818c9fb2015-10-23 14:33:42 -0700148 canvas->insertReorderBarrier(false);
John Recke702c9c2015-10-07 10:26:02 -0700149 }
150 void doFrame(int frameNr) override {
151 int curFrame = frameNr % 150;
152 for (size_t ci = 0; ci < cards.size(); ci++) {
153 cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
154 cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
155 cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
156 }
157 }
158private:
159 sp<RenderNode> createCard(int x, int y, int width, int height) {
160 sp<RenderNode> node = new RenderNode();
161 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
162 node->mutateStagingProperties().setElevation(dp(16));
163 node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
164 node->mutateStagingProperties().mutableOutline().setShouldClip(true);
165 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
166
Chris Craik818c9fb2015-10-23 14:33:42 -0700167 recordNode(*node, [](TestCanvas& canvas) {
168 canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
169 });
John Recke702c9c2015-10-07 10:26:02 -0700170 return node;
171 }
172};
173static Benchmark _ShadowGrid(BenchmarkInfo{
174 "shadowgrid",
175 "A grid of rounded rects that cast a shadow. Simplified scenario of an "
176 "Android TV-style launcher interface. High CPU/GPU load.",
177 TreeContentAnimation::run<ShadowGridAnimation>
178});
179
180class ShadowGrid2Animation : public TreeContentAnimation {
181public:
182 std::vector< sp<RenderNode> > cards;
Chris Craik818c9fb2015-10-23 14:33:42 -0700183 void createContent(int width, int height, TestCanvas* canvas) override {
184 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
185 canvas->insertReorderBarrier(true);
John Recke702c9c2015-10-07 10:26:02 -0700186
187 for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
188 for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
189 sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
Chris Craik818c9fb2015-10-23 14:33:42 -0700190 canvas->drawRenderNode(card.get());
John Recke702c9c2015-10-07 10:26:02 -0700191 cards.push_back(card);
192 }
193 }
194
Chris Craik818c9fb2015-10-23 14:33:42 -0700195 canvas->insertReorderBarrier(false);
John Recke702c9c2015-10-07 10:26:02 -0700196 }
197 void doFrame(int frameNr) override {
198 int curFrame = frameNr % 150;
199 for (size_t ci = 0; ci < cards.size(); ci++) {
200 cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
201 cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
202 cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
203 }
204 }
205private:
206 sp<RenderNode> createCard(int x, int y, int width, int height) {
207 sp<RenderNode> node = new RenderNode();
208 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
209 node->mutateStagingProperties().setElevation(dp(16));
210 node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
211 node->mutateStagingProperties().mutableOutline().setShouldClip(true);
212 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
213
Chris Craik818c9fb2015-10-23 14:33:42 -0700214 recordNode(*node, [](TestCanvas& canvas) {
215 canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
216 });
John Recke702c9c2015-10-07 10:26:02 -0700217 return node;
218 }
219};
220static Benchmark _ShadowGrid2(BenchmarkInfo{
221 "shadowgrid2",
222 "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
223 "variant of shadowgrid. Very high CPU load, high GPU load.",
224 TreeContentAnimation::run<ShadowGrid2Animation>
225});
226
227class RectGridAnimation : public TreeContentAnimation {
228public:
Chris Craik818c9fb2015-10-23 14:33:42 -0700229 sp<RenderNode> card = new RenderNode();
230 void createContent(int width, int height, TestCanvas* canvas) override {
231 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
232 canvas->insertReorderBarrier(true);
John Recke702c9c2015-10-07 10:26:02 -0700233
Chris Craik818c9fb2015-10-23 14:33:42 -0700234 card->mutateStagingProperties().setLeftTopRightBottom(50, 50, 250, 250);
235 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
236 recordNode(*card, [](TestCanvas& canvas) {
237 canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
John Recke702c9c2015-10-07 10:26:02 -0700238
Chris Craik818c9fb2015-10-23 14:33:42 -0700239 SkRegion region;
240 for (int xOffset = 0; xOffset < 200; xOffset+=2) {
241 for (int yOffset = 0; yOffset < 200; yOffset+=2) {
242 region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
243 }
244 }
245
246 SkPaint paint;
247 paint.setColor(0xff00ffff);
248 canvas.drawRegion(region, paint);
249 });
250 canvas->drawRenderNode(card.get());
251
252 canvas->insertReorderBarrier(false);
John Recke702c9c2015-10-07 10:26:02 -0700253 }
254 void doFrame(int frameNr) override {
255 int curFrame = frameNr % 150;
256 card->mutateStagingProperties().setTranslationX(curFrame);
257 card->mutateStagingProperties().setTranslationY(curFrame);
258 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
259 }
John Recke702c9c2015-10-07 10:26:02 -0700260};
261static Benchmark _RectGrid(BenchmarkInfo{
262 "rectgrid",
263 "A dense grid of 1x1 rects that should visually look like a single rect. "
264 "Low CPU/GPU load.",
265 TreeContentAnimation::run<RectGridAnimation>
266});
267
268class OvalAnimation : public TreeContentAnimation {
269public:
Chris Craik818c9fb2015-10-23 14:33:42 -0700270 sp<RenderNode> card = new RenderNode();
271 void createContent(int width, int height, TestCanvas* canvas) override {
272 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
273 canvas->insertReorderBarrier(true);
John Recke702c9c2015-10-07 10:26:02 -0700274
Chris Craik818c9fb2015-10-23 14:33:42 -0700275 card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
276 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
277 recordNode(*card, [](TestCanvas& canvas) {
278 SkPaint paint;
279 paint.setAntiAlias(true);
280 paint.setColor(0xFF000000);
281 canvas.drawOval(0, 0, 200, 200, paint);
282 });
283 canvas->drawRenderNode(card.get());
John Recke702c9c2015-10-07 10:26:02 -0700284
Chris Craik818c9fb2015-10-23 14:33:42 -0700285 canvas->insertReorderBarrier(false);
John Recke702c9c2015-10-07 10:26:02 -0700286 }
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 }
John Recke702c9c2015-10-07 10:26:02 -0700294};
295static Benchmark _Oval(BenchmarkInfo{
296 "oval",
297 "Draws 1 oval.",
298 TreeContentAnimation::run<OvalAnimation>
299});
300
301class PartialDamageTest : public TreeContentAnimation {
302public:
303 std::vector< sp<RenderNode> > cards;
Chris Craik818c9fb2015-10-23 14:33:42 -0700304 void createContent(int width, int height, TestCanvas* canvas) override {
John Recke702c9c2015-10-07 10:26:02 -0700305 static SkColor COLORS[] = {
306 0xFFF44336,
307 0xFF9C27B0,
308 0xFF2196F3,
309 0xFF4CAF50,
310 };
311
Chris Craik818c9fb2015-10-23 14:33:42 -0700312 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
John Recke702c9c2015-10-07 10:26:02 -0700313
314 for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
315 for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
316 sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
317 COLORS[static_cast<int>((y / dp(116))) % 4]);
Chris Craik818c9fb2015-10-23 14:33:42 -0700318 canvas->drawRenderNode(card.get());
John Recke702c9c2015-10-07 10:26:02 -0700319 cards.push_back(card);
320 }
321 }
322 }
323 void doFrame(int frameNr) override {
324 int curFrame = frameNr % 150;
325 cards[0]->mutateStagingProperties().setTranslationX(curFrame);
326 cards[0]->mutateStagingProperties().setTranslationY(curFrame);
327 cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
328
Chris Craik818c9fb2015-10-23 14:33:42 -0700329 recordNode(*cards[0], [curFrame](TestCanvas& canvas) {
330 canvas.drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
331 SkXfermode::kSrcOver_Mode);
332 });
John Recke702c9c2015-10-07 10:26:02 -0700333 }
334
335 static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
336 int startA = (start >> 24) & 0xff;
337 int startR = (start >> 16) & 0xff;
338 int startG = (start >> 8) & 0xff;
339 int startB = start & 0xff;
340
341 int endA = (end >> 24) & 0xff;
342 int endR = (end >> 16) & 0xff;
343 int endG = (end >> 8) & 0xff;
344 int endB = end & 0xff;
345
346 return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
347 (int)((startR + (int)(fraction * (endR - startR))) << 16) |
348 (int)((startG + (int)(fraction * (endG - startG))) << 8) |
349 (int)((startB + (int)(fraction * (endB - startB))));
350 }
351private:
352 sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
353 sp<RenderNode> node = new RenderNode();
354 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
355 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
356
Chris Craik818c9fb2015-10-23 14:33:42 -0700357 recordNode(*node, [color](TestCanvas& canvas) {
358 canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
359 });
John Recke702c9c2015-10-07 10:26:02 -0700360 return node;
361 }
362};
363static Benchmark _PartialDamage(BenchmarkInfo{
364 "partialdamage",
365 "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
366 "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
367 "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
368 TreeContentAnimation::run<PartialDamageTest>
369});
Chris Craikb565df12015-10-05 13:00:52 -0700370
371
Chris Craik818c9fb2015-10-23 14:33:42 -0700372class SaveLayerAnimation : public TreeContentAnimation {
Chris Craikb565df12015-10-05 13:00:52 -0700373public:
Chris Craik818c9fb2015-10-23 14:33:42 -0700374 sp<RenderNode> card = new RenderNode();
375 void createContent(int width, int height, TestCanvas* canvas) override {
376 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
Chris Craikb565df12015-10-05 13:00:52 -0700377
Chris Craik818c9fb2015-10-23 14:33:42 -0700378 card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
379 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
380 recordNode(*card, [](TestCanvas& canvas) {
381 canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
382 canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
383 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
384 canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
385 canvas.restore();
386 canvas.restore();
387 });
388
389 canvas->drawRenderNode(card.get());
Chris Craikb565df12015-10-05 13:00:52 -0700390 }
391 void doFrame(int frameNr) override {
392 int curFrame = frameNr % 150;
393 card->mutateStagingProperties().setTranslationX(curFrame);
394 card->mutateStagingProperties().setTranslationY(curFrame);
395 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
396 }
Chris Craikb565df12015-10-05 13:00:52 -0700397};
Chris Craik818c9fb2015-10-23 14:33:42 -0700398static Benchmark _SaveLayer(BenchmarkInfo{
399 "savelayer",
400 "A nested pair of clipped saveLayer operations. "
401 "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
402 TreeContentAnimation::run<SaveLayerAnimation>
Chris Craikb565df12015-10-05 13:00:52 -0700403});