/* * 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 "AnimationContext.h" #include "RenderNode.h" #include "renderthread/RenderProxy.h" #include "renderthread/RenderTask.h" #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" #include "tests/common/scenes/TestSceneBase.h" #include "utils/TraceUtils.h" #include #include #include #include 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); } }; template class ModifiedMovingAverage { public: explicit ModifiedMovingAverage(int weight) : mWeight(weight) {} T add(T today) { if (!mHasValue) { mAverage = today; } else { mAverage = (((mWeight - 1) * mAverage) + today) / mWeight; } return mAverage; } T average() { return mAverage; } private: bool mHasValue = false; int mWeight; 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 REPORTS = { ReportInfo{50, "_50th"}, ReportInfo{90, "_90th"}, ReportInfo{95, "_95th"}, ReportInfo{99, "_99th"}, }; // 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 reports; BenchmarkReporter::Run report; report.run_name = info.name; report.iterations = static_cast(opts.count); 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 = info.name; reports[0].run_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); } } } void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { // Switch to the real display gDisplay = getInternalDisplay(); Properties::forceDrawFrame = true; TestContext testContext; testContext.setRenderOffscreen(opts.renderOffscreen); // create the native surface const int width = gDisplay.w; const int height = gDisplay.h; sp surface = testContext.surface(); std::unique_ptr scene(info.createScene(opts)); scene->renderTarget = surface; sp rootNode = TestUtils::createNode( 0, 0, width, height, [&scene, width, height](RenderProperties& props, Canvas& canvas) { props.setClipToBounds(false); scene->createContent(width, height, canvas); }); ContextFactory factory; std::unique_ptr proxy(new RenderProxy(false, rootNode.get(), &factory)); proxy->loadSystemProperties(); proxy->setSurface(surface); float lightX = width / 2.0; proxy->setLightAlpha(255 * 0.075, 255 * 0.15); proxy->setLightGeometry((Vector3){lightX, dp(-200.0f), dp(800.0f)}, dp(800.0f)); // Do a few cold runs then reset the stats so that the caches are all hot int warmupFrameCount = 5; if (opts.renderOffscreen) { // Do a few more warmups to try and boost the clocks up warmupFrameCount = 10; } for (int i = 0; i < warmupFrameCount; i++) { testContext.waitForVsync(); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); proxy->syncAndDrawFrame(); } proxy->resetProfileInfo(); proxy->fence(); if (opts.renderAhead) { usleep(33000); } proxy->setRenderAheadDepth(opts.renderAhead); ModifiedMovingAverage avgMs(opts.reportFrametimeWeight); nsecs_t start = systemTime(CLOCK_MONOTONIC); for (int i = 0; i < opts.count; i++) { testContext.waitForVsync(); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); { ATRACE_NAME("UI-Draw Frame"); UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); scene->doFrame(i); proxy->syncAndDrawFrame(); } if (opts.reportFrametimeWeight) { proxy->fence(); nsecs_t done = systemTime(CLOCK_MONOTONIC); avgMs.add((done - vsync) / 1000000.0); if (i % 10 == 9) { printf("Average frametime %.3fms\n", avgMs.average()); } } } proxy->fence(); nsecs_t end = systemTime(CLOCK_MONOTONIC); if (reporter) { outputBenchmarkReport(info, opts, reporter, proxy.get(), (end - start) / (double)s2ns(1)); } else { proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); } }