CmCommandQueue.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. #include "CmCommandQueue.h"
  2. #include "CmException.h"
  3. #include "CmRenderSystem.h"
  4. #include "CmDebug.h"
  5. #include "CmUtil.h"
  6. namespace CamelotFramework
  7. {
  8. #if CM_DEBUG_MODE
  9. CommandQueueBase::CommandQueueBase(CM_THREAD_ID_TYPE threadId, bool allowAllThreads)
  10. :mMyThreadId(threadId), mAllowAllThreads(allowAllThreads), mMaxDebugIdx(0)
  11. {
  12. mCommands = CM_NEW(std::queue<QueuedCommand>, PoolAlloc) std::queue<QueuedCommand>();
  13. {
  14. CM_LOCK_MUTEX(CommandQueueBreakpointMutex);
  15. mCommandQueueIdx = MaxCommandQueueIdx++;
  16. }
  17. }
  18. #else
  19. CommandQueueBase::CommandQueueBase(CM_THREAD_ID_TYPE threadId, bool allowAllThreads)
  20. :mMyThreadId(threadId), mAllowAllThreads(allowAllThreads)
  21. {
  22. mCommands = CM_NEW(std::queue<QueuedCommand>, PoolAlloc) std::queue<QueuedCommand>();
  23. }
  24. #endif
  25. CommandQueueBase::~CommandQueueBase()
  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, queue<QueuedCommand>, PoolAlloc);
  37. }
  38. AsyncOp CommandQueueBase::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. QueuedCommand newCommand(commandCallback, mMaxDebugIdx++, _notifyWhenComplete, _callbackId);
  51. #else
  52. QueuedCommand newCommand(commandCallback, _notifyWhenComplete, _callbackId);
  53. #endif
  54. mCommands->push(newCommand);
  55. #if CM_FORCE_SINGLETHREADED_RENDERING
  56. std::queue<QueuedCommand>* commands = flush();
  57. playback(commands);
  58. #endif
  59. return newCommand.asyncOp;
  60. }
  61. void CommandQueueBase::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. QueuedCommand newCommand(commandCallback, mMaxDebugIdx++, _notifyWhenComplete, _callbackId);
  74. #else
  75. QueuedCommand newCommand(commandCallback, _notifyWhenComplete, _callbackId);
  76. #endif
  77. mCommands->push(newCommand);
  78. #if CM_FORCE_SINGLETHREADED_RENDERING
  79. std::queue<QueuedCommand>* commands = flush();
  80. playback(commands);
  81. #endif
  82. }
  83. std::queue<QueuedCommand>* CommandQueueBase::flush()
  84. {
  85. std::queue<QueuedCommand>* oldCommands = mCommands;
  86. mCommands = CM_NEW(std::queue<QueuedCommand>, PoolAlloc) std::queue<QueuedCommand>();
  87. return oldCommands;
  88. }
  89. void CommandQueueBase::playback(std::queue<QueuedCommand>* commands, boost::function<void(UINT32)> notifyCallback)
  90. {
  91. #if CM_DEBUG_MODE
  92. RenderSystem* rs = RenderSystem::instancePtr();
  93. if(rs->getRenderThreadId() != CM_THREAD_CURRENT_ID)
  94. CM_EXCEPT(InternalErrorException, "This method should only be called from the render thread.");
  95. #endif
  96. if(commands == nullptr)
  97. return;
  98. while(!commands->empty())
  99. {
  100. QueuedCommand& command = commands->front();
  101. if(command.returnsValue)
  102. {
  103. command.callbackWithReturnValue(command.asyncOp);
  104. if(!command.asyncOp.hasCompleted())
  105. {
  106. LOGDBG("Async operation return value wasn't resolved properly. Resolving automatically to nullptr. " \
  107. "Make sure to complete the operation before returning from the command callback method.");
  108. command.asyncOp.completeOperation(nullptr);
  109. }
  110. }
  111. else
  112. {
  113. command.callback();
  114. }
  115. if(command.notifyWhenComplete && !notifyCallback.empty())
  116. {
  117. notifyCallback(command.callbackId);
  118. }
  119. commands->pop();
  120. }
  121. CM_DELETE(commands, queue<QueuedCommand>, PoolAlloc);
  122. }
  123. void CommandQueueBase::playback(std::queue<QueuedCommand>* commands)
  124. {
  125. playback(commands, boost::function<void(UINT32)>());
  126. }
  127. void CommandQueueBase::cancelAll()
  128. {
  129. std::queue<QueuedCommand>* commands = flush();
  130. CM_DELETE(commands, queue<QueuedCommand>, PoolAlloc);
  131. }
  132. bool CommandQueueBase::isEmpty()
  133. {
  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, CommandQueueBase);
  140. UINT32 CommandQueueBase::MaxCommandQueueIdx = 0;
  141. std::unordered_set<CommandQueueBase::QueueBreakpoint, CommandQueueBase::QueueBreakpoint::HashFunction,
  142. CommandQueueBase::QueueBreakpoint::EqualFunction> CommandQueueBase::SetBreakpoints;
  143. inline size_t CommandQueueBase::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 CommandQueueBase::QueueBreakpoint::EqualFunction::operator()(const QueueBreakpoint &a, const QueueBreakpoint &b) const
  151. {
  152. return a.queueIdx == b.queueIdx && a.commandIdx == b.commandIdx;
  153. }
  154. void CommandQueueBase::addBreakpoint(UINT32 queueIdx, UINT32 commandIdx)
  155. {
  156. CM_LOCK_MUTEX(CommandQueueBreakpointMutex);
  157. SetBreakpoints.insert(QueueBreakpoint(queueIdx, commandIdx));
  158. }
  159. void CommandQueueBase::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 CommandQueueBase::addBreakpoint(UINT32 queueIdx, UINT32 commandIdx)
  171. {
  172. // Do nothing, no breakpoints in release
  173. }
  174. #endif
  175. }