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