blob: 891af91715187be3025e8418a2af96bb8e157b2d [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 Craikb565df12015-10-05 13:00:52 -070057static TestCanvas* startRecording(RenderNode* node) {
58 TestCanvas* renderer = new TestCanvas(
John Recke702c9c2015-10-07 10:26:02 -070059 node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
60 return renderer;
61}
62
Chris Craikb565df12015-10-05 13:00:52 -070063static void endRecording(TestCanvas* renderer, RenderNode* node) {
John Recke702c9c2015-10-07 10:26:02 -070064 node->setStagingDisplayList(renderer->finishRecording());
65 delete renderer;
66}
67
68class TreeContentAnimation {
69public:
70 virtual ~TreeContentAnimation() {}
71 int frameCount = 150;
72 virtual int getFrameCount() { return frameCount; }
73 virtual void setFrameCount(int fc) {
74 if (fc > 0) {
75 frameCount = fc;
76 }
77 }
Chris Craikb565df12015-10-05 13:00:52 -070078 virtual void createContent(int width, int height, TestCanvas* renderer) = 0;
John Recke702c9c2015-10-07 10:26:02 -070079 virtual void doFrame(int frameNr) = 0;
80
81 template <class T>
82 static void run(const BenchmarkOptions& opts) {
83 // Switch to the real display
84 gDisplay = getBuiltInDisplay();
85
86 T animation;
87 animation.setFrameCount(opts.count);
88
89 TestContext testContext;
90
91 // create the native surface
92 const int width = gDisplay.w;
93 const int height = gDisplay.h;
94 sp<Surface> surface = testContext.surface();
95
96 RenderNode* rootNode = new RenderNode();
97 rootNode->incStrong(nullptr);
98 rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
99 rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
100 rootNode->mutateStagingProperties().setClipToBounds(false);
101 rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
102
103 ContextFactory factory;
104 std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
105 proxy->loadSystemProperties();
106 proxy->initialize(surface);
107 float lightX = width / 2.0;
108 proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
109 proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
110
111 android::uirenderer::Rect DUMMY;
112
Chris Craikb565df12015-10-05 13:00:52 -0700113 TestCanvas* renderer = startRecording(rootNode);
John Recke702c9c2015-10-07 10:26:02 -0700114 animation.createContent(width, height, renderer);
115 endRecording(renderer, rootNode);
116
117 // Do a few cold runs then reset the stats so that the caches are all hot
118 for (int i = 0; i < 3; i++) {
119 testContext.waitForVsync();
120 proxy->syncAndDrawFrame();
121 }
122 proxy->resetProfileInfo();
123
124 for (int i = 0; i < animation.getFrameCount(); i++) {
125 testContext.waitForVsync();
126
127 ATRACE_NAME("UI-Draw Frame");
128 nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
129 UiFrameInfoBuilder(proxy->frameInfo())
130 .setVsync(vsync, vsync);
131 animation.doFrame(i);
132 proxy->syncAndDrawFrame();
133 }
134
135 proxy->dumpProfileInfo(STDOUT_FILENO, 0);
136 rootNode->decStrong(nullptr);
137 }
138};
139
140class ShadowGridAnimation : public TreeContentAnimation {
141public:
142 std::vector< sp<RenderNode> > cards;
Chris Craikb565df12015-10-05 13:00:52 -0700143 void createContent(int width, int height, TestCanvas* renderer) override {
John Recke702c9c2015-10-07 10:26:02 -0700144 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
145 renderer->insertReorderBarrier(true);
146
147 for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
148 for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
149 sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
150 renderer->drawRenderNode(card.get());
151 cards.push_back(card);
152 }
153 }
154
155 renderer->insertReorderBarrier(false);
156 }
157 void doFrame(int frameNr) override {
158 int curFrame = frameNr % 150;
159 for (size_t ci = 0; ci < cards.size(); ci++) {
160 cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
161 cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
162 cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
163 }
164 }
165private:
166 sp<RenderNode> createCard(int x, int y, int width, int height) {
167 sp<RenderNode> node = new RenderNode();
168 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
169 node->mutateStagingProperties().setElevation(dp(16));
170 node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
171 node->mutateStagingProperties().mutableOutline().setShouldClip(true);
172 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
173
Chris Craikb565df12015-10-05 13:00:52 -0700174 TestCanvas* renderer = startRecording(node.get());
John Recke702c9c2015-10-07 10:26:02 -0700175 renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
176 endRecording(renderer, node.get());
177 return node;
178 }
179};
180static Benchmark _ShadowGrid(BenchmarkInfo{
181 "shadowgrid",
182 "A grid of rounded rects that cast a shadow. Simplified scenario of an "
183 "Android TV-style launcher interface. High CPU/GPU load.",
184 TreeContentAnimation::run<ShadowGridAnimation>
185});
186
187class ShadowGrid2Animation : public TreeContentAnimation {
188public:
189 std::vector< sp<RenderNode> > cards;
Chris Craikb565df12015-10-05 13:00:52 -0700190 void createContent(int width, int height, TestCanvas* renderer) override {
John Recke702c9c2015-10-07 10:26:02 -0700191 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
192 renderer->insertReorderBarrier(true);
193
194 for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
195 for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
196 sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
197 renderer->drawRenderNode(card.get());
198 cards.push_back(card);
199 }
200 }
201
202 renderer->insertReorderBarrier(false);
203 }
204 void doFrame(int frameNr) override {
205 int curFrame = frameNr % 150;
206 for (size_t ci = 0; ci < cards.size(); ci++) {
207 cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
208 cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
209 cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
210 }
211 }
212private:
213 sp<RenderNode> createCard(int x, int y, int width, int height) {
214 sp<RenderNode> node = new RenderNode();
215 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
216 node->mutateStagingProperties().setElevation(dp(16));
217 node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
218 node->mutateStagingProperties().mutableOutline().setShouldClip(true);
219 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
220
Chris Craikb565df12015-10-05 13:00:52 -0700221 TestCanvas* renderer = startRecording(node.get());
John Recke702c9c2015-10-07 10:26:02 -0700222 renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
223 endRecording(renderer, node.get());
224 return node;
225 }
226};
227static Benchmark _ShadowGrid2(BenchmarkInfo{
228 "shadowgrid2",
229 "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
230 "variant of shadowgrid. Very high CPU load, high GPU load.",
231 TreeContentAnimation::run<ShadowGrid2Animation>
232});
233
234class RectGridAnimation : public TreeContentAnimation {
235public:
236 sp<RenderNode> card;
Chris Craikb565df12015-10-05 13:00:52 -0700237 void createContent(int width, int height, TestCanvas* renderer) override {
John Recke702c9c2015-10-07 10:26:02 -0700238 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
239 renderer->insertReorderBarrier(true);
240
241 card = createCard(40, 40, 200, 200);
242 renderer->drawRenderNode(card.get());
243
244 renderer->insertReorderBarrier(false);
245 }
246 void doFrame(int frameNr) override {
247 int curFrame = frameNr % 150;
248 card->mutateStagingProperties().setTranslationX(curFrame);
249 card->mutateStagingProperties().setTranslationY(curFrame);
250 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
251 }
252private:
253 sp<RenderNode> createCard(int x, int y, int width, int height) {
254 sp<RenderNode> node = new RenderNode();
255 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
256 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
257
Chris Craikb565df12015-10-05 13:00:52 -0700258 TestCanvas* renderer = startRecording(node.get());
John Recke702c9c2015-10-07 10:26:02 -0700259 renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
260
261 SkRegion region;
262 for (int xOffset = 0; xOffset < width; xOffset+=2) {
263 for (int yOffset = 0; yOffset < height; yOffset+=2) {
264 region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
265 }
266 }
267
268 SkPaint paint;
269 paint.setColor(0xff00ffff);
270 renderer->drawRegion(region, paint);
271
272 endRecording(renderer, node.get());
273 return node;
274 }
275};
276static Benchmark _RectGrid(BenchmarkInfo{
277 "rectgrid",
278 "A dense grid of 1x1 rects that should visually look like a single rect. "
279 "Low CPU/GPU load.",
280 TreeContentAnimation::run<RectGridAnimation>
281});
282
283class OvalAnimation : public TreeContentAnimation {
284public:
285 sp<RenderNode> card;
Chris Craikb565df12015-10-05 13:00:52 -0700286 void createContent(int width, int height, TestCanvas* renderer) override {
John Recke702c9c2015-10-07 10:26:02 -0700287 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
288 renderer->insertReorderBarrier(true);
289
290 card = createCard(40, 40, 400, 400);
291 renderer->drawRenderNode(card.get());
292
293 renderer->insertReorderBarrier(false);
294 }
295
296 void doFrame(int frameNr) override {
297 int curFrame = frameNr % 150;
298 card->mutateStagingProperties().setTranslationX(curFrame);
299 card->mutateStagingProperties().setTranslationY(curFrame);
300 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
301 }
302private:
303 sp<RenderNode> createCard(int x, int y, int width, int height) {
304 sp<RenderNode> node = new RenderNode();
305 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
306 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
307
Chris Craikb565df12015-10-05 13:00:52 -0700308 TestCanvas* renderer = startRecording(node.get());
John Recke702c9c2015-10-07 10:26:02 -0700309
310 SkPaint paint;
311 paint.setAntiAlias(true);
312 paint.setColor(0xFF000000);
313 renderer->drawOval(0, 0, width, height, paint);
314
315 endRecording(renderer, node.get());
316 return node;
317 }
318};
319static Benchmark _Oval(BenchmarkInfo{
320 "oval",
321 "Draws 1 oval.",
322 TreeContentAnimation::run<OvalAnimation>
323});
324
325class PartialDamageTest : public TreeContentAnimation {
326public:
327 std::vector< sp<RenderNode> > cards;
Chris Craikb565df12015-10-05 13:00:52 -0700328 void createContent(int width, int height, TestCanvas* renderer) override {
John Recke702c9c2015-10-07 10:26:02 -0700329 static SkColor COLORS[] = {
330 0xFFF44336,
331 0xFF9C27B0,
332 0xFF2196F3,
333 0xFF4CAF50,
334 };
335
336 renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
337
338 for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
339 for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
340 sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
341 COLORS[static_cast<int>((y / dp(116))) % 4]);
342 renderer->drawRenderNode(card.get());
343 cards.push_back(card);
344 }
345 }
346 }
347 void doFrame(int frameNr) override {
348 int curFrame = frameNr % 150;
349 cards[0]->mutateStagingProperties().setTranslationX(curFrame);
350 cards[0]->mutateStagingProperties().setTranslationY(curFrame);
351 cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
352
Chris Craikb565df12015-10-05 13:00:52 -0700353 TestCanvas* renderer = startRecording(cards[0].get());
John Recke702c9c2015-10-07 10:26:02 -0700354 renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
355 SkXfermode::kSrcOver_Mode);
356 endRecording(renderer, cards[0].get());
357 }
358
359 static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
360 int startA = (start >> 24) & 0xff;
361 int startR = (start >> 16) & 0xff;
362 int startG = (start >> 8) & 0xff;
363 int startB = start & 0xff;
364
365 int endA = (end >> 24) & 0xff;
366 int endR = (end >> 16) & 0xff;
367 int endG = (end >> 8) & 0xff;
368 int endB = end & 0xff;
369
370 return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
371 (int)((startR + (int)(fraction * (endR - startR))) << 16) |
372 (int)((startG + (int)(fraction * (endG - startG))) << 8) |
373 (int)((startB + (int)(fraction * (endB - startB))));
374 }
375private:
376 sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
377 sp<RenderNode> node = new RenderNode();
378 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
379 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
380
Chris Craikb565df12015-10-05 13:00:52 -0700381 TestCanvas* renderer = startRecording(node.get());
John Recke702c9c2015-10-07 10:26:02 -0700382 renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
383 endRecording(renderer, node.get());
384 return node;
385 }
386};
387static Benchmark _PartialDamage(BenchmarkInfo{
388 "partialdamage",
389 "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
390 "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
391 "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
392 TreeContentAnimation::run<PartialDamageTest>
393});
Chris Craikb565df12015-10-05 13:00:52 -0700394
395
396class SimpleRectGridAnimation : public TreeContentAnimation {
397public:
398 sp<RenderNode> card;
399 void createContent(int width, int height, TestCanvas* renderer) override {
400 SkPaint paint;
401 paint.setColor(0xFF00FFFF);
402 renderer->drawRect(0, 0, width, height, paint);
403
404 card = createCard(40, 40, 200, 200);
405 renderer->drawRenderNode(card.get());
406 }
407 void doFrame(int frameNr) override {
408 int curFrame = frameNr % 150;
409 card->mutateStagingProperties().setTranslationX(curFrame);
410 card->mutateStagingProperties().setTranslationY(curFrame);
411 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
412 }
413private:
414 sp<RenderNode> createCard(int x, int y, int width, int height) {
415 sp<RenderNode> node = new RenderNode();
416 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
417 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
418
419 TestCanvas* renderer = startRecording(node.get());
420 SkPaint paint;
421 paint.setColor(0xFFFF00FF);
422 renderer->drawRect(0, 0, width, height, paint);
423
424 endRecording(renderer, node.get());
425 return node;
426 }
427};
428static Benchmark _SimpleRectGrid(BenchmarkInfo{
429 "simplerectgrid",
430 "A simple collection of rects. "
431 "Low CPU/GPU load.",
432 TreeContentAnimation::run<SimpleRectGridAnimation>
433});