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