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