diff --git a/include/renderer.h b/include/renderer.h index 0ee3d64..908fe27 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -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 Create(const RendererCreateParams& params); diff --git a/src/dx12_renderer.cpp b/src/dx12_renderer.cpp index 0b78942..5d22825 100644 --- a/src/dx12_renderer.cpp +++ b/src/dx12_renderer.cpp @@ -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 m_swapChain = nullptr; ComPtr m_device = nullptr; - ComPtr m_renderTargets[s_MaxBackBufferCount] = {}; - ComPtr m_commandAllocator = nullptr; + ComPtr m_renderTargets[s_FrameCount] = {}; + ComPtr m_commandAllocators[s_FrameCount] = {}; ComPtr m_commandQueue = nullptr; ComPtr m_rootSignature = nullptr; ComPtr m_rtvHeap = nullptr; @@ -129,7 +131,7 @@ struct RendererDX12 final : public IRenderer UINT m_frameIndex = 0u; HANDLE m_fenceEvent = nullptr; ComPtr 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(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); + return; + } + + 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 signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK); + MessageBox(m_windowHandle, "Failed to set fence event on completion", "Fence Failure", MB_ICONERROR | MB_OK); return; } - WaitForSingleObject(m_fenceEvent, INFINITE); + WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE); } - m_frameIndex = m_swapChain->GetCurrentBackBufferIndex(); + // Set the fence value for the next frame. + m_fenceValues[m_frameIndex] = currentFenceValue + 1; } std::unique_ptr IRenderer::Create(const RendererCreateParams& params) @@ -363,13 +387,14 @@ std::unique_ptr 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_commandAllocators[i])))) + { + MessageBoxA(hWnd, "Failed to create Command Allocator", "Device Error", MB_ICONERROR | MB_OK); + return nullptr; + } } - if (FAILED(renderer->m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&renderer->m_commandAllocator)))) - { - 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; @@ -414,9 +439,10 @@ std::unique_ptr IRenderer::Create(const RendererCreateParams& params) // Define the vertex input layout. 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}}; + { + {"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} + }; // Describe and create the graphics pipeline state object (PSO). D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; @@ -441,7 +467,7 @@ std::unique_ptr 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::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; } diff --git a/src/main.cpp b/src/main.cpp index 0af4a6a..dea611d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,7 +34,6 @@ auto main() -> int renderer->RecordCommands(); renderer->Render(); - renderer->WaitForPreviousFrame(); } renderer->Destroy();