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 Render() = 0;
|
||||
virtual void WaitForPreviousFrame() = 0;
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
static std::unique_ptr<IRenderer> Create(const RendererCreateParams& params);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace dx12_starter
|
||||
namespace
|
||||
{
|
||||
|
||||
static constexpr UINT s_MaxBackBufferCount = 2;
|
||||
static constexpr UINT s_FrameCount = 2;
|
||||
|
||||
auto ParseAPIVersion(APIVersion version) -> D3D_FEATURE_LEVEL
|
||||
{
|
||||
@@ -96,9 +96,11 @@ struct RendererDX12 final : public IRenderer
|
||||
|
||||
void RecordCommands();
|
||||
void Render();
|
||||
void WaitForPreviousFrame();
|
||||
void Destroy();
|
||||
|
||||
void WaitForGPU();
|
||||
void AdvanceFrame();
|
||||
|
||||
HWND m_windowHandle = nullptr;
|
||||
|
||||
struct Vertex
|
||||
@@ -112,8 +114,8 @@ struct RendererDX12 final : public IRenderer
|
||||
CD3DX12_RECT m_scissorRect = {};
|
||||
ComPtr<IDXGISwapChain3> m_swapChain = nullptr;
|
||||
ComPtr<ID3D12Device> m_device = nullptr;
|
||||
ComPtr<ID3D12Resource> m_renderTargets[s_MaxBackBufferCount] = {};
|
||||
ComPtr<ID3D12CommandAllocator> m_commandAllocator = nullptr;
|
||||
ComPtr<ID3D12Resource> m_renderTargets[s_FrameCount] = {};
|
||||
ComPtr<ID3D12CommandAllocator> m_commandAllocators[s_FrameCount] = {};
|
||||
ComPtr<ID3D12CommandQueue> m_commandQueue = nullptr;
|
||||
ComPtr<ID3D12RootSignature> m_rootSignature = nullptr;
|
||||
ComPtr<ID3D12DescriptorHeap> m_rtvHeap = nullptr;
|
||||
@@ -129,7 +131,7 @@ struct RendererDX12 final : public IRenderer
|
||||
UINT m_frameIndex = 0u;
|
||||
HANDLE m_fenceEvent = nullptr;
|
||||
ComPtr<ID3D12Fence> m_fence = nullptr;
|
||||
UINT64 m_fenceValue = 0llu;
|
||||
UINT64 m_fenceValues[s_FrameCount] = {};
|
||||
};
|
||||
|
||||
void RendererDX12::RecordCommands()
|
||||
@@ -137,7 +139,7 @@ void RendererDX12::RecordCommands()
|
||||
// Command list allocators can only be reset when the associated
|
||||
// command lists have finished execution on the GPU; apps should use
|
||||
// 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);
|
||||
return;
|
||||
@@ -146,7 +148,7 @@ void RendererDX12::RecordCommands()
|
||||
// However, when ExecuteCommandList() is called on a particular command
|
||||
// list, that command list can then be reset at any time and must be before
|
||||
// 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);
|
||||
return;
|
||||
@@ -196,42 +198,64 @@ void RendererDX12::Render()
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to preset frame", "Swapchain Failure", MB_ICONERROR | MB_OK);
|
||||
}
|
||||
|
||||
AdvanceFrame();
|
||||
}
|
||||
|
||||
void RendererDX12::Destroy()
|
||||
{
|
||||
WaitForPreviousFrame();
|
||||
WaitForGPU();
|
||||
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.
|
||||
const UINT64 fence = std::exchange(m_fenceValue, m_fenceValue + 1);
|
||||
if (FAILED(m_commandQueue->Signal(m_fence.Get(), fence)))
|
||||
if (FAILED(m_commandQueue->Signal(m_fence.Get(), m_fenceValues[m_frameIndex])))
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until the previous frame is finished.
|
||||
if (m_fence->GetCompletedValue() < fence)
|
||||
{
|
||||
if (FAILED(m_fence->SetEventOnCompletion(fence, m_fenceEvent)))
|
||||
if (FAILED(m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent)))
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
||||
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();
|
||||
|
||||
// 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)
|
||||
@@ -363,13 +387,14 @@ std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
||||
|
||||
renderer->m_device->CreateRenderTargetView(renderer->m_renderTargets[i].Get(), nullptr, rtvHandle);
|
||||
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);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create an empty root signature.
|
||||
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
|
||||
@@ -416,7 +441,8 @@ std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
||||
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
|
||||
{
|
||||
{"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).
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
|
||||
@@ -441,7 +467,7 @@ std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
||||
}
|
||||
|
||||
// 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);
|
||||
return nullptr;
|
||||
@@ -512,16 +538,15 @@ std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
renderer->m_fenceValue = 1;
|
||||
renderer->m_fenceValues[renderer->m_frameIndex]++;
|
||||
|
||||
renderer->m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
if (renderer->m_fenceEvent == nullptr)
|
||||
if(renderer->m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); !renderer->m_fenceEvent)
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create Fence Event Handle", "Win32API Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
renderer->WaitForPreviousFrame();
|
||||
renderer->WaitForGPU();
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ auto main() -> int
|
||||
|
||||
renderer->RecordCommands();
|
||||
renderer->Render();
|
||||
renderer->WaitForPreviousFrame();
|
||||
}
|
||||
|
||||
renderer->Destroy();
|
||||
|
||||
Reference in New Issue
Block a user