Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
// 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
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/ConstantBufferDX12.h>
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
ConstantBufferDX12::ConstantBufferDX12(RendererDX12 *inRenderer, uint64 inBufferSize) :
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
mBuffer = mRenderer->CreateD3DResourceOnUploadHeap(inBufferSize);
|
||||
mBufferSize = inBufferSize;
|
||||
}
|
||||
|
||||
ConstantBufferDX12::~ConstantBufferDX12()
|
||||
{
|
||||
if (mBuffer != nullptr)
|
||||
mRenderer->RecycleD3DResourceOnUploadHeap(mBuffer.Get(), mBufferSize);
|
||||
}
|
||||
|
||||
void *ConstantBufferDX12::MapInternal()
|
||||
{
|
||||
void *mapped_resource;
|
||||
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||
FatalErrorIfFailed(mBuffer->Map(0, &range, &mapped_resource));
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void ConstantBufferDX12::Unmap()
|
||||
{
|
||||
mBuffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void ConstantBufferDX12::Bind(int inSlot)
|
||||
{
|
||||
mRenderer->GetCommandList()->SetGraphicsRootConstantBufferView(inSlot, mBuffer->GetGPUVirtualAddress());
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
class RendererDX12;
|
||||
|
||||
/// A binary blob that can be used to pass constants to a shader
|
||||
class ConstantBufferDX12
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
ConstantBufferDX12(RendererDX12 *inRenderer, uint64 inBufferSize);
|
||||
~ConstantBufferDX12();
|
||||
|
||||
/// Map / unmap buffer (get pointer to data). This will discard all data in the buffer.
|
||||
template <typename T> T * Map() { return reinterpret_cast<T *>(MapInternal()); }
|
||||
void Unmap();
|
||||
|
||||
// Bind the constant buffer to a slot
|
||||
void Bind(int inSlot);
|
||||
|
||||
private:
|
||||
friend class RendererDX12;
|
||||
|
||||
void * MapInternal();
|
||||
|
||||
RendererDX12 * mRenderer;
|
||||
ComPtr<ID3D12Resource> mBuffer;
|
||||
uint64 mBufferSize;
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
/// DirectX descriptor heap, used to allocate handles for resources to bind them to shaders
|
||||
class DescriptorHeapDX12
|
||||
{
|
||||
public:
|
||||
/// Initialize the heap
|
||||
/// @param inDevice The DirectX device
|
||||
/// @param inType Type of heap
|
||||
/// @param inFlags Flags for the heap
|
||||
/// @param inNumber Number of handles to reserve
|
||||
void Init(ID3D12Device *inDevice, D3D12_DESCRIPTOR_HEAP_TYPE inType, D3D12_DESCRIPTOR_HEAP_FLAGS inFlags, uint inNumber)
|
||||
{
|
||||
// Create the heap
|
||||
D3D12_DESCRIPTOR_HEAP_DESC heap_desc = {};
|
||||
heap_desc.NumDescriptors = inNumber;
|
||||
heap_desc.Type = inType;
|
||||
heap_desc.Flags = inFlags;
|
||||
FatalErrorIfFailed(inDevice->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&mHeap)));
|
||||
|
||||
// Delta between descriptor elements
|
||||
mDescriptorSize = inDevice->GetDescriptorHandleIncrementSize(heap_desc.Type);
|
||||
|
||||
// Delta between the CPU and GPU heap
|
||||
if (inFlags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
|
||||
mGPUOffset = mHeap->GetGPUDescriptorHandleForHeapStart().ptr - mHeap->GetCPUDescriptorHandleForHeapStart().ptr;
|
||||
|
||||
// Populate the freelist
|
||||
mFreeList.reserve(inNumber);
|
||||
for (uint i = 0; i < inNumber; ++i)
|
||||
mFreeList.push_back(i);
|
||||
}
|
||||
|
||||
/// Allocate and return a new handle
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE Allocate()
|
||||
{
|
||||
JPH_ASSERT(!mFreeList.empty());
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle = mHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
|
||||
uint index = mFreeList.back();
|
||||
mFreeList.pop_back();
|
||||
|
||||
handle.ptr += index * mDescriptorSize;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Free a handle and return it to the freelist
|
||||
void Free(D3D12_CPU_DESCRIPTOR_HANDLE inHandle)
|
||||
{
|
||||
uint index = uint((inHandle.ptr - mHeap->GetCPUDescriptorHandleForHeapStart().ptr) / mDescriptorSize);
|
||||
|
||||
mFreeList.push_back(index);
|
||||
}
|
||||
|
||||
/// Convert from a CPU to a GPU handle
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE ConvertToGPUHandle(D3D12_CPU_DESCRIPTOR_HANDLE inHandle)
|
||||
{
|
||||
JPH_ASSERT(mGPUOffset != -1);
|
||||
return { UINT64(inHandle.ptr) + mGPUOffset };
|
||||
}
|
||||
|
||||
/// Access to the underlying DirectX structure
|
||||
ID3D12DescriptorHeap * Get()
|
||||
{
|
||||
return mHeap.Get();
|
||||
}
|
||||
|
||||
private:
|
||||
ComPtr<ID3D12DescriptorHeap> mHeap;
|
||||
uint mDescriptorSize; ///< The size (in bytes) of a single heap descriptor
|
||||
Array<uint> mFreeList; ///< List of indices in the heap that are still free
|
||||
INT64 mGPUOffset = -1; ///< Offset between CPU and GPU handles
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
#include <Jolt/Core/StringTools.h>
|
||||
#include <Utils/Log.h>
|
||||
|
||||
void FatalErrorIfFailed(HRESULT inHResult)
|
||||
{
|
||||
if (FAILED(inHResult))
|
||||
{
|
||||
string message = system_category().message(inHResult);
|
||||
FatalError("DirectX error returned: %s (%s)", ConvertToString(inHResult).c_str(), message.c_str());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
/// Convert DirectX error to readable text and alert
|
||||
void FatalErrorIfFailed(HRESULT inHResult);
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/PipelineStateDX12.h>
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/DX12/VertexShaderDX12.h>
|
||||
#include <Renderer/DX12/PixelShaderDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
PipelineStateDX12::PipelineStateDX12(RendererDX12 *inRenderer, const VertexShaderDX12 *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderDX12 *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode) :
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
D3D12_PRIMITIVE_TOPOLOGY_TYPE topology = inTopology == ETopology::Triangle? D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE : D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
|
||||
|
||||
Array<D3D12_INPUT_ELEMENT_DESC> input_description;
|
||||
uint vertex_offset = 0, instance_offset = 0;
|
||||
for (uint i = 0; i < inInputDescriptionCount; ++i)
|
||||
switch (inInputDescription[i])
|
||||
{
|
||||
case EInputDescription::Position:
|
||||
input_description.push_back({ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, vertex_offset, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
|
||||
vertex_offset += 3 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::Color:
|
||||
input_description.push_back({ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, vertex_offset, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
|
||||
vertex_offset += 4 * sizeof(uint8);
|
||||
break;
|
||||
|
||||
case EInputDescription::Normal:
|
||||
input_description.push_back({ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, vertex_offset, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
|
||||
vertex_offset += 3 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::TexCoord:
|
||||
input_description.push_back({ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, vertex_offset, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
|
||||
vertex_offset += 2 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceColor:
|
||||
input_description.push_back({ "INSTANCE_COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 1, instance_offset, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 });
|
||||
instance_offset += 4 * sizeof(uint8);
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceTransform:
|
||||
{
|
||||
for (uint j = 0; j < 4; ++j)
|
||||
{
|
||||
input_description.push_back({ "INSTANCE_TRANSFORM", j, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, instance_offset, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 });
|
||||
instance_offset += 4 * sizeof(float);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EInputDescription::InstanceInvTransform:
|
||||
{
|
||||
for (uint j = 0; j < 4; ++j)
|
||||
{
|
||||
input_description.push_back({ "INSTANCE_INV_TRANSFORM", j, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, instance_offset, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 });
|
||||
instance_offset += 4 * sizeof(float);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = {};
|
||||
pso_desc.InputLayout = { input_description.data(), (UINT)input_description.size() };
|
||||
pso_desc.pRootSignature = mRenderer->GetRootSignature();
|
||||
pso_desc.VS = { inVertexShader->mShader->GetBufferPointer(), inVertexShader->mShader->GetBufferSize() };
|
||||
pso_desc.PS = { inPixelShader->mShader->GetBufferPointer(), inPixelShader->mShader->GetBufferSize() };
|
||||
|
||||
pso_desc.RasterizerState.FillMode = inFillMode == EFillMode::Solid? D3D12_FILL_MODE_SOLID : D3D12_FILL_MODE_WIREFRAME;
|
||||
pso_desc.RasterizerState.CullMode = inCullMode == ECullMode::Backface? D3D12_CULL_MODE_FRONT : D3D12_CULL_MODE_BACK; // DX uses left handed system so we reverse the options
|
||||
pso_desc.RasterizerState.FrontCounterClockwise = FALSE;
|
||||
pso_desc.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
|
||||
pso_desc.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
|
||||
pso_desc.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
|
||||
pso_desc.RasterizerState.DepthClipEnable = TRUE;
|
||||
pso_desc.RasterizerState.MultisampleEnable = FALSE;
|
||||
pso_desc.RasterizerState.AntialiasedLineEnable = FALSE;
|
||||
pso_desc.RasterizerState.ForcedSampleCount = 0;
|
||||
pso_desc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
|
||||
|
||||
pso_desc.BlendState.AlphaToCoverageEnable = FALSE;
|
||||
pso_desc.BlendState.IndependentBlendEnable = FALSE;
|
||||
|
||||
D3D12_RENDER_TARGET_BLEND_DESC &blend_desc = pso_desc.BlendState.RenderTarget[0];
|
||||
blend_desc.LogicOpEnable = FALSE;
|
||||
blend_desc.LogicOp = D3D12_LOGIC_OP_NOOP;
|
||||
blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
|
||||
switch (inBlendMode)
|
||||
{
|
||||
case EBlendMode::Write:
|
||||
blend_desc.BlendEnable = FALSE;
|
||||
break;
|
||||
|
||||
case EBlendMode::AlphaBlend:
|
||||
blend_desc.BlendEnable = TRUE;
|
||||
blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
|
||||
blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
|
||||
blend_desc.BlendOp = D3D12_BLEND_OP_ADD;
|
||||
blend_desc.SrcBlendAlpha = D3D12_BLEND_ZERO;
|
||||
blend_desc.DestBlendAlpha = D3D12_BLEND_ZERO;
|
||||
blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
||||
break;
|
||||
}
|
||||
|
||||
pso_desc.DepthStencilState.DepthEnable = inDepthTest == EDepthTest::On? TRUE : FALSE;
|
||||
pso_desc.DepthStencilState.DepthWriteMask = inDepthTest == EDepthTest::On? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
|
||||
pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER_EQUAL;
|
||||
pso_desc.DepthStencilState.StencilEnable = FALSE;
|
||||
|
||||
pso_desc.SampleMask = UINT_MAX;
|
||||
pso_desc.PrimitiveTopologyType = topology;
|
||||
pso_desc.NumRenderTargets = 1;
|
||||
pso_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
|
||||
pso_desc.SampleDesc.Count = 1;
|
||||
|
||||
FatalErrorIfFailed(mRenderer->GetDevice()->CreateGraphicsPipelineState(&pso_desc, IID_PPV_ARGS(&mPSO)));
|
||||
}
|
||||
|
||||
PipelineStateDX12::~PipelineStateDX12()
|
||||
{
|
||||
if (mPSO != nullptr)
|
||||
mRenderer->RecycleD3DObject(mPSO.Get());
|
||||
}
|
||||
|
||||
void PipelineStateDX12::Activate()
|
||||
{
|
||||
mRenderer->GetCommandList()->SetPipelineState(mPSO.Get());
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PipelineState.h>
|
||||
|
||||
class RendererDX12;
|
||||
class VertexShaderDX12;
|
||||
class PixelShaderDX12;
|
||||
|
||||
/// DirectX 12 pipeline state object
|
||||
class PipelineStateDX12 : public PipelineState
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PipelineStateDX12(RendererDX12 *inRenderer, const VertexShaderDX12 *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderDX12 *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode);
|
||||
virtual ~PipelineStateDX12() override;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() override;
|
||||
|
||||
private:
|
||||
RendererDX12 * mRenderer;
|
||||
ComPtr<ID3D12PipelineState> mPSO;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PixelShader.h>
|
||||
|
||||
/// Pixel shader handle for DirectX
|
||||
class PixelShaderDX12 : public PixelShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PixelShaderDX12(ComPtr<ID3DBlob> inShader) : mShader(inShader) { }
|
||||
|
||||
ComPtr<ID3DBlob> mShader; ///< The compiled shader
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/RenderInstancesDX12.h>
|
||||
#include <Renderer/DX12/RenderPrimitiveDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
void RenderInstancesDX12::Clear()
|
||||
{
|
||||
if (mInstanceBuffer != nullptr)
|
||||
mRenderer->RecycleD3DResourceOnUploadHeap(mInstanceBuffer.Get(), mInstanceBufferSize);
|
||||
|
||||
mInstanceBuffer = nullptr;
|
||||
mInstanceBufferSize = 0;
|
||||
mInstanceSize = 0;
|
||||
}
|
||||
|
||||
void RenderInstancesDX12::CreateBuffer(int inNumInstances, int inInstanceSize)
|
||||
{
|
||||
uint new_size = uint(inNumInstances) * inInstanceSize;
|
||||
if (mInstanceBuffer == nullptr || mInstanceBufferSize < new_size)
|
||||
{
|
||||
// Delete the old buffer
|
||||
Clear();
|
||||
|
||||
// Calculate size
|
||||
mInstanceBufferSize = new_size;
|
||||
|
||||
// Create buffer
|
||||
mInstanceBuffer = mRenderer->CreateD3DResourceOnUploadHeap(mInstanceBufferSize);
|
||||
JPH_IF_DEBUG(mInstanceBuffer->SetName(L"Instance Buffer");)
|
||||
}
|
||||
|
||||
// Update parameters
|
||||
mInstanceSize = inInstanceSize;
|
||||
}
|
||||
|
||||
void *RenderInstancesDX12::Lock()
|
||||
{
|
||||
uint32 *mapped_resource;
|
||||
D3D12_RANGE range = { 0, 0 };
|
||||
mInstanceBuffer->Map(0, &range, (void **)&mapped_resource);
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void RenderInstancesDX12::Unlock()
|
||||
{
|
||||
mInstanceBuffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void RenderInstancesDX12::Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const
|
||||
{
|
||||
if (inNumInstances <= 0)
|
||||
return;
|
||||
|
||||
RenderPrimitiveDX12 *primitive = static_cast<RenderPrimitiveDX12 *>(inPrimitive);
|
||||
|
||||
ID3D12GraphicsCommandList *command_list = mRenderer->GetCommandList();
|
||||
|
||||
// Set topology
|
||||
command_list->IASetPrimitiveTopology(primitive->mType == PipelineState::ETopology::Triangle? D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST : D3D_PRIMITIVE_TOPOLOGY_LINELIST);
|
||||
|
||||
D3D12_VERTEX_BUFFER_VIEW vb_view[2];
|
||||
|
||||
// Vertex buffer
|
||||
vb_view[0].BufferLocation = primitive->mVtxBuffer->GetGPUVirtualAddress();
|
||||
vb_view[0].StrideInBytes = primitive->mVtxSize;
|
||||
vb_view[0].SizeInBytes = primitive->mNumVtxToDraw * primitive->mVtxSize;
|
||||
|
||||
// Instances buffer
|
||||
vb_view[1].BufferLocation = mInstanceBuffer->GetGPUVirtualAddress();
|
||||
vb_view[1].StrideInBytes = mInstanceSize;
|
||||
vb_view[1].SizeInBytes = mInstanceBufferSize;
|
||||
|
||||
command_list->IASetVertexBuffers(0, 2, &vb_view[0]);
|
||||
|
||||
if (primitive->mIdxBuffer == nullptr)
|
||||
{
|
||||
// Draw instanced primitive
|
||||
command_list->DrawInstanced(primitive->mNumVtxToDraw, inNumInstances, 0, inStartInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set index buffer
|
||||
D3D12_INDEX_BUFFER_VIEW ib_view;
|
||||
ib_view.BufferLocation = primitive->mIdxBuffer->GetGPUVirtualAddress();
|
||||
ib_view.SizeInBytes = primitive->mNumIdxToDraw * sizeof(uint32);
|
||||
ib_view.Format = DXGI_FORMAT_R32_UINT;
|
||||
command_list->IASetIndexBuffer(&ib_view);
|
||||
|
||||
// Draw instanced primitive
|
||||
command_list->DrawIndexedInstanced(primitive->mNumIdxToDraw, inNumInstances, 0, 0, inStartInstance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// DirectX 12 implementation of a render instances object
|
||||
class RenderInstancesDX12 : public RenderInstances
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderInstancesDX12(RendererDX12 *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderInstancesDX12() override { Clear(); }
|
||||
|
||||
/// Erase all instance data
|
||||
virtual void Clear() override;
|
||||
|
||||
/// Instance buffer management functions
|
||||
virtual void CreateBuffer(int inNumInstances, int inInstanceSize) override;
|
||||
virtual void * Lock() override;
|
||||
virtual void Unlock() override;
|
||||
|
||||
/// Draw the instances when context has been set by Renderer::BindShader
|
||||
virtual void Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const override;
|
||||
|
||||
private:
|
||||
RendererDX12 * mRenderer;
|
||||
|
||||
ComPtr<ID3D12Resource> mInstanceBuffer;
|
||||
uint mInstanceBufferSize = 0;
|
||||
int mInstanceSize = 0;
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/RenderPrimitiveDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
void RenderPrimitiveDX12::ReleaseVertexBuffer()
|
||||
{
|
||||
if (mVtxBuffer != nullptr)
|
||||
{
|
||||
if (mVtxBufferInUploadHeap)
|
||||
mRenderer->RecycleD3DResourceOnUploadHeap(mVtxBuffer.Get(), mNumVtx * mVtxSize);
|
||||
else
|
||||
mRenderer->RecycleD3DObject(mVtxBuffer.Get());
|
||||
mVtxBuffer = nullptr;
|
||||
}
|
||||
|
||||
mVtxBufferInUploadHeap = false;
|
||||
RenderPrimitive::ReleaseVertexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::ReleaseIndexBuffer()
|
||||
{
|
||||
if (mIdxBuffer != nullptr)
|
||||
{
|
||||
if (mIdxBufferInUploadHeap)
|
||||
mRenderer->RecycleD3DResourceOnUploadHeap(mIdxBuffer.Get(), mNumIdx * sizeof(uint32));
|
||||
else
|
||||
mRenderer->RecycleD3DObject(mIdxBuffer.Get());
|
||||
mIdxBuffer = nullptr;
|
||||
}
|
||||
|
||||
mIdxBufferInUploadHeap = false;
|
||||
RenderPrimitive::ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
RenderPrimitive::CreateVertexBuffer(inNumVtx, inVtxSize, inData);
|
||||
|
||||
uint64 size = uint64(inNumVtx) * inVtxSize;
|
||||
|
||||
if (inData != nullptr)
|
||||
{
|
||||
// Data provided, assume the buffer is static so allocate it on the GPU
|
||||
mVtxBuffer = mRenderer->CreateD3DResourceOnDefaultHeap(inData, size);
|
||||
mVtxBufferInUploadHeap = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No data provided, create a buffer that will be uploaded to the GPU every time it is used
|
||||
mVtxBuffer = mRenderer->CreateD3DResourceOnUploadHeap(size);
|
||||
mVtxBufferInUploadHeap = true;
|
||||
}
|
||||
|
||||
JPH_IF_DEBUG(mVtxBuffer->SetName(L"Vertex Buffer");)
|
||||
}
|
||||
|
||||
void *RenderPrimitiveDX12::LockVertexBuffer()
|
||||
{
|
||||
void *mapped_resource;
|
||||
D3D12_RANGE range = { 0, 0 };
|
||||
FatalErrorIfFailed(mVtxBuffer->Map(0, &range, &mapped_resource));
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::UnlockVertexBuffer()
|
||||
{
|
||||
mVtxBuffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
RenderPrimitive::CreateIndexBuffer(inNumIdx, inData);
|
||||
|
||||
uint64 size = uint64(inNumIdx) * sizeof(uint32);
|
||||
|
||||
if (inData != nullptr)
|
||||
{
|
||||
// Data provided, assume the buffer is static so allocate it on the GPU
|
||||
mIdxBuffer = mRenderer->CreateD3DResourceOnDefaultHeap(inData, size);
|
||||
mIdxBufferInUploadHeap = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No data provided, create a buffer that will be uploaded to the GPU every time it is used
|
||||
mIdxBuffer = mRenderer->CreateD3DResourceOnUploadHeap(size);
|
||||
mIdxBufferInUploadHeap = true;
|
||||
}
|
||||
|
||||
JPH_IF_DEBUG(mIdxBuffer->SetName(L"Index Buffer");)
|
||||
}
|
||||
|
||||
uint32 *RenderPrimitiveDX12::LockIndexBuffer()
|
||||
{
|
||||
uint32 *mapped_resource;
|
||||
D3D12_RANGE range = { 0, 0 };
|
||||
FatalErrorIfFailed(mIdxBuffer->Map(0, &range, (void **)&mapped_resource));
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::UnlockIndexBuffer()
|
||||
{
|
||||
mIdxBuffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::Draw() const
|
||||
{
|
||||
ID3D12GraphicsCommandList *command_list = mRenderer->GetCommandList();
|
||||
|
||||
// Set topology
|
||||
command_list->IASetPrimitiveTopology(mType == PipelineState::ETopology::Triangle? D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST : D3D_PRIMITIVE_TOPOLOGY_LINELIST);
|
||||
|
||||
if (mIdxBuffer == nullptr)
|
||||
{
|
||||
// Set vertex buffer
|
||||
D3D12_VERTEX_BUFFER_VIEW vb_view;
|
||||
vb_view.BufferLocation = mVtxBuffer->GetGPUVirtualAddress();
|
||||
vb_view.StrideInBytes = mVtxSize;
|
||||
vb_view.SizeInBytes = mNumVtxToDraw * mVtxSize;
|
||||
command_list->IASetVertexBuffers(0, 1, &vb_view);
|
||||
|
||||
// Draw the non indexed primitive
|
||||
command_list->DrawInstanced(mNumVtxToDraw, 1, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set vertex buffer
|
||||
D3D12_VERTEX_BUFFER_VIEW vb_view;
|
||||
vb_view.BufferLocation = mVtxBuffer->GetGPUVirtualAddress();
|
||||
vb_view.StrideInBytes = mVtxSize;
|
||||
vb_view.SizeInBytes = mNumVtx * mVtxSize;
|
||||
command_list->IASetVertexBuffers(0, 1, &vb_view);
|
||||
|
||||
// Set index buffer
|
||||
D3D12_INDEX_BUFFER_VIEW ib_view;
|
||||
ib_view.BufferLocation = mIdxBuffer->GetGPUVirtualAddress();
|
||||
ib_view.SizeInBytes = mNumIdxToDraw * sizeof(uint32);
|
||||
ib_view.Format = DXGI_FORMAT_R32_UINT;
|
||||
command_list->IASetIndexBuffer(&ib_view);
|
||||
|
||||
// Draw indexed primitive
|
||||
command_list->DrawIndexedInstanced(mNumIdxToDraw, 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
|
||||
/// DirectX 12 implementation of a render primitive
|
||||
class RenderPrimitiveDX12 : public RenderPrimitive
|
||||
{
|
||||
public:
|
||||
JPH_OVERRIDE_NEW_DELETE
|
||||
|
||||
/// Constructor
|
||||
RenderPrimitiveDX12(RendererDX12 *inRenderer, PipelineState::ETopology inType) : mRenderer(inRenderer), mType(inType) { }
|
||||
virtual ~RenderPrimitiveDX12() override { Clear(); }
|
||||
|
||||
/// Vertex buffer management functions
|
||||
virtual void CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData = nullptr) override;
|
||||
virtual void ReleaseVertexBuffer() override;
|
||||
virtual void * LockVertexBuffer() override;
|
||||
virtual void UnlockVertexBuffer() override;
|
||||
|
||||
/// Index buffer management functions
|
||||
virtual void CreateIndexBuffer(int inNumIdx, const uint32 *inData = nullptr) override;
|
||||
virtual void ReleaseIndexBuffer() override;
|
||||
virtual uint32 * LockIndexBuffer() override;
|
||||
virtual void UnlockIndexBuffer() override;
|
||||
|
||||
/// Draw the primitive
|
||||
virtual void Draw() const override;
|
||||
|
||||
private:
|
||||
friend class RenderInstancesDX12;
|
||||
|
||||
RendererDX12 * mRenderer;
|
||||
|
||||
PipelineState::ETopology mType;
|
||||
|
||||
ComPtr<ID3D12Resource> mVtxBuffer;
|
||||
bool mVtxBufferInUploadHeap = false;
|
||||
|
||||
ComPtr<ID3D12Resource> mIdxBuffer;
|
||||
bool mIdxBufferInUploadHeap = false;
|
||||
};
|
||||
719
lib/All/JoltPhysics/TestFramework/Renderer/DX12/RendererDX12.cpp
Normal file
719
lib/All/JoltPhysics/TestFramework/Renderer/DX12/RendererDX12.cpp
Normal file
@@ -0,0 +1,719 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/DX12/RenderPrimitiveDX12.h>
|
||||
#include <Renderer/DX12/PipelineStateDX12.h>
|
||||
#include <Renderer/DX12/VertexShaderDX12.h>
|
||||
#include <Renderer/DX12/PixelShaderDX12.h>
|
||||
#include <Renderer/DX12/TextureDX12.h>
|
||||
#include <Renderer/DX12/RenderInstancesDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
#include <Window/ApplicationWindowWin.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
#include <Utils/ReadData.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
|
||||
#include <d3dcompiler.h>
|
||||
#ifdef JPH_DEBUG
|
||||
#include <d3d12sdklayers.h>
|
||||
#endif
|
||||
|
||||
RendererDX12::~RendererDX12()
|
||||
{
|
||||
// Ensure that the GPU is no longer referencing resources that are about to be cleaned up by the destructor.
|
||||
WaitForGpu();
|
||||
|
||||
// Don't add more stuff to the delay reference list
|
||||
mIsExiting = true;
|
||||
|
||||
CloseHandle(mFenceEvent);
|
||||
}
|
||||
|
||||
void RendererDX12::WaitForGpu()
|
||||
{
|
||||
// Schedule a Signal command in the queue
|
||||
UINT64 current_fence_value = mFenceValues[mFrameIndex];
|
||||
FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), current_fence_value));
|
||||
|
||||
// Wait until the fence has been processed
|
||||
FatalErrorIfFailed(mFence->SetEventOnCompletion(current_fence_value, mFenceEvent));
|
||||
WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
|
||||
|
||||
// Increment the fence value for all frames
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
mFenceValues[n] = current_fence_value + 1;
|
||||
|
||||
// Release all used resources
|
||||
for (Array<ComPtr<ID3D12Object>> &list : mDelayReleased)
|
||||
list.clear();
|
||||
|
||||
// Anything that's not used yet can be removed, delayed objects are now available
|
||||
mResourceCache.clear();
|
||||
mDelayCached[mFrameIndex].swap(mResourceCache);
|
||||
}
|
||||
|
||||
void RendererDX12::CreateRenderTargets()
|
||||
{
|
||||
// Create render targets and views
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
{
|
||||
mRenderTargetViews[n] = mRTVHeap.Allocate();
|
||||
|
||||
FatalErrorIfFailed(mSwapChain->GetBuffer(n, IID_PPV_ARGS(&mRenderTargets[n])));
|
||||
mDevice->CreateRenderTargetView(mRenderTargets[n].Get(), nullptr, mRenderTargetViews[n]);
|
||||
}
|
||||
}
|
||||
|
||||
void RendererDX12::CreateDepthBuffer()
|
||||
{
|
||||
// Free any previous depth stencil view
|
||||
if (mDepthStencilView.ptr != 0)
|
||||
mDSVHeap.Free(mDepthStencilView);
|
||||
|
||||
// Free any previous depth stencil buffer
|
||||
mDepthStencilBuffer.Reset();
|
||||
|
||||
// Allocate depth stencil buffer
|
||||
D3D12_CLEAR_VALUE clear_value = {};
|
||||
clear_value.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
clear_value.DepthStencil.Depth = 0.0f;
|
||||
clear_value.DepthStencil.Stencil = 0;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heap_properties.CreationNodeMask = 1;
|
||||
heap_properties.VisibleNodeMask = 1;
|
||||
|
||||
D3D12_RESOURCE_DESC depth_stencil_desc = {};
|
||||
depth_stencil_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
depth_stencil_desc.Alignment = 0;
|
||||
depth_stencil_desc.Width = mWindow->GetWindowWidth();
|
||||
depth_stencil_desc.Height = mWindow->GetWindowHeight();
|
||||
depth_stencil_desc.DepthOrArraySize = 1;
|
||||
depth_stencil_desc.MipLevels = 1;
|
||||
depth_stencil_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
depth_stencil_desc.SampleDesc.Count = 1;
|
||||
depth_stencil_desc.SampleDesc.Quality = 0;
|
||||
depth_stencil_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
depth_stencil_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
|
||||
FatalErrorIfFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &depth_stencil_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clear_value, IID_PPV_ARGS(&mDepthStencilBuffer)));
|
||||
|
||||
// Allocate depth stencil view
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc = {};
|
||||
depth_stencil_view_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
depth_stencil_view_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
depth_stencil_view_desc.Flags = D3D12_DSV_FLAG_NONE;
|
||||
|
||||
mDepthStencilView = mDSVHeap.Allocate();
|
||||
mDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &depth_stencil_view_desc, mDepthStencilView);
|
||||
}
|
||||
|
||||
void RendererDX12::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
Renderer::Initialize(inWindow);
|
||||
|
||||
#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
|
||||
FatalErrorIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mDXGIFactory)));
|
||||
|
||||
// Find adapter
|
||||
ComPtr<IDXGIAdapter1> adapter;
|
||||
|
||||
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 (UINT index = 0; DXGI_ERROR_NOT_FOUND != factory6->EnumAdapterByGpuPreference(index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)); ++index)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
|
||||
// We don't want software renderers
|
||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
|
||||
continue;
|
||||
|
||||
// Check to see whether the adapter supports Direct3D 12
|
||||
result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice));
|
||||
if (SUCCEEDED(result))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back to the older method that may not get the fastest GPU
|
||||
for (UINT index = 0; DXGI_ERROR_NOT_FOUND != mDXGIFactory->EnumAdapters1(index, &adapter); ++index)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
|
||||
// We don't want software renderers
|
||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
|
||||
continue;
|
||||
|
||||
// Check to see whether the adapter supports Direct3D 12
|
||||
result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice));
|
||||
if (SUCCEEDED(result))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we managed to obtain a device
|
||||
FatalErrorIfFailed(result);
|
||||
|
||||
#ifdef JPH_DEBUG
|
||||
// Enable breaking on errors
|
||||
ComPtr<ID3D12InfoQueue> info_queue;
|
||||
if (SUCCEEDED(mDevice.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
|
||||
|
||||
// Disable full screen transitions
|
||||
FatalErrorIfFailed(mDXGIFactory->MakeWindowAssociation(static_cast<ApplicationWindowWin *>(mWindow)->GetWindowHandle(), DXGI_MWA_NO_ALT_ENTER));
|
||||
|
||||
// Create heaps
|
||||
mRTVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 2);
|
||||
mDSVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 4);
|
||||
mSRVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128);
|
||||
|
||||
// Create a command queue
|
||||
D3D12_COMMAND_QUEUE_DESC queue_desc = {};
|
||||
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
FatalErrorIfFailed(mDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue)));
|
||||
|
||||
// Create a command allocator for each frame
|
||||
for (uint n = 0; n < cFrameCount; n++)
|
||||
FatalErrorIfFailed(mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocators[n])));
|
||||
|
||||
// Describe and create the swap chain
|
||||
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
|
||||
swap_chain_desc.BufferCount = cFrameCount;
|
||||
swap_chain_desc.BufferDesc.Width = mWindow->GetWindowWidth();
|
||||
swap_chain_desc.BufferDesc.Height = mWindow->GetWindowHeight();
|
||||
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
swap_chain_desc.OutputWindow = static_cast<ApplicationWindowWin *>(mWindow)->GetWindowHandle();
|
||||
swap_chain_desc.SampleDesc.Count = 1;
|
||||
swap_chain_desc.Windowed = TRUE;
|
||||
|
||||
ComPtr<IDXGISwapChain> swap_chain;
|
||||
FatalErrorIfFailed(mDXGIFactory->CreateSwapChain(mCommandQueue.Get(), &swap_chain_desc, &swap_chain));
|
||||
FatalErrorIfFailed(swap_chain.As(&mSwapChain));
|
||||
mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
|
||||
|
||||
CreateRenderTargets();
|
||||
|
||||
CreateDepthBuffer();
|
||||
|
||||
// Create a root signature suitable for all our shaders
|
||||
D3D12_ROOT_PARAMETER params[3] = {};
|
||||
|
||||
// Mapping a constant buffer to slot 0 for the vertex shader
|
||||
params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
params[0].Descriptor.ShaderRegister = 0;
|
||||
params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
||||
|
||||
// Mapping a constant buffer to slot 1 in the pixel shader
|
||||
params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
params[1].Descriptor.ShaderRegister = 1;
|
||||
params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
// Mapping a texture to slot 2 in the pixel shader
|
||||
D3D12_DESCRIPTOR_RANGE range = {};
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
range.BaseShaderRegister = 2;
|
||||
range.NumDescriptors = 1;
|
||||
params[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
params[2].DescriptorTable.NumDescriptorRanges = 1;
|
||||
params[2].DescriptorTable.pDescriptorRanges = ⦥
|
||||
params[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
D3D12_STATIC_SAMPLER_DESC samplers[3] = {};
|
||||
|
||||
// Sampler 0: Non-wrapping linear filtering
|
||||
samplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
samplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplers[0].MipLODBias = 0.0f;
|
||||
samplers[0].MaxAnisotropy = 1;
|
||||
samplers[0].ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
samplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
|
||||
samplers[0].MinLOD = 0.0f;
|
||||
samplers[0].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
samplers[0].ShaderRegister = 0;
|
||||
samplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
// Sampler 1: Wrapping and linear filtering
|
||||
samplers[1] = samplers[0];
|
||||
samplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
samplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
samplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
samplers[1].ShaderRegister = 1;
|
||||
|
||||
// Sampler 2: Point filtering, using SampleCmp mode to compare if sampled value >= reference value (for shadows)
|
||||
samplers[2] = samplers[0];
|
||||
samplers[2].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
|
||||
samplers[2].ComparisonFunc = D3D12_COMPARISON_FUNC_GREATER_EQUAL;
|
||||
samplers[2].ShaderRegister = 2;
|
||||
|
||||
D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {};
|
||||
root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
||||
root_signature_desc.NumParameters = ARRAYSIZE(params);
|
||||
root_signature_desc.pParameters = params;
|
||||
root_signature_desc.NumStaticSamplers = ARRAYSIZE(samplers);
|
||||
root_signature_desc.pStaticSamplers = samplers;
|
||||
|
||||
ComPtr<ID3DBlob> signature;
|
||||
ComPtr<ID3DBlob> error;
|
||||
FatalErrorIfFailed(D3D12SerializeRootSignature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
|
||||
FatalErrorIfFailed(mDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&mRootSignature)));
|
||||
|
||||
// Create the command list
|
||||
FatalErrorIfFailed(mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocators[mFrameIndex].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(mDevice->CreateFence(mFenceValues[mFrameIndex], 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
|
||||
mFenceValues[mFrameIndex]++;
|
||||
|
||||
// Create an event handle to use for frame synchronization
|
||||
mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
if (mFenceEvent == nullptr)
|
||||
FatalErrorIfFailed(HRESULT_FROM_WIN32(GetLastError()));
|
||||
|
||||
// Initialize the queue used to upload resources to the GPU
|
||||
mUploadQueue.Initialize(mDevice.Get());
|
||||
|
||||
// Create constant buffer. One per frame to avoid overwriting the constant buffer while the GPU is still using it.
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
{
|
||||
mVertexShaderConstantBufferProjection[n] = CreateConstantBuffer(sizeof(VertexShaderConstantBuffer));
|
||||
mVertexShaderConstantBufferOrtho[n] = CreateConstantBuffer(sizeof(VertexShaderConstantBuffer));
|
||||
mPixelShaderConstantBuffer[n] = CreateConstantBuffer(sizeof(PixelShaderConstantBuffer));
|
||||
}
|
||||
|
||||
// Create depth only texture (no color buffer, as seen from light)
|
||||
mShadowMap = new TextureDX12(this, cShadowMapSize, cShadowMapSize);
|
||||
}
|
||||
|
||||
void RendererDX12::OnWindowResize()
|
||||
{
|
||||
// Wait for the previous frame to be rendered
|
||||
WaitForGpu();
|
||||
|
||||
// Free the render targets and views to allow resizing the swap chain
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
{
|
||||
mRTVHeap.Free(mRenderTargetViews[n]);
|
||||
mRenderTargets[n].Reset();
|
||||
}
|
||||
|
||||
// Resize the swap chain buffers
|
||||
FatalErrorIfFailed(mSwapChain->ResizeBuffers(cFrameCount, mWindow->GetWindowWidth(), mWindow->GetWindowHeight(), DXGI_FORMAT_R8G8B8A8_UNORM, 0));
|
||||
|
||||
// Back buffer index may have changed after the resize (it always seems to go to 0 again)
|
||||
mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
|
||||
|
||||
// Since we may have switched frame index and we know everything is done, we need to update the fence value for our other frame as completed
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
if (mFrameIndex != n)
|
||||
mFenceValues[n] = mFence->GetCompletedValue();
|
||||
|
||||
// Recreate render targets
|
||||
CreateRenderTargets();
|
||||
|
||||
// Recreate depth buffer
|
||||
CreateDepthBuffer();
|
||||
}
|
||||
|
||||
bool RendererDX12::BeginFrame(const CameraState &inCamera, float inWorldScale)
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
Renderer::BeginFrame(inCamera, inWorldScale);
|
||||
|
||||
// Reset command allocator
|
||||
FatalErrorIfFailed(mCommandAllocators[mFrameIndex]->Reset());
|
||||
|
||||
// Reset command list
|
||||
FatalErrorIfFailed(mCommandList->Reset(mCommandAllocators[mFrameIndex].Get(), nullptr));
|
||||
|
||||
// Set root signature
|
||||
mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
|
||||
|
||||
// Set SRV heap
|
||||
ID3D12DescriptorHeap *heaps[] = { mSRVHeap.Get() };
|
||||
mCommandList->SetDescriptorHeaps(_countof(heaps), heaps);
|
||||
|
||||
// Indicate that the back buffer will be used as a render target.
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mRenderTargets[mFrameIndex].Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
mCommandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Clear the back buffer.
|
||||
const float blue[] = { 0.098f, 0.098f, 0.439f, 1.000f };
|
||||
mCommandList->ClearRenderTargetView(mRenderTargetViews[mFrameIndex], blue, 0, nullptr);
|
||||
mCommandList->ClearDepthStencilView(mDepthStencilView, D3D12_CLEAR_FLAG_DEPTH, 0.0f, 0, 0, nullptr);
|
||||
|
||||
// Set constants for vertex shader in projection mode
|
||||
VertexShaderConstantBuffer *vs = mVertexShaderConstantBufferProjection[mFrameIndex]->Map<VertexShaderConstantBuffer>();
|
||||
*vs = mVSBuffer;
|
||||
mVertexShaderConstantBufferProjection[mFrameIndex]->Unmap();
|
||||
|
||||
// Set constants for vertex shader in ortho mode
|
||||
vs = mVertexShaderConstantBufferOrtho[mFrameIndex]->Map<VertexShaderConstantBuffer>();
|
||||
*vs = mVSBufferOrtho;
|
||||
mVertexShaderConstantBufferOrtho[mFrameIndex]->Unmap();
|
||||
|
||||
// Switch to 3d projection mode
|
||||
SetProjectionMode();
|
||||
|
||||
// Set constants for pixel shader
|
||||
PixelShaderConstantBuffer *ps = mPixelShaderConstantBuffer[mFrameIndex]->Map<PixelShaderConstantBuffer>();
|
||||
*ps = mPSBuffer;
|
||||
mPixelShaderConstantBuffer[mFrameIndex]->Unmap();
|
||||
|
||||
// Set the pixel shader constant buffer data.
|
||||
mPixelShaderConstantBuffer[mFrameIndex]->Bind(1);
|
||||
|
||||
// Start drawing the shadow pass
|
||||
mShadowMap->SetAsRenderTarget(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererDX12::EndShadowPass()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
// Finish drawing the shadow pass
|
||||
mShadowMap->SetAsRenderTarget(false);
|
||||
|
||||
// Set the main back buffer as render target
|
||||
mCommandList->OMSetRenderTargets(1, &mRenderTargetViews[mFrameIndex], FALSE, &mDepthStencilView);
|
||||
|
||||
// Set viewport
|
||||
D3D12_VIEWPORT viewport = { 0.0f, 0.0f, static_cast<float>(mWindow->GetWindowWidth()), static_cast<float>(mWindow->GetWindowHeight()), 0.0f, 1.0f };
|
||||
mCommandList->RSSetViewports(1, &viewport);
|
||||
|
||||
// Set scissor rect
|
||||
D3D12_RECT scissor_rect = { 0, 0, static_cast<LONG>(mWindow->GetWindowWidth()), static_cast<LONG>(mWindow->GetWindowHeight()) };
|
||||
mCommandList->RSSetScissorRects(1, &scissor_rect);
|
||||
}
|
||||
|
||||
void RendererDX12::EndFrame()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
Renderer::EndFrame();
|
||||
|
||||
// Indicate that the back buffer will now be used to present.
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mRenderTargets[mFrameIndex].Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
mCommandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Close the command list
|
||||
FatalErrorIfFailed(mCommandList->Close());
|
||||
|
||||
// Execute the command list
|
||||
ID3D12CommandList* command_lists[] = { mCommandList.Get() };
|
||||
mCommandQueue->ExecuteCommandLists(_countof(command_lists), command_lists);
|
||||
|
||||
// Present the frame
|
||||
FatalErrorIfFailed(mSwapChain->Present(1, 0));
|
||||
|
||||
// Schedule a Signal command in the queue
|
||||
UINT64 current_fence_value = mFenceValues[mFrameIndex];
|
||||
FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), current_fence_value));
|
||||
|
||||
// Update the frame index
|
||||
mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
|
||||
|
||||
// If the next frame is not ready to be rendered yet, wait until it is ready
|
||||
UINT64 completed_value = mFence->GetCompletedValue();
|
||||
if (completed_value < mFenceValues[mFrameIndex])
|
||||
{
|
||||
FatalErrorIfFailed(mFence->SetEventOnCompletion(mFenceValues[mFrameIndex], mFenceEvent));
|
||||
WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
|
||||
}
|
||||
|
||||
// Release all used resources
|
||||
mDelayReleased[mFrameIndex].clear();
|
||||
|
||||
// Anything that's not used yet can be removed, delayed objects are now available
|
||||
mResourceCache.clear();
|
||||
mDelayCached[mFrameIndex].swap(mResourceCache);
|
||||
|
||||
// Set the fence value for the next frame.
|
||||
mFenceValues[mFrameIndex] = current_fence_value + 1;
|
||||
}
|
||||
|
||||
|
||||
void RendererDX12::SetProjectionMode()
|
||||
{
|
||||
JPH_ASSERT(mInFrame);
|
||||
|
||||
mVertexShaderConstantBufferProjection[mFrameIndex]->Bind(0);
|
||||
}
|
||||
|
||||
void RendererDX12::SetOrthoMode()
|
||||
{
|
||||
JPH_ASSERT(mInFrame);
|
||||
|
||||
mVertexShaderConstantBufferOrtho[mFrameIndex]->Bind(0);
|
||||
}
|
||||
|
||||
Ref<Texture> RendererDX12::CreateTexture(const Surface *inSurface)
|
||||
{
|
||||
return new TextureDX12(this, inSurface);
|
||||
}
|
||||
|
||||
Ref<VertexShader> RendererDX12::CreateVertexShader(const char *inName)
|
||||
{
|
||||
UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
#ifdef JPH_DEBUG
|
||||
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
#endif
|
||||
|
||||
const D3D_SHADER_MACRO defines[] =
|
||||
{
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
// Read shader source file
|
||||
String file_name = String("Shaders/DX/") + inName + ".hlsl";
|
||||
Array<uint8> data = ReadData(file_name.c_str());
|
||||
|
||||
// Compile source
|
||||
ComPtr<ID3DBlob> shader_blob, error_blob;
|
||||
HRESULT hr = D3DCompile(&data[0],
|
||||
(uint)data.size(),
|
||||
(AssetStream::sGetAssetsBasePath() + file_name).c_str(),
|
||||
defines,
|
||||
D3D_COMPILE_STANDARD_FILE_INCLUDE,
|
||||
"main",
|
||||
"vs_5_0",
|
||||
flags,
|
||||
0,
|
||||
shader_blob.GetAddressOf(),
|
||||
error_blob.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Throw error if compilation failed
|
||||
if (error_blob)
|
||||
OutputDebugStringA((const char *)error_blob->GetBufferPointer());
|
||||
FatalError("Failed to compile vertex shader");
|
||||
}
|
||||
|
||||
return new VertexShaderDX12(shader_blob);
|
||||
}
|
||||
|
||||
Ref<PixelShader> RendererDX12::CreatePixelShader(const char *inName)
|
||||
{
|
||||
UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
#ifdef JPH_DEBUG
|
||||
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
#endif
|
||||
|
||||
const D3D_SHADER_MACRO defines[] =
|
||||
{
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
// Read shader source file
|
||||
String file_name = String("Shaders/DX/") + inName + ".hlsl";
|
||||
Array<uint8> data = ReadData(file_name.c_str());
|
||||
|
||||
// Compile source
|
||||
ComPtr<ID3DBlob> shader_blob, error_blob;
|
||||
HRESULT hr = D3DCompile(&data[0],
|
||||
(uint)data.size(),
|
||||
(AssetStream::sGetAssetsBasePath() + file_name).c_str(),
|
||||
defines,
|
||||
D3D_COMPILE_STANDARD_FILE_INCLUDE,
|
||||
"main",
|
||||
"ps_5_0",
|
||||
flags,
|
||||
0,
|
||||
shader_blob.GetAddressOf(),
|
||||
error_blob.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Throw error if compilation failed
|
||||
if (error_blob)
|
||||
OutputDebugStringA((const char *)error_blob->GetBufferPointer());
|
||||
FatalError("Failed to compile pixel shader");
|
||||
}
|
||||
|
||||
return new PixelShaderDX12(shader_blob);
|
||||
}
|
||||
|
||||
unique_ptr<ConstantBufferDX12> RendererDX12::CreateConstantBuffer(uint inBufferSize)
|
||||
{
|
||||
return make_unique<ConstantBufferDX12>(this, inBufferSize);
|
||||
}
|
||||
|
||||
unique_ptr<PipelineState> RendererDX12::CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode)
|
||||
{
|
||||
return make_unique<PipelineStateDX12>(this, static_cast<const VertexShaderDX12 *>(inVertexShader), inInputDescription, inInputDescriptionCount, static_cast<const PixelShaderDX12 *>(inPixelShader), inDrawPass, inFillMode, inTopology, inDepthTest, inBlendMode, inCullMode);
|
||||
}
|
||||
|
||||
RenderPrimitive *RendererDX12::CreateRenderPrimitive(PipelineState::ETopology inType)
|
||||
{
|
||||
return new RenderPrimitiveDX12(this, inType);
|
||||
}
|
||||
|
||||
RenderInstances *RendererDX12::CreateRenderInstances()
|
||||
{
|
||||
return new RenderInstancesDX12(this);
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> RendererDX12::CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, 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 = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
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;
|
||||
FatalErrorIfFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &desc, inResourceState, nullptr, IID_PPV_ARGS(&resource)));
|
||||
return resource;
|
||||
}
|
||||
|
||||
void RendererDX12::CopyD3DResource(ID3D12Resource *inDest, const void *inSrc, uint64 inSize)
|
||||
{
|
||||
// Copy data to destination buffer
|
||||
void *data;
|
||||
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||
FatalErrorIfFailed(inDest->Map(0, &range, &data));
|
||||
memcpy(data, inSrc, size_t(inSize));
|
||||
inDest->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void RendererDX12::CopyD3DResource(ID3D12Resource *inDest, ID3D12Resource *inSrc, uint64 inSize)
|
||||
{
|
||||
// Start a commandlist for the upload
|
||||
ID3D12GraphicsCommandList *list = mUploadQueue.Start();
|
||||
|
||||
// Copy the data to the GPU
|
||||
list->CopyBufferRegion(inDest, 0, inSrc, 0, inSize);
|
||||
|
||||
// Change the state of the resource to generic read
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = inDest;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_GENERIC_READ;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
list->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Wait for copying to finish
|
||||
mUploadQueue.ExecuteAndWait();
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> RendererDX12::CreateD3DResourceOnDefaultHeap(const void *inData, uint64 inSize)
|
||||
{
|
||||
ComPtr<ID3D12Resource> upload = CreateD3DResourceOnUploadHeap(inSize);
|
||||
ComPtr<ID3D12Resource> resource = CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, inSize);
|
||||
CopyD3DResource(upload.Get(), inData, inSize);
|
||||
CopyD3DResource(resource.Get(), upload.Get(), inSize);
|
||||
RecycleD3DResourceOnUploadHeap(upload.Get(), inSize);
|
||||
return resource;
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> RendererDX12::CreateD3DResourceOnUploadHeap(uint64 inSize)
|
||||
{
|
||||
// Try cache first
|
||||
ResourceCache::iterator i = mResourceCache.find(inSize);
|
||||
if (i != mResourceCache.end() && !i->second.empty())
|
||||
{
|
||||
ComPtr<ID3D12Resource> resource = i->second.back();
|
||||
i->second.pop_back();
|
||||
return resource;
|
||||
}
|
||||
|
||||
return CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, inSize);
|
||||
}
|
||||
|
||||
void RendererDX12::RecycleD3DResourceOnUploadHeap(ID3D12Resource *inResource, uint64 inSize)
|
||||
{
|
||||
if (!mIsExiting)
|
||||
mDelayCached[mFrameIndex][inSize].push_back(inResource);
|
||||
}
|
||||
|
||||
void RendererDX12::RecycleD3DObject(ID3D12Object *inResource)
|
||||
{
|
||||
if (!mIsExiting)
|
||||
mDelayReleased[mFrameIndex].push_back(inResource);
|
||||
}
|
||||
|
||||
#ifndef JPH_ENABLE_VULKAN
|
||||
Renderer *Renderer::sCreate()
|
||||
{
|
||||
return new RendererDX12;
|
||||
}
|
||||
#endif
|
||||
111
lib/All/JoltPhysics/TestFramework/Renderer/DX12/RendererDX12.h
Normal file
111
lib/All/JoltPhysics/TestFramework/Renderer/DX12/RendererDX12.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/DX12/CommandQueueDX12.h>
|
||||
#include <Renderer/DX12/DescriptorHeapDX12.h>
|
||||
#include <Renderer/DX12/ConstantBufferDX12.h>
|
||||
#include <Renderer/DX12/TextureDX12.h>
|
||||
|
||||
/// DirectX 12 renderer
|
||||
class RendererDX12 : public Renderer
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~RendererDX12() override;
|
||||
|
||||
// See: Renderer
|
||||
virtual void Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual bool BeginFrame(const CameraState &inCamera, float inWorldScale) override;
|
||||
virtual void EndShadowPass() override;
|
||||
virtual void EndFrame() override;
|
||||
virtual void SetProjectionMode() override;
|
||||
virtual void SetOrthoMode() override;
|
||||
virtual Ref<Texture> CreateTexture(const Surface *inSurface) override;
|
||||
virtual Ref<VertexShader> CreateVertexShader(const char *inName) override;
|
||||
virtual Ref<PixelShader> CreatePixelShader(const char *inName) override;
|
||||
virtual unique_ptr<PipelineState> CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode) override;
|
||||
virtual RenderPrimitive * CreateRenderPrimitive(PipelineState::ETopology inType) override;
|
||||
virtual RenderInstances * CreateRenderInstances() override;
|
||||
virtual Texture * GetShadowMap() const override { return mShadowMap.GetPtr(); }
|
||||
virtual void OnWindowResize() override;
|
||||
|
||||
/// Create a constant buffer
|
||||
unique_ptr<ConstantBufferDX12> CreateConstantBuffer(uint inBufferSize);
|
||||
|
||||
/// Create a buffer on the default heap (usable for permanent buffers)
|
||||
ComPtr<ID3D12Resource> CreateD3DResourceOnDefaultHeap(const void *inData, uint64 inSize);
|
||||
|
||||
/// Create buffer on the upload heap (usable for temporary buffers).
|
||||
ComPtr<ID3D12Resource> CreateD3DResourceOnUploadHeap(uint64 inSize);
|
||||
|
||||
/// Recycle a buffer on the upload heap. This puts it back in a cache and will reuse it when it is certain the GPU is no longer referencing it.
|
||||
void RecycleD3DResourceOnUploadHeap(ID3D12Resource *inResource, uint64 inSize);
|
||||
|
||||
/// Keeps a reference to the resource until the current frame has finished
|
||||
void RecycleD3DObject(ID3D12Object *inResource);
|
||||
|
||||
/// Access to the most important DirectX structures
|
||||
ID3D12Device * GetDevice() { return mDevice.Get(); }
|
||||
ID3D12RootSignature * GetRootSignature() { return mRootSignature.Get(); }
|
||||
ID3D12GraphicsCommandList * GetCommandList() { JPH_ASSERT(mInFrame); return mCommandList.Get(); }
|
||||
CommandQueueDX12 & GetUploadQueue() { return mUploadQueue; }
|
||||
DescriptorHeapDX12 & GetDSVHeap() { return mDSVHeap; }
|
||||
DescriptorHeapDX12 & GetSRVHeap() { return mSRVHeap; }
|
||||
|
||||
private:
|
||||
// Wait for pending GPU work to complete
|
||||
void WaitForGpu();
|
||||
|
||||
// Create render targets and their views
|
||||
void CreateRenderTargets();
|
||||
|
||||
// Create a depth buffer for the back buffer
|
||||
void CreateDepthBuffer();
|
||||
|
||||
// Function to create a ID3D12Resource on specified heap with specified state
|
||||
ComPtr<ID3D12Resource> CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, uint64 inSize);
|
||||
|
||||
// Copy CPU memory into a ID3D12Resource
|
||||
void CopyD3DResource(ID3D12Resource *inDest, const void *inSrc, uint64 inSize);
|
||||
|
||||
// Copy a CPU resource to a GPU resource
|
||||
void CopyD3DResource(ID3D12Resource *inDest, ID3D12Resource *inSrc, uint64 inSize);
|
||||
|
||||
// DirectX interfaces
|
||||
ComPtr<IDXGIFactory4> mDXGIFactory;
|
||||
ComPtr<ID3D12Device> mDevice;
|
||||
DescriptorHeapDX12 mRTVHeap; ///< Render target view heap
|
||||
DescriptorHeapDX12 mDSVHeap; ///< Depth stencil view heap
|
||||
DescriptorHeapDX12 mSRVHeap; ///< Shader resource view heap
|
||||
ComPtr<IDXGISwapChain3> mSwapChain;
|
||||
ComPtr<ID3D12Resource> mRenderTargets[cFrameCount]; ///< Two render targets (we're double buffering in order for the CPU to continue while the GPU is rendering)
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE mRenderTargetViews[cFrameCount]; ///< The two render views corresponding to the render targets
|
||||
ComPtr<ID3D12Resource> mDepthStencilBuffer; ///< The main depth buffer
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE mDepthStencilView { 0 }; ///< A view for binding the depth buffer
|
||||
ComPtr<ID3D12CommandAllocator> mCommandAllocators[cFrameCount]; ///< Two command allocator lists (one per frame)
|
||||
ComPtr<ID3D12CommandQueue> mCommandQueue; ///< The command queue that will execute commands (there's only 1 since we want to finish rendering 1 frame before moving onto the next)
|
||||
ComPtr<ID3D12GraphicsCommandList> mCommandList; ///< The command list
|
||||
ComPtr<ID3D12RootSignature> mRootSignature; ///< The root signature, we have a simple application so we only need 1, which is suitable for all our shaders
|
||||
Ref<TextureDX12> mShadowMap; ///< Used to render shadow maps
|
||||
CommandQueueDX12 mUploadQueue; ///< Queue used to upload resources to GPU memory
|
||||
unique_ptr<ConstantBufferDX12> mVertexShaderConstantBufferProjection[cFrameCount];
|
||||
unique_ptr<ConstantBufferDX12> mVertexShaderConstantBufferOrtho[cFrameCount];
|
||||
unique_ptr<ConstantBufferDX12> mPixelShaderConstantBuffer[cFrameCount];
|
||||
|
||||
// Synchronization objects used to finish rendering and swapping before reusing a command queue
|
||||
HANDLE mFenceEvent; ///< Fence event to wait for the previous frame rendering to complete (in order to free 1 of the buffers)
|
||||
ComPtr<ID3D12Fence> mFence; ///< Fence object, used to signal the end of a frame
|
||||
UINT64 mFenceValues[cFrameCount] = {}; ///< Values that were used to signal completion of one of the two frames
|
||||
|
||||
using ResourceCache = UnorderedMap<uint64, Array<ComPtr<ID3D12Resource>>>;
|
||||
|
||||
ResourceCache mResourceCache; ///< Cache items ready to be reused
|
||||
ResourceCache mDelayCached[cFrameCount]; ///< List of reusable ID3D12Resources that are potentially referenced by the GPU so can be used only when the GPU finishes
|
||||
Array<ComPtr<ID3D12Object>> mDelayReleased[cFrameCount]; ///< Objects that are potentially referenced by the GPU so can only be freed when the GPU finishes
|
||||
bool mIsExiting = false; ///< When exiting we don't want to add references too buffers
|
||||
};
|
||||
236
lib/All/JoltPhysics/TestFramework/Renderer/DX12/TextureDX12.cpp
Normal file
236
lib/All/JoltPhysics/TestFramework/Renderer/DX12/TextureDX12.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/TextureDX12.h>
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
|
||||
TextureDX12::TextureDX12(RendererDX12 *inRenderer, const Surface *inSurface) :
|
||||
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
// Create description
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.MipLevels = 1;
|
||||
ESurfaceFormat format = inSurface->GetFormat();
|
||||
switch (format)
|
||||
{
|
||||
case ESurfaceFormat::A4L4: desc.Format = DXGI_FORMAT_R8G8_UNORM; format = ESurfaceFormat::A8L8; break;
|
||||
case ESurfaceFormat::L8: desc.Format = DXGI_FORMAT_R8_UNORM; break;
|
||||
case ESurfaceFormat::A8: desc.Format = DXGI_FORMAT_A8_UNORM; break;
|
||||
case ESurfaceFormat::A8L8: desc.Format = DXGI_FORMAT_R8G8_UNORM; break;
|
||||
case ESurfaceFormat::R5G6B5: desc.Format = DXGI_FORMAT_B5G6R5_UNORM; break;
|
||||
case ESurfaceFormat::X1R5G5B5: desc.Format = DXGI_FORMAT_B5G5R5A1_UNORM; format = ESurfaceFormat::A1R5G5B5; break;
|
||||
case ESurfaceFormat::X4R4G4B4: desc.Format = DXGI_FORMAT_B4G4R4A4_UNORM; format = ESurfaceFormat::A4R4G4B4; break;
|
||||
case ESurfaceFormat::A1R5G5B5: desc.Format = DXGI_FORMAT_B5G5R5A1_UNORM; break;
|
||||
case ESurfaceFormat::A4R4G4B4: desc.Format = DXGI_FORMAT_B4G4R4A4_UNORM; break;
|
||||
case ESurfaceFormat::R8G8B8: desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
||||
case ESurfaceFormat::B8G8R8: desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
||||
case ESurfaceFormat::X8R8G8B8: desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; break;
|
||||
case ESurfaceFormat::X8B8G8R8: desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
||||
case ESurfaceFormat::A8R8G8B8: desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; break;
|
||||
case ESurfaceFormat::A8B8G8R8: desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::Invalid:
|
||||
default: JPH_ASSERT(false); break;
|
||||
}
|
||||
desc.Width = mWidth;
|
||||
desc.Height = mHeight;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
|
||||
// Blit the surface to another temporary surface if the format changed
|
||||
const Surface *surface = inSurface;
|
||||
Ref<Surface> tmp;
|
||||
if (format != inSurface->GetFormat())
|
||||
{
|
||||
tmp = new SoftwareSurface(mWidth, mHeight, format);
|
||||
BlitSurface(inSurface, tmp);
|
||||
surface = tmp;
|
||||
}
|
||||
|
||||
// Create texture in default heap
|
||||
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heap_properties.CreationNodeMask = 1;
|
||||
heap_properties.VisibleNodeMask = 1;
|
||||
FatalErrorIfFailed(inRenderer->GetDevice()->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&mTexture)));
|
||||
JPH_IF_DEBUG(mTexture->SetName(L"Texture");)
|
||||
|
||||
// Determine required size of data to copy
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
|
||||
UINT64 row_size_in_bytes;
|
||||
UINT64 required_size = 0;
|
||||
inRenderer->GetDevice()->GetCopyableFootprints(&desc, 0, 1, 0, &footprint, nullptr, &row_size_in_bytes, &required_size);
|
||||
|
||||
// Create the GPU upload buffer
|
||||
ComPtr<ID3D12Resource> upload_resource = mRenderer->CreateD3DResourceOnUploadHeap(required_size);
|
||||
JPH_IF_DEBUG(upload_resource->SetName(L"Texture Upload");)
|
||||
|
||||
// Copy data to upload texture
|
||||
surface->Lock(ESurfaceLockMode::Read);
|
||||
uint8 *upload_data;
|
||||
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||
FatalErrorIfFailed(upload_resource->Map(0, &range, (void **)&upload_data));
|
||||
for (int y = 0; y < mHeight; ++y)
|
||||
memcpy(upload_data + y * row_size_in_bytes, surface->GetData() + y * surface->GetStride(), surface->GetBytesPerPixel() * mWidth);
|
||||
upload_resource->Unmap(0, nullptr);
|
||||
surface->UnLock();
|
||||
|
||||
// Start a commandlist for the upload
|
||||
ID3D12GraphicsCommandList *list = inRenderer->GetUploadQueue().Start();
|
||||
|
||||
// Copy the texture from our upload buffer to our final texture
|
||||
D3D12_TEXTURE_COPY_LOCATION copy_dst;
|
||||
copy_dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
copy_dst.pResource = mTexture.Get();
|
||||
copy_dst.SubresourceIndex = 0;
|
||||
D3D12_TEXTURE_COPY_LOCATION copy_src;
|
||||
copy_src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
copy_src.pResource = upload_resource.Get();
|
||||
copy_src.PlacedFootprint = footprint;
|
||||
list->CopyTextureRegion(©_dst, 0, 0, 0, ©_src, nullptr);
|
||||
|
||||
// Indicate that the texture is now ready to be used by a pixel shader
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mTexture.Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
list->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Create a SRV for the texture
|
||||
mSRV = inRenderer->GetSRVHeap().Allocate();
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
|
||||
srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srv_desc.Format = desc.Format;
|
||||
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srv_desc.Texture2D.MipLevels = 1;
|
||||
inRenderer->GetDevice()->CreateShaderResourceView(mTexture.Get(), &srv_desc, mSRV);
|
||||
|
||||
// Wait for copying to finish so we can destroy the upload texture
|
||||
inRenderer->GetUploadQueue().ExecuteAndWait();
|
||||
|
||||
// Recycle the upload buffer
|
||||
inRenderer->RecycleD3DResourceOnUploadHeap(upload_resource.Get(), required_size);
|
||||
}
|
||||
|
||||
TextureDX12::TextureDX12(RendererDX12 *inRenderer, int inWidth, int inHeight) :
|
||||
Texture(inWidth, inHeight),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
// Allocate depth stencil buffer
|
||||
D3D12_CLEAR_VALUE clear_value = {};
|
||||
clear_value.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
clear_value.DepthStencil.Depth = 0;
|
||||
clear_value.DepthStencil.Stencil = 0;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heap_properties.CreationNodeMask = 1;
|
||||
heap_properties.VisibleNodeMask = 1;
|
||||
|
||||
D3D12_RESOURCE_DESC depth_stencil_desc = {};
|
||||
depth_stencil_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
depth_stencil_desc.Alignment = 0;
|
||||
depth_stencil_desc.Width = mWidth;
|
||||
depth_stencil_desc.Height = mHeight;
|
||||
depth_stencil_desc.DepthOrArraySize = 1;
|
||||
depth_stencil_desc.MipLevels = 1;
|
||||
depth_stencil_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
depth_stencil_desc.SampleDesc.Count = 1;
|
||||
depth_stencil_desc.SampleDesc.Quality = 0;
|
||||
depth_stencil_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
depth_stencil_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
|
||||
FatalErrorIfFailed(inRenderer->GetDevice()->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &depth_stencil_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, &clear_value, IID_PPV_ARGS(&mTexture)));
|
||||
JPH_IF_DEBUG(mTexture->SetName(L"Render Target Texture");)
|
||||
|
||||
// Create DSV for the texture
|
||||
mDSV = inRenderer->GetDSVHeap().Allocate();
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc = {};
|
||||
dsv_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
dsv_desc.Flags = D3D12_DSV_FLAG_NONE;
|
||||
inRenderer->GetDevice()->CreateDepthStencilView(mTexture.Get(), &dsv_desc, mDSV);
|
||||
|
||||
// Create a SRV for the texture
|
||||
mSRV = inRenderer->GetSRVHeap().Allocate();
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
|
||||
srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
|
||||
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srv_desc.Texture2D.MipLevels = 1;
|
||||
inRenderer->GetDevice()->CreateShaderResourceView(mTexture.Get(), &srv_desc, mSRV);
|
||||
}
|
||||
|
||||
TextureDX12::~TextureDX12()
|
||||
{
|
||||
if (mSRV.ptr != 0)
|
||||
mRenderer->GetSRVHeap().Free(mSRV);
|
||||
|
||||
if (mDSV.ptr != 0)
|
||||
mRenderer->GetDSVHeap().Free(mDSV);
|
||||
|
||||
if (mTexture != nullptr)
|
||||
mRenderer->RecycleD3DObject(mTexture.Get());
|
||||
}
|
||||
|
||||
void TextureDX12::Bind() const
|
||||
{
|
||||
mRenderer->GetCommandList()->SetGraphicsRootDescriptorTable(2 /* All shaders use slot 2 to bind their texture */, mRenderer->GetSRVHeap().ConvertToGPUHandle(mSRV));
|
||||
}
|
||||
|
||||
void TextureDX12::SetAsRenderTarget(bool inSet) const
|
||||
{
|
||||
if (inSet)
|
||||
{
|
||||
// Indicate make the texture ready for rendering to
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mTexture.Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
mRenderer->GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Set as render target
|
||||
mRenderer->GetCommandList()->OMSetRenderTargets(0, nullptr, FALSE, &mDSV);
|
||||
|
||||
// Set view port
|
||||
D3D12_VIEWPORT viewport = { 0.0f, 0.0f, static_cast<float>(mWidth), static_cast<float>(mHeight), 0.0f, 1.0f };
|
||||
mRenderer->GetCommandList()->RSSetViewports(1, &viewport);
|
||||
|
||||
// Set scissor rect
|
||||
D3D12_RECT scissor_rect = { 0, 0, static_cast<LONG>(mWidth), static_cast<LONG>(mHeight) };
|
||||
mRenderer->GetCommandList()->RSSetScissorRects(1, &scissor_rect);
|
||||
|
||||
// Clear the render target
|
||||
mRenderer->GetCommandList()->ClearDepthStencilView(mDSV, D3D12_CLEAR_FLAG_DEPTH, 0, 0, 0, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Indicate that the texture is now ready to be used by a pixel shader
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mTexture.Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
mRenderer->GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Texture.h>
|
||||
|
||||
class RendererDX12;
|
||||
|
||||
class TextureDX12 : public Texture
|
||||
{
|
||||
public:
|
||||
/// Constructor, called by Renderer::CreateTextureDX12
|
||||
TextureDX12(RendererDX12 *inRenderer, const Surface *inSurface); // Create a normal TextureDX12
|
||||
TextureDX12(RendererDX12 *inRenderer, int inWidth, int inHeight); // Create a render target (depth only)
|
||||
virtual ~TextureDX12() override;
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const override;
|
||||
|
||||
/// Activate this texture as the current render target, used by RendererDX12::BeginFrame, EndShadowPass
|
||||
void SetAsRenderTarget(bool inSet) const;
|
||||
|
||||
private:
|
||||
RendererDX12 * mRenderer;
|
||||
|
||||
ComPtr<ID3D12Resource> mTexture; ///< The texture data
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE mSRV { 0 }; ///< Shader resource view to bind as texture
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE mDSV { 0 }; ///< Depth shader view to bind as render target
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VertexShader.h>
|
||||
|
||||
/// Vertex shader handle for DirectX
|
||||
class VertexShaderDX12 : public VertexShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
VertexShaderDX12(ComPtr<ID3DBlob> inShader) : mShader(inShader) { }
|
||||
|
||||
ComPtr<ID3DBlob> mShader; ///< The compiled shader
|
||||
};
|
||||
Reference in New Issue
Block a user