• 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  #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