• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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