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