CommandQueue.h 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Renderer/FatalErrorIfFailed.h>
  5. /// Holds a number of DirectX operations with logic to wait for completion
  6. class CommandQueue
  7. {
  8. public:
  9. /// Destructor
  10. ~CommandQueue()
  11. {
  12. WaitUntilFinished();
  13. if (mFenceEvent != INVALID_HANDLE_VALUE)
  14. CloseHandle(mFenceEvent);
  15. }
  16. /// Initialize the queue
  17. void Initialize(ID3D12Device *inDevice)
  18. {
  19. D3D12_COMMAND_QUEUE_DESC queue_desc = {};
  20. queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
  21. queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
  22. FatalErrorIfFailed(inDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue)));
  23. FatalErrorIfFailed(inDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator)));
  24. // Create the command list
  25. FatalErrorIfFailed(inDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&mCommandList)));
  26. // Command lists are created in the recording state, but there is nothing to record yet. The main loop expects it to be closed, so close it now
  27. FatalErrorIfFailed(mCommandList->Close());
  28. // Create synchronization object
  29. FatalErrorIfFailed(inDevice->CreateFence(mFenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));
  30. // Increment fence value so we don't skip waiting the first time a command list is executed
  31. mFenceValue++;
  32. // Create an event handle to use for frame synchronization
  33. mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
  34. if (mFenceEvent == nullptr)
  35. FatalErrorIfFailed(HRESULT_FROM_WIN32(GetLastError()));
  36. }
  37. /// Start the command list (requires waiting until the previous one is finished)
  38. ID3D12GraphicsCommandList * Start()
  39. {
  40. // Reset the allocator
  41. FatalErrorIfFailed(mCommandAllocator->Reset());
  42. // Reset the command list
  43. FatalErrorIfFailed(mCommandList->Reset(mCommandAllocator.Get(), nullptr));
  44. return mCommandList.Get();
  45. }
  46. /// Execute accumulated command list
  47. void Execute()
  48. {
  49. JPH_ASSERT(!mIsExecuting);
  50. // Close the command list
  51. FatalErrorIfFailed(mCommandList->Close());
  52. // Execute the command list
  53. ID3D12CommandList* ppCommandLists[] = { mCommandList.Get() };
  54. mCommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
  55. // Schedule a Signal command in the queue
  56. FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), mFenceValue));
  57. // Mark that we're executing
  58. mIsExecuting = true;
  59. }
  60. /// After executing, this waits until execution is done
  61. void WaitUntilFinished()
  62. {
  63. // Check if we've been started
  64. if (mIsExecuting)
  65. {
  66. if (mFence->GetCompletedValue() < mFenceValue)
  67. {
  68. // Wait until the fence has been processed
  69. FatalErrorIfFailed(mFence->SetEventOnCompletion(mFenceValue, mFenceEvent));
  70. WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
  71. }
  72. // Increment the fence value
  73. mFenceValue++;
  74. // Done executing
  75. mIsExecuting = false;
  76. }
  77. }
  78. /// Execute and wait for the command list to finish
  79. void ExecuteAndWait()
  80. {
  81. Execute();
  82. WaitUntilFinished();
  83. }
  84. private:
  85. ComPtr<ID3D12CommandQueue> mCommandQueue; ///< The command queue that will hold command lists
  86. ComPtr<ID3D12CommandAllocator> mCommandAllocator; ///< Allocator that holds the memory for the commands
  87. ComPtr<ID3D12GraphicsCommandList> mCommandList; ///< The command list that will hold the render commands / state changes
  88. HANDLE mFenceEvent = INVALID_HANDLE_VALUE; ///< Fence event, used to wait for rendering to complete
  89. ComPtr<ID3D12Fence> mFence; ///< Fence object, used to signal the fence event
  90. UINT64 mFenceValue = 0; ///< Current fence value, each time we need to wait we will signal the fence with this value, wait for it and then increase the value
  91. bool mIsExecuting = false; ///< If a commandlist is currently executing on the queue
  92. };