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
|
||||
};
|
||||
511
lib/All/JoltPhysics/TestFramework/Renderer/DebugRendererImp.cpp
Normal file
511
lib/All/JoltPhysics/TestFramework/Renderer/DebugRendererImp.cpp
Normal file
@@ -0,0 +1,511 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/Font.h>
|
||||
|
||||
#ifndef JPH_DEBUG_RENDERER
|
||||
// Hack to still compile DebugRenderer inside the test framework when Jolt is compiled without
|
||||
#define JPH_DEBUG_RENDERER
|
||||
#include <Jolt/Renderer/DebugRenderer.cpp>
|
||||
#undef JPH_DEBUG_RENDERER
|
||||
#endif // !JPH_DEBUG_RENDERER
|
||||
|
||||
DebugRendererImp::DebugRendererImp(Renderer *inRenderer, const Font *inFont) :
|
||||
mRenderer(inRenderer),
|
||||
mFont(inFont)
|
||||
{
|
||||
// Create input layout for lines
|
||||
const PipelineState::EInputDescription line_vertex_desc[] =
|
||||
{
|
||||
PipelineState::EInputDescription::Position,
|
||||
PipelineState::EInputDescription::Color
|
||||
};
|
||||
|
||||
// Lines
|
||||
Ref<VertexShader> vtx_line = mRenderer->CreateVertexShader("LineVertexShader");
|
||||
Ref<PixelShader> pix_line = mRenderer->CreatePixelShader("LinePixelShader");
|
||||
mLineState = mRenderer->CreatePipelineState(vtx_line, line_vertex_desc, std::size(line_vertex_desc), pix_line, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Solid, PipelineState::ETopology::Line, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
|
||||
// Create input layout for triangles
|
||||
const PipelineState::EInputDescription triangles_vertex_desc[] =
|
||||
{
|
||||
PipelineState::EInputDescription::Position,
|
||||
PipelineState::EInputDescription::Normal,
|
||||
PipelineState::EInputDescription::TexCoord,
|
||||
PipelineState::EInputDescription::Color,
|
||||
PipelineState::EInputDescription::InstanceTransform,
|
||||
PipelineState::EInputDescription::InstanceInvTransform,
|
||||
PipelineState::EInputDescription::InstanceColor
|
||||
};
|
||||
|
||||
// Triangles
|
||||
Ref<VertexShader> vtx_triangle = mRenderer->CreateVertexShader("TriangleVertexShader");
|
||||
Ref<PixelShader> pix_triangle = mRenderer->CreatePixelShader("TrianglePixelShader");
|
||||
mTriangleStateBF = mRenderer->CreatePipelineState(vtx_triangle, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_triangle, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
mTriangleStateFF = mRenderer->CreatePipelineState(vtx_triangle, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_triangle, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::FrontFace);
|
||||
mTriangleStateWire = mRenderer->CreatePipelineState(vtx_triangle, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_triangle, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Wireframe, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
|
||||
// Shadow pass
|
||||
Ref<VertexShader> vtx_shadow = mRenderer->CreateVertexShader("TriangleDepthVertexShader");
|
||||
Ref<PixelShader> pix_shadow = mRenderer->CreatePixelShader("TriangleDepthPixelShader");
|
||||
mShadowStateBF = mRenderer->CreatePipelineState(vtx_shadow, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_shadow, PipelineState::EDrawPass::Shadow, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
mShadowStateFF = mRenderer->CreatePipelineState(vtx_shadow, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_shadow, PipelineState::EDrawPass::Shadow, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::FrontFace);
|
||||
mShadowStateWire = mRenderer->CreatePipelineState(vtx_shadow, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_shadow, PipelineState::EDrawPass::Shadow, PipelineState::EFillMode::Wireframe, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
|
||||
// Create instances buffer
|
||||
for (uint n = 0; n < Renderer::cFrameCount; ++n)
|
||||
mInstancesBuffer[n] = mRenderer->CreateRenderInstances();
|
||||
|
||||
// Create empty batch
|
||||
Vertex empty_vertex { Float3(0, 0, 0), Float3(1, 0, 0), Float2(0, 0), Color::sWhite };
|
||||
uint32 empty_indices[] = { 0, 0, 0 };
|
||||
mEmptyBatch = CreateTriangleBatch(&empty_vertex, 1, empty_indices, 3);
|
||||
|
||||
// Initialize base class
|
||||
DebugRenderer::Initialize();
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor)
|
||||
{
|
||||
RVec3 offset = mRenderer->GetBaseOffset();
|
||||
|
||||
Line line;
|
||||
Vec3(inFrom - offset).StoreFloat3(&line.mFrom);
|
||||
line.mFromColor = inColor;
|
||||
Vec3(inTo - offset).StoreFloat3(&line.mTo);
|
||||
line.mToColor = inColor;
|
||||
|
||||
lock_guard lock(mLinesLock);
|
||||
mLines.push_back(line);
|
||||
}
|
||||
|
||||
DebugRenderer::Batch DebugRendererImp::CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount)
|
||||
{
|
||||
if (inTriangles == nullptr || inTriangleCount == 0)
|
||||
return mEmptyBatch;
|
||||
|
||||
RenderPrimitive *primitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Triangle);
|
||||
primitive->CreateVertexBuffer(3 * inTriangleCount, sizeof(Vertex), inTriangles);
|
||||
|
||||
return primitive;
|
||||
}
|
||||
|
||||
DebugRenderer::Batch DebugRendererImp::CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount)
|
||||
{
|
||||
if (inVertices == nullptr || inVertexCount == 0 || inIndices == nullptr || inIndexCount == 0)
|
||||
return mEmptyBatch;
|
||||
|
||||
RenderPrimitive *primitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Triangle);
|
||||
primitive->CreateVertexBuffer(inVertexCount, sizeof(Vertex), inVertices);
|
||||
primitive->CreateIndexBuffer(inIndexCount, inIndices);
|
||||
|
||||
return primitive;
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode)
|
||||
{
|
||||
lock_guard lock(mPrimitivesLock);
|
||||
|
||||
RVec3 offset = mRenderer->GetBaseOffset();
|
||||
|
||||
Mat44 model_matrix = inModelMatrix.PostTranslated(-offset).ToMat44();
|
||||
AABox world_space_bounds = inWorldSpaceBounds;
|
||||
world_space_bounds.Translate(Vec3(-offset));
|
||||
|
||||
// Our pixel shader uses alpha only to turn on/off shadows
|
||||
Color color = inCastShadow == ECastShadow::On? Color(inModelColor, 255) : Color(inModelColor, 0);
|
||||
|
||||
if (inDrawMode == EDrawMode::Wireframe)
|
||||
{
|
||||
mWireframePrimitives[inGeometry].mInstances.push_back({ model_matrix, model_matrix.GetDirectionPreservingMatrix(), color, world_space_bounds, inLODScaleSq });
|
||||
++mNumInstances;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inCullMode != ECullMode::CullFrontFace)
|
||||
{
|
||||
mPrimitives[inGeometry].mInstances.push_back({ model_matrix, model_matrix.GetDirectionPreservingMatrix(), color, world_space_bounds, inLODScaleSq });
|
||||
++mNumInstances;
|
||||
}
|
||||
|
||||
if (inCullMode != ECullMode::CullBackFace)
|
||||
{
|
||||
mPrimitivesBackFacing[inGeometry].mInstances.push_back({ model_matrix, model_matrix.GetDirectionPreservingMatrix(), color, world_space_bounds, inLODScaleSq });
|
||||
++mNumInstances;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::FinalizePrimitive()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
if (mLockedPrimitive != nullptr)
|
||||
{
|
||||
// Unlock the primitive
|
||||
mLockedPrimitive->UnlockVertexBuffer();
|
||||
|
||||
// Set number of indices to draw
|
||||
mLockedPrimitive->SetNumVtxToDraw(int(mLockedVertices - mLockedVerticesStart));
|
||||
|
||||
// Add to draw list
|
||||
mTempPrimitives[new Geometry(mLockedPrimitive.GetPtr(), mLockedPrimitiveBounds)].mInstances.push_back({ Mat44::sIdentity(), Mat44::sIdentity(), Color::sWhite, mLockedPrimitiveBounds, 1.0f });
|
||||
++mNumInstances;
|
||||
|
||||
// Clear pointers
|
||||
mLockedPrimitive = nullptr;
|
||||
mLockedVerticesStart = nullptr;
|
||||
mLockedVertices = nullptr;
|
||||
mLockedVerticesEnd = nullptr;
|
||||
mLockedPrimitiveBounds = AABox();
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::EnsurePrimitiveSpace(int inVtxSize)
|
||||
{
|
||||
const int cVertexBufferSize = 10240;
|
||||
|
||||
if (mLockedPrimitive == nullptr
|
||||
|| mLockedVerticesEnd - mLockedVertices < inVtxSize)
|
||||
{
|
||||
FinalizePrimitive();
|
||||
|
||||
// Create new
|
||||
mLockedPrimitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Triangle);
|
||||
mLockedPrimitive->CreateVertexBuffer(cVertexBufferSize, sizeof(Vertex));
|
||||
|
||||
// Lock buffers
|
||||
mLockedVerticesStart = mLockedVertices = (Vertex *)mLockedPrimitive->LockVertexBuffer();
|
||||
mLockedVerticesEnd = mLockedVertices + cVertexBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow)
|
||||
{
|
||||
RVec3 offset = mRenderer->GetBaseOffset();
|
||||
|
||||
Vec3 v1(inV1 - offset);
|
||||
Vec3 v2(inV2 - offset);
|
||||
Vec3 v3(inV3 - offset);
|
||||
|
||||
lock_guard lock(mPrimitivesLock);
|
||||
|
||||
EnsurePrimitiveSpace(3);
|
||||
|
||||
// Set alpha to zero if we don't want to cast shadows to notify the pixel shader
|
||||
Color color(inColor, inCastShadow == ECastShadow::Off? 0 : 0xff);
|
||||
|
||||
// Construct triangle in separate buffer and then copy it to the target memory block (may be uncached memory)
|
||||
Triangle triangle(v1, v2, v3, color);
|
||||
*(Triangle *)mLockedVertices = triangle;
|
||||
mLockedVertices += 3;
|
||||
|
||||
// Update bounding box
|
||||
mLockedPrimitiveBounds.Encapsulate(v1);
|
||||
mLockedPrimitiveBounds.Encapsulate(v2);
|
||||
mLockedPrimitiveBounds.Encapsulate(v3);
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawInstances(const Geometry *inGeometry, const Array<int> &inStartIdx)
|
||||
{
|
||||
RenderInstances *instances_buffer = mInstancesBuffer[mRenderer->GetCurrentFrameIndex()];
|
||||
|
||||
if (!inStartIdx.empty())
|
||||
{
|
||||
// Get LODs
|
||||
const Array<LOD> &geometry_lods = inGeometry->mLODs;
|
||||
|
||||
// Write instances for all LODS
|
||||
int next_start_idx = inStartIdx.front();
|
||||
for (size_t lod = 0; lod < geometry_lods.size(); ++lod)
|
||||
{
|
||||
int start_idx = next_start_idx;
|
||||
next_start_idx = inStartIdx[lod + 1];
|
||||
int num_instances = next_start_idx - start_idx;
|
||||
instances_buffer->Draw(static_cast<RenderPrimitive *>(geometry_lods[lod].mTriangleBatch.GetPtr()), start_idx, num_instances);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight)
|
||||
{
|
||||
RVec3 offset = mRenderer->GetBaseOffset();
|
||||
|
||||
Vec3 pos(inPosition - offset);
|
||||
|
||||
lock_guard lock(mTextsLock);
|
||||
mTexts.emplace_back(pos, inString, inColor, inHeight);
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawLines()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
lock_guard lock(mLinesLock);
|
||||
|
||||
// Draw the lines
|
||||
if (!mLines.empty())
|
||||
{
|
||||
Ref<RenderPrimitive> primitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Line);
|
||||
primitive->CreateVertexBuffer((int)mLines.size() * 2, sizeof(Line) / 2);
|
||||
void *data = primitive->LockVertexBuffer();
|
||||
memcpy(data, &mLines[0], mLines.size() * sizeof(Line));
|
||||
primitive->UnlockVertexBuffer();
|
||||
mLineState->Activate();
|
||||
primitive->Draw();
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawShadowPass()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
lock_guard lock(mPrimitivesLock);
|
||||
|
||||
// Finish the last primitive
|
||||
FinalizePrimitive();
|
||||
|
||||
// Get the camera and light frustum for culling
|
||||
Vec3 camera_pos(mRenderer->GetCameraState().mPos - mRenderer->GetBaseOffset());
|
||||
const Frustum &camera_frustum = mRenderer->GetCameraFrustum();
|
||||
const Frustum &light_frustum = mRenderer->GetLightFrustum();
|
||||
|
||||
// Resize instances buffer and copy all visible instance data into it
|
||||
if (mNumInstances > 0)
|
||||
{
|
||||
// Create instances buffer
|
||||
RenderInstances *instances_buffer = mInstancesBuffer[mRenderer->GetCurrentFrameIndex()];
|
||||
instances_buffer->CreateBuffer(2 * mNumInstances, sizeof(Instance));
|
||||
Instance *dst_instance = reinterpret_cast<Instance *>(instances_buffer->Lock());
|
||||
|
||||
// Next write index
|
||||
int dst_index = 0;
|
||||
|
||||
// This keeps track of which instances use which lod, first array: 0 = light pass, 1 = geometry pass
|
||||
Array<Array<int>> lod_indices[2];
|
||||
|
||||
for (InstanceMap *primitive_map : { &mPrimitives, &mTempPrimitives, &mPrimitivesBackFacing, &mWireframePrimitives })
|
||||
for (InstanceMap::value_type &v : *primitive_map)
|
||||
{
|
||||
// Get LODs
|
||||
const Array<LOD> &geometry_lods = v.first->mLODs;
|
||||
size_t num_lods = geometry_lods.size();
|
||||
JPH_ASSERT(num_lods > 0);
|
||||
|
||||
// Ensure that our lod index array is big enough (to avoid reallocating memory too often)
|
||||
if (lod_indices[0].size() < num_lods)
|
||||
lod_indices[0].resize(num_lods);
|
||||
if (lod_indices[1].size() < num_lods)
|
||||
lod_indices[1].resize(num_lods);
|
||||
|
||||
// Iterate over all instances
|
||||
const Array<InstanceWithLODInfo> &instances = v.second.mInstances;
|
||||
for (size_t i = 0; i < instances.size(); ++i)
|
||||
{
|
||||
const InstanceWithLODInfo &src_instance = instances[i];
|
||||
|
||||
// Check if it overlaps with the light or camera frustum
|
||||
bool light_overlaps = light_frustum.Overlaps(src_instance.mWorldSpaceBounds);
|
||||
bool camera_overlaps = camera_frustum.Overlaps(src_instance.mWorldSpaceBounds);
|
||||
if (light_overlaps || camera_overlaps)
|
||||
{
|
||||
// Figure out which LOD to use
|
||||
const LOD &lod = v.first->GetLOD(camera_pos, src_instance.mWorldSpaceBounds, src_instance.mLODScaleSq);
|
||||
size_t lod_index = &lod - geometry_lods.data();
|
||||
|
||||
// Store which index goes in which LOD
|
||||
if (light_overlaps)
|
||||
lod_indices[0][lod_index].push_back((int)i);
|
||||
if (camera_overlaps)
|
||||
lod_indices[1][lod_index].push_back((int)i);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over both passes: 0 = light, 1 = geometry
|
||||
Array<int> *start_idx[] = { &v.second.mLightStartIdx, &v.second.mGeometryStartIdx };
|
||||
for (int type = 0; type < 2; ++type)
|
||||
{
|
||||
// Reserve space for instance indices
|
||||
Array<int> &type_start_idx = *start_idx[type];
|
||||
type_start_idx.resize(num_lods + 1);
|
||||
|
||||
// Write out geometry pass instances
|
||||
for (size_t lod = 0; lod < num_lods; ++lod)
|
||||
{
|
||||
// Write start index for this LOD
|
||||
type_start_idx[lod] = dst_index;
|
||||
|
||||
// Copy instances
|
||||
Array<int> &this_lod_indices = lod_indices[type][lod];
|
||||
for (int i : this_lod_indices)
|
||||
{
|
||||
const Instance &src_instance = instances[i];
|
||||
dst_instance[dst_index++] = src_instance;
|
||||
}
|
||||
|
||||
// Prepare for next iteration (will preserve memory)
|
||||
this_lod_indices.clear();
|
||||
}
|
||||
|
||||
// Write out end of last LOD
|
||||
type_start_idx.back() = dst_index;
|
||||
}
|
||||
}
|
||||
|
||||
instances_buffer->Unlock();
|
||||
}
|
||||
|
||||
if (!mPrimitives.empty() || !mTempPrimitives.empty())
|
||||
{
|
||||
// Front face culling, we want to render the back side of the geometry for casting shadows
|
||||
mShadowStateFF->Activate();
|
||||
|
||||
// Draw all primitives as seen from the light
|
||||
if (mNumInstances > 0)
|
||||
for (InstanceMap::value_type &v : mPrimitives)
|
||||
DrawInstances(v.first, v.second.mLightStartIdx);
|
||||
for (InstanceMap::value_type &v : mTempPrimitives)
|
||||
DrawInstances(v.first, v.second.mLightStartIdx);
|
||||
}
|
||||
|
||||
if (!mPrimitivesBackFacing.empty())
|
||||
{
|
||||
// Back face culling, we want to render the front side of back facing geometry
|
||||
mShadowStateBF->Activate();
|
||||
|
||||
// Draw all primitives as seen from the light
|
||||
for (InstanceMap::value_type &v : mPrimitivesBackFacing)
|
||||
DrawInstances(v.first, v.second.mLightStartIdx);
|
||||
}
|
||||
|
||||
if (!mWireframePrimitives.empty())
|
||||
{
|
||||
// Switch to wireframe mode
|
||||
mShadowStateWire->Activate();
|
||||
|
||||
// Draw all wireframe primitives as seen from the light
|
||||
for (InstanceMap::value_type &v : mWireframePrimitives)
|
||||
DrawInstances(v.first, v.second.mLightStartIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawTriangles()
|
||||
{
|
||||
// Bind the shadow map texture
|
||||
mRenderer->GetShadowMap()->Bind();
|
||||
|
||||
if (!mPrimitives.empty() || !mTempPrimitives.empty())
|
||||
{
|
||||
// Bind the normal shader, back face culling
|
||||
mTriangleStateBF->Activate();
|
||||
|
||||
// Draw all primitives
|
||||
if (mNumInstances > 0)
|
||||
for (InstanceMap::value_type &v : mPrimitives)
|
||||
DrawInstances(v.first, v.second.mGeometryStartIdx);
|
||||
for (InstanceMap::value_type &v : mTempPrimitives)
|
||||
DrawInstances(v.first, v.second.mGeometryStartIdx);
|
||||
}
|
||||
|
||||
if (!mPrimitivesBackFacing.empty())
|
||||
{
|
||||
// Front face culling, the next batch needs to render inside out
|
||||
mTriangleStateFF->Activate();
|
||||
|
||||
// Draw all back primitives
|
||||
for (InstanceMap::value_type &v : mPrimitivesBackFacing)
|
||||
DrawInstances(v.first, v.second.mGeometryStartIdx);
|
||||
}
|
||||
|
||||
if (!mWireframePrimitives.empty())
|
||||
{
|
||||
// Wire frame mode
|
||||
mTriangleStateWire->Activate();
|
||||
|
||||
// Draw all wireframe primitives
|
||||
for (InstanceMap::value_type &v : mWireframePrimitives)
|
||||
DrawInstances(v.first, v.second.mGeometryStartIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawTexts()
|
||||
{
|
||||
lock_guard lock(mTextsLock);
|
||||
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
const CameraState &camera_state = mRenderer->GetCameraState();
|
||||
|
||||
for (const Text &t : mTexts)
|
||||
{
|
||||
Vec3 forward = camera_state.mForward;
|
||||
Vec3 right = forward.Cross(camera_state.mUp).Normalized();
|
||||
Vec3 up = right.Cross(forward).Normalized();
|
||||
Mat44 transform(Vec4(right, 0), Vec4(up, 0), Vec4(forward, 0), Vec4(t.mPosition, 1));
|
||||
|
||||
mFont->DrawText3D(transform * Mat44::sScale(t.mHeight), t.mText, t.mColor);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::Draw()
|
||||
{
|
||||
DrawLines();
|
||||
DrawTriangles();
|
||||
DrawTexts();
|
||||
}
|
||||
|
||||
void DebugRendererImp::ClearLines()
|
||||
{
|
||||
lock_guard lock(mLinesLock);
|
||||
mLines.clear();
|
||||
}
|
||||
|
||||
void DebugRendererImp::ClearMap(InstanceMap &ioInstances)
|
||||
{
|
||||
Array<GeometryRef> to_delete;
|
||||
|
||||
for (InstanceMap::value_type &kv : ioInstances)
|
||||
{
|
||||
if (kv.second.mInstances.empty())
|
||||
to_delete.push_back(kv.first);
|
||||
else
|
||||
kv.second.mInstances.clear();
|
||||
}
|
||||
|
||||
for (GeometryRef &b : to_delete)
|
||||
ioInstances.erase(b);
|
||||
}
|
||||
|
||||
void DebugRendererImp::ClearTriangles()
|
||||
{
|
||||
lock_guard lock(mPrimitivesLock);
|
||||
|
||||
// Close any primitive that's being built
|
||||
FinalizePrimitive();
|
||||
|
||||
// Move primitives to draw back to the free list
|
||||
ClearMap(mWireframePrimitives);
|
||||
ClearMap(mPrimitives);
|
||||
mTempPrimitives.clear(); // These are created by FinalizePrimitive() and need to be cleared every frame
|
||||
ClearMap(mPrimitivesBackFacing);
|
||||
mNumInstances = 0;
|
||||
}
|
||||
|
||||
void DebugRendererImp::ClearTexts()
|
||||
{
|
||||
lock_guard lock(mTextsLock);
|
||||
mTexts.clear();
|
||||
}
|
||||
|
||||
void DebugRendererImp::Clear()
|
||||
{
|
||||
ClearLines();
|
||||
ClearTriangles();
|
||||
ClearTexts();
|
||||
NextFrame();
|
||||
}
|
||||
178
lib/All/JoltPhysics/TestFramework/Renderer/DebugRendererImp.h
Normal file
178
lib/All/JoltPhysics/TestFramework/Renderer/DebugRendererImp.h
Normal file
@@ -0,0 +1,178 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef JPH_DEBUG_RENDERER
|
||||
#include <Jolt/Renderer/DebugRenderer.h>
|
||||
#else
|
||||
// Hack to still compile DebugRenderer inside the test framework when Jolt is compiled without
|
||||
#define JPH_DEBUG_RENDERER
|
||||
// Make sure the debug renderer symbols don't get imported or exported
|
||||
#define JPH_DEBUG_RENDERER_EXPORT
|
||||
#include <Jolt/Renderer/DebugRenderer.h>
|
||||
#undef JPH_DEBUG_RENDERER
|
||||
#undef JPH_DEBUG_RENDERER_EXPORT
|
||||
#endif
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Jolt/Core/Mutex.h>
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
|
||||
class Renderer;
|
||||
class Font;
|
||||
|
||||
/// Implementation of DebugRenderer
|
||||
class DebugRendererImp final : public DebugRenderer
|
||||
{
|
||||
public:
|
||||
JPH_OVERRIDE_NEW_DELETE
|
||||
|
||||
/// Constructor
|
||||
DebugRendererImp(Renderer *inRenderer, const Font *inFont);
|
||||
|
||||
/// Implementation of DebugRenderer interface
|
||||
virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) override;
|
||||
virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::Off) override;
|
||||
virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) override;
|
||||
virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) override;
|
||||
virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) override;
|
||||
virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight) override;
|
||||
|
||||
/// Draw all primitives from the light source
|
||||
void DrawShadowPass();
|
||||
|
||||
/// Draw all primitives that were added
|
||||
void Draw();
|
||||
|
||||
/// Clear all primitives (to be called after drawing)
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
/// Helper functions to draw sub parts
|
||||
void DrawLines();
|
||||
void DrawTriangles();
|
||||
void DrawTexts();
|
||||
|
||||
/// Helper functions to clear sub parts
|
||||
void ClearLines();
|
||||
void ClearTriangles();
|
||||
void ClearTexts();
|
||||
|
||||
/// Finalize the current locked primitive and add it to the primitives to draw
|
||||
void FinalizePrimitive();
|
||||
|
||||
/// Ensure that the current locked primitive has space for a primitive consisting inVtxSize vertices
|
||||
void EnsurePrimitiveSpace(int inVtxSize);
|
||||
|
||||
Renderer * mRenderer;
|
||||
|
||||
/// Shaders for triangles
|
||||
unique_ptr<PipelineState> mTriangleStateBF;
|
||||
unique_ptr<PipelineState> mTriangleStateFF;
|
||||
unique_ptr<PipelineState> mTriangleStateWire;
|
||||
|
||||
/// Shaders for shadow pass for triangles
|
||||
unique_ptr<PipelineState> mShadowStateBF;
|
||||
unique_ptr<PipelineState> mShadowStateFF;
|
||||
unique_ptr<PipelineState> mShadowStateWire;
|
||||
|
||||
/// Lock that protects the triangle batches from being accessed from multiple threads
|
||||
Mutex mPrimitivesLock;
|
||||
|
||||
Batch mEmptyBatch;
|
||||
|
||||
/// Properties for a single rendered instance
|
||||
struct Instance
|
||||
{
|
||||
/// Constructor
|
||||
Instance(Mat44Arg inModelMatrix, Mat44Arg inModelMatrixInvTrans, ColorArg inModelColor) : mModelMatrix(inModelMatrix), mModelMatrixInvTrans(inModelMatrixInvTrans), mModelColor(inModelColor) { }
|
||||
|
||||
Mat44 mModelMatrix;
|
||||
Mat44 mModelMatrixInvTrans;
|
||||
Color mModelColor;
|
||||
};
|
||||
|
||||
/// Rendered instance with added information for lodding
|
||||
struct InstanceWithLODInfo : public Instance
|
||||
{
|
||||
/// Constructor
|
||||
InstanceWithLODInfo(Mat44Arg inModelMatrix, Mat44Arg inModelMatrixInvTrans, ColorArg inModelColor, const AABox &inWorldSpaceBounds, float inLODScaleSq) : Instance(inModelMatrix, inModelMatrixInvTrans, inModelColor), mWorldSpaceBounds(inWorldSpaceBounds), mLODScaleSq(inLODScaleSq) { }
|
||||
|
||||
/// Bounding box for culling
|
||||
AABox mWorldSpaceBounds;
|
||||
|
||||
/// Square of scale factor for LODding (1 = original, > 1 = lod out further, < 1 = lod out earlier)
|
||||
float mLODScaleSq;
|
||||
};
|
||||
|
||||
/// Properties for a batch of instances that have the same primitive
|
||||
struct Instances
|
||||
{
|
||||
Array<InstanceWithLODInfo> mInstances;
|
||||
|
||||
/// Start index in mInstancesBuffer for each of the LOD in the geometry pass. Length is one longer than the number of LODs to indicate how many instances the last lod has.
|
||||
Array<int> mGeometryStartIdx;
|
||||
|
||||
/// Start index in mInstancesBuffer for each of the LOD in the light pass. Length is one longer than the number of LODs to indicate how many instances the last lod has.
|
||||
Array<int> mLightStartIdx;
|
||||
};
|
||||
|
||||
using InstanceMap = UnorderedMap<GeometryRef, Instances>;
|
||||
|
||||
/// Clear map of instances and make it ready for the next frame
|
||||
void ClearMap(InstanceMap &ioInstances);
|
||||
|
||||
/// Helper function to draw instances
|
||||
inline void DrawInstances(const Geometry *inGeometry, const Array<int> &inStartIdx);
|
||||
|
||||
/// List of primitives that are finished and ready for drawing
|
||||
InstanceMap mWireframePrimitives;
|
||||
InstanceMap mPrimitives;
|
||||
InstanceMap mTempPrimitives;
|
||||
InstanceMap mPrimitivesBackFacing;
|
||||
int mNumInstances = 0;
|
||||
Ref<RenderInstances> mInstancesBuffer[Renderer::cFrameCount];
|
||||
|
||||
/// Primitive that is being built + its properties
|
||||
Ref<RenderPrimitive> mLockedPrimitive;
|
||||
Vertex * mLockedVerticesStart = nullptr;
|
||||
Vertex * mLockedVertices = nullptr;
|
||||
Vertex * mLockedVerticesEnd = nullptr;
|
||||
AABox mLockedPrimitiveBounds;
|
||||
|
||||
/// A single text string
|
||||
struct Text
|
||||
{
|
||||
Text(Vec3Arg inPosition, const string_view &inText, ColorArg inColor, float inHeight) : mPosition(inPosition), mText(inText), mColor(inColor), mHeight(inHeight) { }
|
||||
|
||||
Vec3 mPosition;
|
||||
String mText;
|
||||
Color mColor;
|
||||
float mHeight;
|
||||
};
|
||||
|
||||
/// All text strings that are to be drawn on screen
|
||||
Array<Text> mTexts;
|
||||
Mutex mTextsLock;
|
||||
|
||||
/// Font with which to draw the texts
|
||||
RefConst<Font> mFont;
|
||||
|
||||
/// A single line segment
|
||||
struct Line
|
||||
{
|
||||
Float3 mFrom;
|
||||
Color mFromColor;
|
||||
Float3 mTo;
|
||||
Color mToColor;
|
||||
};
|
||||
|
||||
/// The list of line segments
|
||||
Array<Line> mLines;
|
||||
Mutex mLinesLock;
|
||||
|
||||
/// The shaders for the line segments
|
||||
unique_ptr<PipelineState> mLineState;
|
||||
};
|
||||
349
lib/All/JoltPhysics/TestFramework/Renderer/Font.cpp
Normal file
349
lib/All/JoltPhysics/TestFramework/Renderer/Font.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/Font.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Image/Surface.h>
|
||||
#include <Utils/ReadData.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
#include <Jolt/Core/ScopeExit.h>
|
||||
|
||||
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wreserved-identifier")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wcast-qual")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wimplicit-fallthrough")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wcomma")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wdouble-promotion")
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include <External/stb_truetype.h>
|
||||
JPH_SUPPRESS_WARNINGS_STD_END
|
||||
|
||||
Font::Font(Renderer *inRenderer) :
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
bool Font::Create(const char *inFontName, int inCharHeight)
|
||||
{
|
||||
JPH_PROFILE("Create");
|
||||
|
||||
// Initialize
|
||||
mFontName = inFontName;
|
||||
mCharHeight = inCharHeight;
|
||||
mHorizontalTexels = 64;
|
||||
mVerticalTexels = 64;
|
||||
|
||||
constexpr int cSpacingH = 2; // Number of pixels to put horizontally between characters
|
||||
constexpr int cSpacingV = 2; // Number of pixels to put vertically between characters
|
||||
|
||||
// Read font data
|
||||
Array<uint8> font_data = ReadData((String("Fonts/") + inFontName + ".ttf").c_str());
|
||||
|
||||
// Construct a font info
|
||||
stbtt_fontinfo font;
|
||||
if (!stbtt_InitFont(&font, font_data.data(), stbtt_GetFontOffsetForIndex(font_data.data(), 0)))
|
||||
return false;
|
||||
|
||||
// Get the base line for the font
|
||||
float scale = stbtt_ScaleForPixelHeight(&font, float(mCharHeight));
|
||||
int ascent;
|
||||
stbtt_GetFontVMetrics(&font, &ascent, nullptr, nullptr);
|
||||
int baseline = int(ascent * scale);
|
||||
|
||||
// Create surface for characters
|
||||
Ref<SoftwareSurface> surface = new SoftwareSurface(mHorizontalTexels, mVerticalTexels, ESurfaceFormat::L8);
|
||||
surface->Clear();
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Draw all printable characters
|
||||
try_again:;
|
||||
int x = 0, y = 0;
|
||||
static_assert(cBeginChar == ' ', "We skip space in the for loop below");
|
||||
for (int c = cBeginChar + 1; c < cEndChar; ++c)
|
||||
{
|
||||
// Get index in the arrays
|
||||
int idx = c - cBeginChar;
|
||||
|
||||
int w, h, xoff, yoff;
|
||||
unsigned char *bitmap = stbtt_GetCodepointBitmap(&font, 0, scale, c, &w, &h, &xoff, &yoff);
|
||||
JPH_SCOPE_EXIT([bitmap]{ STBTT_free(bitmap, nullptr); });
|
||||
yoff = baseline + yoff;
|
||||
|
||||
// Check if there is room on this line
|
||||
if (int(x + xoff + w + cSpacingH) > mHorizontalTexels)
|
||||
{
|
||||
// Next line
|
||||
x = 0;
|
||||
y += mCharHeight + cSpacingV;
|
||||
|
||||
// Check if character fits
|
||||
if (y + mCharHeight + cSpacingV > mVerticalTexels)
|
||||
{
|
||||
// Character doesn't fit, enlarge surface
|
||||
if (mHorizontalTexels < 2 * mVerticalTexels)
|
||||
mHorizontalTexels <<= 1;
|
||||
else
|
||||
mVerticalTexels <<= 1;
|
||||
|
||||
// Create new surface
|
||||
surface->UnLock();
|
||||
surface = new SoftwareSurface(mHorizontalTexels, mVerticalTexels, ESurfaceFormat::L8);
|
||||
surface->Clear();
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Try again with the larger texture
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
|
||||
// Get location of character in font surface
|
||||
JPH_ASSERT(x >= 0 && x <= 0xffff);
|
||||
JPH_ASSERT(y >= 0 && y <= 0xffff);
|
||||
JPH_ASSERT(w <= 0xff);
|
||||
mStartU[idx] = uint16(x);
|
||||
mStartV[idx] = uint16(y);
|
||||
mWidth[idx] = uint8(w + 1);
|
||||
|
||||
// Copy the character data
|
||||
for (int y2 = 0; y2 < h; ++y2)
|
||||
{
|
||||
uint8 *src = bitmap + y2 * w;
|
||||
uint8 *dst = surface->GetScanLine(y + yoff + y2) + x + xoff;
|
||||
memcpy(dst, src, w);
|
||||
}
|
||||
|
||||
// Go to the next character
|
||||
x += w + cSpacingH;
|
||||
}
|
||||
|
||||
// Calculate spacing between characters
|
||||
for (int idx1 = 0; idx1 < cNumChars; ++idx1)
|
||||
for (int idx2 = 0; idx2 < cNumChars; ++idx2)
|
||||
{
|
||||
int c1 = cBeginChar + idx1;
|
||||
int c2 = cBeginChar + idx2;
|
||||
|
||||
int advance;
|
||||
stbtt_GetCodepointHMetrics(&font, c1, &advance, nullptr);
|
||||
int spacing = Clamp(int(scale * (advance + stbtt_GetCodepointKernAdvance(&font, c1, c2))), 0, 0xff);
|
||||
mSpacing[idx1][idx2] = (uint8)spacing;
|
||||
}
|
||||
|
||||
// Unlock surface
|
||||
surface->UnLock();
|
||||
|
||||
// Create input layout
|
||||
const PipelineState::EInputDescription vertex_desc[] =
|
||||
{
|
||||
PipelineState::EInputDescription::Position,
|
||||
PipelineState::EInputDescription::TexCoord,
|
||||
PipelineState::EInputDescription::Color
|
||||
};
|
||||
|
||||
// Load vertex shader
|
||||
Ref<VertexShader> vtx = mRenderer->CreateVertexShader("FontVertexShader");
|
||||
|
||||
// Load pixel shader
|
||||
Ref<PixelShader> pix = mRenderer->CreatePixelShader("FontPixelShader");
|
||||
|
||||
mPipelineState = mRenderer->CreatePipelineState(vtx, vertex_desc, std::size(vertex_desc), pix, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::Off, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
|
||||
// Create texture
|
||||
mTexture = mRenderer->CreateTexture(surface);
|
||||
|
||||
// Trace success
|
||||
Trace("Created font \"%s\" with height %d in a %dx%d surface", mFontName.c_str(), mCharHeight, mHorizontalTexels, mVerticalTexels);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Float2 Font::MeasureText(const string_view &inText) const
|
||||
{
|
||||
JPH_PROFILE("MeasureText");
|
||||
|
||||
Float2 extents(0, 1.0f);
|
||||
|
||||
// Current raster position
|
||||
float x = 0;
|
||||
|
||||
// Loop through string
|
||||
for (uint i = 0; i < inText.size(); ++i)
|
||||
{
|
||||
// Get character
|
||||
int ch = inText[i];
|
||||
|
||||
// Create character if it is printable
|
||||
static_assert(cBeginChar == ' ', "We skip space in the for loop below");
|
||||
if (ch > cBeginChar && ch < cEndChar)
|
||||
{
|
||||
// Update extents
|
||||
int c1 = ch - cBeginChar;
|
||||
extents.x = max(extents.x, x + float(mWidth[c1]) / mCharHeight);
|
||||
}
|
||||
|
||||
// Go to next (x, y) location
|
||||
if (ch == '\n')
|
||||
{
|
||||
// Next line
|
||||
x = 0;
|
||||
extents.y += 1.0f;
|
||||
}
|
||||
else if (i + 1 < inText.size())
|
||||
{
|
||||
// Do spacing between the two characters
|
||||
int c1 = ch - cBeginChar;
|
||||
int c2 = inText[i + 1] - cBeginChar;
|
||||
|
||||
if (c1 >= 0 && c1 < cNumChars && c2 >= 0 && c2 < cNumChars)
|
||||
x += float(mSpacing[c1][c2]) / mCharHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return extents;
|
||||
}
|
||||
|
||||
bool Font::CreateString(Mat44Arg inTransform, const string_view &inText, ColorArg inColor, RenderPrimitive &ioPrimitive) const
|
||||
{
|
||||
JPH_PROFILE("CreateString");
|
||||
|
||||
// Reset primitive
|
||||
ioPrimitive.Clear();
|
||||
|
||||
// Count the number of printable chars
|
||||
int printable = 0;
|
||||
for (uint i = 0; i < inText.size(); ++i)
|
||||
{
|
||||
int ch = inText[i];
|
||||
static_assert(cBeginChar == ' ', "We skip space in the for loop below");
|
||||
if (ch > cBeginChar && ch < cEndChar) // Space is not printable
|
||||
printable++;
|
||||
}
|
||||
if (printable == 0)
|
||||
return false;
|
||||
|
||||
// Get correction factor for texture size
|
||||
float texel_to_u = 1.0f / mHorizontalTexels;
|
||||
float texel_to_v = 1.0f / mVerticalTexels;
|
||||
|
||||
int vtx_size = printable * 4;
|
||||
int idx_size = printable * 6;
|
||||
ioPrimitive.CreateVertexBuffer(vtx_size, sizeof(FontVertex));
|
||||
ioPrimitive.CreateIndexBuffer(idx_size);
|
||||
|
||||
// Current vertex
|
||||
uint32 vtx = 0;
|
||||
|
||||
// Lock buffers
|
||||
FontVertex *font_vtx = (FontVertex *)ioPrimitive.LockVertexBuffer();
|
||||
uint32 *idx_start = ioPrimitive.LockIndexBuffer();
|
||||
uint32 *idx = idx_start;
|
||||
|
||||
// Current raster position
|
||||
float x = 0, y = -1.0f;
|
||||
|
||||
// Loop through string
|
||||
for (uint i = 0; i < inText.size(); ++i)
|
||||
{
|
||||
// Get character
|
||||
int ch = inText[i];
|
||||
|
||||
// Create character if it is printable
|
||||
static_assert(cBeginChar == ' ', "We skip space in the for loop below");
|
||||
if (ch > cBeginChar && ch < cEndChar)
|
||||
{
|
||||
// Get index for character
|
||||
int c1 = ch - cBeginChar;
|
||||
|
||||
// Create indices
|
||||
*idx = vtx;
|
||||
++idx;
|
||||
*idx = vtx + 3;
|
||||
++idx;
|
||||
*idx = vtx + 1;
|
||||
++idx;
|
||||
*idx = vtx;
|
||||
++idx;
|
||||
*idx = vtx + 2;
|
||||
++idx;
|
||||
*idx = vtx + 3;
|
||||
++idx;
|
||||
vtx += 4;
|
||||
|
||||
// Get properties of this character
|
||||
Float2 uv_start(texel_to_u * mStartU[c1], texel_to_v * mStartV[c1]);
|
||||
Float2 uv_end(texel_to_u * (mStartU[c1] + mWidth[c1]), texel_to_v * (mStartV[c1] + mCharHeight));
|
||||
Float2 xy_end(x + float(mWidth[c1]) / mCharHeight, y + 1.0f);
|
||||
|
||||
// Create vertices
|
||||
(inTransform * Vec3(x, y, 0)).StoreFloat3(&font_vtx->mPosition);
|
||||
font_vtx->mColor = inColor;
|
||||
font_vtx->mTexCoord = Float2(uv_start.x, uv_end.y);
|
||||
++font_vtx;
|
||||
|
||||
(inTransform * Vec3(x, xy_end.y, 0)).StoreFloat3(&font_vtx->mPosition);
|
||||
font_vtx->mColor = inColor;
|
||||
font_vtx->mTexCoord = uv_start;
|
||||
++font_vtx;
|
||||
|
||||
(inTransform * Vec3(xy_end.x, y, 0)).StoreFloat3(&font_vtx->mPosition);
|
||||
font_vtx->mColor = inColor;
|
||||
font_vtx->mTexCoord = uv_end;
|
||||
++font_vtx;
|
||||
|
||||
(inTransform * Vec3(xy_end.x, xy_end.y, 0)).StoreFloat3(&font_vtx->mPosition);
|
||||
font_vtx->mColor = inColor;
|
||||
font_vtx->mTexCoord = Float2(uv_end.x, uv_start.y);
|
||||
++font_vtx;
|
||||
}
|
||||
|
||||
// Go to next (x, y) location
|
||||
if (ch == '\n')
|
||||
{
|
||||
// Next line
|
||||
x = 0.0f;
|
||||
y -= 1.0f;
|
||||
}
|
||||
else if (i + 1 < inText.size())
|
||||
{
|
||||
// Do spacing between the two characters
|
||||
int c1 = ch - cBeginChar;
|
||||
int c2 = inText[i + 1] - cBeginChar;
|
||||
|
||||
if (c1 >= 0 && c1 < cNumChars && c2 >= 0 && c2 < cNumChars)
|
||||
x += float(mSpacing[c1][c2]) / mCharHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we completely filled the output buffer
|
||||
JPH_ASSERT(vtx == (uint32)vtx_size);
|
||||
JPH_ASSERT(idx == idx_start + idx_size);
|
||||
|
||||
// Unlock buffers
|
||||
ioPrimitive.UnlockVertexBuffer();
|
||||
ioPrimitive.UnlockIndexBuffer();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Font::DrawText3D(Mat44Arg inTransform, const string_view &inText, ColorArg inColor) const
|
||||
{
|
||||
JPH_PROFILE("DrawText3D");
|
||||
|
||||
// Early out
|
||||
if (inText.empty())
|
||||
return;
|
||||
|
||||
Ref<RenderPrimitive> primitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Triangle);
|
||||
if (CreateString(inTransform, inText, inColor, *primitive))
|
||||
{
|
||||
mTexture->Bind();
|
||||
|
||||
mPipelineState->Activate();
|
||||
|
||||
primitive->Draw();
|
||||
}
|
||||
}
|
||||
70
lib/All/JoltPhysics/TestFramework/Renderer/Font.h
Normal file
70
lib/All/JoltPhysics/TestFramework/Renderer/Font.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
#include <Jolt/Core/Color.h>
|
||||
#include <Jolt/Math/Float2.h>
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/Texture.h>
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <memory>
|
||||
|
||||
class Renderer;
|
||||
class Texture;
|
||||
|
||||
/// Font class, used to display text in 3D mode. Does variable width fonts with kerning. Font names are identical to the Windows font names.
|
||||
class Font : public RefTarget<Font>
|
||||
{
|
||||
public:
|
||||
/// Constants
|
||||
static const int cBeginChar = ' '; ///< First character that is drawable in the character set
|
||||
static const int cEndChar = 256; ///< Last character + 1 that is drawable in the character set
|
||||
static const int cNumChars = cEndChar - cBeginChar; ///< Number of drawable characters in the character set
|
||||
|
||||
/// Constructor
|
||||
Font(Renderer *inRenderer);
|
||||
|
||||
/// Create a font
|
||||
bool Create(const char *inFontName, int inCharHeight);
|
||||
|
||||
/// Properties
|
||||
const String & GetFontName() const { return mFontName; }
|
||||
int GetCharHeight() const { return mCharHeight; }
|
||||
|
||||
/// Get extents of a string, assuming the height of the text is 1 and with the normal aspect ratio of the font
|
||||
Float2 MeasureText(const string_view &inText) const;
|
||||
|
||||
/// Draw a string at a specific location
|
||||
/// If the string is drawn with the identity matrix, it's top left will start at (0, 0, 0)
|
||||
/// The text width is in the X direction and the text height is in the Y direction and it will have a height of 1
|
||||
void DrawText3D(Mat44Arg inTransform, const string_view &inText, ColorArg inColor = Color::sWhite) const;
|
||||
|
||||
private:
|
||||
/// Create a primitive for a string
|
||||
bool CreateString(Mat44Arg inTransform, const string_view &inText, ColorArg inColor, RenderPrimitive &ioPrimitive) const;
|
||||
|
||||
struct FontVertex
|
||||
{
|
||||
Float3 mPosition;
|
||||
Float2 mTexCoord;
|
||||
Color mColor;
|
||||
};
|
||||
|
||||
/// Properties of the font
|
||||
String mFontName; ///< Name of the font
|
||||
int mCharHeight; ///< Height of a character
|
||||
int mHorizontalTexels; ///< Number of texels horizontally, determines the scale of mStartU, mWidth and mSpacing
|
||||
int mVerticalTexels; ///< Number of texels vertically, determines the scale of mStartV
|
||||
uint16 mStartU[cNumChars] = {}; ///< Start U in texels
|
||||
uint16 mStartV[cNumChars] = {}; ///< Start V in texels
|
||||
uint8 mWidth[cNumChars] = {}; ///< Width of character in texels
|
||||
uint8 mSpacing[cNumChars][cNumChars] = {}; ///< Spacing between characters in texels
|
||||
|
||||
/// Structures used for drawing
|
||||
Renderer * mRenderer; ///< Our renderer
|
||||
Ref<Texture> mTexture; ///< The texture containing all characters
|
||||
unique_ptr<PipelineState> mPipelineState; ///< The state used to render characters
|
||||
};
|
||||
57
lib/All/JoltPhysics/TestFramework/Renderer/Frustum.h
Normal file
57
lib/All/JoltPhysics/TestFramework/Renderer/Frustum.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Geometry/Plane.h>
|
||||
#include <Jolt/Geometry/AABox.h>
|
||||
|
||||
/// A camera frustum containing of 6 planes (left, right, top, bottom, near, far) pointing inwards
|
||||
class [[nodiscard]] Frustum
|
||||
{
|
||||
public:
|
||||
/// Empty constructor
|
||||
Frustum() = default;
|
||||
|
||||
/// Construct frustum from position, forward, up, field of view x and y and near plane.
|
||||
/// Note that inUp does not need to be perpendicular to inForward but cannot be collinear.
|
||||
inline Frustum(Vec3Arg inPosition, Vec3Arg inForward, Vec3Arg inUp, float inFOVX, float inFOVY, float inNear)
|
||||
{
|
||||
Vec3 right = inForward.Cross(inUp).Normalized();
|
||||
Vec3 up = right.Cross(inForward).Normalized(); // Calculate the real up vector (inUp does not need to be perpendicular to inForward)
|
||||
|
||||
// Near plane
|
||||
mPlanes[0] = Plane::sFromPointAndNormal(inPosition + inNear * inForward, inForward);
|
||||
|
||||
// Top and bottom planes
|
||||
mPlanes[1] = Plane::sFromPointAndNormal(inPosition, Mat44::sRotation(right, 0.5f * inFOVY) * -up);
|
||||
mPlanes[2] = Plane::sFromPointAndNormal(inPosition, Mat44::sRotation(right, -0.5f * inFOVY) * up);
|
||||
|
||||
// Left and right planes
|
||||
mPlanes[3] = Plane::sFromPointAndNormal(inPosition, Mat44::sRotation(up, 0.5f * inFOVX) * right);
|
||||
mPlanes[4] = Plane::sFromPointAndNormal(inPosition, Mat44::sRotation(up, -0.5f * inFOVX) * -right);
|
||||
}
|
||||
|
||||
/// Test if frustum overlaps with axis aligned box. Note that this is a conservative estimate and can return true if the
|
||||
/// frustum doesn't actually overlap with the box. This is because we only test the plane axis as separating axis
|
||||
/// and skip checking the cross products of the edges of the frustum
|
||||
inline bool Overlaps(const AABox &inBox) const
|
||||
{
|
||||
// Loop over all frustum planes
|
||||
for (const Plane &p : mPlanes)
|
||||
{
|
||||
// Get support point (the maximum extent) in the direction of our normal
|
||||
Vec3 support = inBox.GetSupport(p.GetNormal());
|
||||
|
||||
// If this is behind our plane, the box is not inside the frustum
|
||||
if (p.SignedDistance(support) < 0.0f)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Plane mPlanes[5]; ///< Planes forming the frustum
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
/// Convert Metal error to readable text and alert
|
||||
void FatalErrorIfFailed(NSError *inResult);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Utils/Log.h>
|
||||
|
||||
void FatalErrorIfFailed(NSError *inResult)
|
||||
{
|
||||
if (inResult != nullptr)
|
||||
FatalError("Metal error returned: %s", [[inResult localizedDescription] cStringUsingEncoding: NSUTF8StringEncoding]);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <Renderer/MTL/VertexShaderMTL.h>
|
||||
#include <Renderer/MTL/PixelShaderMTL.h>
|
||||
|
||||
class RendererMTL;
|
||||
|
||||
/// Metal pipeline state object
|
||||
class PipelineStateMTL : public PipelineState
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PipelineStateMTL(RendererMTL *inRenderer, const VertexShaderMTL *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderMTL *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode);
|
||||
virtual ~PipelineStateMTL() override;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() override;
|
||||
|
||||
private:
|
||||
RendererMTL * mRenderer;
|
||||
RefConst<VertexShaderMTL> mVertexShader;
|
||||
RefConst<PixelShaderMTL> mPixelShader;
|
||||
id<MTLRenderPipelineState> mPipelineState;
|
||||
id<MTLDepthStencilState> mDepthState;
|
||||
MTLCullMode mCullMode;
|
||||
MTLTriangleFillMode mFillMode;
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/PipelineStateMTL.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
|
||||
PipelineStateMTL::PipelineStateMTL(RendererMTL *inRenderer, const VertexShaderMTL *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderMTL *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode) :
|
||||
mRenderer(inRenderer),
|
||||
mVertexShader(inVertexShader),
|
||||
mPixelShader(inPixelShader)
|
||||
{
|
||||
// Create a vertex descriptor
|
||||
MTLVertexDescriptor *vertex_descriptor = [[MTLVertexDescriptor alloc] init];
|
||||
uint vertex_offset = 0;
|
||||
uint instance_offset = 0, instance_alignment = 4;
|
||||
uint index = 0;
|
||||
for (uint i = 0; i < inInputDescriptionCount; ++i)
|
||||
switch (inInputDescription[i])
|
||||
{
|
||||
case EInputDescription::Position:
|
||||
case EInputDescription::Normal:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat3;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 3 * sizeof(float);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::Color:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatUChar4;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 4 * sizeof(uint8);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::TexCoord:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat2;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 2 * sizeof(float);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceColor:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatUChar4;
|
||||
vertex_descriptor.attributes[index].offset = instance_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 1;
|
||||
instance_offset += 4 * sizeof(uint8);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceTransform:
|
||||
case EInputDescription::InstanceInvTransform:
|
||||
instance_alignment = max(instance_alignment, 16u);
|
||||
instance_offset = AlignUp(instance_offset, 16u);
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat4;
|
||||
vertex_descriptor.attributes[index].offset = instance_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 1;
|
||||
instance_offset += 4 * sizeof(float);
|
||||
++index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Configure layouts
|
||||
vertex_descriptor.layouts[0].stride = vertex_offset;
|
||||
vertex_descriptor.layouts[0].stepRate = 1;
|
||||
vertex_descriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||
|
||||
if (instance_offset > 0)
|
||||
{
|
||||
vertex_descriptor.layouts[1].stride = AlignUp(instance_offset, instance_alignment);
|
||||
vertex_descriptor.layouts[1].stepRate = 1;
|
||||
vertex_descriptor.layouts[1].stepFunction = MTLVertexStepFunctionPerInstance;
|
||||
}
|
||||
|
||||
// Create the pipeline descriptor
|
||||
MTLRenderPipelineDescriptor *descriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
descriptor.vertexFunction = inVertexShader->GetFunction();
|
||||
descriptor.fragmentFunction = inPixelShader->GetFunction();
|
||||
descriptor.vertexDescriptor = vertex_descriptor;
|
||||
switch (inDrawPass)
|
||||
{
|
||||
case EDrawPass::Shadow:
|
||||
descriptor.depthAttachmentPixelFormat = static_cast<TextureMTL *>(mRenderer->GetShadowMap())->GetTexture().pixelFormat;
|
||||
break;
|
||||
|
||||
case EDrawPass::Normal:
|
||||
descriptor.colorAttachments[0].pixelFormat = mRenderer->GetView().colorPixelFormat;
|
||||
switch (inBlendMode)
|
||||
{
|
||||
case EBlendMode::Write:
|
||||
descriptor.colorAttachments[0].blendingEnabled = NO;
|
||||
break;
|
||||
|
||||
case EBlendMode::AlphaBlend:
|
||||
descriptor.colorAttachments[0].blendingEnabled = YES;
|
||||
descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
|
||||
descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
|
||||
descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorZero;
|
||||
descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
|
||||
break;
|
||||
}
|
||||
descriptor.depthAttachmentPixelFormat = mRenderer->GetView().depthStencilPixelFormat;
|
||||
}
|
||||
|
||||
NSError *error = nullptr;
|
||||
mPipelineState = [mRenderer->GetDevice() newRenderPipelineStateWithDescriptor: descriptor error: &error];
|
||||
FatalErrorIfFailed(error);
|
||||
[descriptor release];
|
||||
[vertex_descriptor release];
|
||||
|
||||
// Create depth descriptor
|
||||
MTLDepthStencilDescriptor *depth_descriptor = [[MTLDepthStencilDescriptor new] init];
|
||||
if (inDepthTest == EDepthTest::On)
|
||||
{
|
||||
depth_descriptor.depthCompareFunction = MTLCompareFunctionGreaterEqual;
|
||||
depth_descriptor.depthWriteEnabled = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
depth_descriptor.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
depth_descriptor.depthWriteEnabled = NO;
|
||||
}
|
||||
mDepthState = [mRenderer->GetDevice() newDepthStencilStateWithDescriptor: depth_descriptor];
|
||||
[depth_descriptor release];
|
||||
|
||||
// Determine cull mode
|
||||
if (inCullMode == ECullMode::FrontFace)
|
||||
mCullMode = MTLCullModeFront;
|
||||
else
|
||||
mCullMode = MTLCullModeBack;
|
||||
|
||||
// Determine fill mode
|
||||
if (inFillMode == EFillMode::Solid)
|
||||
mFillMode = MTLTriangleFillModeFill;
|
||||
else
|
||||
mFillMode = MTLTriangleFillModeLines;
|
||||
}
|
||||
|
||||
PipelineStateMTL::~PipelineStateMTL()
|
||||
{
|
||||
[mPipelineState release];
|
||||
[mDepthState release];
|
||||
}
|
||||
|
||||
void PipelineStateMTL::Activate()
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
[encoder setRenderPipelineState: mPipelineState];
|
||||
[encoder setDepthStencilState: mDepthState];
|
||||
[encoder setCullMode: mCullMode];
|
||||
[encoder setTriangleFillMode: mFillMode];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PixelShader.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Pixel shader handle for Metal
|
||||
class PixelShaderMTL : public PixelShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PixelShaderMTL(id<MTLFunction> inFunction) : mFunction(inFunction) { }
|
||||
virtual ~PixelShaderMTL() override { [mFunction release]; }
|
||||
|
||||
/// Access to the function
|
||||
id<MTLFunction> GetFunction() const { return mFunction; }
|
||||
|
||||
private:
|
||||
id<MTLFunction> mFunction;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// Metal implementation of a render instances object
|
||||
class RenderInstancesMTL : public RenderInstances
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderInstancesMTL(RendererMTL *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderInstancesMTL() 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:
|
||||
RendererMTL * mRenderer;
|
||||
id<MTLBuffer> mBuffer;
|
||||
NSUInteger mBufferSize;
|
||||
NSUInteger mInstanceSize;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RenderInstancesMTL.h>
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
|
||||
void RenderInstancesMTL::Clear()
|
||||
{
|
||||
[mBuffer release];
|
||||
mBuffer = nil;
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::CreateBuffer(int inNumInstances, int inInstanceSize)
|
||||
{
|
||||
mInstanceSize = NSUInteger(inInstanceSize);
|
||||
NSUInteger size = mInstanceSize * inNumInstances;
|
||||
if (mBuffer == nullptr || mBufferSize < size)
|
||||
{
|
||||
Clear();
|
||||
|
||||
mBuffer = [mRenderer->GetView().device newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
mBufferSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
void *RenderInstancesMTL::Lock()
|
||||
{
|
||||
return mBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::Unlock()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const
|
||||
{
|
||||
if (inNumInstances <= 0)
|
||||
return;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
RenderPrimitiveMTL *prim = static_cast<RenderPrimitiveMTL *>(inPrimitive);
|
||||
|
||||
[encoder setVertexBuffer: prim->mVertexBuffer offset: 0 atIndex: 0];
|
||||
[encoder setVertexBuffer: mBuffer offset: mInstanceSize * inStartInstance atIndex: 1];
|
||||
if (prim->mIndexBuffer == nil)
|
||||
[encoder drawPrimitives: prim->mPrimitiveType vertexStart: 0 vertexCount: prim->mNumVtxToDraw instanceCount: inNumInstances];
|
||||
else
|
||||
[encoder drawIndexedPrimitives: prim->mPrimitiveType indexCount: prim->mNumIdxToDraw indexType: MTLIndexTypeUInt32 indexBuffer: prim->mIndexBuffer indexBufferOffset: 0 instanceCount: inNumInstances];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
|
||||
/// Metal implementation of a render primitive
|
||||
class RenderPrimitiveMTL : public RenderPrimitive
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderPrimitiveMTL(RendererMTL *inRenderer, MTLPrimitiveType inType) : mRenderer(inRenderer), mPrimitiveType(inType) { }
|
||||
virtual ~RenderPrimitiveMTL() 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 RenderInstancesMTL;
|
||||
|
||||
RendererMTL * mRenderer;
|
||||
MTLPrimitiveType mPrimitiveType;
|
||||
id<MTLBuffer> mVertexBuffer;
|
||||
id<MTLBuffer> mIndexBuffer;
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
|
||||
void RenderPrimitiveMTL::ReleaseVertexBuffer()
|
||||
{
|
||||
[mVertexBuffer release];
|
||||
mVertexBuffer = nil;
|
||||
|
||||
RenderPrimitive::ReleaseVertexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::ReleaseIndexBuffer()
|
||||
{
|
||||
[mIndexBuffer release];
|
||||
mIndexBuffer = nil;
|
||||
|
||||
RenderPrimitive::ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
RenderPrimitive::CreateVertexBuffer(inNumVtx, inVtxSize, inData);
|
||||
|
||||
NSUInteger size = NSUInteger(inNumVtx) * inVtxSize;
|
||||
if (inData != nullptr)
|
||||
mVertexBuffer = [mRenderer->GetDevice() newBufferWithBytes: inData length: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
else
|
||||
mVertexBuffer = [mRenderer->GetDevice() newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
}
|
||||
|
||||
void *RenderPrimitiveMTL::LockVertexBuffer()
|
||||
{
|
||||
return mVertexBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::UnlockVertexBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
RenderPrimitive::CreateIndexBuffer(inNumIdx, inData);
|
||||
|
||||
NSUInteger size = NSUInteger(inNumIdx) * sizeof(uint32);
|
||||
if (inData != nullptr)
|
||||
mIndexBuffer = [mRenderer->GetDevice() newBufferWithBytes: inData length: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged | MTLResourceHazardTrackingModeTracked];
|
||||
else
|
||||
mIndexBuffer = [mRenderer->GetDevice() newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
}
|
||||
|
||||
uint32 *RenderPrimitiveMTL::LockIndexBuffer()
|
||||
{
|
||||
return (uint32 *)mIndexBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::UnlockIndexBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::Draw() const
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
|
||||
[encoder setVertexBuffer: mVertexBuffer offset: 0 atIndex: 0];
|
||||
if (mIndexBuffer == nil)
|
||||
[encoder drawPrimitives: mPrimitiveType vertexStart: 0 vertexCount: mNumVtxToDraw];
|
||||
else
|
||||
[encoder drawIndexedPrimitives: mPrimitiveType indexCount: mNumIdxToDraw indexType: MTLIndexTypeUInt32 indexBuffer: mIndexBuffer indexBufferOffset: 0];
|
||||
}
|
||||
46
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.h
Normal file
46
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Metal renderer
|
||||
class RendererMTL : public Renderer
|
||||
{
|
||||
public:
|
||||
virtual ~RendererMTL() 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; }
|
||||
virtual void OnWindowResize() override { }
|
||||
|
||||
MTKView * GetView() const { return mView; }
|
||||
id<MTLDevice> GetDevice() const { return mView.device; }
|
||||
id<MTLRenderCommandEncoder> GetRenderEncoder() const { return mRenderEncoder; }
|
||||
|
||||
private:
|
||||
MTKView * mView;
|
||||
MTLRenderPassDescriptor * mShadowRenderPass;
|
||||
Ref<TextureMTL> mShadowMap;
|
||||
id<MTLLibrary> mShaderLibrary;
|
||||
id<MTLCommandQueue> mCommandQueue;
|
||||
id<MTLCommandBuffer> mCommandBuffer;
|
||||
id<MTLRenderCommandEncoder> mRenderEncoder;
|
||||
};
|
||||
184
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.mm
Normal file
184
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.mm
Normal file
@@ -0,0 +1,184 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
#include <Renderer/MTL/RenderInstancesMTL.h>
|
||||
#include <Renderer/MTL/PipelineStateMTL.h>
|
||||
#include <Renderer/MTL/VertexShaderMTL.h>
|
||||
#include <Renderer/MTL/PixelShaderMTL.h>
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Window/ApplicationWindowMacOS.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
RendererMTL::~RendererMTL()
|
||||
{
|
||||
[mCommandQueue release];
|
||||
[mShadowRenderPass release];
|
||||
[mShaderLibrary release];
|
||||
}
|
||||
|
||||
void RendererMTL::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
Renderer::Initialize(inWindow);
|
||||
|
||||
mView = static_cast<ApplicationWindowMacOS *>(inWindow)->GetMetalView();
|
||||
|
||||
id<MTLDevice> device = GetDevice();
|
||||
|
||||
// Load the shader library containing all shaders for the test framework
|
||||
NSError *error = nullptr;
|
||||
NSURL *url = [NSURL URLWithString: [NSString stringWithCString: (AssetStream::sGetAssetsBasePath() + "Shaders/MTL/Shaders.metallib").c_str() encoding: NSUTF8StringEncoding]];
|
||||
mShaderLibrary = [device newLibraryWithURL: url error: &error];
|
||||
FatalErrorIfFailed(error);
|
||||
|
||||
// Create depth only texture (no color buffer, as seen from light)
|
||||
mShadowMap = new TextureMTL(this, cShadowMapSize, cShadowMapSize);
|
||||
|
||||
// Create render pass descriptor for shadow pass
|
||||
mShadowRenderPass = [[MTLRenderPassDescriptor alloc] init];
|
||||
mShadowRenderPass.depthAttachment.texture = mShadowMap->GetTexture();
|
||||
mShadowRenderPass.depthAttachment.loadAction = MTLLoadActionClear;
|
||||
mShadowRenderPass.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
mShadowRenderPass.depthAttachment.clearDepth = 0.0f;
|
||||
|
||||
// Create the command queue
|
||||
mCommandQueue = [device newCommandQueue];
|
||||
}
|
||||
|
||||
bool RendererMTL::BeginFrame(const CameraState &inCamera, float inWorldScale)
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
Renderer::BeginFrame(inCamera, inWorldScale);
|
||||
|
||||
// Update frame index
|
||||
mFrameIndex = (mFrameIndex + 1) % cFrameCount;
|
||||
|
||||
// Create a new command buffer
|
||||
mCommandBuffer = [mCommandQueue commandBuffer];
|
||||
|
||||
// Create shadow render encoder
|
||||
mRenderEncoder = [mCommandBuffer renderCommandEncoderWithDescriptor: mShadowRenderPass];
|
||||
|
||||
// Set viewport to size of shadow map
|
||||
[mRenderEncoder setViewport: (MTLViewport){ 0.0, 0.0, double(cShadowMapSize), double(cShadowMapSize), 0.0, 1.0 }];
|
||||
|
||||
// Set pixel shader constants
|
||||
[mRenderEncoder setFragmentBytes: &mPSBuffer length: sizeof(mPSBuffer) atIndex: 0];
|
||||
|
||||
// Counter clockwise is default winding order
|
||||
[mRenderEncoder setFrontFacingWinding: MTLWindingCounterClockwise];
|
||||
|
||||
// Start with projection mode
|
||||
SetProjectionMode();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererMTL::EndShadowPass()
|
||||
{
|
||||
// Finish the shadow encoder
|
||||
[mRenderEncoder endEncoding];
|
||||
mRenderEncoder = nil;
|
||||
|
||||
// Get the descriptor for the main window
|
||||
MTLRenderPassDescriptor *render_pass_descriptor = mView.currentRenderPassDescriptor;
|
||||
if (render_pass_descriptor == nullptr)
|
||||
return;
|
||||
|
||||
// Create render encoder
|
||||
mRenderEncoder = [mCommandBuffer renderCommandEncoderWithDescriptor: render_pass_descriptor];
|
||||
|
||||
// Set viewport
|
||||
[mRenderEncoder setViewport: (MTLViewport){ 0.0, 0.0, double(mWindow->GetWindowWidth()), double(mWindow->GetWindowHeight()), 0.0, 1.0 }];
|
||||
|
||||
// Set pixel shader constants
|
||||
[mRenderEncoder setFragmentBytes: &mPSBuffer length: sizeof(mPSBuffer) atIndex: 0];
|
||||
|
||||
// Counter clockwise is default winding order
|
||||
[mRenderEncoder setFrontFacingWinding: MTLWindingCounterClockwise];
|
||||
|
||||
// Start with projection mode
|
||||
SetProjectionMode();
|
||||
}
|
||||
|
||||
void RendererMTL::EndFrame()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
// Finish the encoder
|
||||
[mRenderEncoder endEncoding];
|
||||
mRenderEncoder = nil;
|
||||
|
||||
// Schedule a present
|
||||
[mCommandBuffer presentDrawable: mView.currentDrawable];
|
||||
|
||||
// Commit the command buffer
|
||||
[mCommandBuffer commit];
|
||||
|
||||
Renderer::EndFrame();
|
||||
}
|
||||
|
||||
void RendererMTL::SetProjectionMode()
|
||||
{
|
||||
JPH_ASSERT(mInFrame);
|
||||
|
||||
[mRenderEncoder setVertexBytes: &mVSBuffer length: sizeof(mVSBuffer) atIndex: 2];
|
||||
}
|
||||
|
||||
void RendererMTL::SetOrthoMode()
|
||||
{
|
||||
JPH_ASSERT(mInFrame);
|
||||
|
||||
[mRenderEncoder setVertexBytes: &mVSBufferOrtho length: sizeof(mVSBufferOrtho) atIndex: 2];
|
||||
}
|
||||
|
||||
Ref<Texture> RendererMTL::CreateTexture(const Surface *inSurface)
|
||||
{
|
||||
return new TextureMTL(this, inSurface);
|
||||
}
|
||||
|
||||
Ref<VertexShader> RendererMTL::CreateVertexShader(const char *inName)
|
||||
{
|
||||
id<MTLFunction> function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]];
|
||||
if (function == nil)
|
||||
FatalError("Vertex shader %s not found", inName);
|
||||
return new VertexShaderMTL(function);
|
||||
}
|
||||
|
||||
Ref<PixelShader> RendererMTL::CreatePixelShader(const char *inName)
|
||||
{
|
||||
id<MTLFunction> function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]];
|
||||
if (function == nil)
|
||||
FatalError("Pixel shader %s not found", inName);
|
||||
return new PixelShaderMTL(function);
|
||||
}
|
||||
|
||||
unique_ptr<PipelineState> RendererMTL::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<PipelineStateMTL>(this, static_cast<const VertexShaderMTL *>(inVertexShader), inInputDescription, inInputDescriptionCount, static_cast<const PixelShaderMTL *>(inPixelShader), inDrawPass, inFillMode, inTopology, inDepthTest, inBlendMode, inCullMode);
|
||||
}
|
||||
|
||||
RenderPrimitive *RendererMTL::CreateRenderPrimitive(PipelineState::ETopology inType)
|
||||
{
|
||||
return new RenderPrimitiveMTL(this, inType == PipelineState::ETopology::Line? MTLPrimitiveTypeLine : MTLPrimitiveTypeTriangle);
|
||||
}
|
||||
|
||||
RenderInstances *RendererMTL::CreateRenderInstances()
|
||||
{
|
||||
return new RenderInstancesMTL(this);
|
||||
}
|
||||
|
||||
#ifndef JPH_ENABLE_VULKAN
|
||||
Renderer *Renderer::sCreate()
|
||||
{
|
||||
return new RendererMTL;
|
||||
}
|
||||
#endif
|
||||
32
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.h
Normal file
32
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Texture.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
class RendererMTL;
|
||||
|
||||
/// Metal texture
|
||||
class TextureMTL : public Texture
|
||||
{
|
||||
public:
|
||||
/// Constructor, called by Renderer::CreateTextureMTL
|
||||
TextureMTL(RendererMTL *inRenderer, const Surface *inSurface); // Create a normal Texture
|
||||
TextureMTL(RendererMTL *inRenderer, int inWidth, int inHeight); // Create a render target (depth only)
|
||||
virtual ~TextureMTL() override;
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const override;
|
||||
|
||||
/// Access to the metal texture
|
||||
id<MTLTexture> GetTexture() const { return mTexture; }
|
||||
|
||||
private:
|
||||
RendererMTL * mRenderer;
|
||||
id<MTLTexture> mTexture;
|
||||
};
|
||||
|
||||
98
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.mm
Normal file
98
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.mm
Normal file
@@ -0,0 +1,98 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
|
||||
TextureMTL::TextureMTL(RendererMTL *inRenderer, const Surface *inSurface) :
|
||||
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
ESurfaceFormat format = inSurface->GetFormat();
|
||||
MTLPixelFormat mt_format = MTLPixelFormatBGRA8Unorm;
|
||||
switch (format)
|
||||
{
|
||||
case ESurfaceFormat::A4L4:
|
||||
case ESurfaceFormat::A8L8:
|
||||
case ESurfaceFormat::A4R4G4B4:
|
||||
case ESurfaceFormat::R8G8B8:
|
||||
case ESurfaceFormat::B8G8R8:
|
||||
case ESurfaceFormat::X8R8G8B8:
|
||||
case ESurfaceFormat::X8B8G8R8:
|
||||
case ESurfaceFormat::A8R8G8B8:
|
||||
case ESurfaceFormat::A8B8G8R8: mt_format = MTLPixelFormatBGRA8Unorm; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::L8: mt_format = MTLPixelFormatR8Unorm; break;
|
||||
case ESurfaceFormat::A8: mt_format = MTLPixelFormatA8Unorm; break;
|
||||
case ESurfaceFormat::R5G6B5:
|
||||
case ESurfaceFormat::X1R5G5B5:
|
||||
case ESurfaceFormat::X4R4G4B4: mt_format = MTLPixelFormatB5G6R5Unorm; format = ESurfaceFormat::R5G6B5; break;
|
||||
case ESurfaceFormat::A1R5G5B5: mt_format = MTLPixelFormatA1BGR5Unorm; break;
|
||||
case ESurfaceFormat::Invalid:
|
||||
default: JPH_ASSERT(false); break;
|
||||
}
|
||||
|
||||
// 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 descriptor
|
||||
MTLTextureDescriptor *descriptor = [[MTLTextureDescriptor alloc] init];
|
||||
descriptor.textureType = MTLTextureType2D;
|
||||
descriptor.usage = MTLTextureUsageShaderRead;
|
||||
descriptor.pixelFormat = mt_format;
|
||||
descriptor.width = mWidth;
|
||||
descriptor.height = mHeight;
|
||||
descriptor.storageMode = MTLStorageModeManaged;
|
||||
|
||||
MTLRegion region =
|
||||
{
|
||||
{ 0, 0, 0 },
|
||||
{ NSUInteger(mWidth), NSUInteger(mHeight), 1}
|
||||
};
|
||||
|
||||
// Create texture
|
||||
mTexture = [inRenderer->GetDevice() newTextureWithDescriptor: descriptor];
|
||||
surface->Lock(ESurfaceLockMode::Read);
|
||||
[mTexture replaceRegion: region mipmapLevel:0 withBytes: surface->GetData() bytesPerRow: surface->GetStride()];
|
||||
surface->UnLock();
|
||||
|
||||
[descriptor release];
|
||||
}
|
||||
|
||||
TextureMTL::TextureMTL(RendererMTL *inRenderer, int inWidth, int inHeight) :
|
||||
Texture(inWidth, inHeight),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
MTLTextureDescriptor *descriptor = [[MTLTextureDescriptor alloc] init];
|
||||
descriptor.textureType = MTLTextureType2D;
|
||||
descriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
|
||||
descriptor.pixelFormat = MTLPixelFormatDepth32Float;
|
||||
descriptor.width = mWidth;
|
||||
descriptor.height = mHeight;
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
|
||||
mTexture = [inRenderer->GetDevice() newTextureWithDescriptor: descriptor];
|
||||
|
||||
[descriptor release];
|
||||
}
|
||||
|
||||
TextureMTL::~TextureMTL()
|
||||
{
|
||||
[mTexture release];
|
||||
}
|
||||
|
||||
void TextureMTL::Bind() const
|
||||
{
|
||||
[mRenderer->GetRenderEncoder() setFragmentTexture: mTexture atIndex: 0];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VertexShader.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Vertex shader handle for Metal
|
||||
class VertexShaderMTL : public VertexShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
VertexShaderMTL(id<MTLFunction> inFunction) : mFunction(inFunction) { }
|
||||
virtual ~VertexShaderMTL() override { [mFunction release]; }
|
||||
|
||||
/// Access to the function
|
||||
id<MTLFunction> GetFunction() const { return mFunction; }
|
||||
|
||||
private:
|
||||
id<MTLFunction> mFunction;
|
||||
};
|
||||
70
lib/All/JoltPhysics/TestFramework/Renderer/PipelineState.h
Normal file
70
lib/All/JoltPhysics/TestFramework/Renderer/PipelineState.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
/// Defines how primitives should be rendered
|
||||
class PipelineState
|
||||
{
|
||||
public:
|
||||
/// Describes the input layout of the vertex shader
|
||||
enum class EInputDescription
|
||||
{
|
||||
Position, ///< 3 float position
|
||||
Color, ///< 4 uint8 color
|
||||
Normal, ///< 3 float normal
|
||||
TexCoord, ///< 2 float texture coordinate
|
||||
InstanceColor, ///< 4 uint8 per instance color
|
||||
InstanceTransform, ///< 4x4 float per instance transform
|
||||
InstanceInvTransform, ///< 4x4 float per instance inverse transform
|
||||
};
|
||||
|
||||
/// In which draw pass to use this pipeline state
|
||||
enum class EDrawPass
|
||||
{
|
||||
Shadow,
|
||||
Normal
|
||||
};
|
||||
|
||||
/// The type of topology to emit
|
||||
enum class ETopology
|
||||
{
|
||||
Triangle,
|
||||
Line
|
||||
};
|
||||
|
||||
/// Fill mode of the triangles
|
||||
enum class EFillMode
|
||||
{
|
||||
Solid,
|
||||
Wireframe
|
||||
};
|
||||
|
||||
/// If depth write / depth test is on
|
||||
enum class EDepthTest
|
||||
{
|
||||
Off,
|
||||
On
|
||||
};
|
||||
|
||||
/// How to blend the pixel from the shader in the back buffer
|
||||
enum class EBlendMode
|
||||
{
|
||||
Write,
|
||||
AlphaBlend,
|
||||
};
|
||||
|
||||
/// How to cull triangles
|
||||
enum class ECullMode
|
||||
{
|
||||
Backface,
|
||||
FrontFace,
|
||||
};
|
||||
|
||||
/// Destructor
|
||||
virtual ~PipelineState() = default;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() = 0;
|
||||
};
|
||||
15
lib/All/JoltPhysics/TestFramework/Renderer/PixelShader.h
Normal file
15
lib/All/JoltPhysics/TestFramework/Renderer/PixelShader.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
/// Pixel shader handle
|
||||
class PixelShader : public RefTarget<PixelShader>
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~PixelShader() = default;
|
||||
};
|
||||
28
lib/All/JoltPhysics/TestFramework/Renderer/RenderInstances.h
Normal file
28
lib/All/JoltPhysics/TestFramework/Renderer/RenderInstances.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// Buffer that holds a list of instances (usually model transform etc.) for instance based rendering
|
||||
class RenderInstances : public RefTarget<RenderInstances>
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~RenderInstances() = default;
|
||||
|
||||
/// Erase all instance data
|
||||
virtual void Clear() = 0;
|
||||
|
||||
/// Instance buffer management functions
|
||||
virtual void CreateBuffer(int inNumInstances, int inInstanceSize) = 0;
|
||||
virtual void * Lock() = 0;
|
||||
virtual void Unlock() = 0;
|
||||
|
||||
/// Draw the instances when context has been set by Renderer::BindShader
|
||||
virtual void Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const = 0;
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
|
||||
void RenderPrimitive::ReleaseVertexBuffer()
|
||||
{
|
||||
mNumVtx = 0;
|
||||
mNumVtxToDraw = 0;
|
||||
mVtxSize = 0;
|
||||
}
|
||||
|
||||
void RenderPrimitive::ReleaseIndexBuffer()
|
||||
{
|
||||
mNumIdx = 0;
|
||||
mNumIdxToDraw = 0;
|
||||
}
|
||||
|
||||
void RenderPrimitive::Clear()
|
||||
{
|
||||
ReleaseVertexBuffer();
|
||||
ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitive::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
ReleaseVertexBuffer();
|
||||
|
||||
mNumVtx = inNumVtx;
|
||||
mNumVtxToDraw = inNumVtx;
|
||||
mVtxSize = inVtxSize;
|
||||
}
|
||||
|
||||
void RenderPrimitive::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
ReleaseIndexBuffer();
|
||||
|
||||
mNumIdx = inNumIdx;
|
||||
mNumIdxToDraw = inNumIdx;
|
||||
}
|
||||
54
lib/All/JoltPhysics/TestFramework/Renderer/RenderPrimitive.h
Normal file
54
lib/All/JoltPhysics/TestFramework/Renderer/RenderPrimitive.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
/// Simple wrapper around vertex and index buffers
|
||||
class RenderPrimitive : public RefTarget<RenderPrimitive>, public RefTargetVirtual
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~RenderPrimitive() override = default;
|
||||
|
||||
/// Erase all primitive data
|
||||
void Clear();
|
||||
|
||||
/// Check if this primitive contains any data
|
||||
bool IsEmpty() const { return mNumVtx == 0 && mNumIdx == 0; }
|
||||
|
||||
/// Vertex buffer management functions
|
||||
virtual void CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData = nullptr) = 0;
|
||||
virtual void ReleaseVertexBuffer();
|
||||
virtual void * LockVertexBuffer() = 0;
|
||||
virtual void UnlockVertexBuffer() = 0;
|
||||
int GetNumVtx() const { return mNumVtx; }
|
||||
int GetNumVtxToDraw() const { return mNumVtxToDraw; }
|
||||
void SetNumVtxToDraw(int inUsed) { mNumVtxToDraw = inUsed; }
|
||||
|
||||
/// Index buffer management functions
|
||||
virtual void CreateIndexBuffer(int inNumIdx, const uint32 *inData = nullptr) = 0;
|
||||
virtual void ReleaseIndexBuffer();
|
||||
virtual uint32 * LockIndexBuffer() = 0;
|
||||
virtual void UnlockIndexBuffer() = 0;
|
||||
int GetNumIdx() const { return mNumIdx; }
|
||||
int GetNumIdxToDraw() const { return mNumIdxToDraw; }
|
||||
void SetNumIdxToDraw(int inUsed) { mNumIdxToDraw = inUsed; }
|
||||
|
||||
/// Draw the primitive
|
||||
virtual void Draw() const = 0;
|
||||
|
||||
/// Implement RefTargetVirtual, so we can conveniently use this class as DebugRenderer::Batch
|
||||
virtual void AddRef() override { RefTarget<RenderPrimitive>::AddRef(); }
|
||||
virtual void Release() override { RefTarget<RenderPrimitive>::Release(); }
|
||||
|
||||
protected:
|
||||
int mNumVtx = 0;
|
||||
int mNumVtxToDraw = 0;
|
||||
int mVtxSize = 0;
|
||||
|
||||
int mNumIdx = 0;
|
||||
int mNumIdxToDraw = 0;
|
||||
};
|
||||
89
lib/All/JoltPhysics/TestFramework/Renderer/Renderer.cpp
Normal file
89
lib/All/JoltPhysics/TestFramework/Renderer/Renderer.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
|
||||
Renderer::~Renderer()
|
||||
{
|
||||
if (mWindow != nullptr)
|
||||
mWindow->SetWindowResizeListener({});
|
||||
}
|
||||
|
||||
void Renderer::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
// Store window
|
||||
mWindow = inWindow;
|
||||
mWindow->SetWindowResizeListener([this]() { OnWindowResize(); });
|
||||
}
|
||||
|
||||
static Mat44 sPerspectiveInfiniteReverseZ(float inFovY, float inAspect, float inNear, float inYSign)
|
||||
{
|
||||
float height = 1.0f / Tan(0.5f * inFovY);
|
||||
float width = height / inAspect;
|
||||
|
||||
return Mat44(Vec4(width, 0.0f, 0.0f, 0.0f), Vec4(0.0f, inYSign * height, 0.0f, 0.0f), Vec4(0.0f, 0.0f, 0.0f, -1.0f), Vec4(0.0f, 0.0f, inNear, 0.0f));
|
||||
}
|
||||
|
||||
bool Renderer::BeginFrame(const CameraState &inCamera, float inWorldScale)
|
||||
{
|
||||
// Mark that we're in the frame
|
||||
JPH_ASSERT(!mInFrame);
|
||||
mInFrame = true;
|
||||
|
||||
// Store state
|
||||
mCameraState = inCamera;
|
||||
|
||||
// Light properties
|
||||
Vec3 light_pos = inWorldScale * Vec3(250, 250, 250);
|
||||
Vec3 light_tgt = Vec3::sZero();
|
||||
Vec3 light_up = Vec3(0, 1, 0);
|
||||
Vec3 light_fwd = (light_tgt - light_pos).Normalized();
|
||||
float light_fov = DegreesToRadians(20.0f);
|
||||
float light_near = 1.0f;
|
||||
|
||||
// Camera properties
|
||||
Vec3 cam_pos = Vec3(inCamera.mPos - mBaseOffset);
|
||||
float camera_fovy = inCamera.mFOVY;
|
||||
float camera_aspect = static_cast<float>(mWindow->GetWindowWidth()) / mWindow->GetWindowHeight();
|
||||
float camera_fovx = 2.0f * ATan(camera_aspect * Tan(0.5f * camera_fovy));
|
||||
float camera_near = 0.01f * inWorldScale;
|
||||
|
||||
// Calculate camera frustum
|
||||
mCameraFrustum = Frustum(cam_pos, inCamera.mForward, inCamera.mUp, camera_fovx, camera_fovy, camera_near);
|
||||
|
||||
// Calculate light frustum
|
||||
mLightFrustum = Frustum(light_pos, light_fwd, light_up, light_fov, light_fov, light_near);
|
||||
|
||||
// Camera projection and view
|
||||
mVSBuffer.mProjection = sPerspectiveInfiniteReverseZ(camera_fovy, camera_aspect, camera_near, mPerspectiveYSign);
|
||||
Vec3 tgt = cam_pos + inCamera.mForward;
|
||||
mVSBuffer.mView = Mat44::sLookAt(cam_pos, tgt, inCamera.mUp);
|
||||
|
||||
// Light projection and view
|
||||
mVSBuffer.mLightProjection = sPerspectiveInfiniteReverseZ(light_fov, 1.0f, light_near, mPerspectiveYSign);
|
||||
mVSBuffer.mLightView = Mat44::sLookAt(light_pos, light_tgt, light_up);
|
||||
|
||||
// Camera ortho projection and view
|
||||
mVSBufferOrtho.mProjection = Mat44(Vec4(2.0f / mWindow->GetWindowWidth(), 0.0f, 0.0f, 0.0f), Vec4(0.0f, -mPerspectiveYSign * 2.0f / mWindow->GetWindowHeight(), 0.0f, 0.0f), Vec4(0.0f, 0.0f, -1.0f, 0.0f), Vec4(-1.0f, mPerspectiveYSign * 1.0f, 0.0f, 1.0f));
|
||||
mVSBufferOrtho.mView = Mat44::sIdentity();
|
||||
|
||||
// Light projection and view are unused in ortho mode
|
||||
mVSBufferOrtho.mLightView = Mat44::sIdentity();
|
||||
mVSBufferOrtho.mLightProjection = Mat44::sIdentity();
|
||||
|
||||
// Set constants for pixel shader
|
||||
mPSBuffer.mCameraPos = Vec4(cam_pos, 0);
|
||||
mPSBuffer.mLightPos = Vec4(light_pos, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Renderer::EndFrame()
|
||||
{
|
||||
// Mark that we're no longer in the frame
|
||||
JPH_ASSERT(mInFrame);
|
||||
mInFrame = false;
|
||||
}
|
||||
126
lib/All/JoltPhysics/TestFramework/Renderer/Renderer.h
Normal file
126
lib/All/JoltPhysics/TestFramework/Renderer/Renderer.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Image/Surface.h>
|
||||
#include <Window/ApplicationWindow.h>
|
||||
#include <Renderer/Frustum.h>
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <Renderer/VertexShader.h>
|
||||
#include <Renderer/PixelShader.h>
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
#include <memory>
|
||||
|
||||
// Forward declares
|
||||
class Texture;
|
||||
|
||||
/// Camera setup
|
||||
struct CameraState
|
||||
{
|
||||
CameraState() : mPos(RVec3::sZero()), mForward(0, 0, -1), mUp(0, 1, 0), mFOVY(DegreesToRadians(70.0f)) { }
|
||||
|
||||
RVec3 mPos; ///< Camera position
|
||||
Vec3 mForward; ///< Camera forward vector
|
||||
Vec3 mUp; ///< Camera up vector
|
||||
float mFOVY; ///< Field of view in radians in up direction
|
||||
};
|
||||
|
||||
/// Responsible for rendering primitives to the screen
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~Renderer();
|
||||
|
||||
/// Initialize renderer
|
||||
virtual void Initialize(ApplicationWindow *inWindow);
|
||||
|
||||
/// Start / end drawing a frame
|
||||
virtual bool BeginFrame(const CameraState &inCamera, float inWorldScale);
|
||||
virtual void EndShadowPass() = 0;
|
||||
virtual void EndFrame();
|
||||
|
||||
/// Switch between orthographic and 3D projection mode
|
||||
virtual void SetProjectionMode() = 0;
|
||||
virtual void SetOrthoMode() = 0;
|
||||
|
||||
/// Create texture from an image surface
|
||||
virtual Ref<Texture> CreateTexture(const Surface *inSurface) = 0;
|
||||
|
||||
/// Compile a vertex shader
|
||||
virtual Ref<VertexShader> CreateVertexShader(const char *inName) = 0;
|
||||
|
||||
/// Compile a pixel shader
|
||||
virtual Ref<PixelShader> CreatePixelShader(const char *inName) = 0;
|
||||
|
||||
/// Create pipeline state object that defines the complete state of how primitives should be rendered
|
||||
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) = 0;
|
||||
|
||||
/// Create a render primitive
|
||||
virtual RenderPrimitive * CreateRenderPrimitive(PipelineState::ETopology inType) = 0;
|
||||
|
||||
/// Create render instances object to allow drawing batches of objects
|
||||
virtual RenderInstances * CreateRenderInstances() = 0;
|
||||
|
||||
/// Get the shadow map texture
|
||||
virtual Texture * GetShadowMap() const = 0;
|
||||
|
||||
/// Get the camera state / frustum (only valid between BeginFrame() / EndFrame())
|
||||
const CameraState & GetCameraState() const { JPH_ASSERT(mInFrame); return mCameraState; }
|
||||
const Frustum & GetCameraFrustum() const { JPH_ASSERT(mInFrame); return mCameraFrustum; }
|
||||
|
||||
/// Offset relative to which the world is rendered, helps avoiding rendering artifacts at big distances
|
||||
RVec3 GetBaseOffset() const { return mBaseOffset; }
|
||||
void SetBaseOffset(RVec3 inOffset) { mBaseOffset = inOffset; }
|
||||
|
||||
/// Get the light frustum (only valid between BeginFrame() / EndFrame())
|
||||
const Frustum & GetLightFrustum() const { JPH_ASSERT(mInFrame); return mLightFrustum; }
|
||||
|
||||
/// How many frames our pipeline is
|
||||
inline static const uint cFrameCount = 2;
|
||||
|
||||
/// Size of the shadow map will be cShadowMapSize x cShadowMapSize pixels
|
||||
inline static const uint cShadowMapSize = 4096;
|
||||
|
||||
/// Which frame is currently rendering (to keep track of which buffers are free to overwrite)
|
||||
uint GetCurrentFrameIndex() const { JPH_ASSERT(mInFrame); return mFrameIndex; }
|
||||
|
||||
/// Get the window we're rendering to
|
||||
ApplicationWindow * GetWindow() const { return mWindow; }
|
||||
|
||||
/// Callback when the window resizes and the back buffer needs to be adjusted
|
||||
virtual void OnWindowResize() = 0;
|
||||
|
||||
/// Create a platform specific Renderer instance
|
||||
static Renderer * sCreate();
|
||||
|
||||
protected:
|
||||
struct VertexShaderConstantBuffer
|
||||
{
|
||||
Mat44 mView;
|
||||
Mat44 mProjection;
|
||||
Mat44 mLightView;
|
||||
Mat44 mLightProjection;
|
||||
};
|
||||
|
||||
struct PixelShaderConstantBuffer
|
||||
{
|
||||
Vec4 mCameraPos;
|
||||
Vec4 mLightPos;
|
||||
};
|
||||
|
||||
ApplicationWindow * mWindow = nullptr; ///< The window we're rendering to
|
||||
float mPerspectiveYSign = 1.0f; ///< Sign for the Y coordinate in the projection matrix (1 for DX, -1 for Vulkan)
|
||||
bool mInFrame = false; ///< If we're within a BeginFrame() / EndFrame() pair
|
||||
CameraState mCameraState;
|
||||
RVec3 mBaseOffset { RVec3::sZero() }; ///< Offset to subtract from the camera position to deal with large worlds
|
||||
Frustum mCameraFrustum;
|
||||
Frustum mLightFrustum;
|
||||
uint mFrameIndex = 0; ///< Current frame index (0 or 1)
|
||||
VertexShaderConstantBuffer mVSBuffer;
|
||||
VertexShaderConstantBuffer mVSBufferOrtho;
|
||||
PixelShaderConstantBuffer mPSBuffer;
|
||||
};
|
||||
29
lib/All/JoltPhysics/TestFramework/Renderer/Texture.h
Normal file
29
lib/All/JoltPhysics/TestFramework/Renderer/Texture.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
/// Forward declares
|
||||
class Surface;
|
||||
|
||||
class Texture : public RefTarget<Texture>
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Texture(int inWidth, int inHeight) : mWidth(inWidth), mHeight(inHeight) { }
|
||||
virtual ~Texture() = default;
|
||||
|
||||
/// Get dimensions of texture
|
||||
inline int GetWidth() const { return mWidth; }
|
||||
inline int GetHeight() const { return mHeight; }
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const = 0;
|
||||
|
||||
protected:
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
};
|
||||
21
lib/All/JoltPhysics/TestFramework/Renderer/VK/BufferVK.h
Normal file
21
lib/All/JoltPhysics/TestFramework/Renderer/VK/BufferVK.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Simple wrapper class to manage a Vulkan buffer
|
||||
class BufferVK
|
||||
{
|
||||
public:
|
||||
VkBuffer mBuffer = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mMemory = VK_NULL_HANDLE;
|
||||
VkDeviceSize mOffset = 0;
|
||||
VkDeviceSize mSize = 0;
|
||||
|
||||
VkBufferUsageFlags mUsage;
|
||||
VkMemoryPropertyFlags mProperties;
|
||||
VkDeviceSize mAllocatedSize;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/ConstantBufferVK.h>
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
|
||||
ConstantBufferVK::ConstantBufferVK(RendererVK *inRenderer, VkDeviceSize inBufferSize) :
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
mRenderer->CreateBuffer(inBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mBuffer);
|
||||
}
|
||||
|
||||
ConstantBufferVK::~ConstantBufferVK()
|
||||
{
|
||||
mRenderer->FreeBuffer(mBuffer);
|
||||
}
|
||||
|
||||
void *ConstantBufferVK::MapInternal()
|
||||
{
|
||||
void *data = nullptr;
|
||||
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mBuffer.mMemory, mBuffer.mOffset, mBuffer.mSize, 0, &data));
|
||||
return data;
|
||||
}
|
||||
|
||||
void ConstantBufferVK::Unmap()
|
||||
{
|
||||
vkUnmapMemory(mRenderer->GetDevice(), mBuffer.mMemory);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VK/BufferVK.h>
|
||||
|
||||
class RendererVK;
|
||||
|
||||
/// A binary blob that can be used to pass constants to a shader
|
||||
class ConstantBufferVK
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
ConstantBufferVK(RendererVK *inRenderer, VkDeviceSize inBufferSize);
|
||||
~ConstantBufferVK();
|
||||
|
||||
/// 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();
|
||||
|
||||
VkBuffer GetBuffer() const { return mBuffer.mBuffer; }
|
||||
|
||||
private:
|
||||
void * MapInternal();
|
||||
|
||||
RendererVK * mRenderer;
|
||||
BufferVK mBuffer;
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
#include <Utils/Log.h>
|
||||
|
||||
void FatalErrorIfFailed(VkResult inVkResult)
|
||||
{
|
||||
if (inVkResult != VK_SUCCESS)
|
||||
FatalError("Vulkan error returned: %d", inVkResult);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Convert Vulkan error to readable text and alert
|
||||
void FatalErrorIfFailed(VkResult inVkResult);
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/PipelineStateVK.h>
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
|
||||
PipelineStateVK::PipelineStateVK(RendererVK *inRenderer, const VertexShaderVK *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderVK *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode) :
|
||||
mRenderer(inRenderer),
|
||||
mVertexShader(inVertexShader),
|
||||
mPixelShader(inPixelShader)
|
||||
{
|
||||
VkPipelineShaderStageCreateInfo shader_stages[] = { inVertexShader->mStageInfo, inPixelShader->mStageInfo };
|
||||
|
||||
// TODO: This doesn't follow the SPIR-V alignment rules
|
||||
Array<VkVertexInputAttributeDescription> attribute_descriptions;
|
||||
VkVertexInputAttributeDescription temp_vtx = { }, temp_instance = { };
|
||||
temp_instance.binding = 1;
|
||||
uint instance_alignment = 1;
|
||||
for (uint i = 0; i < inInputDescriptionCount; ++i)
|
||||
switch (inInputDescription[i])
|
||||
{
|
||||
case EInputDescription::Position:
|
||||
case EInputDescription::Normal:
|
||||
temp_vtx.format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
attribute_descriptions.push_back(temp_vtx);
|
||||
temp_vtx.offset += 3 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::Color:
|
||||
temp_vtx.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attribute_descriptions.push_back(temp_vtx);
|
||||
temp_vtx.offset += 4 * sizeof(uint8);
|
||||
break;
|
||||
|
||||
case EInputDescription::TexCoord:
|
||||
temp_vtx.format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attribute_descriptions.push_back(temp_vtx);
|
||||
temp_vtx.offset += 2 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceColor:
|
||||
instance_alignment = max(instance_alignment, 4u);
|
||||
temp_instance.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attribute_descriptions.push_back(temp_instance);
|
||||
temp_instance.offset += 4 * sizeof(uint8);
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceTransform:
|
||||
case EInputDescription::InstanceInvTransform:
|
||||
instance_alignment = max(instance_alignment, 16u);
|
||||
temp_instance.format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
attribute_descriptions.push_back(temp_instance);
|
||||
temp_instance.offset += 4 * sizeof(float);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < uint32(attribute_descriptions.size()); ++i)
|
||||
attribute_descriptions[i].location = i;
|
||||
|
||||
VkVertexInputBindingDescription binding_description[2];
|
||||
binding_description[0].binding = 0;
|
||||
binding_description[0].stride = temp_vtx.offset;
|
||||
binding_description[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
binding_description[1].binding = 1;
|
||||
binding_description[1].stride = AlignUp(temp_instance.offset, instance_alignment);
|
||||
binding_description[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
|
||||
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertex_input_info.vertexBindingDescriptionCount = temp_instance.offset > 0? 2 : 1;
|
||||
vertex_input_info.pVertexBindingDescriptions = binding_description;
|
||||
vertex_input_info.vertexAttributeDescriptionCount = uint32(attribute_descriptions.size());
|
||||
vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data();
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
|
||||
input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
input_assembly.topology = inTopology == ETopology::Triangle? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
input_assembly.primitiveRestartEnable = VK_FALSE;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_state = {};
|
||||
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
viewport_state.viewportCount = 1;
|
||||
viewport_state.scissorCount = 1;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterizer = {};
|
||||
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rasterizer.depthClampEnable = VK_FALSE;
|
||||
rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
||||
rasterizer.polygonMode = inFillMode == EFillMode::Solid? VK_POLYGON_MODE_FILL : VK_POLYGON_MODE_LINE;
|
||||
rasterizer.lineWidth = 1.0f;
|
||||
rasterizer.cullMode = inCullMode == ECullMode::Backface? VK_CULL_MODE_BACK_BIT : VK_CULL_MODE_FRONT_BIT;
|
||||
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rasterizer.depthBiasEnable = VK_FALSE;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampling = {};
|
||||
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisampling.sampleShadingEnable = VK_FALSE;
|
||||
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil = {};
|
||||
depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
depth_stencil.depthTestEnable = inDepthTest == EDepthTest::On? VK_TRUE : VK_FALSE;
|
||||
depth_stencil.depthWriteEnable = inDepthTest == EDepthTest::On? VK_TRUE : VK_FALSE;
|
||||
depth_stencil.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; // Reverse-Z, greater is closer
|
||||
|
||||
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
|
||||
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
switch (inBlendMode)
|
||||
{
|
||||
case EBlendMode::Write:
|
||||
color_blend_attachment.blendEnable = VK_FALSE;
|
||||
break;
|
||||
|
||||
case EBlendMode::AlphaBlend:
|
||||
color_blend_attachment.blendEnable = VK_TRUE;
|
||||
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
break;
|
||||
}
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blending = {};
|
||||
color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
color_blending.logicOpEnable = VK_FALSE;
|
||||
color_blending.logicOp = VK_LOGIC_OP_COPY;
|
||||
color_blending.attachmentCount = 1;
|
||||
color_blending.pAttachments = &color_blend_attachment;
|
||||
|
||||
VkDynamicState dynamic_states[] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR
|
||||
};
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state = {};
|
||||
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dynamic_state.dynamicStateCount = std::size(dynamic_states);
|
||||
dynamic_state.pDynamicStates = dynamic_states;
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_info = {};
|
||||
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipeline_info.stageCount = std::size(shader_stages);
|
||||
pipeline_info.pStages = shader_stages;
|
||||
pipeline_info.pVertexInputState = &vertex_input_info;
|
||||
pipeline_info.pInputAssemblyState = &input_assembly;
|
||||
pipeline_info.pViewportState = &viewport_state;
|
||||
pipeline_info.pRasterizationState = &rasterizer;
|
||||
pipeline_info.pMultisampleState = &multisampling;
|
||||
pipeline_info.pDepthStencilState = &depth_stencil;
|
||||
pipeline_info.pColorBlendState = &color_blending;
|
||||
pipeline_info.pDynamicState = &dynamic_state;
|
||||
pipeline_info.layout = mRenderer->GetPipelineLayout();
|
||||
pipeline_info.renderPass = inDrawPass == EDrawPass::Normal? mRenderer->GetRenderPass() : mRenderer->GetRenderPassShadow();
|
||||
FatalErrorIfFailed(vkCreateGraphicsPipelines(mRenderer->GetDevice(), VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &mGraphicsPipeline));
|
||||
}
|
||||
|
||||
PipelineStateVK::~PipelineStateVK()
|
||||
{
|
||||
vkDeviceWaitIdle(mRenderer->GetDevice());
|
||||
|
||||
vkDestroyPipeline(mRenderer->GetDevice(), mGraphicsPipeline, nullptr);
|
||||
}
|
||||
|
||||
void PipelineStateVK::Activate()
|
||||
{
|
||||
vkCmdBindPipeline(mRenderer->GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipeline);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <Renderer/VK/VertexShaderVK.h>
|
||||
#include <Renderer/VK/PixelShaderVK.h>
|
||||
|
||||
class RendererVK;
|
||||
|
||||
/// Vulkan pipeline state object
|
||||
class PipelineStateVK : public PipelineState
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PipelineStateVK(RendererVK *inRenderer, const VertexShaderVK *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderVK *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode);
|
||||
virtual ~PipelineStateVK() override;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() override;
|
||||
|
||||
private:
|
||||
RendererVK * mRenderer;
|
||||
RefConst<VertexShaderVK> mVertexShader;
|
||||
RefConst<PixelShaderVK> mPixelShader;
|
||||
|
||||
VkPipeline mGraphicsPipeline;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PixelShader.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Pixel shader handle for Vulkan
|
||||
class PixelShaderVK : public PixelShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PixelShaderVK(VkDevice inDevice, VkShaderModule inShaderModule) :
|
||||
mDevice(inDevice),
|
||||
mStageInfo()
|
||||
{
|
||||
mStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
mStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
mStageInfo.module = inShaderModule;
|
||||
mStageInfo.pName = "main";
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
virtual ~PixelShaderVK() override
|
||||
{
|
||||
vkDestroyShaderModule(mDevice, mStageInfo.module, nullptr);
|
||||
}
|
||||
|
||||
VkDevice mDevice;
|
||||
VkPipelineShaderStageCreateInfo mStageInfo;
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/RenderInstancesVK.h>
|
||||
#include <Renderer/VK/RenderPrimitiveVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
|
||||
void RenderInstancesVK::Clear()
|
||||
{
|
||||
mRenderer->FreeBuffer(mInstancesBuffer);
|
||||
}
|
||||
|
||||
void RenderInstancesVK::CreateBuffer(int inNumInstances, int inInstanceSize)
|
||||
{
|
||||
Clear();
|
||||
|
||||
mRenderer->CreateBuffer(VkDeviceSize(inNumInstances) * inInstanceSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mInstancesBuffer);
|
||||
}
|
||||
|
||||
void *RenderInstancesVK::Lock()
|
||||
{
|
||||
void *data;
|
||||
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mInstancesBuffer.mMemory, mInstancesBuffer.mOffset, mInstancesBuffer.mSize, 0, &data));
|
||||
return data;
|
||||
}
|
||||
|
||||
void RenderInstancesVK::Unlock()
|
||||
{
|
||||
vkUnmapMemory(mRenderer->GetDevice(), mInstancesBuffer.mMemory);
|
||||
}
|
||||
|
||||
void RenderInstancesVK::Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const
|
||||
{
|
||||
if (inNumInstances <= 0)
|
||||
return;
|
||||
|
||||
VkCommandBuffer command_buffer = mRenderer->GetCommandBuffer();
|
||||
RenderPrimitiveVK *primitive = static_cast<RenderPrimitiveVK *>(inPrimitive);
|
||||
|
||||
VkBuffer buffers[] = { primitive->mVertexBuffer.mBuffer, mInstancesBuffer.mBuffer };
|
||||
VkDeviceSize offsets[] = { 0, 0 };
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 2, buffers, offsets);
|
||||
|
||||
if (primitive->mIndexBuffer.mBuffer == VK_NULL_HANDLE)
|
||||
{
|
||||
vkCmdDraw(command_buffer, primitive->mNumVtxToDraw, inNumInstances, 0, inStartInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
vkCmdBindIndexBuffer(command_buffer, primitive->mIndexBuffer.mBuffer, 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
vkCmdDrawIndexed(command_buffer, primitive->mNumIdxToDraw, inNumInstances, 0, 0, inStartInstance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// Vulkan implementation of a render instances object
|
||||
class RenderInstancesVK : public RenderInstances
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderInstancesVK(RendererVK *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderInstancesVK() 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:
|
||||
RendererVK * mRenderer;
|
||||
|
||||
BufferVK mInstancesBuffer;
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/RenderPrimitiveVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
|
||||
void RenderPrimitiveVK::ReleaseVertexBuffer()
|
||||
{
|
||||
mRenderer->FreeBuffer(mVertexBuffer);
|
||||
mVertexBufferDeviceLocal = false;
|
||||
|
||||
RenderPrimitive::ReleaseVertexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::ReleaseIndexBuffer()
|
||||
{
|
||||
mRenderer->FreeBuffer(mIndexBuffer);
|
||||
mIndexBufferDeviceLocal = false;
|
||||
|
||||
RenderPrimitive::ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
RenderPrimitive::CreateVertexBuffer(inNumVtx, inVtxSize, inData);
|
||||
|
||||
VkDeviceSize size = VkDeviceSize(inNumVtx) * inVtxSize;
|
||||
if (inData != nullptr)
|
||||
{
|
||||
mRenderer->CreateDeviceLocalBuffer(inData, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, mVertexBuffer);
|
||||
mVertexBufferDeviceLocal = true;
|
||||
}
|
||||
else
|
||||
mRenderer->CreateBuffer(size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mVertexBuffer);
|
||||
}
|
||||
|
||||
void *RenderPrimitiveVK::LockVertexBuffer()
|
||||
{
|
||||
JPH_ASSERT(!mVertexBufferDeviceLocal);
|
||||
|
||||
void *data;
|
||||
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mVertexBuffer.mMemory, mVertexBuffer.mOffset, VkDeviceSize(mNumVtx) * mVtxSize, 0, &data));
|
||||
return data;
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::UnlockVertexBuffer()
|
||||
{
|
||||
vkUnmapMemory(mRenderer->GetDevice(), mVertexBuffer.mMemory);
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
RenderPrimitive::CreateIndexBuffer(inNumIdx, inData);
|
||||
|
||||
VkDeviceSize size = VkDeviceSize(inNumIdx) * sizeof(uint32);
|
||||
if (inData != nullptr)
|
||||
{
|
||||
mRenderer->CreateDeviceLocalBuffer(inData, size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, mIndexBuffer);
|
||||
mIndexBufferDeviceLocal = true;
|
||||
}
|
||||
else
|
||||
mRenderer->CreateBuffer(size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mIndexBuffer);
|
||||
}
|
||||
|
||||
uint32 *RenderPrimitiveVK::LockIndexBuffer()
|
||||
{
|
||||
JPH_ASSERT(!mIndexBufferDeviceLocal);
|
||||
|
||||
void *data;
|
||||
vkMapMemory(mRenderer->GetDevice(), mIndexBuffer.mMemory, mIndexBuffer.mOffset, VkDeviceSize(mNumIdx) * sizeof(uint32), 0, &data);
|
||||
return reinterpret_cast<uint32 *>(data);
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::UnlockIndexBuffer()
|
||||
{
|
||||
vkUnmapMemory(mRenderer->GetDevice(), mIndexBuffer.mMemory);
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::Draw() const
|
||||
{
|
||||
VkCommandBuffer command_buffer = mRenderer->GetCommandBuffer();
|
||||
|
||||
VkBuffer vertex_buffers[] = { mVertexBuffer.mBuffer };
|
||||
VkDeviceSize offsets[] = { 0 };
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offsets);
|
||||
|
||||
if (mIndexBuffer.mBuffer == VK_NULL_HANDLE)
|
||||
{
|
||||
vkCmdDraw(command_buffer, mNumVtxToDraw, 1, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
vkCmdBindIndexBuffer(command_buffer, mIndexBuffer.mBuffer, 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
vkCmdDrawIndexed(command_buffer, mNumIdxToDraw, 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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/VK/RendererVK.h>
|
||||
#include <Renderer/VK/BufferVK.h>
|
||||
|
||||
/// Vulkan implementation of a render primitive
|
||||
class RenderPrimitiveVK : public RenderPrimitive
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderPrimitiveVK(RendererVK *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderPrimitiveVK() 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 RenderInstancesVK;
|
||||
|
||||
RendererVK * mRenderer;
|
||||
|
||||
BufferVK mVertexBuffer;
|
||||
bool mVertexBufferDeviceLocal = false;
|
||||
|
||||
BufferVK mIndexBuffer;
|
||||
bool mIndexBufferDeviceLocal = false;
|
||||
};
|
||||
1324
lib/All/JoltPhysics/TestFramework/Renderer/VK/RendererVK.cpp
Normal file
1324
lib/All/JoltPhysics/TestFramework/Renderer/VK/RendererVK.cpp
Normal file
File diff suppressed because it is too large
Load Diff
156
lib/All/JoltPhysics/TestFramework/Renderer/VK/RendererVK.h
Normal file
156
lib/All/JoltPhysics/TestFramework/Renderer/VK/RendererVK.h
Normal file
@@ -0,0 +1,156 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/VK/ConstantBufferVK.h>
|
||||
#include <Renderer/VK/TextureVK.h>
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Vulkan renderer
|
||||
class RendererVK : public Renderer
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~RendererVK() 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;
|
||||
|
||||
VkDevice GetDevice() const { return mDevice; }
|
||||
VkDescriptorPool GetDescriptorPool() const { return mDescriptorPool; }
|
||||
VkDescriptorSetLayout GetDescriptorSetLayoutTexture() const { return mDescriptorSetLayoutTexture; }
|
||||
VkSampler GetTextureSamplerRepeat() const { return mTextureSamplerRepeat; }
|
||||
VkSampler GetTextureSamplerShadow() const { return mTextureSamplerShadow; }
|
||||
VkRenderPass GetRenderPassShadow() const { return mRenderPassShadow; }
|
||||
VkRenderPass GetRenderPass() const { return mRenderPass; }
|
||||
VkPipelineLayout GetPipelineLayout() const { return mPipelineLayout; }
|
||||
VkCommandBuffer GetCommandBuffer() { JPH_ASSERT(mInFrame); return mCommandBuffers[mFrameIndex]; }
|
||||
VkCommandBuffer StartTempCommandBuffer();
|
||||
void EndTempCommandBuffer(VkCommandBuffer inCommandBuffer);
|
||||
void AllocateMemory(VkDeviceSize inSize, uint32 inMemoryTypeBits, VkMemoryPropertyFlags inProperties, VkDeviceMemory &outMemory);
|
||||
void FreeMemory(VkDeviceMemory inMemory, VkDeviceSize inSize);
|
||||
void CreateBuffer(VkDeviceSize inSize, VkBufferUsageFlags inUsage, VkMemoryPropertyFlags inProperties, BufferVK &outBuffer);
|
||||
void CopyBuffer(VkBuffer inSrc, VkBuffer inDst, VkDeviceSize inSize);
|
||||
void CreateDeviceLocalBuffer(const void *inData, VkDeviceSize inSize, VkBufferUsageFlags inUsage, BufferVK &outBuffer);
|
||||
void FreeBuffer(BufferVK &ioBuffer);
|
||||
unique_ptr<ConstantBufferVK> CreateConstantBuffer(VkDeviceSize inBufferSize);
|
||||
void CreateImage(uint32 inWidth, uint32 inHeight, VkFormat inFormat, VkImageTiling inTiling, VkImageUsageFlags inUsage, VkMemoryPropertyFlags inProperties, VkImage &outImage, VkDeviceMemory &outMemory);
|
||||
void DestroyImage(VkImage inImage, VkDeviceMemory inMemory);
|
||||
VkImageView CreateImageView(VkImage inImage, VkFormat inFormat, VkImageAspectFlags inAspectFlags);
|
||||
VkFormat FindDepthFormat();
|
||||
|
||||
private:
|
||||
uint32 FindMemoryType(uint32 inTypeFilter, VkMemoryPropertyFlags inProperties);
|
||||
void FreeBufferInternal(BufferVK &ioBuffer);
|
||||
VkSurfaceFormatKHR SelectFormat(VkPhysicalDevice inDevice);
|
||||
void CreateSwapChain(VkPhysicalDevice inDevice);
|
||||
void DestroySwapChain();
|
||||
void UpdateViewPortAndScissorRect(uint32 inWidth, uint32 inHeight);
|
||||
VkSemaphore AllocateSemaphore();
|
||||
void FreeSemaphore(VkSemaphore inSemaphore);
|
||||
|
||||
VkInstance mInstance = VK_NULL_HANDLE;
|
||||
#ifdef JPH_DEBUG
|
||||
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
|
||||
#endif
|
||||
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
|
||||
VkPhysicalDeviceMemoryProperties mMemoryProperties;
|
||||
VkDevice mDevice = VK_NULL_HANDLE;
|
||||
uint32 mGraphicsQueueIndex = 0;
|
||||
uint32 mPresentQueueIndex = 0;
|
||||
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
|
||||
VkQueue mPresentQueue = VK_NULL_HANDLE;
|
||||
VkSurfaceKHR mSurface = VK_NULL_HANDLE;
|
||||
VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
|
||||
bool mSubOptimalSwapChain = false;
|
||||
Array<VkImage> mSwapChainImages;
|
||||
VkFormat mSwapChainImageFormat;
|
||||
VkExtent2D mSwapChainExtent;
|
||||
Array<VkImageView> mSwapChainImageViews;
|
||||
VkImage mDepthImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mDepthImageMemory = VK_NULL_HANDLE;
|
||||
VkImageView mDepthImageView = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout mDescriptorSetLayoutUBO = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout mDescriptorSetLayoutTexture = VK_NULL_HANDLE;
|
||||
VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
|
||||
VkDescriptorSet mDescriptorSets[cFrameCount];
|
||||
VkDescriptorSet mDescriptorSetsOrtho[cFrameCount];
|
||||
VkSampler mTextureSamplerShadow = VK_NULL_HANDLE;
|
||||
VkSampler mTextureSamplerRepeat = VK_NULL_HANDLE;
|
||||
VkRenderPass mRenderPassShadow = VK_NULL_HANDLE;
|
||||
VkRenderPass mRenderPass = VK_NULL_HANDLE;
|
||||
VkPipelineLayout mPipelineLayout = VK_NULL_HANDLE;
|
||||
VkFramebuffer mShadowFrameBuffer = VK_NULL_HANDLE;
|
||||
Array<VkFramebuffer> mSwapChainFramebuffers;
|
||||
uint32 mImageIndex = 0;
|
||||
VkCommandPool mCommandPool = VK_NULL_HANDLE;
|
||||
VkCommandBuffer mCommandBuffers[cFrameCount];
|
||||
Array<VkSemaphore> mAvailableSemaphores;
|
||||
Array<VkSemaphore> mImageAvailableSemaphores;
|
||||
Array<VkSemaphore> mRenderFinishedSemaphores;
|
||||
VkFence mInFlightFences[cFrameCount];
|
||||
Ref<TextureVK> mShadowMap;
|
||||
unique_ptr<ConstantBufferVK> mVertexShaderConstantBufferProjection[cFrameCount];
|
||||
unique_ptr<ConstantBufferVK> mVertexShaderConstantBufferOrtho[cFrameCount];
|
||||
unique_ptr<ConstantBufferVK> mPixelShaderConstantBuffer[cFrameCount];
|
||||
|
||||
struct Key
|
||||
{
|
||||
bool operator == (const Key &inRHS) const
|
||||
{
|
||||
return mSize == inRHS.mSize && mUsage == inRHS.mUsage && mProperties == inRHS.mProperties;
|
||||
}
|
||||
|
||||
VkDeviceSize mSize;
|
||||
VkBufferUsageFlags mUsage;
|
||||
VkMemoryPropertyFlags mProperties;
|
||||
};
|
||||
|
||||
JPH_MAKE_HASH_STRUCT(Key, KeyHasher, t.mSize, t.mUsage, t.mProperties)
|
||||
|
||||
// We try to recycle buffers from frame to frame
|
||||
using BufferCache = UnorderedMap<Key, Array<BufferVK>, KeyHasher>;
|
||||
|
||||
BufferCache mFreedBuffers[cFrameCount];
|
||||
BufferCache mBufferCache;
|
||||
|
||||
// Smaller allocations (from cMinAllocSize to cMaxAllocSize) will be done in blocks of cBlockSize bytes.
|
||||
// We do this because there is a limit to the number of allocations that we can make in Vulkan.
|
||||
static constexpr VkDeviceSize cMinAllocSize = 512;
|
||||
static constexpr VkDeviceSize cMaxAllocSize = 65536;
|
||||
static constexpr VkDeviceSize cBlockSize = 524288;
|
||||
|
||||
JPH_MAKE_HASH_STRUCT(Key, MemKeyHasher, t.mUsage, t.mProperties, t.mSize)
|
||||
|
||||
struct Memory
|
||||
{
|
||||
VkDeviceMemory mMemory;
|
||||
VkDeviceSize mOffset;
|
||||
};
|
||||
|
||||
using MemoryCache = UnorderedMap<Key, Array<Memory>, KeyHasher>;
|
||||
|
||||
MemoryCache mMemoryCache;
|
||||
uint32 mNumAllocations = 0;
|
||||
uint32 mMaxNumAllocations = 0;
|
||||
VkDeviceSize mTotalAllocated = 0;
|
||||
VkDeviceSize mMaxTotalAllocated = 0;
|
||||
};
|
||||
180
lib/All/JoltPhysics/TestFramework/Renderer/VK/TextureVK.cpp
Normal file
180
lib/All/JoltPhysics/TestFramework/Renderer/VK/TextureVK.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/TextureVK.h>
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
|
||||
TextureVK::TextureVK(RendererVK *inRenderer, const Surface *inSurface) :
|
||||
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
ESurfaceFormat format = inSurface->GetFormat();
|
||||
VkFormat vk_format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
switch (format)
|
||||
{
|
||||
case ESurfaceFormat::A4L4: vk_format = VK_FORMAT_R8G8_UNORM; format = ESurfaceFormat::A8L8; break;
|
||||
case ESurfaceFormat::L8: vk_format = VK_FORMAT_R8_UNORM; break;
|
||||
case ESurfaceFormat::A8: vk_format = VK_FORMAT_A8_UNORM_KHR; break;
|
||||
case ESurfaceFormat::A8L8: vk_format = VK_FORMAT_R8G8_UNORM; break;
|
||||
case ESurfaceFormat::R5G6B5: vk_format = VK_FORMAT_B5G6R5_UNORM_PACK16; break;
|
||||
case ESurfaceFormat::X1R5G5B5: vk_format = VK_FORMAT_B5G5R5A1_UNORM_PACK16; format = ESurfaceFormat::A1R5G5B5; break;
|
||||
case ESurfaceFormat::X4R4G4B4: vk_format = VK_FORMAT_B4G4R4A4_UNORM_PACK16; format = ESurfaceFormat::A4R4G4B4; break;
|
||||
case ESurfaceFormat::A1R5G5B5: vk_format = VK_FORMAT_B5G5R5A1_UNORM_PACK16; break;
|
||||
case ESurfaceFormat::A4R4G4B4: vk_format = VK_FORMAT_B4G4R4A4_UNORM_PACK16; break;
|
||||
case ESurfaceFormat::R8G8B8: vk_format = VK_FORMAT_B8G8R8_UNORM; break;
|
||||
case ESurfaceFormat::B8G8R8: vk_format = VK_FORMAT_B8G8R8_UNORM; break;
|
||||
case ESurfaceFormat::X8R8G8B8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::X8B8G8R8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
||||
case ESurfaceFormat::A8R8G8B8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; break;
|
||||
case ESurfaceFormat::A8B8G8R8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::Invalid:
|
||||
default: JPH_ASSERT(false); break;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
int bpp = surface->GetBytesPerPixel();
|
||||
VkDeviceSize image_size = VkDeviceSize(mWidth) * mHeight * bpp;
|
||||
VkDevice device = mRenderer->GetDevice();
|
||||
|
||||
BufferVK staging_buffer;
|
||||
mRenderer->CreateBuffer(image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer);
|
||||
|
||||
// Copy data to upload texture
|
||||
surface->Lock(ESurfaceLockMode::Read);
|
||||
void *data;
|
||||
vkMapMemory(device, staging_buffer.mMemory, staging_buffer.mOffset, image_size, 0, &data);
|
||||
for (int y = 0; y < mHeight; ++y)
|
||||
memcpy(reinterpret_cast<uint8 *>(data) + y * mWidth * bpp, surface->GetData() + y * surface->GetStride(), mWidth * bpp);
|
||||
vkUnmapMemory(device, staging_buffer.mMemory);
|
||||
surface->UnLock();
|
||||
|
||||
// Create destination image
|
||||
mRenderer->CreateImage(mWidth, mHeight, vk_format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mImage, mImageMemory);
|
||||
|
||||
VkCommandBuffer command_buffer = mRenderer->StartTempCommandBuffer();
|
||||
|
||||
// Make the image suitable for transferring to
|
||||
TransitionImageLayout(command_buffer, mImage, vk_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
// Copy the data to the destination image
|
||||
VkBufferImageCopy region = {};
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
region.imageExtent = { uint32(mWidth), uint32(mHeight), 1 };
|
||||
vkCmdCopyBufferToImage(command_buffer, staging_buffer.mBuffer, mImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
|
||||
// Make the image suitable for sampling
|
||||
TransitionImageLayout(command_buffer, mImage, vk_format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
mRenderer->EndTempCommandBuffer(command_buffer);
|
||||
|
||||
// Destroy temporary buffer
|
||||
mRenderer->FreeBuffer(staging_buffer);
|
||||
|
||||
CreateImageViewAndDescriptorSet(vk_format, VK_IMAGE_ASPECT_COLOR_BIT, mRenderer->GetTextureSamplerRepeat());
|
||||
}
|
||||
|
||||
TextureVK::TextureVK(RendererVK *inRenderer, int inWidth, int inHeight) :
|
||||
Texture(inWidth, inHeight),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
VkFormat vk_format = mRenderer->FindDepthFormat();
|
||||
|
||||
// Create render target
|
||||
mRenderer->CreateImage(mWidth, mHeight, vk_format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mImage, mImageMemory);
|
||||
|
||||
CreateImageViewAndDescriptorSet(vk_format, VK_IMAGE_ASPECT_DEPTH_BIT, mRenderer->GetTextureSamplerShadow());
|
||||
}
|
||||
|
||||
TextureVK::~TextureVK()
|
||||
{
|
||||
if (mImage != VK_NULL_HANDLE)
|
||||
{
|
||||
VkDevice device = mRenderer->GetDevice();
|
||||
|
||||
vkDeviceWaitIdle(device);
|
||||
|
||||
vkDestroyImageView(device, mImageView, nullptr);
|
||||
|
||||
mRenderer->DestroyImage(mImage, mImageMemory);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureVK::Bind() const
|
||||
{
|
||||
if (mDescriptorSet != VK_NULL_HANDLE)
|
||||
vkCmdBindDescriptorSets(mRenderer->GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, mRenderer->GetPipelineLayout(), 1, 1, &mDescriptorSet, 0, nullptr);
|
||||
}
|
||||
|
||||
void TextureVK::CreateImageViewAndDescriptorSet(VkFormat inFormat, VkImageAspectFlags inAspectFlags, VkSampler inSampler)
|
||||
{
|
||||
VkDevice device = mRenderer->GetDevice();
|
||||
|
||||
// Create image view
|
||||
mImageView = mRenderer->CreateImageView(mImage, inFormat, inAspectFlags);
|
||||
|
||||
// Allocate descriptor set for binding the texture
|
||||
VkDescriptorSetLayout layout = mRenderer->GetDescriptorSetLayoutTexture();
|
||||
VkDescriptorSetAllocateInfo descriptor_set_alloc_info = {};
|
||||
descriptor_set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
descriptor_set_alloc_info.descriptorPool = mRenderer->GetDescriptorPool();
|
||||
descriptor_set_alloc_info.descriptorSetCount = 1;
|
||||
descriptor_set_alloc_info.pSetLayouts = &layout;
|
||||
FatalErrorIfFailed(vkAllocateDescriptorSets(device, &descriptor_set_alloc_info, &mDescriptorSet));
|
||||
|
||||
VkDescriptorImageInfo image_info = {};
|
||||
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
image_info.imageView = mImageView;
|
||||
image_info.sampler = inSampler;
|
||||
|
||||
VkWriteDescriptorSet descriptor_write = {};
|
||||
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descriptor_write.dstSet = mDescriptorSet;
|
||||
descriptor_write.dstBinding = 0;
|
||||
descriptor_write.dstArrayElement = 0;
|
||||
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
descriptor_write.descriptorCount = 1;
|
||||
descriptor_write.pImageInfo = &image_info;
|
||||
vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, nullptr);
|
||||
}
|
||||
|
||||
void TextureVK::TransitionImageLayout(VkCommandBuffer inCommandBuffer, VkImage inImage, VkFormat inFormat, VkImageLayout inOldLayout, VkImageLayout inNewLayout)
|
||||
{
|
||||
VkImageMemoryBarrier barrier = {};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.oldLayout = inOldLayout;
|
||||
barrier.newLayout = inNewLayout;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = inImage;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
barrier.subresourceRange.levelCount = 1;
|
||||
barrier.subresourceRange.layerCount = 1;
|
||||
|
||||
if (inOldLayout == VK_IMAGE_LAYOUT_UNDEFINED && inNewLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
||||
{
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
vkCmdPipelineBarrier(inCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
else if (inOldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && inNewLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||
{
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
vkCmdPipelineBarrier(inCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
}
|
||||
35
lib/All/JoltPhysics/TestFramework/Renderer/VK/TextureVK.h
Normal file
35
lib/All/JoltPhysics/TestFramework/Renderer/VK/TextureVK.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Texture.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
class RendererVK;
|
||||
|
||||
class TextureVK : public Texture
|
||||
{
|
||||
public:
|
||||
/// Constructor, called by Renderer::CreateTextureVK
|
||||
TextureVK(RendererVK *inRenderer, const Surface *inSurface); // Create a normal TextureVK
|
||||
TextureVK(RendererVK *inRenderer, int inWidth, int inHeight); // Create a render target (depth only)
|
||||
virtual ~TextureVK() override;
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const override;
|
||||
|
||||
VkImageView GetImageView() const { return mImageView; }
|
||||
|
||||
private:
|
||||
void CreateImageViewAndDescriptorSet(VkFormat inFormat, VkImageAspectFlags inAspectFlags, VkSampler inSampler);
|
||||
void TransitionImageLayout(VkCommandBuffer inCommandBuffer, VkImage inImage, VkFormat inFormat, VkImageLayout inOldLayout, VkImageLayout inNewLayout);
|
||||
|
||||
RendererVK * mRenderer;
|
||||
VkImage mImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mImageMemory = VK_NULL_HANDLE;
|
||||
VkImageView mImageView = VK_NULL_HANDLE;
|
||||
VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VertexShader.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Vertex shader handle for Vulkan
|
||||
class VertexShaderVK : public VertexShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
VertexShaderVK(VkDevice inDevice, VkShaderModule inShaderModule) :
|
||||
mDevice(inDevice),
|
||||
mStageInfo()
|
||||
{
|
||||
mStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
mStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
mStageInfo.module = inShaderModule;
|
||||
mStageInfo.pName = "main";
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
virtual ~VertexShaderVK() override
|
||||
{
|
||||
vkDestroyShaderModule(mDevice, mStageInfo.module, nullptr);
|
||||
}
|
||||
|
||||
VkDevice mDevice;
|
||||
VkPipelineShaderStageCreateInfo mStageInfo;
|
||||
};
|
||||
15
lib/All/JoltPhysics/TestFramework/Renderer/VertexShader.h
Normal file
15
lib/All/JoltPhysics/TestFramework/Renderer/VertexShader.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
/// Vertex shader handle
|
||||
class VertexShader : public RefTarget<VertexShader>
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~VertexShader() = default;
|
||||
};
|
||||
Reference in New Issue
Block a user