| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
- // SPDX-FileCopyrightText: 2025 Jorrit Rouwe
- // SPDX-License-Identifier: MIT
- #include <Jolt/Jolt.h>
- #ifdef JPH_USE_DX12
- #include <Jolt/Compute/DX12/ComputeQueueDX12.h>
- #include <Jolt/Compute/DX12/ComputeShaderDX12.h>
- #include <Jolt/Compute/DX12/ComputeBufferDX12.h>
- JPH_NAMESPACE_BEGIN
- ComputeQueueDX12::~ComputeQueueDX12()
- {
- Wait();
- if (mFenceEvent != INVALID_HANDLE_VALUE)
- CloseHandle(mFenceEvent);
- }
- bool ComputeQueueDX12::Initialize(ID3D12Device *inDevice, D3D12_COMMAND_LIST_TYPE inType)
- {
- D3D12_COMMAND_QUEUE_DESC queue_desc = {};
- queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
- queue_desc.Type = inType;
- queue_desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
- if (HRFailed(inDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue))))
- return false;
- if (HRFailed(inDevice->CreateCommandAllocator(inType, IID_PPV_ARGS(&mCommandAllocator))))
- return false;
- // Create the command list
- if (HRFailed(inDevice->CreateCommandList(0, inType, mCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&mCommandList))))
- return false;
- // 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
- if (HRFailed(mCommandList->Close()))
- return false;
- // Create synchronization object
- if (HRFailed(inDevice->CreateFence(mFenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence))))
- return false;
- // 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 (HRFailed(HRESULT_FROM_WIN32(GetLastError())))
- return false;
- return true;
- }
- ID3D12GraphicsCommandList *ComputeQueueDX12::Start()
- {
- JPH_ASSERT(!mIsExecuting);
- if (!mIsStarted)
- {
- // Reset the allocator
- if (HRFailed(mCommandAllocator->Reset()))
- return nullptr;
- // Reset the command list
- if (HRFailed(mCommandList->Reset(mCommandAllocator.Get(), nullptr)))
- return nullptr;
- // Now we have started recording commands
- mIsStarted = true;
- }
- return mCommandList.Get();
- }
- void ComputeQueueDX12::SetShader(const ComputeShader *inShader)
- {
- ID3D12GraphicsCommandList *command_list = Start();
- mShader = static_cast<const ComputeShaderDX12 *>(inShader);
- command_list->SetPipelineState(mShader->GetPipelineState());
- command_list->SetComputeRootSignature(mShader->GetRootSignature());
- }
- void ComputeQueueDX12::SyncCPUToGPU(const ComputeBufferDX12 *inBuffer)
- {
- // Ensure that any CPU writes are visible to the GPU
- if (inBuffer->SyncCPUToGPU(mCommandList.Get()))
- {
- // After the first upload, the CPU buffer is no longer needed for Buffer and RWBuffer types
- if (inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer)
- mDelayedFreedBuffers.emplace_back(inBuffer->ReleaseResourceCPU());
- }
- }
- void ComputeQueueDX12::SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer)
- {
- if (inBuffer == nullptr)
- return;
- JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::ConstantBuffer);
- ID3D12GraphicsCommandList *command_list = Start();
- const ComputeBufferDX12 *buffer = static_cast<const ComputeBufferDX12 *>(inBuffer);
- command_list->SetComputeRootConstantBufferView(mShader->NameToIndex(inName), buffer->GetResourceCPU()->GetGPUVirtualAddress());
- mUsedBuffers.insert(buffer);
- }
- void ComputeQueueDX12::SetBuffer(const char *inName, const ComputeBuffer *inBuffer)
- {
- if (inBuffer == nullptr)
- return;
- JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::UploadBuffer || inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
- ID3D12GraphicsCommandList *command_list = Start();
- const ComputeBufferDX12 *buffer = static_cast<const ComputeBufferDX12 *>(inBuffer);
- uint parameter_index = mShader->NameToIndex(inName);
- SyncCPUToGPU(buffer);
- buffer->Barrier(command_list, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
- command_list->SetComputeRootShaderResourceView(parameter_index, buffer->GetResourceGPU()->GetGPUVirtualAddress());
- mUsedBuffers.insert(buffer);
- }
- void ComputeQueueDX12::SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier)
- {
- if (inBuffer == nullptr)
- return;
- JPH_ASSERT(inBuffer->GetType() == ComputeBuffer::EType::RWBuffer);
- ID3D12GraphicsCommandList *command_list = Start();
- ComputeBufferDX12 *buffer = static_cast<ComputeBufferDX12 *>(inBuffer);
- uint parameter_index = mShader->NameToIndex(inName);
- SyncCPUToGPU(buffer);
- if (!buffer->Barrier(command_list, D3D12_RESOURCE_STATE_UNORDERED_ACCESS) && inBarrier == EBarrier::Yes)
- buffer->RWBarrier(command_list);
- command_list->SetComputeRootUnorderedAccessView(parameter_index, buffer->GetResourceGPU()->GetGPUVirtualAddress());
- mUsedBuffers.insert(buffer);
- }
- void ComputeQueueDX12::ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc)
- {
- if (inDst == nullptr || inSrc == nullptr)
- return;
- JPH_ASSERT(inDst->GetType() == ComputeBuffer::EType::ReadbackBuffer);
- ID3D12GraphicsCommandList *command_list = Start();
- ComputeBufferDX12 *dst = static_cast<ComputeBufferDX12 *>(inDst);
- const ComputeBufferDX12 *src = static_cast<const ComputeBufferDX12 *>(inSrc);
- dst->Barrier(command_list, D3D12_RESOURCE_STATE_COPY_DEST);
- src->Barrier(command_list, D3D12_RESOURCE_STATE_COPY_SOURCE);
- command_list->CopyResource(dst->GetResourceCPU(), src->GetResourceGPU());
- mUsedBuffers.insert(src);
- mUsedBuffers.insert(dst);
- }
- void ComputeQueueDX12::Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ)
- {
- ID3D12GraphicsCommandList *command_list = Start();
- command_list->Dispatch(inThreadGroupsX, inThreadGroupsY, inThreadGroupsZ);
- }
- void ComputeQueueDX12::Execute()
- {
- JPH_ASSERT(mIsStarted);
- JPH_ASSERT(!mIsExecuting);
- // Close the command list
- if (HRFailed(mCommandList->Close()))
- return;
- // Execute the command list
- ID3D12CommandList *command_lists[] = { mCommandList.Get() };
- mCommandQueue->ExecuteCommandLists(std::size(command_lists), command_lists);
- // Schedule a Signal command in the queue
- if (HRFailed(mCommandQueue->Signal(mFence.Get(), mFenceValue)))
- return;
- // Clear the current shader
- mShader = nullptr;
- // Mark that we're executing
- mIsExecuting = true;
- }
- void ComputeQueueDX12::Wait()
- {
- // Check if we've been started
- if (mIsExecuting)
- {
- if (mFence->GetCompletedValue() < mFenceValue)
- {
- // Wait until the fence has been processed
- if (HRFailed(mFence->SetEventOnCompletion(mFenceValue, mFenceEvent)))
- return;
- WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
- }
- // Increment the fence value
- mFenceValue++;
- // Buffers can be freed now
- mUsedBuffers.clear();
- // Free buffers
- mDelayedFreedBuffers.clear();
- // Done executing
- mIsExecuting = false;
- mIsStarted = false;
- }
- }
- JPH_NAMESPACE_END
- #endif // JPH_USE_DX12
|