Loading libs/hwui/tests/common/TestScene.h +2 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,8 @@ namespace test { class TestScene { public: struct Options { int count = 0; int frameCount = 150; int repeatCount = 1; int reportFrametimeWeight = 0; bool renderOffscreen = true; }; Loading libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp 0 → 100644 +200 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 <SkFont.h> #include <cstdio> #include "TestSceneBase.h" #include "hwui/Paint.h" #include "tests/common/TestUtils.h" class StretchyListViewAnimation; class StretchyListViewHolePunch; class StretchyLinearListView; class StretchyLinearListViewHolePunch; static TestScene::Registrar _StretchyListViewAnimation(TestScene::Info{ "stretchylistview", "A mock ListView of scrolling content that's stretching. Doesn't re-bind/re-record views " "as they are recycled, so won't upload much content (either glyphs, or bitmaps).", TestScene::simpleCreateScene<StretchyListViewAnimation>}); static TestScene::Registrar _StretchyListViewHolePunch(TestScene::Info{ "stretchylistview_holepunch", "A mock ListView of scrolling content that's stretching. Includes a hole punch", TestScene::simpleCreateScene<StretchyListViewHolePunch>}); static TestScene::Registrar _StretchyLinearListView(TestScene::Info{ "stretchylistview_linear", "A mock ListView of scrolling content that's stretching using a linear stretch effect.", TestScene::simpleCreateScene<StretchyLinearListView>}); static TestScene::Registrar _StretchyLinearListViewHolePunch(TestScene::Info{ "stretchylistview_linear_holepunch", "A mock ListView of scrolling content that's stretching using a linear stretch effect. " "Includes a hole punch", TestScene::simpleCreateScene<StretchyLinearListViewHolePunch>}); class StretchyListViewAnimation : public TestScene { protected: virtual StretchEffectBehavior stretchBehavior() { return StretchEffectBehavior::Shader; } virtual bool haveHolePunch() { return false; } private: int mItemHeight; int mItemSpacing; int mItemWidth; int mItemLeft; sp<RenderNode> mListView; std::vector<sp<RenderNode> > mListItems; sk_sp<Bitmap> createRandomCharIcon(int cardHeight) { SkBitmap skBitmap; int size = cardHeight - (dp(10) * 2); sk_sp<Bitmap> bitmap(TestUtils::createBitmap(size, size, &skBitmap)); SkCanvas canvas(skBitmap); canvas.clear(0); SkPaint paint; paint.setAntiAlias(true); SkColor randomColor = BrightColors[rand() % BrightColorsCount]; paint.setColor(randomColor); canvas.drawCircle(size / 2, size / 2, size / 2, paint); bool bgDark = SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) < 128 * 3; paint.setColor(bgDark ? Color::White : Color::Grey_700); SkFont font; font.setSize(size / 2); char charToShow = 'A' + (rand() % 26); const SkPoint pos = {SkIntToScalar(size / 2), /*approximate centering*/ SkFloatToScalar(size * 0.7f)}; canvas.drawSimpleText(&charToShow, 1, SkTextEncoding::kUTF8, pos.fX, pos.fY, font, paint); return bitmap; } static sk_sp<Bitmap> createBoxBitmap(bool filled) { int size = dp(20); int stroke = dp(2); SkBitmap skBitmap; auto bitmap = TestUtils::createBitmap(size, size, &skBitmap); SkCanvas canvas(skBitmap); canvas.clear(Color::Transparent); SkPaint paint; paint.setAntiAlias(true); paint.setColor(filled ? Color::Yellow_500 : Color::Grey_700); paint.setStyle(filled ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style); paint.setStrokeWidth(stroke); canvas.drawRect(SkRect::MakeLTRB(stroke, stroke, size - stroke, size - stroke), paint); return bitmap; } void createListItem(RenderProperties& props, Canvas& canvas, int cardId, int itemWidth, int itemHeight) { static sk_sp<Bitmap> filledBox(createBoxBitmap(true)); static sk_sp<Bitmap> strokedBox(createBoxBitmap(false)); const bool addHolePunch = cardId == 2 && haveHolePunch(); // TODO: switch to using round rect clipping, once merging correctly handles that Paint roundRectPaint; roundRectPaint.setAntiAlias(true); roundRectPaint.setColor(Color::White); if (addHolePunch) { // Punch a hole but then cover it up, we don't want to actually see it canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight))); } canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); Paint textPaint; textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500); textPaint.getSkFont().setSize(dp(20)); textPaint.setAntiAlias(true); char buf[256]; snprintf(buf, sizeof(buf), "This card is #%d", cardId); TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, itemHeight, dp(25)); textPaint.getSkFont().setSize(dp(15)); if (addHolePunch) { TestUtils::drawUtf8ToCanvas(&canvas, "I have a hole punch", textPaint, itemHeight, dp(45)); } else { TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint, itemHeight, dp(45)); } auto randomIcon = createRandomCharIcon(itemHeight); canvas.drawBitmap(*randomIcon, dp(10), dp(10), nullptr); auto box = rand() % 2 ? filledBox : strokedBox; canvas.drawBitmap(*box, itemWidth - dp(10) - box->width(), dp(10), nullptr); } void createContent(int width, int height, Canvas& canvas) override { srand(0); mItemHeight = dp(60); mItemSpacing = dp(16); mItemWidth = std::min((height - mItemSpacing * 2), (int)dp(300)); mItemLeft = (width - mItemWidth) / 2; int heightWithSpacing = mItemHeight + mItemSpacing; for (int y = 0; y < height + (heightWithSpacing - 1); y += heightWithSpacing) { int id = mListItems.size(); auto node = TestUtils::createNode(mItemLeft, y, mItemLeft + mItemWidth, y + mItemHeight, [this, id](RenderProperties& props, Canvas& canvas) { createListItem(props, canvas, id, mItemWidth, mItemHeight); }); mListItems.push_back(node); } mListView = TestUtils::createNode(0, 0, width, height, [this](RenderProperties& props, Canvas& canvas) { for (size_t ci = 0; ci < mListItems.size(); ci++) { canvas.drawRenderNode(mListItems[ci].get()); } }); canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver); canvas.drawRenderNode(mListView.get()); } void doFrame(int frameNr) override { if (frameNr == 0) { Properties::stretchEffectBehavior = stretchBehavior(); } auto& props = mListView->mutateStagingProperties(); auto& stretch = props.mutateLayerProperties().mutableStretchEffect(); stretch.setEmpty(); frameNr = frameNr % 150; // Animate from 0f to .1f const float sY = (frameNr > 75 ? 150 - frameNr : frameNr) / 1500.f; stretch.mergeWith({{.fX = 0, .fY = sY}, static_cast<float>(props.getWidth()), static_cast<float>(props.getHeight())}); mListView->setPropertyFieldsDirty(RenderNode::GENERIC); } }; class StretchyListViewHolePunch : public StretchyListViewAnimation { bool haveHolePunch() override { return true; } }; class StretchyLinearListView : public StretchyListViewAnimation { StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::LinearScale; } }; class StretchyLinearListViewHolePunch : public StretchyListViewAnimation { StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::LinearScale; } bool haveHolePunch() override { return true; } }; No newline at end of file libs/hwui/tests/macrobench/TestSceneRunner.cpp +29 −44 Original line number Diff line number Diff line Loading @@ -62,53 +62,23 @@ private: T mAverage; }; void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter, RenderProxy* proxy, double durationInS) { using namespace benchmark; struct ReportInfo { int percentile; const char* suffix; }; static std::array<ReportInfo, 4> REPORTS = { ReportInfo{50, "_50th"}, ReportInfo{90, "_90th"}, ReportInfo{95, "_95th"}, ReportInfo{99, "_99th"}, }; using BenchmarkResults = std::vector<benchmark::BenchmarkReporter::Run>; // Although a vector is used, it must stay with only a single element // otherwise the BenchmarkReporter will automatically compute // mean and stddev which doesn't make sense for our usage std::vector<BenchmarkReporter::Run> reports; BenchmarkReporter::Run report; void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, double durationInS, int repetationIndex, BenchmarkResults* reports) { benchmark::BenchmarkReporter::Run report; report.repetitions = opts.repeatCount; report.repetition_index = repetationIndex; report.run_name.function_name = info.name; report.iterations = static_cast<int64_t>(opts.count); report.iterations = static_cast<int64_t>(opts.frameCount); report.real_accumulated_time = durationInS; report.cpu_accumulated_time = durationInS; report.counters["items_per_second"] = opts.count / durationInS; reports.push_back(report); reporter->ReportRuns(reports); // Pretend the percentiles are single-iteration runs of the test // If rendering offscreen skip this as it's fps that's more interesting // in that test case than percentiles. if (!opts.renderOffscreen) { for (auto& ri : REPORTS) { reports[0].run_name.function_name = info.name; reports[0].run_name.function_name += ri.suffix; durationInS = proxy->frameTimePercentile(ri.percentile) / 1000.0; reports[0].real_accumulated_time = durationInS; reports[0].cpu_accumulated_time = durationInS; reports[0].iterations = 1; reports[0].counters["items_per_second"] = 0; reporter->ReportRuns(reports); } } report.counters["items_per_second"] = opts.frameCount / durationInS; reports->push_back(report); } void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { static void doRun(const TestScene::Info& info, const TestScene::Options& opts, int repetitionIndex, BenchmarkResults* reports) { Properties::forceDrawFrame = true; TestContext testContext; testContext.setRenderOffscreen(opts.renderOffscreen); Loading Loading @@ -158,7 +128,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC); for (int i = 0; i < opts.count; i++) { for (int i = 0; i < opts.frameCount; i++) { testContext.waitForVsync(); nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); { Loading @@ -182,9 +152,24 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, proxy->fence(); nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); if (reporter) { outputBenchmarkReport(info, opts, reporter, proxy.get(), (end - start) / (double)s2ns(1)); if (reports) { outputBenchmarkReport(info, opts, (end - start) / (double)s2ns(1), repetitionIndex, reports); } else { proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); } } void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { BenchmarkResults results; for (int i = 0; i < opts.repeatCount; i++) { doRun(info, opts, i, reporter ? &results : nullptr); } if (reporter) { reporter->ReportRuns(results); if (results.size() > 1) { // TODO: Report summary } } } libs/hwui/tests/macrobench/main.cpp +18 −14 Original line number Diff line number Diff line Loading @@ -40,9 +40,9 @@ using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::test; static int gRepeatCount = 1; static std::vector<TestScene::Info> gRunTests; static TestScene::Options gOpts; static bool gRunLeakCheck = true; std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter; void run(const TestScene::Info& info, const TestScene::Options& opts, Loading @@ -69,6 +69,7 @@ OPTIONS: are offscreen rendered --benchmark_format Set output format. Possible values are tabular, json, csv --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk --skip-leak-check Skips the memory leak check )"); } Loading Loading @@ -170,6 +171,7 @@ enum { Onscreen, Offscreen, Renderer, SkipLeakCheck, }; } Loading @@ -185,6 +187,7 @@ static const struct option LONG_OPTIONS[] = { {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, {"renderer", required_argument, nullptr, LongOpts::Renderer}, {"skip-leak-check", no_argument, nullptr, LongOpts::SkipLeakCheck}, {0, 0, 0, 0}}; static const char* SHORT_OPTIONS = "c:r:h"; Loading Loading @@ -214,20 +217,20 @@ void parseOptions(int argc, char* argv[]) { break; case 'c': gOpts.count = atoi(optarg); if (!gOpts.count) { gOpts.frameCount = atoi(optarg); if (!gOpts.frameCount) { fprintf(stderr, "Invalid frames argument '%s'\n", optarg); error = true; } break; case 'r': gRepeatCount = atoi(optarg); if (!gRepeatCount) { gOpts.repeatCount = atoi(optarg); if (!gOpts.repeatCount) { fprintf(stderr, "Invalid repeat argument '%s'\n", optarg); error = true; } else { gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX); gOpts.repeatCount = (gOpts.repeatCount > 0 ? gOpts.repeatCount : INT_MAX); } break; Loading Loading @@ -283,6 +286,10 @@ void parseOptions(int argc, char* argv[]) { gOpts.renderOffscreen = true; break; case LongOpts::SkipLeakCheck: gRunLeakCheck = false; break; case 'h': printHelp(); exit(EXIT_SUCCESS); Loading Loading @@ -322,9 +329,6 @@ void parseOptions(int argc, char* argv[]) { } int main(int argc, char* argv[]) { // set defaults gOpts.count = 150; Typeface::setRobotoTypefaceForTest(); parseOptions(argc, argv); Loading @@ -345,11 +349,9 @@ int main(int argc, char* argv[]) { gBenchmarkReporter->ReportContext(context); } for (int i = 0; i < gRepeatCount; i++) { for (auto&& test : gRunTests) { run(test, gOpts, gBenchmarkReporter.get()); } } if (gBenchmarkReporter) { gBenchmarkReporter->Finalize(); Loading @@ -358,6 +360,8 @@ int main(int argc, char* argv[]) { renderthread::RenderProxy::trimMemory(100); HardwareBitmapUploader::terminate(); if (gRunLeakCheck) { LeakChecker::checkForLeaks(); } return 0; } Loading
libs/hwui/tests/common/TestScene.h +2 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,8 @@ namespace test { class TestScene { public: struct Options { int count = 0; int frameCount = 150; int repeatCount = 1; int reportFrametimeWeight = 0; bool renderOffscreen = true; }; Loading
libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp 0 → 100644 +200 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 <SkFont.h> #include <cstdio> #include "TestSceneBase.h" #include "hwui/Paint.h" #include "tests/common/TestUtils.h" class StretchyListViewAnimation; class StretchyListViewHolePunch; class StretchyLinearListView; class StretchyLinearListViewHolePunch; static TestScene::Registrar _StretchyListViewAnimation(TestScene::Info{ "stretchylistview", "A mock ListView of scrolling content that's stretching. Doesn't re-bind/re-record views " "as they are recycled, so won't upload much content (either glyphs, or bitmaps).", TestScene::simpleCreateScene<StretchyListViewAnimation>}); static TestScene::Registrar _StretchyListViewHolePunch(TestScene::Info{ "stretchylistview_holepunch", "A mock ListView of scrolling content that's stretching. Includes a hole punch", TestScene::simpleCreateScene<StretchyListViewHolePunch>}); static TestScene::Registrar _StretchyLinearListView(TestScene::Info{ "stretchylistview_linear", "A mock ListView of scrolling content that's stretching using a linear stretch effect.", TestScene::simpleCreateScene<StretchyLinearListView>}); static TestScene::Registrar _StretchyLinearListViewHolePunch(TestScene::Info{ "stretchylistview_linear_holepunch", "A mock ListView of scrolling content that's stretching using a linear stretch effect. " "Includes a hole punch", TestScene::simpleCreateScene<StretchyLinearListViewHolePunch>}); class StretchyListViewAnimation : public TestScene { protected: virtual StretchEffectBehavior stretchBehavior() { return StretchEffectBehavior::Shader; } virtual bool haveHolePunch() { return false; } private: int mItemHeight; int mItemSpacing; int mItemWidth; int mItemLeft; sp<RenderNode> mListView; std::vector<sp<RenderNode> > mListItems; sk_sp<Bitmap> createRandomCharIcon(int cardHeight) { SkBitmap skBitmap; int size = cardHeight - (dp(10) * 2); sk_sp<Bitmap> bitmap(TestUtils::createBitmap(size, size, &skBitmap)); SkCanvas canvas(skBitmap); canvas.clear(0); SkPaint paint; paint.setAntiAlias(true); SkColor randomColor = BrightColors[rand() % BrightColorsCount]; paint.setColor(randomColor); canvas.drawCircle(size / 2, size / 2, size / 2, paint); bool bgDark = SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) < 128 * 3; paint.setColor(bgDark ? Color::White : Color::Grey_700); SkFont font; font.setSize(size / 2); char charToShow = 'A' + (rand() % 26); const SkPoint pos = {SkIntToScalar(size / 2), /*approximate centering*/ SkFloatToScalar(size * 0.7f)}; canvas.drawSimpleText(&charToShow, 1, SkTextEncoding::kUTF8, pos.fX, pos.fY, font, paint); return bitmap; } static sk_sp<Bitmap> createBoxBitmap(bool filled) { int size = dp(20); int stroke = dp(2); SkBitmap skBitmap; auto bitmap = TestUtils::createBitmap(size, size, &skBitmap); SkCanvas canvas(skBitmap); canvas.clear(Color::Transparent); SkPaint paint; paint.setAntiAlias(true); paint.setColor(filled ? Color::Yellow_500 : Color::Grey_700); paint.setStyle(filled ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style); paint.setStrokeWidth(stroke); canvas.drawRect(SkRect::MakeLTRB(stroke, stroke, size - stroke, size - stroke), paint); return bitmap; } void createListItem(RenderProperties& props, Canvas& canvas, int cardId, int itemWidth, int itemHeight) { static sk_sp<Bitmap> filledBox(createBoxBitmap(true)); static sk_sp<Bitmap> strokedBox(createBoxBitmap(false)); const bool addHolePunch = cardId == 2 && haveHolePunch(); // TODO: switch to using round rect clipping, once merging correctly handles that Paint roundRectPaint; roundRectPaint.setAntiAlias(true); roundRectPaint.setColor(Color::White); if (addHolePunch) { // Punch a hole but then cover it up, we don't want to actually see it canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight))); } canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); Paint textPaint; textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500); textPaint.getSkFont().setSize(dp(20)); textPaint.setAntiAlias(true); char buf[256]; snprintf(buf, sizeof(buf), "This card is #%d", cardId); TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, itemHeight, dp(25)); textPaint.getSkFont().setSize(dp(15)); if (addHolePunch) { TestUtils::drawUtf8ToCanvas(&canvas, "I have a hole punch", textPaint, itemHeight, dp(45)); } else { TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint, itemHeight, dp(45)); } auto randomIcon = createRandomCharIcon(itemHeight); canvas.drawBitmap(*randomIcon, dp(10), dp(10), nullptr); auto box = rand() % 2 ? filledBox : strokedBox; canvas.drawBitmap(*box, itemWidth - dp(10) - box->width(), dp(10), nullptr); } void createContent(int width, int height, Canvas& canvas) override { srand(0); mItemHeight = dp(60); mItemSpacing = dp(16); mItemWidth = std::min((height - mItemSpacing * 2), (int)dp(300)); mItemLeft = (width - mItemWidth) / 2; int heightWithSpacing = mItemHeight + mItemSpacing; for (int y = 0; y < height + (heightWithSpacing - 1); y += heightWithSpacing) { int id = mListItems.size(); auto node = TestUtils::createNode(mItemLeft, y, mItemLeft + mItemWidth, y + mItemHeight, [this, id](RenderProperties& props, Canvas& canvas) { createListItem(props, canvas, id, mItemWidth, mItemHeight); }); mListItems.push_back(node); } mListView = TestUtils::createNode(0, 0, width, height, [this](RenderProperties& props, Canvas& canvas) { for (size_t ci = 0; ci < mListItems.size(); ci++) { canvas.drawRenderNode(mListItems[ci].get()); } }); canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver); canvas.drawRenderNode(mListView.get()); } void doFrame(int frameNr) override { if (frameNr == 0) { Properties::stretchEffectBehavior = stretchBehavior(); } auto& props = mListView->mutateStagingProperties(); auto& stretch = props.mutateLayerProperties().mutableStretchEffect(); stretch.setEmpty(); frameNr = frameNr % 150; // Animate from 0f to .1f const float sY = (frameNr > 75 ? 150 - frameNr : frameNr) / 1500.f; stretch.mergeWith({{.fX = 0, .fY = sY}, static_cast<float>(props.getWidth()), static_cast<float>(props.getHeight())}); mListView->setPropertyFieldsDirty(RenderNode::GENERIC); } }; class StretchyListViewHolePunch : public StretchyListViewAnimation { bool haveHolePunch() override { return true; } }; class StretchyLinearListView : public StretchyListViewAnimation { StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::LinearScale; } }; class StretchyLinearListViewHolePunch : public StretchyListViewAnimation { StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::LinearScale; } bool haveHolePunch() override { return true; } }; No newline at end of file
libs/hwui/tests/macrobench/TestSceneRunner.cpp +29 −44 Original line number Diff line number Diff line Loading @@ -62,53 +62,23 @@ private: T mAverage; }; void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter, RenderProxy* proxy, double durationInS) { using namespace benchmark; struct ReportInfo { int percentile; const char* suffix; }; static std::array<ReportInfo, 4> REPORTS = { ReportInfo{50, "_50th"}, ReportInfo{90, "_90th"}, ReportInfo{95, "_95th"}, ReportInfo{99, "_99th"}, }; using BenchmarkResults = std::vector<benchmark::BenchmarkReporter::Run>; // Although a vector is used, it must stay with only a single element // otherwise the BenchmarkReporter will automatically compute // mean and stddev which doesn't make sense for our usage std::vector<BenchmarkReporter::Run> reports; BenchmarkReporter::Run report; void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, double durationInS, int repetationIndex, BenchmarkResults* reports) { benchmark::BenchmarkReporter::Run report; report.repetitions = opts.repeatCount; report.repetition_index = repetationIndex; report.run_name.function_name = info.name; report.iterations = static_cast<int64_t>(opts.count); report.iterations = static_cast<int64_t>(opts.frameCount); report.real_accumulated_time = durationInS; report.cpu_accumulated_time = durationInS; report.counters["items_per_second"] = opts.count / durationInS; reports.push_back(report); reporter->ReportRuns(reports); // Pretend the percentiles are single-iteration runs of the test // If rendering offscreen skip this as it's fps that's more interesting // in that test case than percentiles. if (!opts.renderOffscreen) { for (auto& ri : REPORTS) { reports[0].run_name.function_name = info.name; reports[0].run_name.function_name += ri.suffix; durationInS = proxy->frameTimePercentile(ri.percentile) / 1000.0; reports[0].real_accumulated_time = durationInS; reports[0].cpu_accumulated_time = durationInS; reports[0].iterations = 1; reports[0].counters["items_per_second"] = 0; reporter->ReportRuns(reports); } } report.counters["items_per_second"] = opts.frameCount / durationInS; reports->push_back(report); } void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { static void doRun(const TestScene::Info& info, const TestScene::Options& opts, int repetitionIndex, BenchmarkResults* reports) { Properties::forceDrawFrame = true; TestContext testContext; testContext.setRenderOffscreen(opts.renderOffscreen); Loading Loading @@ -158,7 +128,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC); for (int i = 0; i < opts.count; i++) { for (int i = 0; i < opts.frameCount; i++) { testContext.waitForVsync(); nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC); { Loading @@ -182,9 +152,24 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, proxy->fence(); nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); if (reporter) { outputBenchmarkReport(info, opts, reporter, proxy.get(), (end - start) / (double)s2ns(1)); if (reports) { outputBenchmarkReport(info, opts, (end - start) / (double)s2ns(1), repetitionIndex, reports); } else { proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); } } void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { BenchmarkResults results; for (int i = 0; i < opts.repeatCount; i++) { doRun(info, opts, i, reporter ? &results : nullptr); } if (reporter) { reporter->ReportRuns(results); if (results.size() > 1) { // TODO: Report summary } } }
libs/hwui/tests/macrobench/main.cpp +18 −14 Original line number Diff line number Diff line Loading @@ -40,9 +40,9 @@ using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::test; static int gRepeatCount = 1; static std::vector<TestScene::Info> gRunTests; static TestScene::Options gOpts; static bool gRunLeakCheck = true; std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter; void run(const TestScene::Info& info, const TestScene::Options& opts, Loading @@ -69,6 +69,7 @@ OPTIONS: are offscreen rendered --benchmark_format Set output format. Possible values are tabular, json, csv --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk --skip-leak-check Skips the memory leak check )"); } Loading Loading @@ -170,6 +171,7 @@ enum { Onscreen, Offscreen, Renderer, SkipLeakCheck, }; } Loading @@ -185,6 +187,7 @@ static const struct option LONG_OPTIONS[] = { {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, {"renderer", required_argument, nullptr, LongOpts::Renderer}, {"skip-leak-check", no_argument, nullptr, LongOpts::SkipLeakCheck}, {0, 0, 0, 0}}; static const char* SHORT_OPTIONS = "c:r:h"; Loading Loading @@ -214,20 +217,20 @@ void parseOptions(int argc, char* argv[]) { break; case 'c': gOpts.count = atoi(optarg); if (!gOpts.count) { gOpts.frameCount = atoi(optarg); if (!gOpts.frameCount) { fprintf(stderr, "Invalid frames argument '%s'\n", optarg); error = true; } break; case 'r': gRepeatCount = atoi(optarg); if (!gRepeatCount) { gOpts.repeatCount = atoi(optarg); if (!gOpts.repeatCount) { fprintf(stderr, "Invalid repeat argument '%s'\n", optarg); error = true; } else { gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX); gOpts.repeatCount = (gOpts.repeatCount > 0 ? gOpts.repeatCount : INT_MAX); } break; Loading Loading @@ -283,6 +286,10 @@ void parseOptions(int argc, char* argv[]) { gOpts.renderOffscreen = true; break; case LongOpts::SkipLeakCheck: gRunLeakCheck = false; break; case 'h': printHelp(); exit(EXIT_SUCCESS); Loading Loading @@ -322,9 +329,6 @@ void parseOptions(int argc, char* argv[]) { } int main(int argc, char* argv[]) { // set defaults gOpts.count = 150; Typeface::setRobotoTypefaceForTest(); parseOptions(argc, argv); Loading @@ -345,11 +349,9 @@ int main(int argc, char* argv[]) { gBenchmarkReporter->ReportContext(context); } for (int i = 0; i < gRepeatCount; i++) { for (auto&& test : gRunTests) { run(test, gOpts, gBenchmarkReporter.get()); } } if (gBenchmarkReporter) { gBenchmarkReporter->Finalize(); Loading @@ -358,6 +360,8 @@ int main(int argc, char* argv[]) { renderthread::RenderProxy::trimMemory(100); HardwareBitmapUploader::terminate(); if (gRunLeakCheck) { LeakChecker::checkForLeaks(); } return 0; }