BsCommandQueue.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. #include "BsCommandQueue.h"
  2. #include "BsException.h"
  3. #include "BsCoreThread.h"
  4. #include "BsDebug.h"
  5. namespace BansheeEngine
  6. {
  7. #if BS_DEBUG_MODE
  8. CommandQueueBase::CommandQueueBase(BS_THREAD_ID_TYPE threadId)
  9. :mMyThreadId(threadId), mMaxDebugIdx(0)
  10. {
  11. mAsyncOpSyncData = bs_shared_ptr_new<AsyncOpSyncData>();
  12. mCommands = bs_new<BansheeEngine::Queue<QueuedCommand>>();
  13. {
  14. BS_LOCK_MUTEX(CommandQueueBreakpointMutex);
  15. mCommandQueueIdx = MaxCommandQueueIdx++;
  16. }
  17. }
  18. #else
  19. CommandQueueBase::CommandQueueBase(BS_THREAD_ID_TYPE threadId)
  20. :mMyThreadId(threadId)
  21. {
  22. mAsyncOpSyncData = bs_shared_ptr_new<AsyncOpSyncData>();
  23. mCommands = bs_new<BansheeEngine::Queue<QueuedCommand>>();
  24. }
  25. #endif
  26. CommandQueueBase::~CommandQueueBase()
  27. {
  28. if(mCommands != nullptr)
  29. bs_delete(mCommands);
  30. while(!mEmptyCommandQueues.empty())
  31. {
  32. bs_delete(mEmptyCommandQueues.top());
  33. mEmptyCommandQueues.pop();
  34. }
  35. }
  36. AsyncOp CommandQueueBase::queueReturn(std::function<void(AsyncOp&)> commandCallback, bool _notifyWhenComplete, UINT32 _callbackId)
  37. {
  38. #if BS_DEBUG_MODE
  39. breakIfNeeded(mCommandQueueIdx, mMaxDebugIdx);
  40. QueuedCommand newCommand(commandCallback, mMaxDebugIdx++, mAsyncOpSyncData, _notifyWhenComplete, _callbackId);
  41. #else
  42. QueuedCommand newCommand(commandCallback, mAsyncOpSyncData, _notifyWhenComplete, _callbackId);
  43. #endif
  44. mCommands->push(newCommand);
  45. #if BS_FORCE_SINGLETHREADED_RENDERING
  46. Queue<QueuedCommand>* commands = flush();
  47. playback(commands);
  48. #endif
  49. return newCommand.asyncOp;
  50. }
  51. void CommandQueueBase::queue(std::function<void()> commandCallback, bool _notifyWhenComplete, UINT32 _callbackId)
  52. {
  53. #if BS_DEBUG_MODE
  54. breakIfNeeded(mCommandQueueIdx, mMaxDebugIdx);
  55. QueuedCommand newCommand(commandCallback, mMaxDebugIdx++, _notifyWhenComplete, _callbackId);
  56. #else
  57. QueuedCommand newCommand(commandCallback, _notifyWhenComplete, _callbackId);
  58. #endif
  59. mCommands->push(newCommand);
  60. #if BS_FORCE_SINGLETHREADED_RENDERING
  61. Queue<QueuedCommand>* commands = flush();
  62. playback(commands);
  63. #endif
  64. }
  65. BansheeEngine::Queue<QueuedCommand>* CommandQueueBase::flush()
  66. {
  67. BansheeEngine::Queue<QueuedCommand>* oldCommands = mCommands;
  68. if(!mEmptyCommandQueues.empty())
  69. {
  70. mCommands = mEmptyCommandQueues.top();
  71. mEmptyCommandQueues.pop();
  72. }
  73. else
  74. {
  75. mCommands = bs_new<BansheeEngine::Queue<QueuedCommand>>();
  76. }
  77. return oldCommands;
  78. }
  79. void CommandQueueBase::playbackWithNotify(BansheeEngine::Queue<QueuedCommand>* commands, std::function<void(UINT32)> notifyCallback)
  80. {
  81. THROW_IF_NOT_CORE_THREAD;
  82. if(commands == nullptr)
  83. return;
  84. while(!commands->empty())
  85. {
  86. QueuedCommand& command = commands->front();
  87. if(command.returnsValue)
  88. {
  89. AsyncOp& op = command.asyncOp;
  90. command.callbackWithReturnValue(op);
  91. if(!command.asyncOp.hasCompleted())
  92. {
  93. LOGDBG("Async operation return value wasn't resolved properly. Resolving automatically to nullptr. " \
  94. "Make sure to complete the operation before returning from the command callback method.");
  95. command.asyncOp._completeOperation(nullptr);
  96. }
  97. }
  98. else
  99. {
  100. command.callback();
  101. }
  102. if(command.notifyWhenComplete && notifyCallback != nullptr)
  103. {
  104. notifyCallback(command.callbackId);
  105. }
  106. commands->pop();
  107. }
  108. mEmptyCommandQueues.push(commands);
  109. }
  110. void CommandQueueBase::playback(BansheeEngine::Queue<QueuedCommand>* commands)
  111. {
  112. playbackWithNotify(commands, std::function<void(UINT32)>());
  113. }
  114. void CommandQueueBase::cancelAll()
  115. {
  116. BansheeEngine::Queue<QueuedCommand>* commands = flush();
  117. while(!commands->empty())
  118. commands->pop();
  119. mEmptyCommandQueues.push(commands);
  120. }
  121. bool CommandQueueBase::isEmpty()
  122. {
  123. if(mCommands != nullptr && mCommands->size() > 0)
  124. return false;
  125. return true;
  126. }
  127. void CommandQueueBase::throwInvalidThreadException(const String& message) const
  128. {
  129. BS_EXCEPT(InternalErrorException, message);
  130. }
  131. #if BS_DEBUG_MODE
  132. BS_STATIC_MUTEX_CLASS_INSTANCE(CommandQueueBreakpointMutex, CommandQueueBase);
  133. UINT32 CommandQueueBase::MaxCommandQueueIdx = 0;
  134. UnorderedSet<CommandQueueBase::QueueBreakpoint, CommandQueueBase::QueueBreakpoint::HashFunction,
  135. CommandQueueBase::QueueBreakpoint::EqualFunction> CommandQueueBase::SetBreakpoints;
  136. inline size_t CommandQueueBase::QueueBreakpoint::HashFunction::operator()(const QueueBreakpoint& v) const
  137. {
  138. size_t seed = 0;
  139. hash_combine(seed, v.queueIdx);
  140. hash_combine(seed, v.commandIdx);
  141. return seed;
  142. }
  143. inline bool CommandQueueBase::QueueBreakpoint::EqualFunction::operator()(const QueueBreakpoint &a, const QueueBreakpoint &b) const
  144. {
  145. return a.queueIdx == b.queueIdx && a.commandIdx == b.commandIdx;
  146. }
  147. void CommandQueueBase::addBreakpoint(UINT32 queueIdx, UINT32 commandIdx)
  148. {
  149. BS_LOCK_MUTEX(CommandQueueBreakpointMutex);
  150. SetBreakpoints.insert(QueueBreakpoint(queueIdx, commandIdx));
  151. }
  152. void CommandQueueBase::breakIfNeeded(UINT32 queueIdx, UINT32 commandIdx)
  153. {
  154. // I purposely don't use a mutex here, as this gets called very often. Generally breakpoints
  155. // will only be added at the start of the application, so race conditions should not occur.
  156. auto iterFind = SetBreakpoints.find(QueueBreakpoint(queueIdx, commandIdx));
  157. if(iterFind != SetBreakpoints.end())
  158. {
  159. assert(false && "Command queue breakpoint triggered!");
  160. }
  161. }
  162. #else
  163. void CommandQueueBase::addBreakpoint(UINT32 queueIdx, UINT32 commandIdx)
  164. {
  165. // Do nothing, no breakpoints in release
  166. }
  167. #endif
  168. }