2
0

CommandQueue.h 4.5 KB

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