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