2
0

BsCoreThread.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. //__________________________ Banshee Project - A modern game development toolkit _________________________________//
  2. //_____________________________________ www.banshee-project.com __________________________________________________//
  3. //________________________ Copyright (c) 2014 Marko Pintera. All rights reserved. ________________________________//
  4. #include "BsCoreThread.h"
  5. #include "BsThreadPool.h"
  6. #include "BsTaskScheduler.h"
  7. #include "BsFrameAlloc.h"
  8. using namespace std::placeholders;
  9. namespace BansheeEngine
  10. {
  11. BS_THREADLOCAL CoreThread::AccessorContainer* CoreThread::mAccessor = nullptr;
  12. CoreThread::CoreThread()
  13. : mCoreThreadShutdown(false)
  14. , mCommandQueue(nullptr)
  15. , mMaxCommandNotifyId(0)
  16. , mSyncedCoreAccessor(nullptr)
  17. , mActiveFrameAlloc(0)
  18. {
  19. mFrameAllocs[0] = bs_new<FrameAlloc>();
  20. mFrameAllocs[1] = bs_new<FrameAlloc>();
  21. mCoreThreadId = BS_THREAD_CURRENT_ID;
  22. mCommandQueue = bs_new<CommandQueue<CommandQueueSync>>(BS_THREAD_CURRENT_ID);
  23. initCoreThread();
  24. }
  25. CoreThread::~CoreThread()
  26. {
  27. // TODO - What if something gets queued between the queued call to destroy_internal and this!?
  28. shutdownCoreThread();
  29. {
  30. BS_LOCK_MUTEX(mAccessorMutex);
  31. for(auto& accessor : mAccessors)
  32. {
  33. bs_delete(accessor);
  34. }
  35. mAccessors.clear();
  36. }
  37. if(mCommandQueue != nullptr)
  38. {
  39. bs_delete(mCommandQueue);
  40. mCommandQueue = nullptr;
  41. }
  42. bs_delete(mFrameAllocs[0]);
  43. bs_delete(mFrameAllocs[1]);
  44. }
  45. void CoreThread::initCoreThread()
  46. {
  47. #if !BS_FORCE_SINGLETHREADED_RENDERING
  48. #if BS_THREAD_SUPPORT
  49. mCoreThread = ThreadPool::instance().run("Core", std::bind(&CoreThread::runCoreThread, this));
  50. #else
  51. BS_EXCEPT(InternalErrorException, "Attempting to start a core thread but application isn't compiled with thread support.");
  52. #endif
  53. #endif
  54. }
  55. void CoreThread::runCoreThread()
  56. {
  57. #if !BS_FORCE_SINGLETHREADED_RENDERING
  58. TaskScheduler::instance().removeWorker(); // One less worker because we are reserving one core for this thread
  59. mCoreThreadId = BS_THREAD_CURRENT_ID;
  60. mSyncedCoreAccessor = bs_new<CoreThreadAccessor<CommandQueueSync>>(BS_THREAD_CURRENT_ID);
  61. while(true)
  62. {
  63. // Wait until we get some ready commands
  64. Queue<QueuedCommand>* commands = nullptr;
  65. {
  66. BS_LOCK_MUTEX_NAMED(mCommandQueueMutex, lock)
  67. while(mCommandQueue->isEmpty())
  68. {
  69. if(mCoreThreadShutdown)
  70. {
  71. bs_delete(mSyncedCoreAccessor);
  72. TaskScheduler::instance().addWorker();
  73. return;
  74. }
  75. TaskScheduler::instance().addWorker(); // Do something else while we wait, otherwise this core will be unused
  76. BS_THREAD_WAIT(mCommandReadyCondition, mCommandQueueMutex, lock);
  77. TaskScheduler::instance().removeWorker();
  78. }
  79. commands = mCommandQueue->flush();
  80. }
  81. // Play commands
  82. mCommandQueue->playbackWithNotify(commands, std::bind(&CoreThread::commandCompletedNotify, this, _1));
  83. }
  84. #endif
  85. }
  86. void CoreThread::shutdownCoreThread()
  87. {
  88. #if !BS_FORCE_SINGLETHREADED_RENDERING
  89. {
  90. BS_LOCK_MUTEX(mCommandQueueMutex);
  91. mCoreThreadShutdown = true;
  92. }
  93. // Wake all threads. They will quit after they see the shutdown flag
  94. BS_THREAD_NOTIFY_ALL(mCommandReadyCondition);
  95. mCoreThreadId = BS_THREAD_CURRENT_ID;
  96. mCoreThread.blockUntilComplete();
  97. #endif
  98. }
  99. CoreAccessorPtr CoreThread::getAccessor()
  100. {
  101. if(mAccessor == nullptr)
  102. {
  103. CoreAccessorPtr newAccessor = bs_shared_ptr<CoreThreadAccessor<CommandQueueNoSync>>(BS_THREAD_CURRENT_ID);
  104. mAccessor = bs_new<AccessorContainer>();
  105. mAccessor->accessor = newAccessor;
  106. BS_LOCK_MUTEX(mAccessorMutex);
  107. mAccessors.push_back(mAccessor);
  108. }
  109. return mAccessor->accessor;
  110. }
  111. SyncedCoreAccessor& CoreThread::getSyncedAccessor()
  112. {
  113. return *mSyncedCoreAccessor;
  114. }
  115. void CoreThread::submitAccessors(bool blockUntilComplete)
  116. {
  117. Vector<AccessorContainer*> accessorCopies;
  118. {
  119. BS_LOCK_MUTEX(mAccessorMutex);
  120. accessorCopies = mAccessors;
  121. }
  122. for(auto& accessor : accessorCopies)
  123. accessor->accessor->submitToCoreThread(blockUntilComplete);
  124. mSyncedCoreAccessor->submitToCoreThread(blockUntilComplete);
  125. }
  126. AsyncOp CoreThread::queueReturnCommand(std::function<void(AsyncOp&)> commandCallback, bool blockUntilComplete)
  127. {
  128. AsyncOp op;
  129. if(BS_THREAD_CURRENT_ID == getCoreThreadId())
  130. {
  131. commandCallback(op); // Execute immediately
  132. return op;
  133. }
  134. UINT32 commandId = -1;
  135. {
  136. BS_LOCK_MUTEX(mCommandQueueMutex);
  137. if(blockUntilComplete)
  138. {
  139. commandId = mMaxCommandNotifyId++;
  140. op = mCommandQueue->queueReturn(commandCallback, true, commandId);
  141. }
  142. else
  143. op = mCommandQueue->queueReturn(commandCallback);
  144. }
  145. BS_THREAD_NOTIFY_ALL(mCommandReadyCondition);
  146. if(blockUntilComplete)
  147. blockUntilCommandCompleted(commandId);
  148. return op;
  149. }
  150. void CoreThread::queueCommand(std::function<void()> commandCallback, bool blockUntilComplete)
  151. {
  152. if(BS_THREAD_CURRENT_ID == getCoreThreadId())
  153. {
  154. commandCallback(); // Execute immediately
  155. return;
  156. }
  157. UINT32 commandId = -1;
  158. {
  159. BS_LOCK_MUTEX(mCommandQueueMutex);
  160. if(blockUntilComplete)
  161. {
  162. commandId = mMaxCommandNotifyId++;
  163. mCommandQueue->queue(commandCallback, true, commandId);
  164. }
  165. else
  166. mCommandQueue->queue(commandCallback);
  167. }
  168. BS_THREAD_NOTIFY_ALL(mCommandReadyCondition);
  169. if(blockUntilComplete)
  170. blockUntilCommandCompleted(commandId);
  171. }
  172. void CoreThread::update()
  173. {
  174. mActiveFrameAlloc = (mActiveFrameAlloc + 1) % 2;
  175. mFrameAllocs[mActiveFrameAlloc]->clear();
  176. }
  177. FrameAlloc* CoreThread::getFrameAlloc() const
  178. {
  179. return mFrameAllocs[mActiveFrameAlloc];
  180. }
  181. void CoreThread::blockUntilCommandCompleted(UINT32 commandId)
  182. {
  183. #if !BS_FORCE_SINGLETHREADED_RENDERING
  184. BS_LOCK_MUTEX_NAMED(mCommandNotifyMutex, lock);
  185. while(true)
  186. {
  187. // TODO - This might be causing a deadlock in Release mode. I'm thinking because mCommandsCompleted isn't marked as volatile.
  188. // Check if our command id is in the completed list
  189. auto iter = mCommandsCompleted.begin();
  190. for(; iter != mCommandsCompleted.end(); ++iter)
  191. {
  192. if(*iter == commandId)
  193. break;
  194. }
  195. if(iter != mCommandsCompleted.end())
  196. {
  197. mCommandsCompleted.erase(iter);
  198. break;
  199. }
  200. BS_THREAD_WAIT(mCommandCompleteCondition, mCommandNotifyMutex, lock);
  201. }
  202. #endif
  203. }
  204. void CoreThread::commandCompletedNotify(UINT32 commandId)
  205. {
  206. {
  207. BS_LOCK_MUTEX(mCommandNotifyMutex);
  208. mCommandsCompleted.push_back(commandId);
  209. }
  210. BS_THREAD_NOTIFY_ALL(mCommandCompleteCondition);
  211. }
  212. CoreThread& gCoreThread()
  213. {
  214. return CoreThread::instance();
  215. }
  216. CoreThreadAccessor<CommandQueueNoSync>& gCoreAccessor()
  217. {
  218. return *CoreThread::instance().getAccessor();
  219. }
  220. void throwIfNotCoreThread()
  221. {
  222. #if !BS_FORCE_SINGLETHREADED_RENDERING
  223. if(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId())
  224. BS_EXCEPT(InternalErrorException, "This method can only be accessed from the core thread.");
  225. #endif
  226. }
  227. void throwIfCoreThread()
  228. {
  229. #if !BS_FORCE_SINGLETHREADED_RENDERING
  230. if(BS_THREAD_CURRENT_ID == CoreThread::instance().getCoreThreadId())
  231. BS_EXCEPT(InternalErrorException, "This method cannot be accessed from the core thread.");
  232. #endif
  233. }
  234. }