CmCommandQueue.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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(mCommands != nullptr)
  28. CM_DELETE(mCommands, queue<QueuedCommand>, PoolAlloc);
  29. }
  30. AsyncOp CommandQueueBase::queueReturn(boost::function<void(AsyncOp&)> commandCallback, bool _notifyWhenComplete, UINT32 _callbackId)
  31. {
  32. #if CM_DEBUG_MODE
  33. breakIfNeeded(mCommandQueueIdx, mMaxDebugIdx);
  34. QueuedCommand newCommand(commandCallback, mMaxDebugIdx++, _notifyWhenComplete, _callbackId);
  35. #else
  36. QueuedCommand newCommand(commandCallback, _notifyWhenComplete, _callbackId);
  37. #endif
  38. mCommands->push(newCommand);
  39. #if CM_FORCE_SINGLETHREADED_RENDERING
  40. std::queue<QueuedCommand>* commands = flush();
  41. playback(commands);
  42. #endif
  43. return newCommand.asyncOp;
  44. }
  45. void CommandQueueBase::queue(boost::function<void()> commandCallback, bool _notifyWhenComplete, UINT32 _callbackId)
  46. {
  47. #if CM_DEBUG_MODE
  48. breakIfNeeded(mCommandQueueIdx, mMaxDebugIdx);
  49. QueuedCommand newCommand(commandCallback, mMaxDebugIdx++, _notifyWhenComplete, _callbackId);
  50. #else
  51. QueuedCommand newCommand(commandCallback, _notifyWhenComplete, _callbackId);
  52. #endif
  53. mCommands->push(newCommand);
  54. #if CM_FORCE_SINGLETHREADED_RENDERING
  55. std::queue<QueuedCommand>* commands = flush();
  56. playback(commands);
  57. #endif
  58. }
  59. std::queue<QueuedCommand>* CommandQueueBase::flush()
  60. {
  61. std::queue<QueuedCommand>* oldCommands = mCommands;
  62. mCommands = CM_NEW(std::queue<QueuedCommand>, PoolAlloc) std::queue<QueuedCommand>();
  63. return oldCommands;
  64. }
  65. void CommandQueueBase::playback(std::queue<QueuedCommand>* commands, boost::function<void(UINT32)> notifyCallback)
  66. {
  67. #if CM_DEBUG_MODE
  68. RenderSystem* rs = RenderSystem::instancePtr();
  69. if(rs->getRenderThreadId() != CM_THREAD_CURRENT_ID)
  70. CM_EXCEPT(InternalErrorException, "This method should only be called from the render thread.");
  71. #endif
  72. if(commands == nullptr)
  73. return;
  74. while(!commands->empty())
  75. {
  76. QueuedCommand& command = commands->front();
  77. if(command.returnsValue)
  78. {
  79. command.callbackWithReturnValue(command.asyncOp);
  80. if(!command.asyncOp.hasCompleted())
  81. {
  82. LOGDBG("Async operation return value wasn't resolved properly. Resolving automatically to nullptr. " \
  83. "Make sure to complete the operation before returning from the command callback method.");
  84. command.asyncOp.completeOperation(nullptr);
  85. }
  86. }
  87. else
  88. {
  89. command.callback();
  90. }
  91. if(command.notifyWhenComplete && !notifyCallback.empty())
  92. {
  93. notifyCallback(command.callbackId);
  94. }
  95. commands->pop();
  96. }
  97. CM_DELETE(commands, queue<QueuedCommand>, PoolAlloc);
  98. }
  99. void CommandQueueBase::playback(std::queue<QueuedCommand>* commands)
  100. {
  101. playback(commands, boost::function<void(UINT32)>());
  102. }
  103. void CommandQueueBase::cancelAll()
  104. {
  105. std::queue<QueuedCommand>* commands = flush();
  106. CM_DELETE(commands, queue<QueuedCommand>, PoolAlloc);
  107. }
  108. bool CommandQueueBase::isEmpty()
  109. {
  110. if(mCommands != nullptr && mCommands->size() > 0)
  111. return false;
  112. return true;
  113. }
  114. void CommandQueueBase::throwInvalidThreadException(const String& message) const
  115. {
  116. CM_EXCEPT(InternalErrorException, message);
  117. }
  118. #if CM_DEBUG_MODE
  119. CM_STATIC_MUTEX_CLASS_INSTANCE(CommandQueueBreakpointMutex, CommandQueueBase);
  120. UINT32 CommandQueueBase::MaxCommandQueueIdx = 0;
  121. std::unordered_set<CommandQueueBase::QueueBreakpoint, CommandQueueBase::QueueBreakpoint::HashFunction,
  122. CommandQueueBase::QueueBreakpoint::EqualFunction> CommandQueueBase::SetBreakpoints;
  123. inline size_t CommandQueueBase::QueueBreakpoint::HashFunction::operator()(const QueueBreakpoint& v) const
  124. {
  125. size_t seed = 0;
  126. hash_combine(seed, v.queueIdx);
  127. hash_combine(seed, v.commandIdx);
  128. return seed;
  129. }
  130. inline bool CommandQueueBase::QueueBreakpoint::EqualFunction::operator()(const QueueBreakpoint &a, const QueueBreakpoint &b) const
  131. {
  132. return a.queueIdx == b.queueIdx && a.commandIdx == b.commandIdx;
  133. }
  134. void CommandQueueBase::addBreakpoint(UINT32 queueIdx, UINT32 commandIdx)
  135. {
  136. CM_LOCK_MUTEX(CommandQueueBreakpointMutex);
  137. SetBreakpoints.insert(QueueBreakpoint(queueIdx, commandIdx));
  138. }
  139. void CommandQueueBase::breakIfNeeded(UINT32 queueIdx, UINT32 commandIdx)
  140. {
  141. // I purposely don't use a mutex here, as this gets called very often. Generally breakpoints
  142. // will only be added at the start of the application, so race conditions should not occur.
  143. auto iterFind = SetBreakpoints.find(QueueBreakpoint(queueIdx, commandIdx));
  144. if(iterFind != SetBreakpoints.end())
  145. {
  146. assert(false && "Command queue breakpoint triggered!");
  147. }
  148. }
  149. #else
  150. void CommandQueueBase::addBreakpoint(UINT32 queueIdx, UINT32 commandIdx)
  151. {
  152. // Do nothing, no breakpoints in release
  153. }
  154. #endif
  155. }