• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/native_activity.h>
20 #include <android/native_window.h>
21 #include <android/performance_hint.h>
22 #include <android/surface_control.h>
23 #include <jni.h>
24 
25 #include <chrono>
26 #include <map>
27 #include <memory>
28 #include <optional>
29 
30 #include "EventLoop.h"
31 #include "Model.h"
32 #include "Shader.h"
33 #include "external/android_native_app_glue.h"
34 
35 const constexpr size_t kStages = 3;
36 const constexpr size_t kHeadsPerStage = 4;
37 const constexpr size_t kHeads = kStages * kHeadsPerStage;
38 
39 struct android_app;
40 
41 struct Vsync {
42     size_t index;
43     int64_t deadlineNanos;
44     int64_t presentationNanos;
45     int64_t vsyncId;
46 };
47 
48 struct VsyncData {
49     int64_t frameTimeNanos;
50     size_t numVsyncs;
51     size_t preferredVsync;
52     std::vector<Vsync> vsyncs{};
53 };
54 
55 /*!
56  * The output of running the per-stage lambda
57  */
58 struct StageState {
59     int64_t start;
60     int64_t end;
61     std::array<Model, kHeadsPerStage> models{};
62 };
63 
64 /*!
65  * The output state of running the whole stack of the per-stage updates for each stage
66  */
67 struct StackState {
68     Vsync intendedVsync;
69     std::array<StageState, kStages> stages{};
70 };
71 
72 Vsync &getClosestCallbackToTarget(int64_t target, VsyncData &data);
73 
74 struct FrameStats {
75     /*!
76      * Median of the durations
77      */
78     int64_t medianWorkDuration;
79     /*!
80      * Median of the intervals
81      */
82     int64_t medianFrameInterval;
83     /*!
84      * Standard deviation of a given run
85      */
86     double deviation;
87     /*!
88      * The total number of frames that exceeded target
89      */
90     std::optional<int64_t> exceededCount;
91     /*!
92      * The percent of frames that exceeded target
93      */
94     std::optional<double> exceededFraction;
95     /*!
96      * Efficiency of a given run is calculated by how close to min(target, baseline) the median is
97      */
98     std::optional<double> efficiency;
99     /*!
100      * Median of the target duration that was closest to what we wanted
101      */
102     int64_t medianClosestDeadlineToTarget;
103     // Actual target duration, related to session target but not exactly the same
104     // This is based on the intended frame deadline based on results from Choreographer
105     int64_t actualTargetDuration;
dumpFrameStats106     void dump(std::string testName) const {
107         aerr << "Stats for: " << testName;
108         aerr << "medianWorkDuration: " << medianWorkDuration << std::endl;
109         aerr << "medianFrameInterval: " << medianFrameInterval << std::endl;
110         aerr << "deviation: " << deviation << std::endl;
111         aerr << "exceededCount: " << exceededCount.value_or(-1) << std::endl;
112         aerr << "exceededFraction: " << exceededFraction.value_or(-1) << std::endl;
113         aerr << "efficiency: " << efficiency.value_or(-1) << std::endl;
114         aerr << "medianClosestDeadlineToTarget: " << medianClosestDeadlineToTarget << std::endl;
115         aerr << "actualTargetDuration: " << actualTargetDuration << std::endl;
116     };
117 };
118 
119 struct FrameBatchData {
120     std::vector<int64_t> durations{};
121     std::vector<int64_t> intervals{};
122     std::vector<VsyncData> vsyncs{};
123     std::vector<Vsync> selectedVsync{};
124 };
125 
126 Renderer *getRenderer();
127 
128 class Renderer {
129 public:
130     /*!
131      * @param pApp the android_app this Renderer belongs to, needed to configure GL
132      */
Renderer(android_app * pApp)133     inline Renderer(android_app *pApp)
134           : app_(pApp),
135             display_(EGL_NO_DISPLAY),
136             surface_(EGL_NO_SURFACE),
137             context_(EGL_NO_CONTEXT),
138             width_(0),
139             height_(0),
140             shaderNeedsNewProjectionMatrix_(true) {
141         threadedInit();
142     }
143 
144     virtual ~Renderer();
145 
146     /*!
147      * Draw all models waiting to render in a given StackState.
148      */
149     void render(const StackState &state);
150 
151     /*!
152      * Attempts to start hint session and returns whether ADPF is supported on a given device.
153      */
154     bool startHintSession(int64_t target);
155     void closeHintSession();
156     void reportActualWorkDuration(int64_t duration);
157     void updateTargetWorkDuration(int64_t target);
158     bool isHintSessionRunning();
159     int64_t getTargetWorkDuration();
160 
161     /*!
162      * Sets the number of iterations of physics before during each draw to control the CPU overhead.
163      */
164     void setPhysicsIterations(int iterations);
165 
166     /*!
167      * Adds an entry to the final result map that gets passed up to the Java side of the app, and
168      * eventually to the test runner.
169      */
170     void addResult(std::string name, std::string value);
171 
172     /*!
173      * Retrieve the results map.
174      */
175     std::map<std::string, std::string> &getResults();
176 
177     /*!
178      * Sets the baseline median, used to determine efficiency score
179      */
180     void setBaselineMedian(int64_t median);
181 
182     /*!
183      * Calculates the above frame stats for a given run
184      */
185     FrameStats getFrameStats(FrameBatchData &batchData, std::string &testName);
186 
187     void setChoreographer(AChoreographer *choreographer);
188     FrameStats drawFramesSync(int frames, int &events, android_poll_source *&pSource,
189                               std::string testName = "");
190 
191     /*!
192      * Run a few frames to figure out what vsync targets SF uses
193      */
194     std::vector<int64_t> findVsyncTargets(int &events, android_poll_source *&pSource,
195                                           int numFrames);
196 
197     static Renderer *getInstance();
198     static void makeInstance(android_app *pApp);
199 
200 private:
201     /*!
202      * Creates the set of android heads for load testing
203      */
204     void createHeads();
205 
206     /*!
207      * Swap the waiting render buffer after everything is drawn
208      */
209     void swapBuffers(const StackState &state);
210 
211     /*!
212      * Performs necessary OpenGL initialization. Customize this if you want to change your EGL
213      * context or application-wide settings.
214      */
215     void initRenderer(std::promise<bool> &syncOnCompletion);
216 
217     /*!
218      * Run initRenderer on the drawing thread to hold the context there
219      */
220     void threadedInit();
221 
222     /*!
223      * @brief we have to check every frame to see if the framebuffer has changed in size. If it has,
224      * update the viewport accordingly
225      */
226     void updateRenderArea();
227 
228     /*!
229      * Run physics and spin the models in a pipeline
230      */
231     StageState updateModels(StageState &lastUpdate);
232 
233     void choreographerCallback(const AChoreographerFrameCallbackData *data);
234 
235     /*!
236      * Passed directly choreographer as the callback method
237      */
238     static void rawChoreographerCallback(const AChoreographerFrameCallbackData *data, void *appPtr);
239 
240     /*!
241      * Post a new callback to Choreographer for the next frame
242      */
243     void postCallback();
244 
245     android_app *app_;
246     EGLDisplay display_;
247     EGLSurface surface_;
248     EGLContext context_;
249     EGLint width_;
250     EGLint height_;
251     APerformanceHintSession *hintSession_ = nullptr;
252     APerformanceHintManager *hintManager_ = nullptr;
253     AChoreographer *choreographer_;
254     int64_t lastTarget_ = 0;
255     int64_t baselineMedian_ = 0;
256 
257     bool shaderNeedsNewProjectionMatrix_ = true;
258 
259     /*!
260      * Schedules a method to run against each level of the pipeline. At each step, the
261      * method will be passed a "Data" object and output an object of the same type, allowing
262      * different levels of the same pipeline to easily share data or work on the same problem.
263      */
264     template <class Data>
recursiveSchedule(std::function<void (int level,Data & data)> fn,int level,Data && data)265     void recursiveSchedule(std::function<void(int level, Data &data)> fn, int level, Data &&data) {
266         loops_[level].queueWork(
267                 [=, this, fn = std::move(fn), dataInner = std::move(data)]() mutable {
268                     // Run the method
269                     fn(level, dataInner);
270 
271                     if (level > 0) {
272                         this->recursiveSchedule(fn, level - 1, std::move(dataInner));
273                     }
274                 });
275     }
276 
277     template <class Data>
stackSchedule(std::function<void (int level,Data & data)> fn,Data startData)278     void stackSchedule(std::function<void(int level, Data &data)> fn, Data startData) {
279         recursiveSchedule<Data>(fn, loops_.size() - 1, std::move(startData));
280     }
281 
282     std::unique_ptr<Shader> shader_;
283     std::array<StageState, kStages> latest_stage_models_;
284     int headsSize_ = 0;
285     int physicsIterations_ = 1;
286 
287     /*!
288      * Holds onto the results object in the renderer, so
289      * we can reach the data anywhere in the rendering step.
290      */
291     std::map<std::string, std::string> results_;
292 
293     std::array<EventLoop, kStages> loops_{};
294     EventLoop drawLoop_{};
295     std::vector<pid_t> tids_{};
296     std::vector<int64_t> targets_{};
297 
298     FrameBatchData batchData_{};
299 
300     std::optional<std::function<void(VsyncData &data)>> wakeupMethod_ = std::nullopt;
301 
302     std::optional<int64_t> lastStart_ = std::nullopt;
303     int framesRemaining_ = 0;
304     std::promise<bool> framesDone_{};
305 
306     static std::unique_ptr<Renderer> sRenderer;
307 };
308