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