Home | History | Annotate | Download | only in macrobench
      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