CmCommandQueue.cpp 5.9 KB

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