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