• 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 #include "EGL.h"
18 
19 #include <vector>
20 
21 #define LOG_TAG "Swappy::EGL"
22 
23 #include "Log.h"
24 
25 using namespace std::chrono_literals;
26 
27 namespace swappy {
28 
create(std::chrono::nanoseconds refreshPeriod)29 std::unique_ptr<EGL> EGL::create(std::chrono::nanoseconds refreshPeriod) {
30     auto eglPresentationTimeANDROID = reinterpret_cast<eglPresentationTimeANDROID_type>(
31         eglGetProcAddress("eglPresentationTimeANDROID"));
32     if (eglPresentationTimeANDROID == nullptr) {
33         ALOGE("Failed to load eglPresentationTimeANDROID");
34         return nullptr;
35     }
36 
37     auto eglCreateSyncKHR = reinterpret_cast<eglCreateSyncKHR_type>(
38         eglGetProcAddress("eglCreateSyncKHR"));
39     if (eglCreateSyncKHR == nullptr) {
40         ALOGE("Failed to load eglCreateSyncKHR");
41         return nullptr;
42     }
43 
44     auto eglDestroySyncKHR = reinterpret_cast<eglDestroySyncKHR_type>(
45         eglGetProcAddress("eglDestroySyncKHR"));
46     if (eglDestroySyncKHR == nullptr) {
47         ALOGE("Failed to load eglDestroySyncKHR");
48         return nullptr;
49     }
50 
51     auto eglGetSyncAttribKHR = reinterpret_cast<eglGetSyncAttribKHR_type>(
52         eglGetProcAddress("eglGetSyncAttribKHR"));
53     if (eglGetSyncAttribKHR == nullptr) {
54         ALOGE("Failed to load eglGetSyncAttribKHR");
55         return nullptr;
56     }
57 
58     auto eglGetError = reinterpret_cast<eglGetError_type>(
59             eglGetProcAddress("eglGetError"));
60     if (eglGetError == nullptr) {
61         ALOGE("Failed to load eglGetError");
62         return nullptr;
63     }
64 
65     auto eglSurfaceAttrib = reinterpret_cast<eglSurfaceAttrib_type>(
66             eglGetProcAddress("eglSurfaceAttrib"));
67     if (eglSurfaceAttrib == nullptr) {
68         ALOGE("Failed to load eglSurfaceAttrib");
69         return nullptr;
70     }
71 
72     // stats may not be supported on all versions
73     auto eglGetNextFrameIdANDROID = reinterpret_cast<eglGetNextFrameIdANDROID_type>(
74             eglGetProcAddress("eglGetNextFrameIdANDROID"));
75     if (eglGetNextFrameIdANDROID == nullptr) {
76         ALOGI("Failed to load eglGetNextFrameIdANDROID");
77     }
78 
79     auto eglGetFrameTimestampsANDROID = reinterpret_cast<eglGetFrameTimestampsANDROID_type>(
80             eglGetProcAddress("eglGetFrameTimestampsANDROID"));
81     if (eglGetFrameTimestampsANDROID == nullptr) {
82         ALOGI("Failed to load eglGetFrameTimestampsANDROID");
83     }
84 
85     auto egl = std::make_unique<EGL>(refreshPeriod, ConstructorTag{});
86     egl->eglPresentationTimeANDROID = eglPresentationTimeANDROID;
87     egl->eglCreateSyncKHR = eglCreateSyncKHR;
88     egl->eglDestroySyncKHR = eglDestroySyncKHR;
89     egl->eglGetSyncAttribKHR = eglGetSyncAttribKHR;
90     egl->eglGetError = eglGetError;
91     egl->eglSurfaceAttrib = eglSurfaceAttrib;
92     egl->eglGetNextFrameIdANDROID = eglGetNextFrameIdANDROID;
93     egl->eglGetFrameTimestampsANDROID = eglGetFrameTimestampsANDROID;
94     return egl;
95 }
96 
resetSyncFence(EGLDisplay display)97 void EGL::resetSyncFence(EGLDisplay display) {
98     std::lock_guard<std::mutex> lock(mSyncFenceMutex);
99     mFenceWaiter.waitForIdle();
100 
101     if (mSyncFence != EGL_NO_SYNC_KHR) {
102         EGLBoolean result = eglDestroySyncKHR(display, mSyncFence);
103         if (result == EGL_FALSE) {
104             ALOGE("Failed to destroy sync fence");
105         }
106     }
107 
108     mSyncFence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
109 
110     // kick of the thread work to wait for the fence and measure its time
111     mFenceWaiter.onFenceCreation(display, mSyncFence);
112 }
113 
lastFrameIsComplete(EGLDisplay display)114 bool EGL::lastFrameIsComplete(EGLDisplay display) {
115     std::lock_guard<std::mutex> lock(mSyncFenceMutex);
116 
117     // This will be the case on the first frame
118     if (mSyncFence == EGL_NO_SYNC_KHR) {
119         return true;
120     }
121 
122     EGLint status = 0;
123     EGLBoolean result = eglGetSyncAttribKHR(display, mSyncFence, EGL_SYNC_STATUS_KHR, &status);
124     if (result == EGL_FALSE) {
125         ALOGE("Failed to get sync status");
126         return true;
127     }
128 
129     if (status == EGL_SIGNALED_KHR) {
130         return true;
131     } else if (status == EGL_UNSIGNALED_KHR) {
132         return false;
133     } else {
134         ALOGE("Unexpected sync status: %d", status);
135         return true;
136     }
137 }
138 
setPresentationTime(EGLDisplay display,EGLSurface surface,std::chrono::steady_clock::time_point time)139 bool EGL::setPresentationTime(EGLDisplay display,
140                               EGLSurface surface,
141                               std::chrono::steady_clock::time_point time) {
142     eglPresentationTimeANDROID(display, surface, time.time_since_epoch().count());
143     return EGL_TRUE;
144 }
145 
statsSupported()146 bool EGL::statsSupported() {
147     return (eglGetNextFrameIdANDROID != nullptr && eglGetFrameTimestampsANDROID != nullptr);
148 }
149 
getNextFrameId(EGLDisplay dpy,EGLSurface surface)150 std::pair<bool,EGLuint64KHR> EGL::getNextFrameId(EGLDisplay dpy, EGLSurface surface) {
151     if (eglGetNextFrameIdANDROID == nullptr) {
152         ALOGE("stats are not supported on this platform");
153         return {false, 0};
154     }
155 
156     EGLuint64KHR frameId;
157     EGLBoolean result = eglGetNextFrameIdANDROID(dpy, surface, &frameId);
158     if (result == EGL_FALSE) {
159         ALOGE("Failed to get next frame ID");
160         return {false, 0};
161     }
162 
163     return {true, frameId};
164 }
165 
getFrameTimestamps(EGLDisplay dpy,EGLSurface surface,EGLuint64KHR frameId)166 std::unique_ptr<EGL::FrameTimestamps> EGL::getFrameTimestamps(EGLDisplay dpy,
167                                                               EGLSurface surface,
168                                                               EGLuint64KHR frameId) {
169     if (eglGetFrameTimestampsANDROID == nullptr) {
170         ALOGE("stats are not supported on this platform");
171         return nullptr;
172     }
173 
174     const std::vector<EGLint> timestamps = {
175             EGL_REQUESTED_PRESENT_TIME_ANDROID,
176             EGL_RENDERING_COMPLETE_TIME_ANDROID,
177             EGL_COMPOSITION_LATCH_TIME_ANDROID,
178             EGL_DISPLAY_PRESENT_TIME_ANDROID,
179     };
180 
181     std::vector<EGLnsecsANDROID> values(timestamps.size());
182 
183     EGLBoolean result = eglGetFrameTimestampsANDROID(dpy, surface, frameId,
184            timestamps.size(), timestamps.data(), values.data());
185     if (result == EGL_FALSE) {
186         EGLint reason = eglGetError();
187         if (reason == EGL_BAD_SURFACE) {
188             eglSurfaceAttrib(dpy, surface, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);
189         } else {
190             ALOGE("Failed to get timestamps for frame %llu", (unsigned long long) frameId);
191         }
192         return nullptr;
193     }
194 
195     // try again if we got some pending stats
196     for (auto i : values) {
197         if (i == EGL_TIMESTAMP_PENDING_ANDROID) return nullptr;
198     }
199 
200     std::unique_ptr<EGL::FrameTimestamps> frameTimestamps =
201             std::make_unique<EGL::FrameTimestamps>();
202     frameTimestamps->requested = values[0];
203     frameTimestamps->renderingCompleted = values[1];
204     frameTimestamps->compositionLatched = values[2];
205     frameTimestamps->presented = values[3];
206 
207     return frameTimestamps;
208 }
209 
FenceWaiter()210 EGL::FenceWaiter::FenceWaiter(): mFenceWaiter(&FenceWaiter::threadMain, this) {
211     std::unique_lock<std::mutex> lock(mFenceWaiterLock);
212 
213     eglClientWaitSyncKHR = reinterpret_cast<eglClientWaitSyncKHR_type>(
214             eglGetProcAddress("eglClientWaitSyncKHR"));
215     if (eglClientWaitSyncKHR == nullptr)
216         ALOGE("Failed to load eglClientWaitSyncKHR");
217 }
218 
~FenceWaiter()219 EGL::FenceWaiter::~FenceWaiter() {
220     {
221         std::lock_guard<std::mutex> lock(mFenceWaiterLock);
222         mFenceWaiterRunning = false;
223         mFenceWaiterCondition.notify_all();
224     }
225     mFenceWaiter.join();
226 }
227 
waitForIdle()228 void EGL::FenceWaiter::waitForIdle() {
229     std::lock_guard<std::mutex> lock(mFenceWaiterLock);
230     mFenceWaiterCondition.wait(mFenceWaiterLock, [this]() REQUIRES(mFenceWaiterLock) {
231                                          return !mFenceWaiterPending;
232                                       });
233 }
234 
onFenceCreation(EGLDisplay display,EGLSyncKHR syncFence)235 void EGL::FenceWaiter::onFenceCreation(EGLDisplay display, EGLSyncKHR syncFence) {
236     std::lock_guard<std::mutex> lock(mFenceWaiterLock);
237     mDisplay = display;
238     mSyncFence = syncFence;
239     mFenceWaiterPending = true;
240     mFenceWaiterCondition.notify_all();
241 }
242 
threadMain()243 void EGL::FenceWaiter::threadMain() {
244     std::lock_guard<std::mutex> lock(mFenceWaiterLock);
245     while (mFenceWaiterRunning) {
246         // wait for new fence object
247         mFenceWaiterCondition.wait(mFenceWaiterLock,
248                                    [this]() REQUIRES(mFenceWaiterLock) {
249                                        return mFenceWaiterPending || !mFenceWaiterRunning;
250                                    });
251 
252         if (!mFenceWaiterRunning) {
253             break;
254         }
255 
256         const auto startTime = std::chrono::steady_clock::now();
257         EGLBoolean result = eglClientWaitSyncKHR(mDisplay, mSyncFence, 0, EGL_FOREVER_KHR);
258         if (result == EGL_FALSE) {
259             ALOGE("Failed to wait sync");
260         }
261 
262         mFencePendingTime = std::chrono::steady_clock::now() - startTime;
263 
264         mFenceWaiterPending = false;
265         mFenceWaiterCondition.notify_all();
266     }
267 }
268 
getFencePendingTime()269 std::chrono::nanoseconds EGL::FenceWaiter::getFencePendingTime() {
270     // return mFencePendingTime without a lock to avoid blocking the main thread
271     // worst case, the time will be of some previous frame
272     return mFencePendingTime.load();
273 }
274 
275 } // namespace swappy
276