BRE12
CommandListExecutor.h
1 #pragma once
2 
3 #include <atomic>
4 #include <d3d12.h>
5 #include <tbb/concurrent_queue.h>
6 #include <tbb/task.h>
7 
8 #include <Utils\DebugUtils.h>
9 
10 namespace BRE {
11 
12 // To check for new command lists and execute them.
13 // Steps:
14 // - Use CommandListExecutor::Create() to create and spawn an instance.
15 // - When you spawn it, execute() method is automatically called. You should fill the queue with
16 // command lists. You can use CommandListExecutor::GetCommandListQueue() to get it.
17 // - When you want to terminate this task, you should call CommandListExecutor::Terminate()
18 class CommandListExecutor : public tbb::task {
19 public:
20  // maxNumberOfCommandListsToExecute is the maximum number of command lists to execute
21  // by ID3D12CommandQueue::ExecuteCommandLists() operation.
22  // Preconditions:
23  // - Create() must be called once
24  // - "maxNumberOfCommandListsToExecute" must be greater than zero
25  static void Create(const std::uint32_t maxNumberOfCommandListsToExecute) noexcept;
26 
27  // Preconditions:
28  // - Create() must be called before this method
29  static CommandListExecutor& CommandListExecutor::Get() noexcept;
30 
31  ~CommandListExecutor() = default;
33  const CommandListExecutor& operator=(const CommandListExecutor&) = delete;
35  CommandListExecutor& operator=(CommandListExecutor&&) = delete;
36 
37  // As I did not discover yet, why it is not thread safe, then I only use it for debugging purposes.
38  __forceinline bool AreTherePendingCommandListsToExecute() const noexcept
39  {
40  return mCommandListsToExecute.empty() && mPendingCommandListCount == 0;
41  }
42 
43  // A thread safe way to know if CommandListExecutor finished processing and executing all the command lists.
44  // If you are going to execute N command lists, then you should:
45  // - Call ResetExecutedCommandListCount()
46  // - Fill queue through GetCommandListQueue()
47  // - Check if GetExecutedCommandListCount() is equal to N, to be sure all was executed properly (sent to GPU)
48  __forceinline void ResetExecutedCommandListCount() noexcept
49  {
50  mExecutedCommandListCount = 0U;
51  }
52  __forceinline std::uint32_t GetExecutedCommandListCount() const noexcept
53  {
54  return mExecutedCommandListCount;
55  }
56 
57  __forceinline void AddCommandList(ID3D12CommandList& commandList) noexcept
58  {
59  mCommandListsToExecute.push(&commandList);
60  }
61 
62  __forceinline ID3D12CommandQueue& GetCommandQueue() noexcept
63  {
64  BRE_ASSERT(mCommandQueue != nullptr);
65  return *mCommandQueue;
66  }
67 
68  void SignalFenceAndWaitForCompletion(ID3D12Fence& fence,
69  const std::uint64_t valueToSignal,
70  const std::uint64_t valueToWaitFor) noexcept;
71 
72  void ExecuteCommandListAndWaitForCompletion(ID3D12CommandList& cmdList) noexcept;
73 
74  void Terminate() noexcept;
75 
76 private:
77  explicit CommandListExecutor(const std::uint32_t maxNumCmdLists);
78 
79  // Called when tbb::task is spawned
80  tbb::task* execute() final override;
81 
82  static CommandListExecutor* sExecutor;
83 
84  bool mTerminate{ false };
85 
86  std::uint32_t mExecutedCommandListCount{ 0U };
87  std::atomic<std::uint32_t> mPendingCommandListCount{ 0U };
88  std::uint32_t mMaxNumberOfCommandListsToExecute{ 1U };
89 
90  ID3D12CommandQueue* mCommandQueue{ nullptr };
91  tbb::concurrent_queue<ID3D12CommandList*> mCommandListsToExecute;
92  ID3D12Fence* mFence{ nullptr };
93 };
94 
95 }
Definition: Camera.cpp:8
A high-performance thread-safe non-blocking concurrent queue.
Definition: concurrent_queue.h:35
Definition: CommandListExecutor.h:18