generated from McMassiveNZ/opengl-starter
Updated to handle frame buffering a bit better
This commit is contained in:
@@ -24,7 +24,6 @@ public:
|
|||||||
|
|
||||||
virtual void RecordCommands() = 0;
|
virtual void RecordCommands() = 0;
|
||||||
virtual void Render() = 0;
|
virtual void Render() = 0;
|
||||||
virtual void WaitForPreviousFrame() = 0;
|
|
||||||
virtual void Destroy() = 0;
|
virtual void Destroy() = 0;
|
||||||
|
|
||||||
static std::unique_ptr<IRenderer> Create(const RendererCreateParams& params);
|
static std::unique_ptr<IRenderer> Create(const RendererCreateParams& params);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace dx12_starter
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
static constexpr UINT s_MaxBackBufferCount = 2;
|
static constexpr UINT s_FrameCount = 2;
|
||||||
|
|
||||||
auto ParseAPIVersion(APIVersion version) -> D3D_FEATURE_LEVEL
|
auto ParseAPIVersion(APIVersion version) -> D3D_FEATURE_LEVEL
|
||||||
{
|
{
|
||||||
@@ -96,9 +96,11 @@ struct RendererDX12 final : public IRenderer
|
|||||||
|
|
||||||
void RecordCommands();
|
void RecordCommands();
|
||||||
void Render();
|
void Render();
|
||||||
void WaitForPreviousFrame();
|
|
||||||
void Destroy();
|
void Destroy();
|
||||||
|
|
||||||
|
void WaitForGPU();
|
||||||
|
void AdvanceFrame();
|
||||||
|
|
||||||
HWND m_windowHandle = nullptr;
|
HWND m_windowHandle = nullptr;
|
||||||
|
|
||||||
struct Vertex
|
struct Vertex
|
||||||
@@ -112,8 +114,8 @@ struct RendererDX12 final : public IRenderer
|
|||||||
CD3DX12_RECT m_scissorRect = {};
|
CD3DX12_RECT m_scissorRect = {};
|
||||||
ComPtr<IDXGISwapChain3> m_swapChain = nullptr;
|
ComPtr<IDXGISwapChain3> m_swapChain = nullptr;
|
||||||
ComPtr<ID3D12Device> m_device = nullptr;
|
ComPtr<ID3D12Device> m_device = nullptr;
|
||||||
ComPtr<ID3D12Resource> m_renderTargets[s_MaxBackBufferCount] = {};
|
ComPtr<ID3D12Resource> m_renderTargets[s_FrameCount] = {};
|
||||||
ComPtr<ID3D12CommandAllocator> m_commandAllocator = nullptr;
|
ComPtr<ID3D12CommandAllocator> m_commandAllocators[s_FrameCount] = {};
|
||||||
ComPtr<ID3D12CommandQueue> m_commandQueue = nullptr;
|
ComPtr<ID3D12CommandQueue> m_commandQueue = nullptr;
|
||||||
ComPtr<ID3D12RootSignature> m_rootSignature = nullptr;
|
ComPtr<ID3D12RootSignature> m_rootSignature = nullptr;
|
||||||
ComPtr<ID3D12DescriptorHeap> m_rtvHeap = nullptr;
|
ComPtr<ID3D12DescriptorHeap> m_rtvHeap = nullptr;
|
||||||
@@ -129,7 +131,7 @@ struct RendererDX12 final : public IRenderer
|
|||||||
UINT m_frameIndex = 0u;
|
UINT m_frameIndex = 0u;
|
||||||
HANDLE m_fenceEvent = nullptr;
|
HANDLE m_fenceEvent = nullptr;
|
||||||
ComPtr<ID3D12Fence> m_fence = nullptr;
|
ComPtr<ID3D12Fence> m_fence = nullptr;
|
||||||
UINT64 m_fenceValue = 0llu;
|
UINT64 m_fenceValues[s_FrameCount] = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
void RendererDX12::RecordCommands()
|
void RendererDX12::RecordCommands()
|
||||||
@@ -137,7 +139,7 @@ void RendererDX12::RecordCommands()
|
|||||||
// Command list allocators can only be reset when the associated
|
// Command list allocators can only be reset when the associated
|
||||||
// command lists have finished execution on the GPU; apps should use
|
// command lists have finished execution on the GPU; apps should use
|
||||||
// fences to determine GPU execution progress.
|
// fences to determine GPU execution progress.
|
||||||
if (FAILED(m_commandAllocator->Reset()))
|
if (FAILED(m_commandAllocators[m_frameIndex]->Reset()))
|
||||||
{
|
{
|
||||||
MessageBox(m_windowHandle, "Failed to reset command allocator", "Command Allocator Failure", MB_ICONERROR | MB_OK);
|
MessageBox(m_windowHandle, "Failed to reset command allocator", "Command Allocator Failure", MB_ICONERROR | MB_OK);
|
||||||
return;
|
return;
|
||||||
@@ -146,7 +148,7 @@ void RendererDX12::RecordCommands()
|
|||||||
// However, when ExecuteCommandList() is called on a particular command
|
// However, when ExecuteCommandList() is called on a particular command
|
||||||
// list, that command list can then be reset at any time and must be before
|
// list, that command list can then be reset at any time and must be before
|
||||||
// re-recording.
|
// re-recording.
|
||||||
if (FAILED(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get())))
|
if (FAILED(m_commandList->Reset(m_commandAllocators[m_frameIndex].Get(), m_pipelineState.Get())))
|
||||||
{
|
{
|
||||||
MessageBox(m_windowHandle, "Failed to reset command list", "Command Allocator Failure", MB_ICONERROR | MB_OK);
|
MessageBox(m_windowHandle, "Failed to reset command list", "Command Allocator Failure", MB_ICONERROR | MB_OK);
|
||||||
return;
|
return;
|
||||||
@@ -196,42 +198,64 @@ void RendererDX12::Render()
|
|||||||
{
|
{
|
||||||
MessageBox(m_windowHandle, "Failed to preset frame", "Swapchain Failure", MB_ICONERROR | MB_OK);
|
MessageBox(m_windowHandle, "Failed to preset frame", "Swapchain Failure", MB_ICONERROR | MB_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AdvanceFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererDX12::Destroy()
|
void RendererDX12::Destroy()
|
||||||
{
|
{
|
||||||
WaitForPreviousFrame();
|
WaitForGPU();
|
||||||
CloseHandle(m_fenceEvent);
|
CloseHandle(m_fenceEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererDX12::WaitForPreviousFrame()
|
void RendererDX12::WaitForGPU()
|
||||||
{
|
{
|
||||||
// WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
|
|
||||||
// This is code implemented as such for simplicity. The D3D12HelloFrameBuffering
|
|
||||||
// sample illustrates how to use fences for efficient resource usage and to
|
|
||||||
// maximize GPU utilization.
|
|
||||||
|
|
||||||
// Signal and increment the fence value.
|
// Signal and increment the fence value.
|
||||||
const UINT64 fence = std::exchange(m_fenceValue, m_fenceValue + 1);
|
if (FAILED(m_commandQueue->Signal(m_fence.Get(), m_fenceValues[m_frameIndex])))
|
||||||
if (FAILED(m_commandQueue->Signal(m_fence.Get(), fence)))
|
|
||||||
{
|
{
|
||||||
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until the previous frame is finished.
|
// Wait until the previous frame is finished.
|
||||||
if (m_fence->GetCompletedValue() < fence)
|
if (FAILED(m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent)))
|
||||||
{
|
|
||||||
if (FAILED(m_fence->SetEventOnCompletion(fence, m_fenceEvent)))
|
|
||||||
{
|
{
|
||||||
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitForSingleObject(m_fenceEvent, INFINITE);
|
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
||||||
|
|
||||||
|
m_fenceValues[m_frameIndex]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererDX12::AdvanceFrame()
|
||||||
|
{
|
||||||
|
// Schedule a Signal command in the queue.
|
||||||
|
const UINT64 currentFenceValue = m_fenceValues[m_frameIndex];
|
||||||
|
if (FAILED(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)))
|
||||||
|
{
|
||||||
|
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the frame index.
|
||||||
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||||
|
|
||||||
|
// If the next frame is not ready to be rendered yet, wait until it is ready.
|
||||||
|
if (m_fence->GetCompletedValue() < m_fenceValues[m_frameIndex])
|
||||||
|
{
|
||||||
|
if (FAILED(m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent)))
|
||||||
|
{
|
||||||
|
MessageBox(m_windowHandle, "Failed to set fence event on completion", "Fence Failure", MB_ICONERROR | MB_OK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the fence value for the next frame.
|
||||||
|
m_fenceValues[m_frameIndex] = currentFenceValue + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
||||||
@@ -363,13 +387,14 @@ std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
|||||||
|
|
||||||
renderer->m_device->CreateRenderTargetView(renderer->m_renderTargets[i].Get(), nullptr, rtvHandle);
|
renderer->m_device->CreateRenderTargetView(renderer->m_renderTargets[i].Get(), nullptr, rtvHandle);
|
||||||
rtvHandle.Offset(1, renderer->m_rtvDescriptorSize);
|
rtvHandle.Offset(1, renderer->m_rtvDescriptorSize);
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(renderer->m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&renderer->m_commandAllocator))))
|
if (FAILED(renderer->m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&renderer->m_commandAllocators[i]))))
|
||||||
{
|
{
|
||||||
MessageBoxA(hWnd, "Failed to create Command Allocator", "Device Error", MB_ICONERROR | MB_OK);
|
MessageBoxA(hWnd, "Failed to create Command Allocator", "Device Error", MB_ICONERROR | MB_OK);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Create an empty root signature.
|
// Create an empty root signature.
|
||||||
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
|
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
|
||||||
@@ -416,7 +441,8 @@ std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
|||||||
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
|
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
|
||||||
{
|
{
|
||||||
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
|
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
|
||||||
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
|
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
|
||||||
|
};
|
||||||
|
|
||||||
// Describe and create the graphics pipeline state object (PSO).
|
// Describe and create the graphics pipeline state object (PSO).
|
||||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
|
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
|
||||||
@@ -441,7 +467,7 @@ std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the command list.
|
// Create the command list.
|
||||||
if (FAILED(renderer->m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, renderer->m_commandAllocator.Get(), renderer->m_pipelineState.Get(), IID_PPV_ARGS(&renderer->m_commandList))))
|
if (FAILED(renderer->m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, renderer->m_commandAllocators[renderer->m_frameIndex].Get(), renderer->m_pipelineState.Get(), IID_PPV_ARGS(&renderer->m_commandList))))
|
||||||
{
|
{
|
||||||
MessageBox(nullptr, "Failed to Create Command List", "Device failure", MB_ICONERROR | MB_OK);
|
MessageBox(nullptr, "Failed to Create Command List", "Device failure", MB_ICONERROR | MB_OK);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -512,16 +538,15 @@ std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer->m_fenceValue = 1;
|
renderer->m_fenceValues[renderer->m_frameIndex]++;
|
||||||
|
|
||||||
renderer->m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
if(renderer->m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); !renderer->m_fenceEvent)
|
||||||
if (renderer->m_fenceEvent == nullptr)
|
|
||||||
{
|
{
|
||||||
MessageBoxA(hWnd, "Failed to create Fence Event Handle", "Win32API Error", MB_ICONERROR | MB_OK);
|
MessageBoxA(hWnd, "Failed to create Fence Event Handle", "Win32API Error", MB_ICONERROR | MB_OK);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer->WaitForPreviousFrame();
|
renderer->WaitForGPU();
|
||||||
|
|
||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ auto main() -> int
|
|||||||
|
|
||||||
renderer->RecordCommands();
|
renderer->RecordCommands();
|
||||||
renderer->Render();
|
renderer->Render();
|
||||||
renderer->WaitForPreviousFrame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer->Destroy();
|
renderer->Destroy();
|
||||||
|
|||||||
Reference in New Issue
Block a user