#include "ViewportHostService.h" #include "Ports/ViewportRenderPort.h" #include #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::RHI::ResourceStates; } // namespace ViewportHostService::ViewportHostService() = default; ViewportHostService::~ViewportHostService() = default; void ViewportHostService::AttachWindowRenderer( Ports::ViewportRenderPort& windowRenderer) { if (m_windowRenderer == &windowRenderer) { m_device = windowRenderer.GetRHIDevice(); return; } ReleaseWindowResources(); m_windowRenderer = &windowRenderer; m_device = windowRenderer.GetRHIDevice(); } void ViewportHostService::DetachWindowRenderer() { ReleaseWindowResources(); } void ViewportHostService::SetSurfacePresentationEnabled(bool enabled) { m_surfacePresentationEnabled = enabled; } void ViewportHostService::SetContentRenderer( std::string_view viewportId, IViewportContentRenderer* renderer, const ViewportResourceRequirements& requirements) { ViewportEntry& entry = GetOrCreateEntry(viewportId); entry.renderer = renderer; entry.requirements = requirements; } void ViewportHostService::Shutdown() { for (auto& [viewportId, entry] : m_entries) { DestroyViewportEntry(entry); } m_windowRenderer = nullptr; m_device = nullptr; m_surfacePresentationEnabled = false; m_entries.clear(); } void ViewportHostService::BeginFrame() { for (auto& [viewportId, entry] : m_entries) { entry.requestedWidth = 0; entry.requestedHeight = 0; entry.requestedThisFrame = false; entry.renderedThisFrame = false; } } void ViewportHostService::ReleaseWindowResources() { for (auto& [viewportId, entry] : m_entries) { DestroyViewportEntry(entry); } m_windowRenderer = nullptr; m_device = nullptr; m_surfacePresentationEnabled = false; } ViewportHostService::ViewportEntry& ViewportHostService::GetOrCreateEntry( std::string_view viewportId) { const auto [it, inserted] = m_entries.try_emplace(std::string(viewportId)); return it->second; } void ViewportHostService::DestroyViewportEntry(ViewportEntry& entry) { m_renderTargetManager.DestroyTargets( m_windowRenderer, entry.renderTargets); entry.requestedWidth = 0; entry.requestedHeight = 0; entry.requestedThisFrame = false; entry.renderedThisFrame = false; entry.renderTargets = {}; entry.statusText.clear(); } ViewportFrame ViewportHostService::RequestViewport( std::string_view viewportId, const ::XCEngine::UI::UISize& requestedSize) { ViewportEntry& entry = GetOrCreateEntry(viewportId); 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 ViewportHostService::RenderRequestedViewports( const ::XCEngine::Rendering::RenderContext& renderContext) { if (m_windowRenderer == nullptr || m_device == nullptr || !renderContext.IsValid()) { return; } for (auto& [viewportId, entry] : m_entries) { if (!entry.requestedThisFrame || !EnsureViewportResources(entry)) { continue; } IViewportContentRenderer* renderer = entry.renderer; if (renderer != nullptr) { const ViewportRenderResult renderResult = renderer->Render( entry.renderTargets, *m_device, renderContext); entry.statusText = renderResult.statusText; if (!renderResult.rendered && renderResult.requiresFallbackClear) { ApplyViewportFallback( entry, renderContext, renderResult); } } else { entry.statusText.clear(); ClearViewport(entry, renderContext, 0.09f, 0.09f, 0.09f, 1.0f); } entry.renderedThisFrame = true; } } bool ViewportHostService::EnsureViewportResources(ViewportEntry& entry) { const ViewportResourceReuseQuery reuseQuery = BuildViewportRenderTargetsReuseQuery( entry.requirements, entry.renderTargets, entry.requestedWidth, entry.requestedHeight); if (CanReuseViewportResources(reuseQuery)) { return true; } if (m_windowRenderer == nullptr || m_device == nullptr || entry.requestedWidth == 0u || entry.requestedHeight == 0u) { return false; } return m_renderTargetManager.EnsureTargets( entry.requirements, entry.requestedWidth, entry.requestedHeight, *m_device, *m_windowRenderer, entry.renderTargets); } void ViewportHostService::ApplyViewportFallback( ViewportEntry& entry, const ::XCEngine::Rendering::RenderContext& renderContext, const ViewportRenderResult& renderResult) { entry.statusText = renderResult.statusText; entry.renderTargets.hasValidObjectIdFrame = false; ClearViewport( entry, renderContext, renderResult.fallbackClearR, renderResult.fallbackClearG, renderResult.fallbackClearB, renderResult.fallbackClearA); } void ViewportHostService::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.objectIdState = ResourceStates::Common; entry.renderTargets.selectionMaskState = ResourceStates::Common; entry.renderTargets.hasValidObjectIdFrame = false; } ViewportFrame ViewportHostService::BuildFrame( const ViewportEntry& entry, const ::XCEngine::UI::UISize& requestedSize) const { ViewportFrame 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