#include "ProductViewportHostService.h" #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::RHI::ResourceStates; } // namespace void ProductViewportHostService::AttachWindowRenderer( Host::D3D12WindowRenderer& windowRenderer) { if (m_windowRenderer == &windowRenderer) { m_device = windowRenderer.GetRHIDevice(); if (m_device != nullptr && !m_textureDescriptorAllocator.IsInitialized()) { m_textureDescriptorAllocator.Initialize(*m_device); } return; } Shutdown(); m_windowRenderer = &windowRenderer; m_device = windowRenderer.GetRHIDevice(); if (m_device != nullptr) { m_textureDescriptorAllocator.Initialize(*m_device); } } void ProductViewportHostService::DetachWindowRenderer() { Shutdown(); } void ProductViewportHostService::SetSurfacePresentationEnabled(bool enabled) { m_surfacePresentationEnabled = enabled; } void ProductViewportHostService::Shutdown() { for (ViewportEntry& entry : m_entries) { DestroyViewportEntry(entry); } m_textureDescriptorAllocator.Shutdown(); m_windowRenderer = nullptr; m_device = nullptr; m_surfacePresentationEnabled = false; } void ProductViewportHostService::BeginFrame() { for (ViewportEntry& entry : m_entries) { entry.requestedWidth = 0; entry.requestedHeight = 0; entry.requestedThisFrame = false; entry.renderedThisFrame = false; entry.kind = (&entry == &m_entries[0]) ? ProductViewportKind::Scene : ProductViewportKind::Game; } } ProductViewportFrame ProductViewportHostService::RequestViewport( ProductViewportKind kind, const ::XCEngine::UI::UISize& requestedSize) { ViewportEntry& entry = GetEntry(kind); entry.requestedThisFrame = requestedSize.width > 1.0f && requestedSize.height > 1.0f; entry.requestedWidth = entry.requestedThisFrame ? static_cast(requestedSize.width) : 0u; entry.requestedHeight = entry.requestedThisFrame ? static_cast(requestedSize.height) : 0u; if (!entry.requestedThisFrame) { return BuildFrame(entry, requestedSize); } if (m_windowRenderer == nullptr || m_device == nullptr) { return BuildFrame(entry, requestedSize); } if (!EnsureViewportResources(entry)) { return BuildFrame(entry, requestedSize); } return BuildFrame(entry, requestedSize); } void ProductViewportHostService::RenderRequestedViewports( const ::XCEngine::Rendering::RenderContext& renderContext) { if (m_windowRenderer == nullptr || m_device == nullptr || !renderContext.IsValid()) { return; } for (ViewportEntry& entry : m_entries) { if (!entry.requestedThisFrame || !EnsureViewportResources(entry)) { continue; } if (entry.kind == ProductViewportKind::Scene) { ClearViewport(entry, renderContext, 0.16f, 0.17f, 0.19f, 1.0f); } else { ClearViewport(entry, renderContext, 0.11f, 0.11f, 0.12f, 1.0f); } entry.renderedThisFrame = true; } } ProductViewportHostService::ViewportEntry& ProductViewportHostService::GetEntry( ProductViewportKind kind) { const std::size_t index = kind == ProductViewportKind::Scene ? 0u : 1u; ViewportEntry& entry = m_entries[index]; entry.kind = kind; return entry; } const ProductViewportHostService::ViewportEntry& ProductViewportHostService::GetEntry( ProductViewportKind kind) const { const std::size_t index = kind == ProductViewportKind::Scene ? 0u : 1u; return m_entries[index]; } void ProductViewportHostService::DestroyViewportEntry(ViewportEntry& entry) { m_renderTargetManager.DestroyTargets(&m_textureDescriptorAllocator, entry.renderTargets); entry = {}; } bool ProductViewportHostService::EnsureViewportResources(ViewportEntry& entry) { const ProductViewportResourceReuseQuery reuseQuery = BuildProductViewportRenderTargetsReuseQuery( entry.kind, entry.renderTargets, entry.requestedWidth, entry.requestedHeight); if (CanReuseProductViewportResources(reuseQuery)) { return true; } if (m_windowRenderer == nullptr || m_device == nullptr || entry.requestedWidth == 0u || entry.requestedHeight == 0u) { return false; } return m_renderTargetManager.EnsureTargets( entry.kind, entry.requestedWidth, entry.requestedHeight, *m_device, m_textureDescriptorAllocator, entry.renderTargets); } void ProductViewportHostService::ClearViewport( ViewportEntry& entry, const ::XCEngine::Rendering::RenderContext& renderContext, float r, float g, float b, float a) { if (renderContext.commandList == nullptr || entry.renderTargets.colorView == nullptr || entry.renderTargets.depthView == nullptr) { return; } auto* commandList = renderContext.commandList; auto* colorView = entry.renderTargets.colorView; const float clearColor[4] = { r, g, b, a }; commandList->TransitionBarrier( colorView, entry.renderTargets.colorState, ResourceStates::RenderTarget); commandList->SetRenderTargets(1, &colorView, entry.renderTargets.depthView); commandList->ClearRenderTarget(colorView, clearColor); commandList->ClearDepthStencil(entry.renderTargets.depthView, 1.0f, 0); commandList->TransitionBarrier( colorView, ResourceStates::RenderTarget, ResourceStates::PixelShaderResource); entry.renderTargets.colorState = ResourceStates::PixelShaderResource; entry.renderTargets.hasValidObjectIdFrame = false; } ProductViewportFrame ProductViewportHostService::BuildFrame( const ViewportEntry& entry, const ::XCEngine::UI::UISize& requestedSize) const { ProductViewportFrame frame = {}; frame.requestedSize = requestedSize; frame.renderSize = ::XCEngine::UI::UISize( static_cast(entry.renderTargets.width), static_cast(entry.renderTargets.height)); frame.wasRequested = entry.requestedThisFrame; frame.statusText = entry.statusText; if (m_surfacePresentationEnabled && entry.renderTargets.textureHandle.IsValid()) { frame.texture = entry.renderTargets.textureHandle; frame.hasTexture = true; } return frame; } } // namespace XCEngine::UI::Editor::App