Shove the tests into a different file
Also add descriptions because those are nice
Change-Id: I2d0480acf17af2700c8b7fb7228203e16a068f6b
diff --git a/libs/hwui/tests/Benchmark.h b/libs/hwui/tests/Benchmark.h
new file mode 100644
index 0000000..e16310e
--- /dev/null
+++ b/libs/hwui/tests/Benchmark.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TESTS_BENCHMARK_H
+#define TESTS_BENCHMARK_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+struct BenchmarkOptions {
+ int count;
+};
+
+typedef void (*BenchmarkFunctor)(const BenchmarkOptions&);
+
+struct BenchmarkInfo {
+ std::string name;
+ std::string description;
+ BenchmarkFunctor functor;
+};
+
+class Benchmark {
+public:
+ Benchmark(const BenchmarkInfo& info) {
+ registerBenchmark(info);
+ }
+
+private:
+ Benchmark() = delete;
+ Benchmark(const Benchmark&) = delete;
+ Benchmark& operator=(const Benchmark&) = delete;
+
+ static void registerBenchmark(const BenchmarkInfo& info);
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TESTS_BENCHMARK_H */
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
index cebe765..ba763a8 100644
--- a/libs/hwui/tests/TestContext.cpp
+++ b/libs/hwui/tests/TestContext.cpp
@@ -22,16 +22,35 @@
static const int IDENT_DISPLAYEVENT = 1;
-static DisplayInfo getBuiltInDisplay() {
+static android::DisplayInfo DUMMY_DISPLAY {
+ 1080, //w
+ 1920, //h
+ 320.0, // xdpi
+ 320.0, // ydpi
+ 60.0, // fps
+ 2.0, // density
+ 0, // orientation
+ false, // secure?
+ 0, // appVsyncOffset
+ 0, // presentationDeadline
+ 0, // colorTransform
+};
+
+DisplayInfo getBuiltInDisplay() {
+#if !HWUI_NULL_GPU
DisplayInfo display;
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
return display;
+#else
+ return DUMMY_DISPLAY;
+#endif
}
-android::DisplayInfo gDisplay = getBuiltInDisplay();
+// Initialize to a dummy default
+android::DisplayInfo gDisplay = DUMMY_DISPLAY;
TestContext::TestContext() {
mLooper = new Looper(true);
diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h
index 7b30fc1..2bbe5df 100644
--- a/libs/hwui/tests/TestContext.h
+++ b/libs/hwui/tests/TestContext.h
@@ -32,6 +32,8 @@
extern DisplayInfo gDisplay;
#define dp(x) ((x) * android::uirenderer::test::gDisplay.density)
+DisplayInfo getBuiltInDisplay();
+
class TestContext {
public:
TestContext();
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
new file mode 100644
index 0000000..a59261c
--- /dev/null
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/log.h>
+#include <gui/Surface.h>
+#include <ui/PixelFormat.h>
+
+#include <AnimationContext.h>
+#include <DisplayListCanvas.h>
+#include <RenderNode.h>
+#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderTask.h>
+
+#include "Benchmark.h"
+#include "TestContext.h"
+
+#include "protos/hwui.pb.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+static DisplayListCanvas* startRecording(RenderNode* node) {
+ DisplayListCanvas* renderer = new DisplayListCanvas(
+ node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+ return renderer;
+}
+
+static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
+ node->setStagingDisplayList(renderer->finishRecording());
+ delete renderer;
+}
+
+class TreeContentAnimation {
+public:
+ virtual ~TreeContentAnimation() {}
+ int frameCount = 150;
+ virtual int getFrameCount() { return frameCount; }
+ virtual void setFrameCount(int fc) {
+ if (fc > 0) {
+ frameCount = fc;
+ }
+ }
+ virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
+ virtual void doFrame(int frameNr) = 0;
+
+ template <class T>
+ static void run(const BenchmarkOptions& opts) {
+ // Switch to the real display
+ gDisplay = getBuiltInDisplay();
+
+ T animation;
+ animation.setFrameCount(opts.count);
+
+ TestContext testContext;
+
+ // create the native surface
+ const int width = gDisplay.w;
+ const int height = gDisplay.h;
+ sp<Surface> surface = testContext.surface();
+
+ RenderNode* rootNode = new RenderNode();
+ rootNode->incStrong(nullptr);
+ rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+ rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ rootNode->mutateStagingProperties().setClipToBounds(false);
+ rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+ ContextFactory factory;
+ std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
+ proxy->loadSystemProperties();
+ proxy->initialize(surface);
+ float lightX = width / 2.0;
+ proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
+ proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
+
+ android::uirenderer::Rect DUMMY;
+
+ DisplayListCanvas* renderer = startRecording(rootNode);
+ animation.createContent(width, height, renderer);
+ endRecording(renderer, rootNode);
+
+ // Do a few cold runs then reset the stats so that the caches are all hot
+ for (int i = 0; i < 3; i++) {
+ testContext.waitForVsync();
+ proxy->syncAndDrawFrame();
+ }
+ proxy->resetProfileInfo();
+
+ for (int i = 0; i < animation.getFrameCount(); i++) {
+ testContext.waitForVsync();
+
+ ATRACE_NAME("UI-Draw Frame");
+ nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync);
+ animation.doFrame(i);
+ proxy->syncAndDrawFrame();
+ }
+
+ proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+ rootNode->decStrong(nullptr);
+ }
+};
+
+class ShadowGridAnimation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid(BenchmarkInfo{
+ "shadowgrid",
+ "A grid of rounded rects that cast a shadow. Simplified scenario of an "
+ "Android TV-style launcher interface. High CPU/GPU load.",
+ TreeContentAnimation::run<ShadowGridAnimation>
+});
+
+class ShadowGrid2Animation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+ for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+ sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid2(BenchmarkInfo{
+ "shadowgrid2",
+ "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
+ "variant of shadowgrid. Very high CPU load, high GPU load.",
+ TreeContentAnimation::run<ShadowGrid2Animation>
+});
+
+class RectGridAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 200, 200);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+ SkRegion region;
+ for (int xOffset = 0; xOffset < width; xOffset+=2) {
+ for (int yOffset = 0; yOffset < height; yOffset+=2) {
+ region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+ }
+ }
+
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ renderer->drawRegion(region, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _RectGrid(BenchmarkInfo{
+ "rectgrid",
+ "A dense grid of 1x1 rects that should visually look like a single rect. "
+ "Low CPU/GPU load.",
+ TreeContentAnimation::run<RectGridAnimation>
+});
+
+class OvalAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 400, 400);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ renderer->drawOval(0, 0, width, height, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _Oval(BenchmarkInfo{
+ "oval",
+ "Draws 1 oval.",
+ TreeContentAnimation::run<OvalAnimation>
+});
+
+class PartialDamageTest : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ static SkColor COLORS[] = {
+ 0xFFF44336,
+ 0xFF9C27B0,
+ 0xFF2196F3,
+ 0xFF4CAF50,
+ };
+
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
+ COLORS[static_cast<int>((y / dp(116))) % 4]);
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ cards[0]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[0]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(cards[0].get());
+ renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+ SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, cards[0].get());
+ }
+
+ static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
+ int startA = (start >> 24) & 0xff;
+ int startR = (start >> 16) & 0xff;
+ int startG = (start >> 8) & 0xff;
+ int startB = start & 0xff;
+
+ int endA = (end >> 24) & 0xff;
+ int endR = (end >> 16) & 0xff;
+ int endG = (end >> 8) & 0xff;
+ int endB = end & 0xff;
+
+ return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+ (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+ (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+ (int)((startB + (int)(fraction * (endB - startB))));
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _PartialDamage(BenchmarkInfo{
+ "partialdamage",
+ "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
+ "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
+ "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
+ TreeContentAnimation::run<PartialDamageTest>
+});
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index f794507..aee84de 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -14,361 +14,34 @@
* limitations under the License.
*/
-#include <cutils/log.h>
-#include <gui/Surface.h>
-#include <ui/PixelFormat.h>
-
-#include <AnimationContext.h>
-#include <DisplayListCanvas.h>
-#include <RenderNode.h>
-#include <renderthread/RenderProxy.h>
-#include <renderthread/RenderTask.h>
-
-#include "TestContext.h"
+#include "Benchmark.h"
#include "protos/hwui.pb.h"
-#include <stdio.h>
-#include <unistd.h>
#include <getopt.h>
+#include <stdio.h>
+#include <string>
+#include <unistd.h>
+#include <unordered_map>
#include <vector>
using namespace android;
using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::test;
-class ContextFactory : public IContextFactory {
-public:
- virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
- return new AnimationContext(clock);
- }
-};
-
-static DisplayListCanvas* startRecording(RenderNode* node) {
- DisplayListCanvas* renderer = new DisplayListCanvas(
- node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
- return renderer;
+// Not a static global because we need to force the map to be constructed
+// before we try to add things to it.
+std::unordered_map<std::string, BenchmarkInfo>& testMap() {
+ static std::unordered_map<std::string, BenchmarkInfo> testMap;
+ return testMap;
}
-static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
- node->setStagingDisplayList(renderer->finishRecording());
- delete renderer;
+void Benchmark::registerBenchmark(const BenchmarkInfo& info) {
+ testMap()[info.name] = info;
}
-class TreeContentAnimation {
-public:
- virtual ~TreeContentAnimation() {}
- int frameCount = 150;
- virtual int getFrameCount() { return frameCount; }
- virtual void setFrameCount(int fc) {
- if (fc > 0) {
- frameCount = fc;
- }
- }
- virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
- virtual void doFrame(int frameNr) = 0;
-
- template <class T>
- static void run(int frameCount) {
- T animation;
- animation.setFrameCount(frameCount);
-
- TestContext testContext;
-
- // create the native surface
- const int width = gDisplay.w;
- const int height = gDisplay.h;
- sp<Surface> surface = testContext.surface();
-
- RenderNode* rootNode = new RenderNode();
- rootNode->incStrong(nullptr);
- rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
- rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- rootNode->mutateStagingProperties().setClipToBounds(false);
- rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
-
- ContextFactory factory;
- std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
- proxy->loadSystemProperties();
- proxy->initialize(surface);
- float lightX = width / 2.0;
- proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
- proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
-
- android::uirenderer::Rect DUMMY;
-
- DisplayListCanvas* renderer = startRecording(rootNode);
- animation.createContent(width, height, renderer);
- endRecording(renderer, rootNode);
-
- // Do a few cold runs then reset the stats so that the caches are all hot
- for (int i = 0; i < 3; i++) {
- testContext.waitForVsync();
- proxy->syncAndDrawFrame();
- }
- proxy->resetProfileInfo();
-
- for (int i = 0; i < animation.getFrameCount(); i++) {
- testContext.waitForVsync();
-
- ATRACE_NAME("UI-Draw Frame");
- nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo())
- .setVsync(vsync, vsync);
- animation.doFrame(i);
- proxy->syncAndDrawFrame();
- }
-
- proxy->dumpProfileInfo(STDOUT_FILENO, 0);
- rootNode->decStrong(nullptr);
- }
-};
-
-class ShadowGridAnimation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class ShadowGrid2Animation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
- for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
- sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class RectGridAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 200, 200);
- renderer->drawRenderNode(card.get());
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
-
- SkRegion region;
- for (int xOffset = 0; xOffset < width; xOffset+=2) {
- for (int yOffset = 0; yOffset < height; yOffset+=2) {
- region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
- }
- }
-
- SkPaint paint;
- paint.setColor(0xff00ffff);
- renderer->drawRegion(region, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class OvalAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 400, 400);
- renderer->drawRenderNode(card.get());
-
- renderer->insertReorderBarrier(false);
- }
-
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
-
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(0xFF000000);
- renderer->drawOval(0, 0, width, height, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class PartialInvalTest : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- static SkColor COLORS[] = {
- 0xFFF44336,
- 0xFF9C27B0,
- 0xFF2196F3,
- 0xFF4CAF50,
- };
-
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
- COLORS[static_cast<int>((y / dp(116))) % 4]);
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- cards[0]->mutateStagingProperties().setTranslationX(curFrame);
- cards[0]->mutateStagingProperties().setTranslationY(curFrame);
- cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(cards[0].get());
- renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
- SkXfermode::kSrcOver_Mode);
- endRecording(renderer, cards[0].get());
- }
-
- static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
- int startA = (start >> 24) & 0xff;
- int startR = (start >> 16) & 0xff;
- int startG = (start >> 8) & 0xff;
- int startB = start & 0xff;
-
- int endA = (end >> 24) & 0xff;
- int endR = (end >> 16) & 0xff;
- int endG = (end >> 8) & 0xff;
- int endB = end & 0xff;
-
- return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
- (int)((startR + (int)(fraction * (endR - startR))) << 16) |
- (int)((startG + (int)(fraction * (endG - startG))) << 8) |
- (int)((startB + (int)(fraction * (endB - startB))));
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-struct cstr_cmp {
- bool operator()(const char *a, const char *b) const {
- return std::strcmp(a, b) < 0;
- }
-};
-
-typedef void (*testProc)(int);
-
-std::map<const char*, testProc, cstr_cmp> gTestMap {
- {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
- {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
- {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
- {"oval", TreeContentAnimation::run<OvalAnimation> },
- {"partialinval", TreeContentAnimation::run<PartialInvalTest> },
-};
-
static int gFrameCount = 150;
static int gRepeatCount = 1;
-static std::vector<testProc> gRunTests;
+static std::vector<BenchmarkInfo> gRunTests;
static void printHelp() {
printf("\
@@ -384,8 +57,31 @@
static void listTests() {
printf("Tests: \n");
- for (auto&& test : gTestMap) {
- printf("%-20s <TODO DESCRIPTION>\n", test.first);
+ for (auto&& test : testMap()) {
+ auto&& info = test.second;
+ const char* col1 = info.name.c_str();
+ int dlen = info.description.length();
+ const char* col2 = info.description.c_str();
+ // World's best line breaking algorithm.
+ do {
+ int toPrint = dlen;
+ if (toPrint > 50) {
+ char* found = (char*) memrchr(col2, ' ', 50);
+ if (found) {
+ toPrint = found - col2;
+ } else {
+ toPrint = 50;
+ }
+ }
+ printf("%-20s %.*s\n", col1, toPrint, col2);
+ col1 = "";
+ col2 += toPrint;
+ dlen -= toPrint;
+ while (*col2 == ' ') {
+ col2++; dlen--;
+ }
+ } while (dlen > 0);
+ printf("\n");
}
}
@@ -470,8 +166,8 @@
if (optind < argc) {
do {
const char* test = argv[optind++];
- auto pos = gTestMap.find(test);
- if (pos == gTestMap.end()) {
+ auto pos = testMap().find(test);
+ if (pos == testMap().end()) {
fprintf(stderr, "Unknown test '%s'\n", test);
exit(EXIT_FAILURE);
} else {
@@ -479,16 +175,18 @@
}
} while (optind < argc);
} else {
- gRunTests.push_back(gTestMap["shadowgrid"]);
+ gRunTests.push_back(testMap()["shadowgrid"]);
}
}
int main(int argc, char* argv[]) {
parseOptions(argc, argv);
+ BenchmarkOptions opts;
+ opts.count = gFrameCount;
for (int i = 0; i < gRepeatCount; i++) {
for (auto&& test : gRunTests) {
- test(gFrameCount);
+ test.functor(opts);
}
}
printf("Success!\n");