#include "D3D12HostDevice.h" #include namespace XCEngine::UI::Editor::Host { using ::XCEngine::RHI::CommandListDesc; using ::XCEngine::RHI::CommandQueueDesc; using ::XCEngine::RHI::D3D12CommandList; using ::XCEngine::RHI::D3D12CommandQueue; using ::XCEngine::RHI::D3D12Device; using ::XCEngine::RHI::RHICommandList; using ::XCEngine::RHI::RHICommandQueue; using ::XCEngine::RHI::RHIDevice; using ::XCEngine::RHI::RHIDeviceDesc; using ::XCEngine::RHI::RHIFactory; using ::XCEngine::RHI::RHIType; bool D3D12HostDevice::Initialize() { Shutdown(); m_device = RHIFactory::CreateRHIDevice(RHIType::D3D12); if (m_device == nullptr) { m_lastError = "Failed to create the D3D12 RHI device."; return false; } RHIDeviceDesc deviceDesc = {}; #ifdef _DEBUG deviceDesc.enableDebugLayer = true; deviceDesc.enableGPUValidation = true; #endif if (!m_device->Initialize(deviceDesc)) { m_lastError = "Failed to initialize the D3D12 RHI device."; Shutdown(); return false; } CommandQueueDesc queueDesc = {}; queueDesc.queueType = static_cast(::XCEngine::RHI::CommandQueueType::Direct); m_commandQueue = m_device->CreateCommandQueue(queueDesc); if (m_commandQueue == nullptr || GetD3D12CommandQueue() == nullptr) { m_lastError = "Failed to create the D3D12 command queue."; Shutdown(); return false; } CommandListDesc commandListDesc = {}; commandListDesc.commandListType = static_cast(::XCEngine::RHI::CommandQueueType::Direct); for (std::uint32_t commandListIndex = 0; commandListIndex < kFrameContextCount; ++commandListIndex) { m_commandLists[commandListIndex] = m_device->CreateCommandList(commandListDesc); if (m_commandLists[commandListIndex] == nullptr || GetD3D12CommandList(commandListIndex) == nullptr) { m_lastError = "Failed to create the D3D12 command list."; Shutdown(); return false; } m_commandLists[commandListIndex]->Close(); } if (!InitializeFrameCompletionFence()) { m_lastError = "Failed to create the host frame completion fence."; Shutdown(); return false; } m_frameFenceValues.fill(0u); m_lastError.clear(); return true; } void D3D12HostDevice::Shutdown() { WaitForGpuIdle(); ReleaseFrameCompletionFence(); for (auto*& commandList : m_commandLists) { if (commandList != nullptr) { commandList->Shutdown(); delete commandList; commandList = nullptr; } } if (m_commandQueue != nullptr) { m_commandQueue->Shutdown(); delete m_commandQueue; m_commandQueue = nullptr; } if (m_device != nullptr) { m_device->Shutdown(); delete m_device; m_device = nullptr; } m_lastError.clear(); } bool D3D12HostDevice::BeginFrame(std::uint32_t frameIndex) { WaitForFrame(frameIndex); RHICommandList* commandList = GetCommandList(frameIndex); D3D12CommandList* d3d12CommandList = GetD3D12CommandList(frameIndex); if (commandList == nullptr || d3d12CommandList == nullptr) { m_lastError = "BeginFrame could not resolve the active command list."; return false; } commandList->Reset(); m_lastError.clear(); return true; } bool D3D12HostDevice::SubmitFrame(std::uint32_t frameIndex) { RHICommandList* commandList = GetCommandList(frameIndex); if (commandList == nullptr || m_commandQueue == nullptr) { m_lastError = "SubmitFrame requires an initialized command list and queue."; return false; } commandList->Close(); void* commandLists[] = { commandList }; m_commandQueue->ExecuteCommandLists(1, commandLists); m_lastError.clear(); return true; } bool D3D12HostDevice::SignalFrameCompletion(std::uint32_t frameIndex) { ID3D12CommandQueue* commandQueue = GetCommandQueue(); if (commandQueue == nullptr || m_frameCompletionFence == nullptr || frameIndex >= m_frameFenceValues.size()) { return false; } ++m_lastSubmittedFrameValue; const HRESULT hr = commandQueue->Signal( m_frameCompletionFence.Get(), m_lastSubmittedFrameValue); if (SUCCEEDED(hr)) { m_frameFenceValues[frameIndex] = m_lastSubmittedFrameValue; m_lastError.clear(); } return SUCCEEDED(hr); } void D3D12HostDevice::WaitForFrame(std::uint32_t frameIndex) { if (m_frameCompletionFence == nullptr || m_frameCompletionEvent == nullptr || frameIndex >= m_frameFenceValues.size()) { return; } const std::uint64_t fenceValue = m_frameFenceValues[frameIndex]; if (fenceValue == 0u || m_frameCompletionFence->GetCompletedValue() >= fenceValue) { return; } const HRESULT hr = m_frameCompletionFence->SetEventOnCompletion( fenceValue, m_frameCompletionEvent); if (SUCCEEDED(hr)) { WaitForSingleObject(m_frameCompletionEvent, INFINITE); } } void D3D12HostDevice::WaitForGpuIdle() { ID3D12CommandQueue* commandQueue = GetCommandQueue(); if (commandQueue == nullptr || m_frameCompletionFence == nullptr || m_frameCompletionEvent == nullptr) { if (m_commandQueue != nullptr) { m_commandQueue->WaitForIdle(); } return; } ++m_lastSubmittedFrameValue; const std::uint64_t fenceValue = m_lastSubmittedFrameValue; const HRESULT signalHr = commandQueue->Signal( m_frameCompletionFence.Get(), fenceValue); if (FAILED(signalHr)) { return; } if (m_frameCompletionFence->GetCompletedValue() >= fenceValue) { return; } const HRESULT waitHr = m_frameCompletionFence->SetEventOnCompletion( fenceValue, m_frameCompletionEvent); if (SUCCEEDED(waitHr)) { WaitForSingleObject(m_frameCompletionEvent, INFINITE); } } void D3D12HostDevice::ResetFrameTracking() { m_frameFenceValues.fill(0u); } ID3D12Device* D3D12HostDevice::GetDevice() const { const D3D12Device* device = GetD3D12Device(); return device != nullptr ? device->GetDevice() : nullptr; } ID3D12CommandQueue* D3D12HostDevice::GetCommandQueue() const { const D3D12CommandQueue* queue = GetD3D12CommandQueue(); return queue != nullptr ? queue->GetCommandQueue() : nullptr; } const std::string& D3D12HostDevice::GetLastError() const { return m_lastError; } RHIDevice* D3D12HostDevice::GetRHIDevice() const { return m_device; } RHICommandQueue* D3D12HostDevice::GetRHICommandQueue() const { return m_commandQueue; } RHICommandList* D3D12HostDevice::GetCommandList(std::uint32_t frameIndex) const { return frameIndex < m_commandLists.size() ? m_commandLists[frameIndex] : nullptr; } ::XCEngine::Rendering::RenderContext D3D12HostDevice::GetRenderContext( std::uint32_t frameIndex) const { ::XCEngine::Rendering::RenderContext context = {}; context.device = m_device; context.commandList = GetCommandList(frameIndex); context.commandQueue = m_commandQueue; context.backendType = RHIType::D3D12; return context; } D3D12Device* D3D12HostDevice::GetD3D12Device() const { return m_device != nullptr ? static_cast(m_device) : nullptr; } D3D12CommandQueue* D3D12HostDevice::GetD3D12CommandQueue() const { return m_commandQueue != nullptr ? static_cast(m_commandQueue) : nullptr; } D3D12CommandList* D3D12HostDevice::GetD3D12CommandList(std::uint32_t frameIndex) const { RHICommandList* commandList = GetCommandList(frameIndex); return commandList != nullptr ? static_cast(commandList) : nullptr; } bool D3D12HostDevice::InitializeFrameCompletionFence() { ReleaseFrameCompletionFence(); ID3D12Device* device = GetDevice(); if (device == nullptr) { return false; } const HRESULT hr = device->CreateFence( 0u, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_frameCompletionFence.ReleaseAndGetAddressOf())); if (FAILED(hr)) { return false; } m_frameCompletionEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); if (m_frameCompletionEvent == nullptr) { m_frameCompletionFence.Reset(); return false; } m_lastSubmittedFrameValue = 0u; return true; } void D3D12HostDevice::ReleaseFrameCompletionFence() { if (m_frameCompletionEvent != nullptr) { CloseHandle(m_frameCompletionEvent); m_frameCompletionEvent = nullptr; } m_frameCompletionFence.Reset(); m_frameFenceValues.fill(0u); m_lastSubmittedFrameValue = 0u; } } // namespace XCEngine::UI::Editor::Host