blob: 29d9803ddf9c95fdb7979ea1259122fd79baea54 [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>
Chris Craik0b7e8242015-10-28 16:50:44 -070027#include <unit_tests/TestUtils.h>
John Recke702c9c2015-10-07 10:26:02 -070028
29#include "Benchmark.h"
30#include "TestContext.h"
31
32#include "protos/hwui.pb.h"
33
34#include <stdio.h>
35#include <unistd.h>
36#include <getopt.h>
37#include <vector>
38
39using namespace android;
40using namespace android::uirenderer;
41using namespace android::uirenderer::renderthread;
42using namespace android::uirenderer::test;
43
Chris Craikb565df12015-10-05 13:00:52 -070044#if HWUI_NEW_OPS
45typedef RecordingCanvas TestCanvas;
46#else
47typedef DisplayListCanvas TestCanvas;
48#endif
49
50
John Recke702c9c2015-10-07 10:26:02 -070051class ContextFactory : public IContextFactory {
52public:
53 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
54 return new AnimationContext(clock);
55 }
56};
57
Chris Craik818c9fb2015-10-23 14:33:42 -070058static void recordNode(RenderNode& node, std::function<void(TestCanvas&)> contentCallback) {
59 TestCanvas canvas(node.stagingProperties().getWidth(), node.stagingProperties().getHeight());
60 contentCallback(canvas);
61 node.setStagingDisplayList(canvas.finishRecording());
John Recke702c9c2015-10-07 10:26:02 -070062}
63
64class TreeContentAnimation {
65public:
66 virtual ~TreeContentAnimation() {}
67 int frameCount = 150;
68 virtual int getFrameCount() { return frameCount; }
69 virtual void setFrameCount(int fc) {
70 if (fc > 0) {
71 frameCount = fc;
72 }
73 }
Chris Craik818c9fb2015-10-23 14:33:42 -070074 virtual void createContent(int width, int height, TestCanvas* canvas) = 0;
John Recke702c9c2015-10-07 10:26:02 -070075 virtual void doFrame(int frameNr) = 0;
76
77 template <class T>
78 static void run(const BenchmarkOptions& opts) {
79 // Switch to the real display
80 gDisplay = getBuiltInDisplay();
81
82 T animation;
83 animation.setFrameCount(opts.count);
84
85 TestContext testContext;
86
87 // create the native surface
88 const int width = gDisplay.w;
89 const int height = gDisplay.h;
90 sp<Surface> surface = testContext.surface();
91
92 RenderNode* rootNode = new RenderNode();
93 rootNode->incStrong(nullptr);
94 rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
95 rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
96 rootNode->mutateStagingProperties().setClipToBounds(false);
97 rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
98
99 ContextFactory factory;
100 std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
101 proxy->loadSystemProperties();
102 proxy->initialize(surface);
103 float lightX = width / 2.0;
104 proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
105 proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
106
Chris Craik818c9fb2015-10-23 14:33:42 -0700107 recordNode(*rootNode, [&animation, width, height](TestCanvas& canvas) {
108 animation.createContent(width, height, &canvas); //TODO: no&
109 });
John Recke702c9c2015-10-07 10:26:02 -0700110
111 // Do a few cold runs then reset the stats so that the caches are all hot
112 for (int i = 0; i < 3; i++) {
113 testContext.waitForVsync();
114 proxy->syncAndDrawFrame();
115 }
116 proxy->resetProfileInfo();
117
118 for (int i = 0; i < animation.getFrameCount(); i++) {
119 testContext.waitForVsync();
120
121 ATRACE_NAME("UI-Draw Frame");
122 nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
123 UiFrameInfoBuilder(proxy->frameInfo())
124 .setVsync(vsync, vsync);
125 animation.doFrame(i);
126 proxy->syncAndDrawFrame();
127 }
128
129 proxy->dumpProfileInfo(STDOUT_FILENO, 0);
130 rootNode->decStrong(nullptr);
131 }
132};
133
134class ShadowGridAnimation : public TreeContentAnimation {
135public:
136 std::vector< sp<RenderNode> > cards;
Chris Craik818c9fb2015-10-23 14:33:42 -0700137 void createContent(int width, int height, TestCanvas* canvas) override {
138 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
139 canvas->insertReorderBarrier(true);
John Recke702c9c2015-10-07 10:26:02 -0700140
141 for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
142 for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
143 sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
Chris Craik818c9fb2015-10-23 14:33:42 -0700144 canvas->drawRenderNode(card.get());
John Recke702c9c2015-10-07 10:26:02 -0700145 cards.push_back(card);
146 }
147 }
148
Chris Craik818c9fb2015-10-23 14:33:42 -0700149 canvas->insertReorderBarrier(false);
John Recke702c9c2015-10-07 10:26:02 -0700150 }
151 void doFrame(int frameNr) override {
152 int curFrame = frameNr % 150;
153 for (size_t ci = 0; ci < cards.size(); ci++) {
154 cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
155 cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
156 cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
157 }
158 }
159private:
160 sp<RenderNode> createCard(int x, int y, int width, int height) {
161 sp<RenderNode> node = new RenderNode();
162 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
163 node->mutateStagingProperties().setElevation(dp(16));
164 node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
165 node->mutateStagingProperties().mutableOutline().setShouldClip(true);
166 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
167
Chris Craik818c9fb2015-10-23 14:33:42 -0700168 recordNode(*node, [](TestCanvas& canvas) {
169 canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
170 });
John Recke702c9c2015-10-07 10:26:02 -0700171 return node;
172 }
173};
174static Benchmark _ShadowGrid(BenchmarkInfo{
175 "shadowgrid",
176 "A grid of rounded rects that cast a shadow. Simplified scenario of an "
177 "Android TV-style launcher interface. High CPU/GPU load.",
178 TreeContentAnimation::run<ShadowGridAnimation>
179});
180
181class ShadowGrid2Animation : public TreeContentAnimation {
182public:
183 std::vector< sp<RenderNode> > cards;
Chris Craik818c9fb2015-10-23 14:33:42 -0700184 void createContent(int width, int height, TestCanvas* canvas) override {
185 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
186 canvas->insertReorderBarrier(true);
John Recke702c9c2015-10-07 10:26:02 -0700187
188 for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
189 for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
190 sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
Chris Craik818c9fb2015-10-23 14:33:42 -0700191 canvas->drawRenderNode(card.get());
John Recke702c9c2015-10-07 10:26:02 -0700192 cards.push_back(card);
193 }
194 }
195
Chris Craik818c9fb2015-10-23 14:33:42 -0700196 canvas->insertReorderBarrier(false);
John Recke702c9c2015-10-07 10:26:02 -0700197 }
198 void doFrame(int frameNr) override {
199 int curFrame = frameNr % 150;
200 for (size_t ci = 0; ci < cards.size(); ci++) {
201 cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
202 cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
203 cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
204 }
205 }
206private:
207 sp<RenderNode> createCard(int x, int y, int width, int height) {
208 sp<RenderNode> node = new RenderNode();
209 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
210 node->mutateStagingProperties().setElevation(dp(16));
211 node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
212 node->mutateStagingProperties().mutableOutline().setShouldClip(true);
213 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
214
Chris Craik818c9fb2015-10-23 14:33:42 -0700215 recordNode(*node, [](TestCanvas& canvas) {
216 canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
217 });
John Recke702c9c2015-10-07 10:26:02 -0700218 return node;
219 }
220};
221static Benchmark _ShadowGrid2(BenchmarkInfo{
222 "shadowgrid2",
223 "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
224 "variant of shadowgrid. Very high CPU load, high GPU load.",
225 TreeContentAnimation::run<ShadowGrid2Animation>
226});
227
228class RectGridAnimation : public TreeContentAnimation {
229public:
Chris Craik818c9fb2015-10-23 14:33:42 -0700230 sp<RenderNode> card = new RenderNode();
231 void createContent(int width, int height, TestCanvas* canvas) override {
232 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
233 canvas->insertReorderBarrier(true);
John Recke702c9c2015-10-07 10:26:02 -0700234
Chris Craik818c9fb2015-10-23 14:33:42 -0700235 card->mutateStagingProperties().setLeftTopRightBottom(50, 50, 250, 250);
236 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
237 recordNode(*card, [](TestCanvas& canvas) {
238 canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
John Recke702c9c2015-10-07 10:26:02 -0700239
Chris Craik818c9fb2015-10-23 14:33:42 -0700240 SkRegion region;
241 for (int xOffset = 0; xOffset < 200; xOffset+=2) {
242 for (int yOffset = 0; yOffset < 200; yOffset+=2) {
243 region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
244 }
245 }
246
247 SkPaint paint;
248 paint.setColor(0xff00ffff);
249 canvas.drawRegion(region, paint);
250 });
251 canvas->drawRenderNode(card.get());
252
253 canvas->insertReorderBarrier(false);
John Recke702c9c2015-10-07 10:26:02 -0700254 }
255 void doFrame(int frameNr) override {
256 int curFrame = frameNr % 150;
257 card->mutateStagingProperties().setTranslationX(curFrame);
258 card->mutateStagingProperties().setTranslationY(curFrame);
259 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
260 }
John Recke702c9c2015-10-07 10:26:02 -0700261};
262static Benchmark _RectGrid(BenchmarkInfo{
263 "rectgrid",
264 "A dense grid of 1x1 rects that should visually look like a single rect. "
265 "Low CPU/GPU load.",
266 TreeContentAnimation::run<RectGridAnimation>
267});
268
269class OvalAnimation : public TreeContentAnimation {
270public:
Chris Craik818c9fb2015-10-23 14:33:42 -0700271 sp<RenderNode> card = new RenderNode();
272 void createContent(int width, int height, TestCanvas* canvas) override {
273 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
274 canvas->insertReorderBarrier(true);
John Recke702c9c2015-10-07 10:26:02 -0700275
Chris Craik818c9fb2015-10-23 14:33:42 -0700276 card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
277 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
278 recordNode(*card, [](TestCanvas& canvas) {
279 SkPaint paint;
280 paint.setAntiAlias(true);
281 paint.setColor(0xFF000000);
282 canvas.drawOval(0, 0, 200, 200, paint);
283 });
284 canvas->drawRenderNode(card.get());
John Recke702c9c2015-10-07 10:26:02 -0700285
Chris Craik818c9fb2015-10-23 14:33:42 -0700286 canvas->insertReorderBarrier(false);
John Recke702c9c2015-10-07 10:26:02 -0700287 }
288
289 void doFrame(int frameNr) override {
290 int curFrame = frameNr % 150;
291 card->mutateStagingProperties().setTranslationX(curFrame);
292 card->mutateStagingProperties().setTranslationY(curFrame);
293 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
294 }
John Recke702c9c2015-10-07 10:26:02 -0700295};
296static Benchmark _Oval(BenchmarkInfo{
297 "oval",
298 "Draws 1 oval.",
299 TreeContentAnimation::run<OvalAnimation>
300});
301
302class PartialDamageTest : public TreeContentAnimation {
303public:
304 std::vector< sp<RenderNode> > cards;
Chris Craik818c9fb2015-10-23 14:33:42 -0700305 void createContent(int width, int height, TestCanvas* canvas) override {
John Recke702c9c2015-10-07 10:26:02 -0700306 static SkColor COLORS[] = {
307 0xFFF44336,
308 0xFF9C27B0,
309 0xFF2196F3,
310 0xFF4CAF50,
311 };
312
Chris Craik818c9fb2015-10-23 14:33:42 -0700313 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
John Recke702c9c2015-10-07 10:26:02 -0700314
315 for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
316 for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
317 sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
318 COLORS[static_cast<int>((y / dp(116))) % 4]);
Chris Craik818c9fb2015-10-23 14:33:42 -0700319 canvas->drawRenderNode(card.get());
John Recke702c9c2015-10-07 10:26:02 -0700320 cards.push_back(card);
321 }
322 }
323 }
324 void doFrame(int frameNr) override {
325 int curFrame = frameNr % 150;
326 cards[0]->mutateStagingProperties().setTranslationX(curFrame);
327 cards[0]->mutateStagingProperties().setTranslationY(curFrame);
328 cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
329
Chris Craik818c9fb2015-10-23 14:33:42 -0700330 recordNode(*cards[0], [curFrame](TestCanvas& canvas) {
331 canvas.drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
332 SkXfermode::kSrcOver_Mode);
333 });
John Recke702c9c2015-10-07 10:26:02 -0700334 }
335
336 static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
337 int startA = (start >> 24) & 0xff;
338 int startR = (start >> 16) & 0xff;
339 int startG = (start >> 8) & 0xff;
340 int startB = start & 0xff;
341
342 int endA = (end >> 24) & 0xff;
343 int endR = (end >> 16) & 0xff;
344 int endG = (end >> 8) & 0xff;
345 int endB = end & 0xff;
346
347 return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
348 (int)((startR + (int)(fraction * (endR - startR))) << 16) |
349 (int)((startG + (int)(fraction * (endG - startG))) << 8) |
350 (int)((startB + (int)(fraction * (endB - startB))));
351 }
352private:
353 sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
354 sp<RenderNode> node = new RenderNode();
355 node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
356 node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
357
Chris Craik818c9fb2015-10-23 14:33:42 -0700358 recordNode(*node, [color](TestCanvas& canvas) {
359 canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
360 });
John Recke702c9c2015-10-07 10:26:02 -0700361 return node;
362 }
363};
364static Benchmark _PartialDamage(BenchmarkInfo{
365 "partialdamage",
366 "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
367 "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
368 "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
369 TreeContentAnimation::run<PartialDamageTest>
370});
Chris Craikb565df12015-10-05 13:00:52 -0700371
372
Chris Craik818c9fb2015-10-23 14:33:42 -0700373class SaveLayerAnimation : public TreeContentAnimation {
Chris Craikb565df12015-10-05 13:00:52 -0700374public:
Chris Craik818c9fb2015-10-23 14:33:42 -0700375 sp<RenderNode> card = new RenderNode();
376 void createContent(int width, int height, TestCanvas* canvas) override {
377 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
Chris Craikb565df12015-10-05 13:00:52 -0700378
Chris Craik818c9fb2015-10-23 14:33:42 -0700379 card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
380 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
381 recordNode(*card, [](TestCanvas& canvas) {
382 canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
383 canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
384 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
385 canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
386 canvas.restore();
387 canvas.restore();
388 });
389
390 canvas->drawRenderNode(card.get());
Chris Craikb565df12015-10-05 13:00:52 -0700391 }
392 void doFrame(int frameNr) override {
393 int curFrame = frameNr % 150;
394 card->mutateStagingProperties().setTranslationX(curFrame);
395 card->mutateStagingProperties().setTranslationY(curFrame);
396 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
397 }
Chris Craikb565df12015-10-05 13:00:52 -0700398};
Chris Craik818c9fb2015-10-23 14:33:42 -0700399static Benchmark _SaveLayer(BenchmarkInfo{
400 "savelayer",
401 "A nested pair of clipped saveLayer operations. "
402 "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
403 TreeContentAnimation::run<SaveLayerAnimation>
Chris Craikb565df12015-10-05 13:00:52 -0700404});
Chris Craik0b7e8242015-10-28 16:50:44 -0700405
406
407class HwLayerAnimation : public TreeContentAnimation {
408public:
409 sp<RenderNode> card = TestUtils::createNode<TestCanvas>(0, 0, 200, 200, [] (TestCanvas& canvas) {
410 canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
411 }, true);
412 void createContent(int width, int height, TestCanvas* canvas) override {
413 canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
414 canvas->drawRenderNode(card.get());
415 }
416 void doFrame(int frameNr) override {
417 int curFrame = frameNr % 150;
418 card->mutateStagingProperties().setTranslationX(curFrame);
419 card->mutateStagingProperties().setTranslationY(curFrame);
420 card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
421 }
422};
423static Benchmark _HwLayer(BenchmarkInfo{
424 "hwlayer",
425 "A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. "
426 "Tests the hardware layer codepath.",
427 TreeContentAnimation::run<HwLayerAnimation>
428});