HaxeJolt
This commit is contained in:
167
lib/haxejolt/JoltPhysics/Jolt/Compute/DX12/ComputeBufferDX12.cpp
Normal file
167
lib/haxejolt/JoltPhysics/Jolt/Compute/DX12/ComputeBufferDX12.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
// 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/ComputeBufferDX12.h>
|
||||
#include <Jolt/Compute/DX12/ComputeSystemDX12.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
ComputeBufferDX12::ComputeBufferDX12(ComputeSystemDX12 *inComputeSystem, EType inType, uint64 inSize, uint inStride) :
|
||||
ComputeBuffer(inType, inSize, inStride),
|
||||
mComputeSystem(inComputeSystem)
|
||||
{
|
||||
}
|
||||
|
||||
bool ComputeBufferDX12::Initialize(const void *inData)
|
||||
{
|
||||
uint64 buffer_size = mSize * mStride;
|
||||
|
||||
switch (mType)
|
||||
{
|
||||
case EType::UploadBuffer:
|
||||
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||
mBufferGPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||
if (mBufferCPU == nullptr || mBufferGPU == nullptr)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case EType::ConstantBuffer:
|
||||
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||
if (mBufferCPU == nullptr)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case EType::ReadbackBuffer:
|
||||
JPH_ASSERT(inData == nullptr, "Can't upload data to a readback buffer");
|
||||
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||
if (mBufferCPU == nullptr)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case EType::Buffer:
|
||||
JPH_ASSERT(inData != nullptr);
|
||||
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||
mBufferGPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||
if (mBufferCPU == nullptr || mBufferGPU == nullptr)
|
||||
return false;
|
||||
mNeedsSync = true;
|
||||
break;
|
||||
|
||||
case EType::RWBuffer:
|
||||
if (inData != nullptr)
|
||||
{
|
||||
mBufferCPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE, buffer_size);
|
||||
if (mBufferCPU == nullptr)
|
||||
return false;
|
||||
mNeedsSync = true;
|
||||
}
|
||||
mBufferGPU = mComputeSystem->CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, buffer_size);
|
||||
if (mBufferGPU == nullptr)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy data to upload buffer
|
||||
if (inData != nullptr)
|
||||
{
|
||||
void *data = nullptr;
|
||||
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||
mBufferCPU->Map(0, &range, &data);
|
||||
memcpy(data, inData, size_t(buffer_size));
|
||||
mBufferCPU->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ComputeBufferDX12::Barrier(ID3D12GraphicsCommandList *inCommandList, D3D12_RESOURCE_STATES inTo) const
|
||||
{
|
||||
// Check if state changed
|
||||
if (mCurrentState == inTo)
|
||||
return false;
|
||||
|
||||
// Only buffers in GPU memory can change state
|
||||
if (mType != ComputeBuffer::EType::Buffer && mType != ComputeBuffer::EType::RWBuffer)
|
||||
return true;
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = GetResourceGPU();
|
||||
barrier.Transition.StateBefore = mCurrentState;
|
||||
barrier.Transition.StateAfter = inTo;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
inCommandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
mCurrentState = inTo;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ComputeBufferDX12::RWBarrier(ID3D12GraphicsCommandList *inCommandList)
|
||||
{
|
||||
JPH_ASSERT(mCurrentState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = GetResourceGPU();
|
||||
inCommandList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
bool ComputeBufferDX12::SyncCPUToGPU(ID3D12GraphicsCommandList *inCommandList) const
|
||||
{
|
||||
if (!mNeedsSync)
|
||||
return false;
|
||||
|
||||
Barrier(inCommandList, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
|
||||
inCommandList->CopyResource(GetResourceGPU(), GetResourceCPU());
|
||||
|
||||
mNeedsSync = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void *ComputeBufferDX12::MapInternal(EMode inMode)
|
||||
{
|
||||
void *mapped_resource = nullptr;
|
||||
|
||||
switch (inMode)
|
||||
{
|
||||
case EMode::Read:
|
||||
JPH_ASSERT(mType == EType::ReadbackBuffer);
|
||||
if (HRFailed(mBufferCPU->Map(0, nullptr, &mapped_resource)))
|
||||
return nullptr;
|
||||
break;
|
||||
|
||||
case EMode::Write:
|
||||
{
|
||||
JPH_ASSERT(mType == EType::UploadBuffer || mType == EType::ConstantBuffer);
|
||||
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||
if (HRFailed(mBufferCPU->Map(0, &range, &mapped_resource)))
|
||||
return nullptr;
|
||||
mNeedsSync = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void ComputeBufferDX12::UnmapInternal()
|
||||
{
|
||||
mBufferCPU->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
ComputeBufferResult ComputeBufferDX12::CreateReadBackBuffer() const
|
||||
{
|
||||
return mComputeSystem->CreateComputeBuffer(EType::ReadbackBuffer, mSize, mStride);
|
||||
}
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
|
||||
#endif // JPH_USE_DX12
|
||||
@ -0,0 +1,51 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Compute/ComputeBuffer.h>
|
||||
|
||||
#ifdef JPH_USE_DX12
|
||||
|
||||
#include <Jolt/Compute/DX12/IncludeDX12.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
class ComputeSystemDX12;
|
||||
|
||||
/// Buffer that can be read from / written to by a compute shader
|
||||
class JPH_EXPORT ComputeBufferDX12 final : public ComputeBuffer
|
||||
{
|
||||
public:
|
||||
JPH_OVERRIDE_NEW_DELETE
|
||||
|
||||
/// Constructor
|
||||
ComputeBufferDX12(ComputeSystemDX12 *inComputeSystem, EType inType, uint64 inSize, uint inStride);
|
||||
|
||||
bool Initialize(const void *inData);
|
||||
|
||||
ID3D12Resource * GetResourceCPU() const { return mBufferCPU.Get(); }
|
||||
ID3D12Resource * GetResourceGPU() const { return mBufferGPU.Get(); }
|
||||
ComPtr<ID3D12Resource> ReleaseResourceCPU() const { return std::move(mBufferCPU); }
|
||||
|
||||
bool Barrier(ID3D12GraphicsCommandList *inCommandList, D3D12_RESOURCE_STATES inTo) const;
|
||||
void RWBarrier(ID3D12GraphicsCommandList *inCommandList);
|
||||
bool SyncCPUToGPU(ID3D12GraphicsCommandList *inCommandList) const;
|
||||
|
||||
ComputeBufferResult CreateReadBackBuffer() const override;
|
||||
|
||||
private:
|
||||
virtual void * MapInternal(EMode inMode) override;
|
||||
virtual void UnmapInternal() override;
|
||||
|
||||
ComputeSystemDX12 * mComputeSystem;
|
||||
mutable ComPtr<ID3D12Resource> mBufferCPU;
|
||||
ComPtr<ID3D12Resource> mBufferGPU;
|
||||
mutable bool mNeedsSync = false; ///< If this buffer needs to be synced from CPU to GPU
|
||||
mutable D3D12_RESOURCE_STATES mCurrentState = D3D12_RESOURCE_STATE_COPY_DEST; ///< State of the GPU buffer so we can do proper barriers
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
|
||||
#endif // JPH_USE_DX12
|
||||
221
lib/haxejolt/JoltPhysics/Jolt/Compute/DX12/ComputeQueueDX12.cpp
Normal file
221
lib/haxejolt/JoltPhysics/Jolt/Compute/DX12/ComputeQueueDX12.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
// 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, ComputeQueueResult &outResult)
|
||||
{
|
||||
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)), outResult))
|
||||
return false;
|
||||
|
||||
if (HRFailed(inDevice->CreateCommandAllocator(inType, IID_PPV_ARGS(&mCommandAllocator)), outResult))
|
||||
return false;
|
||||
|
||||
// Create the command list
|
||||
if (HRFailed(inDevice->CreateCommandList(0, inType, mCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&mCommandList)), outResult))
|
||||
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(), outResult))
|
||||
return false;
|
||||
|
||||
// Create synchronization object
|
||||
if (HRFailed(inDevice->CreateFence(mFenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)), outResult))
|
||||
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()), outResult))
|
||||
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())
|
||||
&& (inBuffer->GetType() == ComputeBuffer::EType::Buffer || inBuffer->GetType() == ComputeBuffer::EType::RWBuffer))
|
||||
{
|
||||
// After the first upload, the CPU buffer is no longer needed for Buffer and RWBuffer types
|
||||
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((UINT)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
|
||||
@ -0,0 +1,61 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef JPH_USE_DX12
|
||||
|
||||
#include <Jolt/Compute/ComputeQueue.h>
|
||||
#include <Jolt/Compute/DX12/ComputeShaderDX12.h>
|
||||
#include <Jolt/Core/UnorderedSet.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
class ComputeBufferDX12;
|
||||
|
||||
/// A command queue for DirectX for executing compute workloads on the GPU.
|
||||
class JPH_EXPORT ComputeQueueDX12 final : public ComputeQueue
|
||||
{
|
||||
public:
|
||||
JPH_OVERRIDE_NEW_DELETE
|
||||
|
||||
/// Destructor
|
||||
virtual ~ComputeQueueDX12() override;
|
||||
|
||||
/// Initialize the queue
|
||||
bool Initialize(ID3D12Device *inDevice, D3D12_COMMAND_LIST_TYPE inType, ComputeQueueResult &outResult);
|
||||
|
||||
/// Start the command list (requires waiting until the previous one is finished)
|
||||
ID3D12GraphicsCommandList * Start();
|
||||
|
||||
// See: ComputeQueue
|
||||
virtual void SetShader(const ComputeShader *inShader) override;
|
||||
virtual void SetConstantBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||
virtual void SetBuffer(const char *inName, const ComputeBuffer *inBuffer) override;
|
||||
virtual void SetRWBuffer(const char *inName, ComputeBuffer *inBuffer, EBarrier inBarrier = EBarrier::Yes) override;
|
||||
virtual void ScheduleReadback(ComputeBuffer *inDst, const ComputeBuffer *inSrc) override;
|
||||
virtual void Dispatch(uint inThreadGroupsX, uint inThreadGroupsY, uint inThreadGroupsZ) override;
|
||||
virtual void Execute() override;
|
||||
virtual void Wait() override;
|
||||
|
||||
private:
|
||||
/// Copy the CPU buffer to the GPU buffer if needed
|
||||
void SyncCPUToGPU(const ComputeBufferDX12 *inBuffer);
|
||||
|
||||
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
|
||||
RefConst<ComputeShaderDX12> mShader = nullptr; ///< Current active shader
|
||||
bool mIsStarted = false; ///< If the command list has been started (reset) and is ready to record commands
|
||||
bool mIsExecuting = false; ///< If a command list is currently executing on the queue
|
||||
UnorderedSet<RefConst<ComputeBuffer>> mUsedBuffers; ///< Buffers that are in use by the current execution, these will be retained until execution is finished so that we don't free buffers that are in use
|
||||
Array<ComPtr<ID3D12Resource>> mDelayedFreedBuffers; ///< Buffers freed during the execution
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
|
||||
#endif // JPH_USE_DX12
|
||||
@ -0,0 +1,54 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef JPH_USE_DX12
|
||||
|
||||
#include <Jolt/Compute/ComputeShader.h>
|
||||
#include <Jolt/Compute/DX12/IncludeDX12.h>
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
/// Compute shader handle for DirectX
|
||||
class JPH_EXPORT ComputeShaderDX12 : public ComputeShader
|
||||
{
|
||||
public:
|
||||
JPH_OVERRIDE_NEW_DELETE
|
||||
|
||||
/// Constructor
|
||||
ComputeShaderDX12(ComPtr<ID3DBlob> inShader, ComPtr<ID3D12RootSignature> inRootSignature, ComPtr<ID3D12PipelineState> inPipelineState, Array<String> &&inBindingNames, UnorderedMap<string_view, uint> &&inNameToIndex, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) :
|
||||
ComputeShader(inGroupSizeX, inGroupSizeY, inGroupSizeZ),
|
||||
mShader(inShader),
|
||||
mRootSignature(inRootSignature),
|
||||
mPipelineState(inPipelineState),
|
||||
mBindingNames(std::move(inBindingNames)),
|
||||
mNameToIndex(std::move(inNameToIndex))
|
||||
{
|
||||
}
|
||||
|
||||
/// Get index of shader parameter
|
||||
uint NameToIndex(const char *inName) const
|
||||
{
|
||||
UnorderedMap<string_view, uint>::const_iterator it = mNameToIndex.find(inName);
|
||||
JPH_ASSERT(it != mNameToIndex.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/// Getters
|
||||
ID3D12PipelineState * GetPipelineState() const { return mPipelineState.Get(); }
|
||||
ID3D12RootSignature * GetRootSignature() const { return mRootSignature.Get(); }
|
||||
|
||||
private:
|
||||
ComPtr<ID3DBlob> mShader; ///< The compiled shader
|
||||
ComPtr<ID3D12RootSignature> mRootSignature; ///< The root signature for this shader
|
||||
ComPtr<ID3D12PipelineState> mPipelineState; ///< The pipeline state object for this shader
|
||||
Array<String> mBindingNames; ///< A list of binding names, mNameToIndex points to these strings
|
||||
UnorderedMap<string_view, uint> mNameToIndex; ///< Maps names to indices for the shader parameters, using a string_view so we can do find() without an allocation
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
|
||||
#endif // JPH_USE_DX12
|
||||
443
lib/haxejolt/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12.cpp
Normal file
443
lib/haxejolt/JoltPhysics/Jolt/Compute/DX12/ComputeSystemDX12.cpp
Normal file
@ -0,0 +1,443 @@
|
||||
// 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/ComputeSystemDX12.h>
|
||||
#include <Jolt/Compute/DX12/ComputeQueueDX12.h>
|
||||
#include <Jolt/Compute/DX12/ComputeShaderDX12.h>
|
||||
#include <Jolt/Compute/DX12/ComputeBufferDX12.h>
|
||||
#include <Jolt/Core/StringTools.h>
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
|
||||
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||
JPH_MSVC_SUPPRESS_WARNING(5204) // 'X': class has virtual functions, but its trivial destructor is not virtual; instances of objects derived from this class may not be destructed correctly
|
||||
JPH_MSVC2026_PLUS_SUPPRESS_WARNING(4865) // wingdi.h(2806,1): '<unnamed-enum-DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER>': the underlying type will change from 'int' to '__int64' when '/Zc:enumTypes' is specified on the command line
|
||||
#include <fstream>
|
||||
#include <d3dcompiler.h>
|
||||
#include <dxcapi.h>
|
||||
#ifdef JPH_DEBUG
|
||||
#include <d3d12sdklayers.h>
|
||||
#endif
|
||||
JPH_SUPPRESS_WARNINGS_STD_END
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemDX12)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ComputeSystemDX12, ComputeSystem)
|
||||
}
|
||||
|
||||
void ComputeSystemDX12::Initialize(ID3D12Device *inDevice, EDebug inDebug)
|
||||
{
|
||||
mDevice = inDevice;
|
||||
mDebug = inDebug;
|
||||
}
|
||||
|
||||
void ComputeSystemDX12::Shutdown()
|
||||
{
|
||||
mDevice.Reset();
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> ComputeSystemDX12::CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, D3D12_RESOURCE_FLAGS inFlags, uint64 inSize)
|
||||
{
|
||||
// Create a new resource
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Alignment = 0;
|
||||
desc.Width = inSize;
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = inFlags;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||
heap_properties.Type = inHeapType;
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heap_properties.CreationNodeMask = 1;
|
||||
heap_properties.VisibleNodeMask = 1;
|
||||
|
||||
ComPtr<ID3D12Resource> resource;
|
||||
if (HRFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &desc, inResourceState, nullptr, IID_PPV_ARGS(&resource))))
|
||||
return nullptr;
|
||||
return resource;
|
||||
}
|
||||
|
||||
ComputeShaderResult ComputeSystemDX12::CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ)
|
||||
{
|
||||
ComputeShaderResult result;
|
||||
|
||||
// Read shader source file
|
||||
Array<uint8> data;
|
||||
String error;
|
||||
String file_name = String(inName) + ".hlsl";
|
||||
if (!mShaderLoader(file_name.c_str(), data, error))
|
||||
{
|
||||
result.SetError(error);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef JPH_USE_DXC // Use FXC, the old shader compiler?
|
||||
|
||||
UINT flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS | D3DCOMPILE_ALL_RESOURCES_BOUND;
|
||||
#ifdef JPH_DEBUG
|
||||
flags |= D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
#else
|
||||
flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
|
||||
#endif
|
||||
if (mDebug == EDebug::DebugSymbols)
|
||||
flags |= D3DCOMPILE_DEBUG;
|
||||
|
||||
const D3D_SHADER_MACRO defines[] =
|
||||
{
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
// Handles loading include files through the shader loader
|
||||
struct IncludeHandler : public ID3DInclude
|
||||
{
|
||||
IncludeHandler(const ShaderLoader &inShaderLoader) : mShaderLoader(inShaderLoader) { }
|
||||
virtual ~IncludeHandler() = default;
|
||||
|
||||
STDMETHOD (Open)(D3D_INCLUDE_TYPE, LPCSTR inFileName, LPCVOID, LPCVOID *outData, UINT *outNumBytes) override
|
||||
{
|
||||
// Read the header file
|
||||
Array<uint8> file_data;
|
||||
String error;
|
||||
if (!mShaderLoader(inFileName, file_data, error))
|
||||
return E_FAIL;
|
||||
if (file_data.empty())
|
||||
{
|
||||
*outData = nullptr;
|
||||
*outNumBytes = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Copy to a new memory block
|
||||
void *mem = CoTaskMemAlloc(file_data.size());
|
||||
if (mem == nullptr)
|
||||
return E_OUTOFMEMORY;
|
||||
memcpy(mem, file_data.data(), file_data.size());
|
||||
*outData = mem;
|
||||
*outNumBytes = (UINT)file_data.size();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHOD (Close)(LPCVOID inData) override
|
||||
{
|
||||
if (inData != nullptr)
|
||||
CoTaskMemFree(const_cast<void *>(inData));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
const ShaderLoader & mShaderLoader;
|
||||
};
|
||||
IncludeHandler include_handler(mShaderLoader);
|
||||
|
||||
// Compile source
|
||||
ComPtr<ID3DBlob> shader_blob, error_blob;
|
||||
if (FAILED(D3DCompile(&data[0],
|
||||
(uint)data.size(),
|
||||
file_name.c_str(),
|
||||
defines,
|
||||
&include_handler,
|
||||
"main",
|
||||
"cs_5_0",
|
||||
flags,
|
||||
0,
|
||||
shader_blob.GetAddressOf(),
|
||||
error_blob.GetAddressOf())))
|
||||
{
|
||||
if (error_blob)
|
||||
result.SetError((const char *)error_blob->GetBufferPointer());
|
||||
else
|
||||
result.SetError("Shader compile error");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get shader description
|
||||
ComPtr<ID3D12ShaderReflection> reflector;
|
||||
if (FAILED(D3DReflect(shader_blob->GetBufferPointer(), shader_blob->GetBufferSize(), IID_PPV_ARGS(&reflector))))
|
||||
{
|
||||
result.SetError("Failed to reflect shader");
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
ComPtr<IDxcUtils> utils;
|
||||
DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(utils.GetAddressOf()));
|
||||
|
||||
// Custom include handler that forwards include loads to mShaderLoader
|
||||
struct DxcIncludeHandler : public IDxcIncludeHandler
|
||||
{
|
||||
DxcIncludeHandler(IDxcUtils *inUtils, const ShaderLoader &inLoader) : mUtils(inUtils), mShaderLoader(inLoader) { }
|
||||
virtual ~DxcIncludeHandler() = default;
|
||||
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
|
||||
{
|
||||
JPH_ASSERT(false);
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) AddRef(void) override
|
||||
{
|
||||
// Allocated on the stack, we don't do ref counting
|
||||
return 1;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) Release(void) override
|
||||
{
|
||||
// Allocated on the stack, we don't do ref counting
|
||||
return 1;
|
||||
}
|
||||
|
||||
// IDxcIncludeHandler::LoadSource uses IDxcBlob**
|
||||
STDMETHODIMP LoadSource(LPCWSTR inFilename, IDxcBlob **outIncludeSource) override
|
||||
{
|
||||
*outIncludeSource = nullptr;
|
||||
|
||||
// Convert to UTF-8
|
||||
char file_name[MAX_PATH];
|
||||
WideCharToMultiByte(CP_UTF8, 0, inFilename, -1, file_name, sizeof(file_name), nullptr, nullptr);
|
||||
|
||||
// Load the header
|
||||
Array<uint8> file_data;
|
||||
String error;
|
||||
if (!mShaderLoader(file_name, file_data, error))
|
||||
return E_FAIL;
|
||||
|
||||
// Create a blob from the loaded data
|
||||
ComPtr<IDxcBlobEncoding> blob_encoder;
|
||||
HRESULT hr = mUtils->CreateBlob(file_data.empty()? nullptr : file_data.data(), (uint)file_data.size(), CP_UTF8, blob_encoder.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
// Return as IDxcBlob
|
||||
*outIncludeSource = blob_encoder.Detach();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IDxcUtils * mUtils;
|
||||
const ShaderLoader & mShaderLoader;
|
||||
};
|
||||
DxcIncludeHandler include_handler(utils.Get(), mShaderLoader);
|
||||
|
||||
ComPtr<IDxcBlobEncoding> source;
|
||||
if (HRFailed(utils->CreateBlob(data.data(), (uint)data.size(), CP_UTF8, source.GetAddressOf()), result))
|
||||
return result;
|
||||
|
||||
ComPtr<IDxcCompiler3> compiler;
|
||||
DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(compiler.GetAddressOf()));
|
||||
|
||||
Array<LPCWSTR> arguments;
|
||||
arguments.push_back(L"-E");
|
||||
arguments.push_back(L"main");
|
||||
arguments.push_back(L"-T");
|
||||
arguments.push_back(L"cs_6_0");
|
||||
arguments.push_back(DXC_ARG_WARNINGS_ARE_ERRORS);
|
||||
arguments.push_back(DXC_ARG_OPTIMIZATION_LEVEL3);
|
||||
arguments.push_back(DXC_ARG_ALL_RESOURCES_BOUND);
|
||||
if (mDebug == EDebug::DebugSymbols)
|
||||
{
|
||||
arguments.push_back(DXC_ARG_DEBUG);
|
||||
arguments.push_back(L"-Qembed_debug");
|
||||
}
|
||||
|
||||
// Provide file name so tools know what the original shader was called (the actual source comes from the blob)
|
||||
wchar_t w_file_name[MAX_PATH];
|
||||
MultiByteToWideChar(CP_UTF8, 0, file_name.c_str(), -1, w_file_name, MAX_PATH);
|
||||
arguments.push_back(w_file_name);
|
||||
|
||||
// Compile the shader
|
||||
DxcBuffer source_buffer;
|
||||
source_buffer.Ptr = source->GetBufferPointer();
|
||||
source_buffer.Size = source->GetBufferSize();
|
||||
source_buffer.Encoding = 0;
|
||||
ComPtr<IDxcResult> compile_result;
|
||||
if (FAILED(compiler->Compile(&source_buffer, arguments.data(), (uint32)arguments.size(), &include_handler, IID_PPV_ARGS(compile_result.GetAddressOf()))))
|
||||
{
|
||||
result.SetError("Failed to compile shader");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check for compilation errors
|
||||
ComPtr<IDxcBlobUtf8> errors;
|
||||
compile_result->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(errors.GetAddressOf()), nullptr);
|
||||
if (errors != nullptr && errors->GetStringLength() > 0)
|
||||
{
|
||||
result.SetError((const char *)errors->GetBufferPointer());
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get the compiled shader code
|
||||
ComPtr<ID3DBlob> shader_blob;
|
||||
if (HRFailed(compile_result->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(shader_blob.GetAddressOf()), nullptr), result))
|
||||
return result;
|
||||
|
||||
// Get reflection data
|
||||
ComPtr<IDxcBlob> reflection_data;
|
||||
if (HRFailed(compile_result->GetOutput(DXC_OUT_REFLECTION, IID_PPV_ARGS(reflection_data.GetAddressOf()), nullptr), result))
|
||||
return result;
|
||||
DxcBuffer reflection_buffer;
|
||||
reflection_buffer.Ptr = reflection_data->GetBufferPointer();
|
||||
reflection_buffer.Size = reflection_data->GetBufferSize();
|
||||
reflection_buffer.Encoding = 0;
|
||||
ComPtr<ID3D12ShaderReflection> reflector;
|
||||
if (HRFailed(utils->CreateReflection(&reflection_buffer, IID_PPV_ARGS(reflector.GetAddressOf())), result))
|
||||
return result;
|
||||
|
||||
#endif // JPH_USE_DXC
|
||||
|
||||
// Get the shader description
|
||||
D3D12_SHADER_DESC shader_desc;
|
||||
if (HRFailed(reflector->GetDesc(&shader_desc), result))
|
||||
return result;
|
||||
|
||||
// Verify that the group sizes match the shader's thread group size
|
||||
UINT thread_group_size_x, thread_group_size_y, thread_group_size_z;
|
||||
if (HRFailed(reflector->GetThreadGroupSize(&thread_group_size_x, &thread_group_size_y, &thread_group_size_z), result))
|
||||
return result;
|
||||
JPH_ASSERT(inGroupSizeX == thread_group_size_x, "Group size X mismatch");
|
||||
JPH_ASSERT(inGroupSizeY == thread_group_size_y, "Group size Y mismatch");
|
||||
JPH_ASSERT(inGroupSizeZ == thread_group_size_z, "Group size Z mismatch");
|
||||
|
||||
// Convert parameters to root signature description
|
||||
Array<String> binding_names;
|
||||
binding_names.reserve(shader_desc.BoundResources);
|
||||
UnorderedMap<string_view, uint> name_to_index;
|
||||
Array<D3D12_ROOT_PARAMETER1> root_params;
|
||||
for (UINT i = 0; i < shader_desc.BoundResources; ++i)
|
||||
{
|
||||
D3D12_SHADER_INPUT_BIND_DESC bind_desc;
|
||||
reflector->GetResourceBindingDesc(i, &bind_desc);
|
||||
|
||||
D3D12_ROOT_PARAMETER1 param = {};
|
||||
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
|
||||
switch (bind_desc.Type)
|
||||
{
|
||||
case D3D_SIT_CBUFFER:
|
||||
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
break;
|
||||
|
||||
case D3D_SIT_STRUCTURED:
|
||||
case D3D_SIT_BYTEADDRESS:
|
||||
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
|
||||
break;
|
||||
|
||||
case D3D_SIT_UAV_RWTYPED:
|
||||
case D3D_SIT_UAV_RWSTRUCTURED:
|
||||
case D3D_SIT_UAV_RWBYTEADDRESS:
|
||||
case D3D_SIT_UAV_APPEND_STRUCTURED:
|
||||
case D3D_SIT_UAV_CONSUME_STRUCTURED:
|
||||
case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
|
||||
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
|
||||
break;
|
||||
|
||||
case D3D_SIT_TBUFFER:
|
||||
case D3D_SIT_TEXTURE:
|
||||
case D3D_SIT_SAMPLER:
|
||||
case D3D_SIT_RTACCELERATIONSTRUCTURE:
|
||||
case D3D_SIT_UAV_FEEDBACKTEXTURE:
|
||||
JPH_ASSERT(false, "Unsupported shader input type");
|
||||
continue;
|
||||
}
|
||||
|
||||
param.Descriptor.RegisterSpace = bind_desc.Space;
|
||||
param.Descriptor.ShaderRegister = bind_desc.BindPoint;
|
||||
param.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE;
|
||||
|
||||
binding_names.push_back(bind_desc.Name); // Add all strings to a pool to keep them alive
|
||||
name_to_index[string_view(binding_names.back())] = (uint)root_params.size();
|
||||
root_params.push_back(param);
|
||||
}
|
||||
|
||||
// Create the root signature
|
||||
D3D12_VERSIONED_ROOT_SIGNATURE_DESC root_sig_desc = {};
|
||||
root_sig_desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
|
||||
root_sig_desc.Desc_1_1.NumParameters = (UINT)root_params.size();
|
||||
root_sig_desc.Desc_1_1.pParameters = root_params.data();
|
||||
root_sig_desc.Desc_1_1.NumStaticSamplers = 0;
|
||||
root_sig_desc.Desc_1_1.pStaticSamplers = nullptr;
|
||||
root_sig_desc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
|
||||
ComPtr<ID3DBlob> serialized_sig;
|
||||
ComPtr<ID3DBlob> root_sig_error_blob;
|
||||
if (FAILED(D3D12SerializeVersionedRootSignature(&root_sig_desc, &serialized_sig, &root_sig_error_blob)))
|
||||
{
|
||||
if (root_sig_error_blob)
|
||||
{
|
||||
error = StringFormat("Failed to create root signature: %s", (const char *)root_sig_error_blob->GetBufferPointer());
|
||||
result.SetError(error);
|
||||
}
|
||||
else
|
||||
result.SetError("Failed to create root signature");
|
||||
return result;
|
||||
}
|
||||
ComPtr<ID3D12RootSignature> root_sig;
|
||||
if (FAILED(mDevice->CreateRootSignature(0, serialized_sig->GetBufferPointer(), serialized_sig->GetBufferSize(), IID_PPV_ARGS(&root_sig))))
|
||||
{
|
||||
result.SetError("Failed to create root signature");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create a pipeline state object from the root signature and the shader
|
||||
ComPtr<ID3D12PipelineState> pipeline_state;
|
||||
D3D12_COMPUTE_PIPELINE_STATE_DESC compute_state_desc = {};
|
||||
compute_state_desc.pRootSignature = root_sig.Get();
|
||||
compute_state_desc.CS = { shader_blob->GetBufferPointer(), shader_blob->GetBufferSize() };
|
||||
if (FAILED(mDevice->CreateComputePipelineState(&compute_state_desc, IID_PPV_ARGS(&pipeline_state))))
|
||||
{
|
||||
result.SetError("Failed to create compute pipeline state");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Set name on DX12 objects for easier debugging
|
||||
wchar_t w_name[1024];
|
||||
size_t converted_chars = 0;
|
||||
mbstowcs_s(&converted_chars, w_name, 1024, inName, _TRUNCATE);
|
||||
pipeline_state->SetName(w_name);
|
||||
|
||||
result.Set(new ComputeShaderDX12(shader_blob, root_sig, pipeline_state, std::move(binding_names), std::move(name_to_index), inGroupSizeX, inGroupSizeY, inGroupSizeZ));
|
||||
return result;
|
||||
}
|
||||
|
||||
ComputeBufferResult ComputeSystemDX12::CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData)
|
||||
{
|
||||
ComputeBufferResult result;
|
||||
|
||||
Ref<ComputeBufferDX12> buffer = new ComputeBufferDX12(this, inType, inSize, inStride);
|
||||
if (!buffer->Initialize(inData))
|
||||
{
|
||||
result.SetError("Failed to create compute buffer");
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Set(buffer.GetPtr());
|
||||
return result;
|
||||
}
|
||||
|
||||
ComputeQueueResult ComputeSystemDX12::CreateComputeQueue()
|
||||
{
|
||||
ComputeQueueResult result;
|
||||
|
||||
Ref<ComputeQueueDX12> queue = new ComputeQueueDX12();
|
||||
if (!queue->Initialize(mDevice.Get(), D3D12_COMMAND_LIST_TYPE_COMPUTE, result))
|
||||
return result;
|
||||
|
||||
result.Set(queue.GetPtr());
|
||||
return result;
|
||||
}
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
|
||||
#endif // JPH_USE_DX12
|
||||
@ -0,0 +1,52 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
#include <Jolt/Compute/ComputeSystem.h>
|
||||
|
||||
#ifdef JPH_USE_DX12
|
||||
|
||||
#include <Jolt/Compute/DX12/IncludeDX12.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
/// Interface to run a workload on the GPU using DirectX 12.
|
||||
/// Minimal implementation that can integrate with your own DirectX 12 setup.
|
||||
class JPH_EXPORT ComputeSystemDX12 : public ComputeSystem
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemDX12)
|
||||
|
||||
/// How we want to compile our shaders
|
||||
enum class EDebug
|
||||
{
|
||||
NoDebugSymbols,
|
||||
DebugSymbols
|
||||
};
|
||||
|
||||
/// Initialize / shutdown
|
||||
void Initialize(ID3D12Device *inDevice, EDebug inDebug);
|
||||
void Shutdown();
|
||||
|
||||
// See: ComputeSystem
|
||||
virtual ComputeShaderResult CreateComputeShader(const char *inName, uint32 inGroupSizeX, uint32 inGroupSizeY, uint32 inGroupSizeZ) override;
|
||||
virtual ComputeBufferResult CreateComputeBuffer(ComputeBuffer::EType inType, uint64 inSize, uint inStride, const void *inData = nullptr) override;
|
||||
virtual ComputeQueueResult CreateComputeQueue() override;
|
||||
|
||||
/// Access to the DX12 device
|
||||
ID3D12Device * GetDevice() const { return mDevice.Get(); }
|
||||
|
||||
// Function to create a ID3D12Resource on specified heap with specified state
|
||||
ComPtr<ID3D12Resource> CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, D3D12_RESOURCE_FLAGS inFlags, uint64 inSize);
|
||||
|
||||
private:
|
||||
ComPtr<ID3D12Device> mDevice;
|
||||
EDebug mDebug = EDebug::NoDebugSymbols;
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
|
||||
#endif // JPH_USE_DX12
|
||||
@ -0,0 +1,154 @@
|
||||
// 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/ComputeSystemDX12Impl.h>
|
||||
|
||||
#ifdef JPH_DEBUG
|
||||
#include <d3d12sdklayers.h>
|
||||
#endif
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ComputeSystemDX12Impl)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ComputeSystemDX12Impl, ComputeSystemDX12)
|
||||
}
|
||||
|
||||
ComputeSystemDX12Impl::~ComputeSystemDX12Impl()
|
||||
{
|
||||
Shutdown();
|
||||
mDXGIFactory.Reset();
|
||||
|
||||
#ifdef JPH_DEBUG
|
||||
// Test for leaks
|
||||
ComPtr<IDXGIDebug1> dxgi_debug;
|
||||
if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgi_debug))))
|
||||
dxgi_debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ComputeSystemDX12Impl::Initialize(ComputeSystemResult &outResult)
|
||||
{
|
||||
#if defined(JPH_DEBUG)
|
||||
// Enable the D3D12 debug layer
|
||||
ComPtr<ID3D12Debug> debug_controller;
|
||||
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller))))
|
||||
debug_controller->EnableDebugLayer();
|
||||
#endif
|
||||
|
||||
// Create DXGI factory
|
||||
if (HRFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mDXGIFactory)), outResult))
|
||||
return false;
|
||||
|
||||
// Find adapter
|
||||
ComPtr<IDXGIAdapter1> adapter;
|
||||
ComPtr<ID3D12Device> device;
|
||||
|
||||
HRESULT result = E_FAIL;
|
||||
|
||||
// First check if we have the Windows 1803 IDXGIFactory6 interface
|
||||
ComPtr<IDXGIFactory6> factory6;
|
||||
if (SUCCEEDED(mDXGIFactory->QueryInterface(IID_PPV_ARGS(&factory6))))
|
||||
{
|
||||
for (int search_software = 0; search_software < 2 && device == nullptr; ++search_software)
|
||||
for (UINT index = 0; factory6->EnumAdapterByGpuPreference(index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)) != DXGI_ERROR_NOT_FOUND; ++index)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
|
||||
// We don't want software renderers in the first pass
|
||||
int is_software = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0? 1 : 0;
|
||||
if (search_software != is_software)
|
||||
continue;
|
||||
|
||||
// Check to see whether the adapter supports Direct3D 12
|
||||
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||
int prev_state = _CrtSetDbgFlag(0); // Temporarily disable leak detection as this call reports false positives
|
||||
#endif
|
||||
result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device));
|
||||
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||
_CrtSetDbgFlag(prev_state);
|
||||
#endif
|
||||
if (SUCCEEDED(result))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back to the older method that may not get the fastest GPU
|
||||
for (int search_software = 0; search_software < 2 && device == nullptr; ++search_software)
|
||||
for (UINT index = 0; mDXGIFactory->EnumAdapters1(index, &adapter) != DXGI_ERROR_NOT_FOUND; ++index)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
|
||||
// We don't want software renderers in the first pass
|
||||
int is_software = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0? 1 : 0;
|
||||
if (search_software != is_software)
|
||||
continue;
|
||||
|
||||
// Check to see whether the adapter supports Direct3D 12
|
||||
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||
int prev_state = _CrtSetDbgFlag(0); // Temporarily disable leak detection as this call reports false positives
|
||||
#endif
|
||||
result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device));
|
||||
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||
_CrtSetDbgFlag(prev_state);
|
||||
#endif
|
||||
if (SUCCEEDED(result))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we managed to obtain a device
|
||||
if (HRFailed(result, outResult))
|
||||
return false;
|
||||
|
||||
// Initialize the compute interface
|
||||
ComputeSystemDX12::Initialize(device.Get(), EDebug::DebugSymbols);
|
||||
|
||||
#ifdef JPH_DEBUG
|
||||
// Enable breaking on errors
|
||||
ComPtr<ID3D12InfoQueue> info_queue;
|
||||
if (SUCCEEDED(device.As(&info_queue)))
|
||||
{
|
||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
|
||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
|
||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
|
||||
|
||||
// Disable an error that triggers on Windows 11 with a hybrid graphic system
|
||||
// See: https://stackoverflow.com/questions/69805245/directx-12-application-is-crashing-in-windows-11
|
||||
D3D12_MESSAGE_ID hide[] =
|
||||
{
|
||||
D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE,
|
||||
};
|
||||
D3D12_INFO_QUEUE_FILTER filter = { };
|
||||
filter.DenyList.NumIDs = static_cast<UINT>(std::size(hide));
|
||||
filter.DenyList.pIDList = hide;
|
||||
info_queue->AddStorageFilterEntries(&filter);
|
||||
}
|
||||
#endif // JPH_DEBUG
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ComputeSystemResult CreateComputeSystemDX12()
|
||||
{
|
||||
ComputeSystemResult result;
|
||||
|
||||
Ref<ComputeSystemDX12Impl> compute = new ComputeSystemDX12Impl();
|
||||
if (!compute->Initialize(result))
|
||||
return result;
|
||||
|
||||
result.Set(compute.GetPtr());
|
||||
return result;
|
||||
}
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
|
||||
#endif // JPH_USE_DX12
|
||||
@ -0,0 +1,33 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef JPH_USE_DX12
|
||||
|
||||
#include <Jolt/Compute/DX12/ComputeSystemDX12.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
/// Implementation of ComputeSystemDX12 that fully initializes DirectX 12
|
||||
class JPH_EXPORT ComputeSystemDX12Impl : public ComputeSystemDX12
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_EXPORT, ComputeSystemDX12Impl)
|
||||
|
||||
/// Destructor
|
||||
virtual ~ComputeSystemDX12Impl() override;
|
||||
|
||||
/// Initialize the compute system
|
||||
bool Initialize(ComputeSystemResult &outResult);
|
||||
|
||||
IDXGIFactory4 * GetDXGIFactory() const { return mDXGIFactory.Get(); }
|
||||
|
||||
private:
|
||||
ComPtr<IDXGIFactory4> mDXGIFactory;
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
|
||||
#endif // JPH_USE_DX12
|
||||
49
lib/haxejolt/JoltPhysics/Jolt/Compute/DX12/IncludeDX12.h
Normal file
49
lib/haxejolt/JoltPhysics/Jolt/Compute/DX12/IncludeDX12.h
Normal file
@ -0,0 +1,49 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/IncludeWindows.h>
|
||||
#include <Jolt/Core/StringTools.h>
|
||||
|
||||
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||
JPH_MSVC_SUPPRESS_WARNING(4265) // 'X': class has virtual functions, but its non-trivial destructor is not virtual; instances of this class may not be destructed correctly
|
||||
JPH_MSVC_SUPPRESS_WARNING(4625) // 'X': copy constructor was implicitly defined as deleted
|
||||
JPH_MSVC_SUPPRESS_WARNING(4626) // 'X': assignment operator was implicitly defined as deleted
|
||||
JPH_MSVC_SUPPRESS_WARNING(5204) // 'X': class has virtual functions, but its trivial destructor is not virtual; instances of objects derived from this class may not be destructed correctly
|
||||
JPH_MSVC_SUPPRESS_WARNING(5220) // 'X': a non-static data member with a volatile qualified type no longer implies
|
||||
JPH_MSVC2026_PLUS_SUPPRESS_WARNING(4865) // wingdi.h(2806,1): '<unnamed-enum-DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER>': the underlying type will change from 'int' to '__int64' when '/Zc:enumTypes' is specified on the command line
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <dxgidebug.h>
|
||||
#include <wrl.h>
|
||||
JPH_SUPPRESS_WARNINGS_STD_END
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
template <class Result>
|
||||
inline bool HRFailed(HRESULT inHR, Result &outResult)
|
||||
{
|
||||
if (SUCCEEDED(inHR))
|
||||
return false;
|
||||
|
||||
String error = StringFormat("Call failed with error code: %08X", inHR);
|
||||
outResult.SetError(error);
|
||||
JPH_ASSERT(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool HRFailed(HRESULT inHR)
|
||||
{
|
||||
if (SUCCEEDED(inHR))
|
||||
return false;
|
||||
|
||||
Trace("Call failed with error code: %08X", inHR);
|
||||
JPH_ASSERT(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
JPH_NAMESPACE_END
|
||||
Reference in New Issue
Block a user