1 /*
2 * Copyright (C) 2016 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 "SyncThread.h"
18
19 #include "OpenGLESDispatch/OpenGLDispatchLoader.h"
20 #include "aemu/base/Metrics.h"
21 #include "aemu/base/system/System.h"
22 #include "aemu/base/threads/Thread.h"
23 #include "host-common/GfxstreamFatalError.h"
24 #include "host-common/crash_reporter.h"
25 #include "host-common/logging.h"
26 #include "host-common/sync_device.h"
27
28 #ifndef _MSC_VER
29 #include <sys/time.h>
30 #endif
31 #include <memory>
32
33 namespace gfxstream {
34
35 using android::base::EventHangMetadata;
36 using emugl::ABORT_REASON_OTHER;
37 using emugl::FatalError;
38 using gl::EGLDispatch;
39 using gl::EmulatedEglFenceSync;
40
41 #define DEBUG 0
42
43 #if DEBUG
44
curr_ms()45 static uint64_t curr_ms() {
46 struct timeval tv;
47 gettimeofday(&tv, NULL);
48 return tv.tv_usec / 1000 + tv.tv_sec * 1000;
49 }
50
51 #define DPRINT(fmt, ...) do { \
52 if (!VERBOSE_CHECK(syncthreads)) VERBOSE_ENABLE(syncthreads); \
53 VERBOSE_TID_FUNCTION_DPRINT(syncthreads, "@ time=%llu: " fmt, curr_ms(), ##__VA_ARGS__); \
54 } while(0)
55
56 #else
57
58 #define DPRINT(...)
59
60 #endif
61
62 #define SYNC_THREAD_CHECK(condition) \
63 do { \
64 if (!(condition)) { \
65 GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << \
66 #condition << " is false"; \
67 } \
68 } while (0)
69
70 // The single global sync thread instance.
71 class GlobalSyncThread {
72 public:
73 GlobalSyncThread() = default;
74
initialize(bool hasGl,HealthMonitor<> * healthMonitor)75 void initialize(bool hasGl, HealthMonitor<>* healthMonitor) {
76 AutoLock mutex(mLock);
77 SYNC_THREAD_CHECK(!mSyncThread);
78 mSyncThread = std::make_unique<SyncThread>(hasGl, healthMonitor);
79 }
syncThreadPtr()80 SyncThread* syncThreadPtr() {
81 AutoLock mutex(mLock);
82 return mSyncThread.get();
83 }
84
destroy()85 void destroy() {
86 AutoLock mutex(mLock);
87 mSyncThread = nullptr;
88 }
89
90 private:
91 std::unique_ptr<SyncThread> mSyncThread = nullptr;
92 // lock for the access to this object
93 android::base::Lock mLock;
94 using AutoLock = android::base::AutoLock;
95 };
96
sGlobalSyncThread()97 static GlobalSyncThread* sGlobalSyncThread() {
98 static GlobalSyncThread* t = new GlobalSyncThread;
99 return t;
100 }
101
102 static const uint32_t kTimelineInterval = 1;
103 static const uint64_t kDefaultTimeoutNsecs = 5ULL * 1000ULL * 1000ULL * 1000ULL;
104
SyncThread(bool hasGl,HealthMonitor<> * healthMonitor)105 SyncThread::SyncThread(bool hasGl, HealthMonitor<>* healthMonitor)
106 : android::base::Thread(android::base::ThreadFlags::MaskSignals, 512 * 1024),
107 mWorkerThreadPool(kNumWorkerThreads,
108 [this](Command&& command, ThreadPool::WorkerId id) {
109 doSyncThreadCmd(std::move(command), id);
110 }),
111 mHasGl(hasGl),
112 mHealthMonitor(healthMonitor) {
113 this->start();
114 mWorkerThreadPool.start();
115 if (hasGl) {
116 initSyncEGLContext();
117 }
118 }
119
~SyncThread()120 SyncThread::~SyncThread() {
121 cleanup();
122 }
123
triggerWait(EmulatedEglFenceSync * fenceSync,uint64_t timeline)124 void SyncThread::triggerWait(EmulatedEglFenceSync* fenceSync,
125 uint64_t timeline) {
126 std::stringstream ss;
127 ss << "triggerWait fenceSyncInfo=0x" << std::hex << reinterpret_cast<uintptr_t>(fenceSync)
128 << " timeline=0x" << std::hex << timeline;
129 sendAsync(
130 [fenceSync, timeline, this](WorkerId) {
131 doSyncWait(fenceSync, [timeline] {
132 DPRINT("wait done (with fence), use goldfish sync timeline inc");
133 emugl::emugl_sync_timeline_inc(timeline, kTimelineInterval);
134 });
135 },
136 ss.str());
137 }
138
triggerWaitVk(VkFence vkFence,uint64_t timeline)139 void SyncThread::triggerWaitVk(VkFence vkFence, uint64_t timeline) {
140 std::stringstream ss;
141 ss << "triggerWaitVk vkFence=0x" << std::hex << reinterpret_cast<uintptr_t>(vkFence)
142 << " timeline=0x" << std::hex << timeline;
143 sendAsync(
144 [vkFence, timeline](WorkerId) {
145 doSyncWaitVk(vkFence, [timeline] {
146 DPRINT("vk wait done, use goldfish sync timeline inc");
147 emugl::emugl_sync_timeline_inc(timeline, kTimelineInterval);
148 });
149 },
150 ss.str());
151 }
152
triggerBlockedWaitNoTimeline(EmulatedEglFenceSync * fenceSync)153 void SyncThread::triggerBlockedWaitNoTimeline(EmulatedEglFenceSync* fenceSync) {
154 std::stringstream ss;
155 ss << "triggerBlockedWaitNoTimeline fenceSyncInfo=0x" << std::hex
156 << reinterpret_cast<uintptr_t>(fenceSync);
157 sendAndWaitForResult(
158 [fenceSync, this](WorkerId) {
159 doSyncWait(fenceSync, std::function<void()>());
160 return 0;
161 },
162 ss.str());
163 }
164
triggerWaitWithCompletionCallback(EmulatedEglFenceSync * fenceSync,FenceCompletionCallback cb)165 void SyncThread::triggerWaitWithCompletionCallback(EmulatedEglFenceSync* fenceSync, FenceCompletionCallback cb) {
166 std::stringstream ss;
167 ss << "triggerWaitWithCompletionCallback fenceSyncInfo=0x" << std::hex
168 << reinterpret_cast<uintptr_t>(fenceSync);
169 sendAsync(
170 [fenceSync, cb = std::move(cb), this](WorkerId) { doSyncWait(fenceSync, std::move(cb)); },
171 ss.str());
172 }
173
174
triggerWaitVkWithCompletionCallback(VkFence vkFence,FenceCompletionCallback cb)175 void SyncThread::triggerWaitVkWithCompletionCallback(VkFence vkFence, FenceCompletionCallback cb) {
176 std::stringstream ss;
177 ss << "triggerWaitVkWithCompletionCallback vkFence=0x" << std::hex
178 << reinterpret_cast<uintptr_t>(vkFence);
179 sendAsync([vkFence, cb = std::move(cb)](WorkerId) { doSyncWaitVk(vkFence, std::move(cb)); },
180 ss.str());
181 }
182
triggerWaitVkQsriWithCompletionCallback(VkImage vkImage,FenceCompletionCallback cb)183 void SyncThread::triggerWaitVkQsriWithCompletionCallback(VkImage vkImage, FenceCompletionCallback cb) {
184 std::stringstream ss;
185 ss << "triggerWaitVkQsriWithCompletionCallback vkImage=0x"
186 << reinterpret_cast<uintptr_t>(vkImage);
187 sendAsync(
188 [vkImage, cb = std::move(cb)](WorkerId) {
189 auto decoder = vk::VkDecoderGlobalState::get();
190 auto res = decoder->registerQsriCallback(vkImage, cb);
191 // If registerQsriCallback does not schedule the callback, we still need to complete
192 // the task, otherwise we may hit deadlocks on tasks on the same ring.
193 if (!res.CallbackScheduledOrFired()) {
194 cb();
195 }
196 },
197 ss.str());
198 }
199
triggerGeneral(FenceCompletionCallback cb,std::string description)200 void SyncThread::triggerGeneral(FenceCompletionCallback cb, std::string description) {
201 std::stringstream ss;
202 ss << "triggerGeneral: " << description;
203 sendAsync(std::bind(std::move(cb)), ss.str());
204 }
205
cleanup()206 void SyncThread::cleanup() {
207 sendAndWaitForResult(
208 [this](WorkerId workerId) {
209 if (mHasGl) {
210 const EGLDispatch* egl = gl::LazyLoadedEGLDispatch::get();
211
212 egl->eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
213
214 egl->eglDestroyContext(mDisplay, mContext[workerId]);
215 egl->eglDestroySurface(mDisplay, mSurface[workerId]);
216 mContext[workerId] = EGL_NO_CONTEXT;
217 mSurface[workerId] = EGL_NO_SURFACE;
218 }
219 return 0;
220 },
221 "cleanup");
222 DPRINT("signal");
223 mLock.lock();
224 mExiting = true;
225 mCv.signalAndUnlock(&mLock);
226 DPRINT("exit");
227 // Wait for the control thread to exit. We can't destroy the SyncThread
228 // before we wait the control thread.
229 if (!wait(nullptr)) {
230 ERR("Fail to wait the control thread of the SyncThread to exit.");
231 }
232 }
233
234 // Private methods below////////////////////////////////////////////////////////
235
main()236 intptr_t SyncThread::main() {
237 DPRINT("in sync thread");
238 mLock.lock();
239 mCv.wait(&mLock, [this] { return mExiting; });
240
241 mWorkerThreadPool.done();
242 mWorkerThreadPool.join();
243 DPRINT("exited sync thread");
244 return 0;
245 }
246
sendAndWaitForResult(std::function<int (WorkerId)> job,std::string description)247 int SyncThread::sendAndWaitForResult(std::function<int(WorkerId)> job, std::string description) {
248 DPRINT("sendAndWaitForResult task(%s)", description.c_str());
249 std::packaged_task<int(WorkerId)> task(std::move(job));
250 std::future<int> resFuture = task.get_future();
251 Command command = {
252 .mTask = std::move(task),
253 .mDescription = std::move(description),
254 };
255
256 mWorkerThreadPool.enqueue(std::move(command));
257 auto res = resFuture.get();
258 DPRINT("exit");
259 return res;
260 }
261
sendAsync(std::function<void (WorkerId)> job,std::string description)262 void SyncThread::sendAsync(std::function<void(WorkerId)> job, std::string description) {
263 DPRINT("send task(%s)", description.c_str());
264 mWorkerThreadPool.enqueue(Command{
265 .mTask =
266 std::packaged_task<int(WorkerId)>([job = std::move(job)](WorkerId workerId) mutable {
267 job(workerId);
268 return 0;
269 }),
270 .mDescription = std::move(description),
271 });
272 DPRINT("exit");
273 }
274
doSyncThreadCmd(Command && command,WorkerId workerId)275 void SyncThread::doSyncThreadCmd(Command&& command, WorkerId workerId) {
276 std::unique_ptr<std::unordered_map<std::string, std::string>> syncThreadData =
277 std::make_unique<std::unordered_map<std::string, std::string>>();
278 syncThreadData->insert({{"syncthread_cmd_desc", command.mDescription}});
279 auto watchdog = WATCHDOG_BUILDER(mHealthMonitor, "SyncThread task execution")
280 .setHangType(EventHangMetadata::HangType::kSyncThread)
281 .setAnnotations(std::move(syncThreadData))
282 .build();
283 command.mTask(workerId);
284 }
285
initSyncEGLContext()286 void SyncThread::initSyncEGLContext() {
287 mWorkerThreadPool.broadcast([this] {
288 return Command{
289 .mTask = std::packaged_task<int(WorkerId)>([this](WorkerId workerId) {
290 DPRINT("for worker id: %d", workerId);
291 // We shouldn't initialize EGL context, when SyncThread is initialized
292 // without GL enabled.
293 SYNC_THREAD_CHECK(mHasGl);
294
295 const EGLDispatch* egl = gl::LazyLoadedEGLDispatch::get();
296
297 mDisplay = egl->eglGetDisplay(EGL_DEFAULT_DISPLAY);
298 int eglMaj, eglMin;
299 egl->eglInitialize(mDisplay, &eglMaj, &eglMin);
300
301 const EGLint configAttribs[] = {
302 EGL_SURFACE_TYPE,
303 EGL_PBUFFER_BIT,
304 EGL_RENDERABLE_TYPE,
305 EGL_OPENGL_ES2_BIT,
306 EGL_RED_SIZE,
307 8,
308 EGL_GREEN_SIZE,
309 8,
310 EGL_BLUE_SIZE,
311 8,
312 EGL_NONE,
313 };
314
315 EGLint nConfigs;
316 EGLConfig config;
317
318 egl->eglChooseConfig(mDisplay, configAttribs, &config, 1, &nConfigs);
319
320 const EGLint pbufferAttribs[] = {
321 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE,
322 };
323
324 mSurface[workerId] = egl->eglCreatePbufferSurface(mDisplay, config, pbufferAttribs);
325
326 const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
327 mContext[workerId] =
328 egl->eglCreateContext(mDisplay, config, EGL_NO_CONTEXT, contextAttribs);
329
330 egl->eglMakeCurrent(mDisplay, mSurface[workerId], mSurface[workerId],
331 mContext[workerId]);
332 return 0;
333 }),
334 .mDescription = "init sync EGL context",
335 };
336 });
337 mWorkerThreadPool.waitAllItems();
338 }
339
doSyncWait(EmulatedEglFenceSync * fenceSync,std::function<void ()> onComplete)340 void SyncThread::doSyncWait(EmulatedEglFenceSync* fenceSync, std::function<void()> onComplete) {
341 DPRINT("enter");
342
343 if (!EmulatedEglFenceSync::getFromHandle((uint64_t)(uintptr_t)fenceSync)) {
344 if (onComplete) {
345 onComplete();
346 }
347 return;
348 }
349 // We shouldn't use EmulatedEglFenceSync to wait, when SyncThread is initialized
350 // without GL enabled, because EmulatedEglFenceSync uses EGL/GLES.
351 SYNC_THREAD_CHECK(mHasGl);
352
353 EGLint wait_result = 0x0;
354
355 DPRINT("wait on sync obj: %p", fenceSync);
356 wait_result = fenceSync->wait(kDefaultTimeoutNsecs);
357
358 DPRINT("done waiting, with wait result=0x%x. "
359 "increment timeline (and signal fence)",
360 wait_result);
361
362 if (wait_result != EGL_CONDITION_SATISFIED_KHR) {
363 EGLint error = gl::s_egl.eglGetError();
364 DPRINT("error: eglClientWaitSync abnormal exit 0x%x. sync handle 0x%llx. egl error = %#x\n",
365 wait_result, (unsigned long long)fenceSync, error);
366 (void)error;
367 }
368
369 DPRINT("issue timeline increment");
370
371 // We always unconditionally increment timeline at this point, even
372 // if the call to eglClientWaitSync returned abnormally.
373 // There are three cases to consider:
374 // - EGL_CONDITION_SATISFIED_KHR: either the sync object is already
375 // signaled and we need to increment this timeline immediately, or
376 // we have waited until the object is signaled, and then
377 // we increment the timeline.
378 // - EGL_TIMEOUT_EXPIRED_KHR: the fence command we put in earlier
379 // in the OpenGL stream is not actually ever signaled, and we
380 // end up blocking in the above eglClientWaitSyncKHR call until
381 // our timeout runs out. In this case, provided we have waited
382 // for |kDefaultTimeoutNsecs|, the guest will have received all
383 // relevant error messages about fence fd's not being signaled
384 // in time, so we are properly emulating bad behavior even if
385 // we now increment the timeline.
386 // - EGL_FALSE (error): chances are, the underlying EGL implementation
387 // on the host doesn't actually support fence objects. In this case,
388 // we should fail safe: 1) It must be only very old or faulty
389 // graphics drivers / GPU's that don't support fence objects.
390 // 2) The consequences of signaling too early are generally, out of
391 // order frames and scrambled textures in some apps. But, not
392 // incrementing the timeline means that the app's rendering freezes.
393 // So, despite the faulty GPU driver, not incrementing is too heavyweight a response.
394
395 if (onComplete) {
396 onComplete();
397 }
398 EmulatedEglFenceSync::incrementTimelineAndDeleteOldFences();
399
400 DPRINT("done timeline increment");
401
402 DPRINT("exit");
403 }
404
doSyncWaitVk(VkFence vkFence,std::function<void ()> onComplete)405 int SyncThread::doSyncWaitVk(VkFence vkFence, std::function<void()> onComplete) {
406 DPRINT("enter");
407
408 auto decoder = vk::VkDecoderGlobalState::get();
409 auto result = decoder->waitForFence(vkFence, kDefaultTimeoutNsecs);
410 if (result == VK_TIMEOUT) {
411 DPRINT("SYNC_WAIT_VK timeout: vkFence=%p", vkFence);
412 } else if (result != VK_SUCCESS) {
413 DPRINT("SYNC_WAIT_VK error: %d vkFence=%p", result, vkFence);
414 }
415
416 DPRINT("issue timeline increment");
417
418 // We always unconditionally increment timeline at this point, even
419 // if the call to vkWaitForFences returned abnormally.
420 // See comments in |doSyncWait| about the rationale.
421 if (onComplete) {
422 onComplete();
423 }
424
425 DPRINT("done timeline increment");
426
427 DPRINT("exit");
428 return result;
429 }
430
431 /* static */
get()432 SyncThread* SyncThread::get() {
433 auto res = sGlobalSyncThread()->syncThreadPtr();
434 SYNC_THREAD_CHECK(res);
435 return res;
436 }
437
initialize(bool hasGl,HealthMonitor<> * healthMonitor)438 void SyncThread::initialize(bool hasGl, HealthMonitor<>* healthMonitor) {
439 sGlobalSyncThread()->initialize(hasGl, healthMonitor);
440 }
441
destroy()442 void SyncThread::destroy() { sGlobalSyncThread()->destroy(); }
443
444 } // namespace gfxstream