118 lines
4.0 KiB
C
118 lines
4.0 KiB
C
|
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||
|
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||
|
|
// SPDX-License-Identifier: MIT
|
||
|
|
|
||
|
|
#pragma once
|
||
|
|
|
||
|
|
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||
|
|
|
||
|
|
/// Holds a number of DirectX operations with logic to wait for completion
|
||
|
|
class CommandQueueDX12
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
/// Destructor
|
||
|
|
~CommandQueueDX12()
|
||
|
|
{
|
||
|
|
WaitUntilFinished();
|
||
|
|
|
||
|
|
if (mFenceEvent != INVALID_HANDLE_VALUE)
|
||
|
|
CloseHandle(mFenceEvent);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Initialize the queue
|
||
|
|
void Initialize(ID3D12Device *inDevice)
|
||
|
|
{
|
||
|
|
D3D12_COMMAND_QUEUE_DESC queue_desc = {};
|
||
|
|
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||
|
|
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||
|
|
FatalErrorIfFailed(inDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue)));
|
||
|
|
|
||
|
|
FatalErrorIfFailed(inDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator)));
|
||
|
|
|
||
|
|
// Create the command list
|
||
|
|
FatalErrorIfFailed(inDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&mCommandList)));
|
||
|
|
|
||
|
|
// 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
|
||
|
|
FatalErrorIfFailed(mCommandList->Close());
|
||
|
|
|
||
|
|
// Create synchronization object
|
||
|
|
FatalErrorIfFailed(inDevice->CreateFence(mFenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));
|
||
|
|
|
||
|
|
// Increment fence value so we don't skip waiting the first time a command list is executed
|
||
|
|
mFenceValue++;
|
||
|
|
|
||
|
|
// Create an event handle to use for frame synchronization
|
||
|
|
mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||
|
|
if (mFenceEvent == nullptr)
|
||
|
|
FatalErrorIfFailed(HRESULT_FROM_WIN32(GetLastError()));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Start the command list (requires waiting until the previous one is finished)
|
||
|
|
ID3D12GraphicsCommandList * Start()
|
||
|
|
{
|
||
|
|
// Reset the allocator
|
||
|
|
FatalErrorIfFailed(mCommandAllocator->Reset());
|
||
|
|
|
||
|
|
// Reset the command list
|
||
|
|
FatalErrorIfFailed(mCommandList->Reset(mCommandAllocator.Get(), nullptr));
|
||
|
|
|
||
|
|
return mCommandList.Get();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Execute accumulated command list
|
||
|
|
void Execute()
|
||
|
|
{
|
||
|
|
JPH_ASSERT(!mIsExecuting);
|
||
|
|
|
||
|
|
// Close the command list
|
||
|
|
FatalErrorIfFailed(mCommandList->Close());
|
||
|
|
|
||
|
|
// Execute the command list
|
||
|
|
ID3D12CommandList* ppCommandLists[] = { mCommandList.Get() };
|
||
|
|
mCommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
|
||
|
|
|
||
|
|
// Schedule a Signal command in the queue
|
||
|
|
FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), mFenceValue));
|
||
|
|
|
||
|
|
// Mark that we're executing
|
||
|
|
mIsExecuting = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// After executing, this waits until execution is done
|
||
|
|
void WaitUntilFinished()
|
||
|
|
{
|
||
|
|
// Check if we've been started
|
||
|
|
if (mIsExecuting)
|
||
|
|
{
|
||
|
|
if (mFence->GetCompletedValue() < mFenceValue)
|
||
|
|
{
|
||
|
|
// Wait until the fence has been processed
|
||
|
|
FatalErrorIfFailed(mFence->SetEventOnCompletion(mFenceValue, mFenceEvent));
|
||
|
|
WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Increment the fence value
|
||
|
|
mFenceValue++;
|
||
|
|
|
||
|
|
// Done executing
|
||
|
|
mIsExecuting = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Execute and wait for the command list to finish
|
||
|
|
void ExecuteAndWait()
|
||
|
|
{
|
||
|
|
Execute();
|
||
|
|
WaitUntilFinished();
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
ComPtr<ID3D12CommandQueue> mCommandQueue; ///< The command queue that will hold command lists
|
||
|
|
ComPtr<ID3D12CommandAllocator> mCommandAllocator; ///< Allocator that holds the memory for the commands
|
||
|
|
ComPtr<ID3D12GraphicsCommandList> mCommandList; ///< The command list that will hold the render commands / state changes
|
||
|
|
HANDLE mFenceEvent = INVALID_HANDLE_VALUE; ///< Fence event, used to wait for rendering to complete
|
||
|
|
ComPtr<ID3D12Fence> mFence; ///< Fence object, used to signal the fence event
|
||
|
|
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
|
||
|
|
bool mIsExecuting = false; ///< If a commandlist is currently executing on the queue
|
||
|
|
};
|