• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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 #pragma once
18 
19 #include <chrono>
20 #include <memory>
21 #include <mutex>
22 #include <list>
23 #include <vector>
24 #include <atomic>
25 #include <condition_variable>
26 
27 #include <EGL/egl.h>
28 #include <EGL/eglext.h>
29 
30 #include <jni.h>
31 
32 #include "swappy/swappy.h"
33 #include "swappy/swappy_extra.h"
34 
35 #include "Thread.h"
36 
37 namespace swappy {
38 
39 class ChoreographerFilter;
40 class ChoreographerThread;
41 class EGL;
42 class FrameStatistics;
43 
44 using EGLDisplay = void *;
45 using EGLSurface = void *;
46 
47 using namespace std::chrono_literals;
48 
49 class Swappy {
50   private:
51     // Allows construction with std::unique_ptr from a static method, but disallows construction
52     // outside of the class since no one else can construct a ConstructorTag
53     struct ConstructorTag {
54     };
55   public:
56     Swappy(JavaVM *vm,
57            std::chrono::nanoseconds refreshPeriod,
58            std::chrono::nanoseconds appOffset,
59            std::chrono::nanoseconds sfOffset,
60            ConstructorTag tag);
61 
62     static void init(JNIEnv *env, jobject jactivity);
63 
64     static void onChoreographer(int64_t frameTimeNanos);
65 
66     static bool swap(EGLDisplay display, EGLSurface surface);
67 
68     static void init(JavaVM *vm,
69                      std::chrono::nanoseconds refreshPeriod,
70                      std::chrono::nanoseconds appOffset,
71                      std::chrono::nanoseconds sfOffset);
72 
73     // Pass callbacks for tracing within the swap function
74     static void addTracer(const SwappyTracer *tracer);
75 
76     static uint64_t getSwapIntervalNS();
77 
78     static void setAutoSwapInterval(bool enabled);
79 
80     static void setAutoPipelineMode(bool enabled);
81 
82     static void overrideAutoSwapInterval(uint64_t swap_ns);
83 
84     static void enableStats(bool enabled);
85     static void recordFrameStart(EGLDisplay display, EGLSurface surface);
86     static void getStats(Swappy_Stats *stats);
87     static bool isEnabled();
88     static void destroyInstance();
89 
90 private:
91     class FrameDuration {
92     public:
93         FrameDuration() = default;
94 
FrameDuration(std::chrono::nanoseconds cpuTime,std::chrono::nanoseconds gpuTime)95         FrameDuration(std::chrono::nanoseconds cpuTime, std::chrono::nanoseconds gpuTime) :
96             mCpuTime(cpuTime), mGpuTime(gpuTime) {
97             mCpuTime = std::min(mCpuTime, MAX_DURATION);
98             mGpuTime = std::min(mGpuTime, MAX_DURATION);
99         }
100 
getCpuTime()101         std::chrono::nanoseconds getCpuTime() const { return mCpuTime; }
getGpuTime()102         std::chrono::nanoseconds getGpuTime() const { return mGpuTime; }
getTime(bool pipeline)103         std::chrono::nanoseconds getTime(bool pipeline) const {
104             if (pipeline) {
105                 return std::max(mCpuTime, mGpuTime);
106             }
107 
108             return mCpuTime + mGpuTime;
109         }
110 
111         FrameDuration& operator+=(const FrameDuration& other) {
112             mCpuTime += other.mCpuTime;
113             mGpuTime += other.mGpuTime;
114             return *this;
115         }
116 
117         FrameDuration& operator-=(const FrameDuration& other) {
118             mCpuTime -= other.mCpuTime;
119             mGpuTime -= other.mGpuTime;
120             return *this;
121         }
122 
123         friend FrameDuration operator/(FrameDuration lhs, int rhs) {
124             lhs.mCpuTime /= rhs;
125             lhs.mGpuTime /= rhs;
126             return lhs;
127         }
128     private:
129         std::chrono::nanoseconds mCpuTime = std::chrono::nanoseconds(0);
130         std::chrono::nanoseconds mGpuTime = std::chrono::nanoseconds(0);
131 
132         static constexpr std::chrono::nanoseconds MAX_DURATION =
133                 std::chrono::milliseconds(100);
134     };
135 
136     static Swappy *getInstance();
137 
enabled()138     bool enabled() const { return !mDisableSwappy; }
139 
140     EGL *getEgl();
141 
142     bool swapInternal(EGLDisplay display, EGLSurface surface);
143 
144     void addTracerCallbacks(SwappyTracer tracer);
145 
146     void preSwapBuffersCallbacks();
147     void postSwapBuffersCallbacks();
148     void preWaitCallbacks();
149     void postWaitCallbacks();
150     void startFrameCallbacks();
151     void swapIntervalChangedCallbacks();
152 
153     void onSettingsChanged();
154 
155     void handleChoreographer();
156     std::chrono::nanoseconds wakeClient();
157 
158     void startFrame();
159 
160     void waitUntil(int32_t frameNumber);
161 
162     void waitOneFrame();
163 
164     // Waits for the next frame, considering both Choreographer and the prior frame's completion
165     bool waitForNextFrame(EGLDisplay display);
166 
167     // Destroys the previous sync fence (if any) and creates a new one for this frame
168     void resetSyncFence(EGLDisplay display);
169 
170     // Computes the desired presentation time based on the swap interval and sets it
171     // using eglPresentationTimeANDROID
172     bool setPresentationTime(EGLDisplay display, EGLSurface surface);
173 
174     void updateSwapDuration(std::chrono::nanoseconds duration);
175 
176     void addFrameDuration(FrameDuration duration);
177 
178     bool updateSwapInterval();
179 
180     void swapFaster(const FrameDuration& averageFrameTime,
181                     const std::chrono::nanoseconds& upperBound,
182                     const std::chrono::nanoseconds& lowerBound,
183                     const int32_t& newSwapInterval) REQUIRES(mFrameDurationsMutex);
184 
185     void swapSlower(const FrameDuration& averageFrameTime,
186                       const std::chrono::nanoseconds& upperBound,
187                       const std::chrono::nanoseconds& lowerBound,
188                       const int32_t& newSwapInterval) REQUIRES(mFrameDurationsMutex);
189 
190     bool mDisableSwappy = false;
191 
192     int32_t nanoToSwapInterval(std::chrono::nanoseconds);
193 
194     std::atomic<std::chrono::nanoseconds> mSwapDuration;
195 
196     static std::mutex sInstanceMutex;
197     static std::unique_ptr<Swappy> sInstance;
198 
199     std::atomic<int32_t> mSwapInterval;
200     std::atomic<int32_t> mAutoSwapInterval;
201     int mAutoSwapIntervalThreshold = 0;
202 
203     std::mutex mWaitingMutex;
204     std::condition_variable mWaitingCondition;
205     std::chrono::steady_clock::time_point mCurrentFrameTimestamp = std::chrono::steady_clock::now();
206     int32_t mCurrentFrame = 0;
207 
208     std::mutex mEglMutex;
209     std::shared_ptr<EGL> mEgl;
210 
211     int32_t mTargetFrame = 0;
212     std::chrono::steady_clock::time_point mPresentationTime = std::chrono::steady_clock::now();
213     bool mPipelineMode = false;
214 
215     const std::chrono::nanoseconds mRefreshPeriod;
216     std::unique_ptr<ChoreographerFilter> mChoreographerFilter;
217 
218     bool mUsingExternalChoreographer = false;
219     std::unique_ptr<ChoreographerThread> mChoreographerThread;
220 
221     template <typename ...T>
222     using Tracer = std::function<void (T...)>;
223 
224     struct SwappyTracerCallbacks {
225         std::list<Tracer<>> preWait;
226         std::list<Tracer<>> postWait;
227         std::list<Tracer<>> preSwapBuffers;
228         std::list<Tracer<long>> postSwapBuffers;
229         std::list<Tracer<int32_t, long>> startFrame;
230         std::list<Tracer<>> swapIntervalChanged;
231     };
232 
233     SwappyTracerCallbacks mInjectedTracers;
234 
235     std::mutex mFrameDurationsMutex;
236     std::vector<FrameDuration> mFrameDurations GUARDED_BY(mFrameDurationsMutex);
237     FrameDuration mFrameDurationsSum GUARDED_BY(mFrameDurationsMutex);
238     static constexpr int mFrameDurationSamples = 10;
239     bool mAutoSwapIntervalEnabled GUARDED_BY(mFrameDurationsMutex) = true;
240     bool mPipelineModeAutoMode GUARDED_BY(mFrameDurationsMutex) = true;
241     static constexpr std::chrono::nanoseconds FRAME_HYSTERESIS = 3ms;
242     std::chrono::steady_clock::time_point mSwapTime;
243     std::chrono::steady_clock::time_point mStartFrameTime;
244     std::unique_ptr<FrameStatistics> mFrameStatistics;
245 
246     const std::chrono::nanoseconds mSfOffset;
247 };
248 
249 } //namespace swappy
250