#include "D3D12WindowRenderLoop.h" namespace XCEngine::UI::Editor::Host { D3D12WindowRenderLoopAttachResult D3D12WindowRenderLoop::Attach( D3D12UiRenderer& uiRenderer, D3D12WindowRenderer& windowRenderer) { m_uiRenderer = &uiRenderer; m_windowRenderer = &windowRenderer; D3D12WindowRenderLoopAttachResult result = {}; result.hasViewportSurfacePresentation = m_windowRenderer->GetSwapChain() != nullptr && m_windowRenderer->GetRHIDevice() != nullptr; if (!result.hasViewportSurfacePresentation) { result.warning = "window render loop attach requires an initialized D3D12 window renderer."; } return result; } void D3D12WindowRenderLoop::Detach() { m_uiRenderer = nullptr; m_windowRenderer = nullptr; } D3D12WindowRenderLoopFrameContext D3D12WindowRenderLoop::BeginFrame() const { D3D12WindowRenderLoopFrameContext context = {}; if (!HasViewportSurfacePresentation()) { return context; } if (!m_windowRenderer->BeginFrame()) { const std::string& frameError = m_windowRenderer->GetLastError(); context.warning = frameError.empty() ? "d3d12 frame begin failed" : "d3d12 frame begin failed: " + frameError; return context; } context.canRenderViewports = true; context.renderContext = m_windowRenderer->GetRenderContext(); return context; } D3D12WindowRenderLoopResizeResult D3D12WindowRenderLoop::ApplyResize(UINT width, UINT height) { D3D12WindowRenderLoopResizeResult result = {}; if (m_uiRenderer == nullptr || m_windowRenderer == nullptr) { result.windowRendererWarning = "window render loop is detached."; return result; } const bool resizedWindowRenderer = m_windowRenderer->Resize(static_cast(width), static_cast(height)); if (!resizedWindowRenderer || !m_windowRenderer->GetLastError().empty()) { const std::string& resizeError = m_windowRenderer->GetLastError(); result.windowRendererWarning = resizeError.empty() ? "window renderer resize warning." : "window renderer resize warning: " + resizeError; } result.hasViewportSurfacePresentation = resizedWindowRenderer && m_windowRenderer->GetSwapChain() != nullptr && m_windowRenderer->GetCurrentRenderSurface() != nullptr; return result; } D3D12WindowRenderLoopPresentResult D3D12WindowRenderLoop::Present( const ::XCEngine::UI::UIDrawData& drawData, const std::filesystem::path* captureOutputPath) { D3D12WindowRenderLoopPresentResult result = {}; if (m_uiRenderer == nullptr) { result.warning = "window render loop has no ui renderer."; return result; } if (!HasViewportSurfacePresentation()) { result.warning = "window render loop has no active D3D12 presentation path."; return result; } if (!m_windowRenderer->PrepareCurrentBackBufferForUiRender()) { const std::string& error = m_windowRenderer->GetLastError(); result.warning = error.empty() ? "failed to prepare the current back buffer for UI rendering." : error; return result; } const ::XCEngine::Rendering::RenderContext renderContext = m_windowRenderer->GetRenderContext(); const ::XCEngine::Rendering::RenderSurface* surface = m_windowRenderer->GetCurrentRenderSurface(); if (surface == nullptr) { result.warning = "window render loop could not resolve the current back buffer surface."; return result; } if (!m_uiRenderer->Render(drawData, renderContext, *surface)) { const std::string& error = m_uiRenderer->GetLastError(); result.warning = error.empty() ? "d3d12 ui renderer failed to render the current frame." : error; return result; } if (!m_windowRenderer->FinalizeCurrentBackBufferForPresent()) { const std::string& error = m_windowRenderer->GetLastError(); result.warning = error.empty() ? "failed to finalize the current back buffer for present." : error; return result; } if (!m_windowRenderer->SubmitFrame()) { const std::string& error = m_windowRenderer->GetLastError(); result.warning = error.empty() ? "failed to submit the current D3D12 frame." : error; return result; } if (captureOutputPath != nullptr) { std::string captureError = {}; if (!m_windowRenderer->CaptureCurrentBackBufferToPng(*captureOutputPath, captureError)) { result.captureError = captureError.empty() ? "failed to capture the current D3D12 back buffer." : std::move(captureError); } else { result.captureSucceeded = true; } } const bool presented = m_windowRenderer->PresentFrame(); const std::string presentError = m_windowRenderer->GetLastError(); if (!m_windowRenderer->SignalFrameCompletion()) { const std::string& error = m_windowRenderer->GetLastError(); result.warning = error.empty() ? "failed to signal current frame completion." : error; return result; } if (!presented) { result.warning = presentError.empty() ? "failed to present the swap chain." : presentError; return result; } result.framePresented = true; return result; } bool D3D12WindowRenderLoop::HasViewportSurfacePresentation() const { return m_uiRenderer != nullptr && m_windowRenderer != nullptr && m_windowRenderer->GetSwapChain() != nullptr && m_windowRenderer->GetCurrentRenderSurface() != nullptr; } } // namespace XCEngine::UI::Editor::Host