1 /*
2  * Copyright 2023 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 #pragma once
17 
18 #include <EGL/egl.h>
19 #include <android/performance_hint.h>
20 #include <jni.h>
21 
22 #include <chrono>
23 #include <map>
24 #include <memory>
25 #include <optional>
26 
27 #include "Model.h"
28 #include "Shader.h"
29 #include "external/android_native_app_glue.h"
30 
31 struct android_app;
32 
33 struct FrameStats {
34     // Median of the durations
35     int64_t medianWorkDuration;
36     // Median of the intervals
37     int64_t medianFrameInterval;
38     // Standard deviation of a given run
39     double deviation;
40     // The total number of frames that exceeded target
41     std::optional<int64_t> exceededCount;
42     // The percent of frames that exceeded target
43     std::optional<double> exceededFraction;
44     // Efficiency of a given run is calculated by how close to min(target, baseline) the median is
45     std::optional<double> efficiency;
46 };
47 
48 class Renderer {
49 public:
50     /*!
51      * @param pApp the android_app this Renderer belongs to, needed to configure GL
52      */
Renderer(android_app * pApp)53     inline Renderer(android_app *pApp)
54           : app_(pApp),
55             display_(EGL_NO_DISPLAY),
56             surface_(EGL_NO_SURFACE),
57             context_(EGL_NO_CONTEXT),
58             width_(0),
59             height_(0),
60             shaderNeedsNewProjectionMatrix_(true) {
61         initRenderer();
62     }
63 
64     virtual ~Renderer();
65 
66     /*!
67      * Renders all the models in the renderer, returns time spent waiting for CPU work
68      * to finish.
69      */
70     jlong render();
71 
72     void startHintSession(std::vector<pid_t> &threads, int64_t target);
73     void closeHintSession();
74     void reportActualWorkDuration(int64_t duration);
75     void updateTargetWorkDuration(int64_t target);
76     bool isHintSessionRunning();
77     int64_t getTargetWorkDuration();
78 
79     /*!
80      * Sets the number of android "heads" in the scene, these are used to create a synthetic
81      * workload that scales with performance, and by adjusting the number of them, the test can
82      * adjust the amount of stress to place the system under.
83      */
84     void setNumHeads(int headCount);
85 
86     /*!
87      * Adds an entry to the final result map that gets passed up to the Java side of the app, and
88      * eventually to the test runner.
89      */
90     void addResult(std::string name, std::string value);
91 
92     /*!
93      * Retrieve the results map.
94      */
95     std::map<std::string, std::string> &getResults();
96 
97     /*!
98      * Informs the test whether ADPF is supported on a given device.
99      */
100     bool getAdpfSupported();
101 
102     /*
103      * Finds the test settings that best match this device, and returns the
104      * duration of the frame's work
105      */
106     double calibrate(int &events, android_poll_source *pSource);
107 
108     /*!
109      * Sets the baseline median, used to determine efficiency score
110      */
111     void setBaselineMedian(int64_t median);
112 
113     /*!
114      * Calculates the above frame stats for a given run
115      */
116     FrameStats getFrameStats(std::vector<int64_t> &durations, std::vector<int64_t> &intervals,
117                              std::string &testName);
118 
119 private:
120     /*!
121      * Performs necessary OpenGL initialization. Customize this if you want to change your EGL
122      * context or application-wide settings.
123      */
124     void initRenderer();
125 
126     /*!
127      * @brief we have to check every frame to see if the framebuffer has changed in size. If it has,
128      * update the viewport accordingly
129      */
130     void updateRenderArea();
131 
132     /*!
133      * Adds an android "head" to the scene.
134      */
135     void addHead();
136 
137     android_app *app_;
138     EGLDisplay display_;
139     EGLSurface surface_;
140     EGLContext context_;
141     EGLint width_;
142     EGLint height_;
143     APerformanceHintSession *hintSession_ = nullptr;
144     APerformanceHintManager *hintManager_ = nullptr;
145     int64_t lastTarget_ = 0;
146     int64_t baselineMedian_ = 0;
147 
148     bool shaderNeedsNewProjectionMatrix_;
149 
150     std::unique_ptr<Shader> shader_;
151     std::vector<Model> heads_;
152 
153     // Hold on to the results object in the renderer, so
154     // we can reach the data anywhere in the rendering step.
155     std::map<std::string, std::string> results_;
156 };
157