1 /* 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 "AnimationContext.h" 18 #include "RenderNode.h" 19 #include "tests/common/TestContext.h" 20 #include "tests/common/TestScene.h" 21 #include "tests/common/scenes/TestSceneBase.h" 22 #include "renderthread/RenderProxy.h" 23 #include "renderthread/RenderTask.h" 24 25 #include <benchmark/benchmark.h> 26 #include <gui/Surface.h> 27 #include <log/log.h> 28 #include <ui/PixelFormat.h> 29 30 using namespace android; 31 using namespace android::uirenderer; 32 using namespace android::uirenderer::renderthread; 33 using namespace android::uirenderer::test; 34 35 class ContextFactory : public IContextFactory { 36 public: 37 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { 38 return new AnimationContext(clock); 39 } 40 }; 41 42 template<class T> 43 class ModifiedMovingAverage { 44 public: 45 explicit ModifiedMovingAverage(int weight) : mWeight(weight) {} 46 47 T add(T today) { 48 if (!mHasValue) { 49 mAverage = today; 50 } else { 51 mAverage = (((mWeight - 1) * mAverage) + today) / mWeight; 52 } 53 return mAverage; 54 } 55 56 T average() { 57 return mAverage; 58 } 59 60 private: 61 bool mHasValue = false; 62 int mWeight; 63 T mAverage; 64 }; 65 66 void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, 67 benchmark::BenchmarkReporter* reporter, RenderProxy* proxy, 68 double durationInS) { 69 using namespace benchmark; 70 71 struct ReportInfo { 72 int percentile; 73 const char* suffix; 74 }; 75 76 static std::array<ReportInfo, 4> REPORTS = { 77 ReportInfo { 50, "_50th" }, 78 ReportInfo { 90, "_90th" }, 79 ReportInfo { 95, "_95th" }, 80 ReportInfo { 99, "_99th" }, 81 }; 82 83 // Although a vector is used, it must stay with only a single element 84 // otherwise the BenchmarkReporter will automatically compute 85 // mean and stddev which doesn't make sense for our usage 86 std::vector<BenchmarkReporter::Run> reports; 87 BenchmarkReporter::Run report; 88 report.benchmark_name = info.name; 89 report.iterations = static_cast<int64_t>(opts.count); 90 report.real_accumulated_time = durationInS; 91 report.cpu_accumulated_time = durationInS; 92 report.items_per_second = opts.count / durationInS; 93 reports.push_back(report); 94 reporter->ReportRuns(reports); 95 96 // Pretend the percentiles are single-iteration runs of the test 97 // If rendering offscreen skip this as it's fps that's more interesting 98 // in that test case than percentiles. 99 if (!opts.renderOffscreen) { 100 for (auto& ri : REPORTS) { 101 reports[0].benchmark_name = info.name; 102 reports[0].benchmark_name += ri.suffix; 103 durationInS = proxy->frameTimePercentile(ri.percentile) / 1000.0; 104 reports[0].real_accumulated_time = durationInS; 105 reports[0].cpu_accumulated_time = durationInS; 106 reports[0].iterations = 1; 107 reports[0].items_per_second = 0; 108 reporter->ReportRuns(reports); 109 } 110 } 111 } 112 113 void run(const TestScene::Info& info, const TestScene::Options& opts, 114 benchmark::BenchmarkReporter* reporter) { 115 // Switch to the real display 116 gDisplay = getBuiltInDisplay(); 117 118 std::unique_ptr<TestScene> scene(info.createScene(opts)); 119 120 Properties::forceDrawFrame = true; 121 TestContext testContext; 122 testContext.setRenderOffscreen(opts.renderOffscreen); 123 124 // create the native surface 125 const int width = gDisplay.w; 126 const int height = gDisplay.h; 127 sp<Surface> surface = testContext.surface(); 128 129 sp<RenderNode> rootNode = TestUtils::createNode(0, 0, width, height, 130 [&scene, width, height](RenderProperties& props, Canvas& canvas) { 131 props.setClipToBounds(false); 132 scene->createContent(width, height, canvas); 133 }); 134 135 ContextFactory factory; 136 std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, 137 rootNode.get(), &factory)); 138 proxy->loadSystemProperties(); 139 proxy->initialize(surface); 140 float lightX = width / 2.0; 141 proxy->setup(dp(800.0f), 255 * 0.075, 255 * 0.15); 142 proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)}); 143 144 // Do a few cold runs then reset the stats so that the caches are all hot 145 int warmupFrameCount = 5; 146 if (opts.renderOffscreen) { 147 // Do a few more warmups to try and boost the clocks up 148 warmupFrameCount = 10; 149 } 150 for (int i = 0; i < warmupFrameCount; i++) { 151 testContext.waitForVsync(); 152 nsecs_t vsync = systemTime(CLOCK_MONOTONIC); 153 UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); 154 proxy->syncAndDrawFrame(); 155 } 156 157 proxy->resetProfileInfo(); 158 proxy->fence(); 159 160 ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); 161 162 nsecs_t start = systemTime(CLOCK_MONOTONIC); 163 for (int i = 0; i < opts.count; i++) { 164 testContext.waitForVsync(); 165 nsecs_t vsync = systemTime(CLOCK_MONOTONIC); 166 { 167 ATRACE_NAME("UI-Draw Frame"); 168 UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); 169 scene->doFrame(i); 170 proxy->syncAndDrawFrame(); 171 } 172 if (opts.reportFrametimeWeight) { 173 proxy->fence(); 174 nsecs_t done = systemTime(CLOCK_MONOTONIC); 175 avgMs.add((done - vsync) / 1000000.0); 176 if (i % 10 == 9) { 177 printf("Average frametime %.3fms\n", avgMs.average()); 178 } 179 } 180 } 181 proxy->fence(); 182 nsecs_t end = systemTime(CLOCK_MONOTONIC); 183 184 if (reporter) { 185 outputBenchmarkReport(info, opts, reporter, proxy.get(), 186 (end - start) / (double) s2ns(1)); 187 } else { 188 proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); 189 } 190 } 191