#include "Viewport/SceneViewportRenderService.h" #include "Engine/EditorEngineServices.h" #include "Viewport/ViewportObjectIdPicker.h" #include namespace XCEngine::UI::Editor::App { namespace { ViewportRenderResult BuildFallbackResult( std::string statusText, float r, float g, float b, float a) { ViewportRenderResult result = {}; result.rendered = false; result.requiresFallbackClear = true; result.statusText = std::move(statusText); result.fallbackClearR = r; result.fallbackClearG = g; result.fallbackClearB = b; result.fallbackClearA = a; return result; } void SetStatusIfEmpty( std::string& statusText, std::string_view message) { if (statusText.empty()) { statusText = std::string(message); } } } // namespace SceneViewportRenderService::SceneViewportRenderService() = default; SceneViewportRenderService::~SceneViewportRenderService() = default; EditorViewportResourceRequirements SceneViewportRenderService::GetViewportResourceRequirements() { EditorViewportResourceRequirements requirements = {}; requirements.requiresDepthSampling = true; requirements.requiresObjectIdSurface = true; requirements.requiresSelectionMaskSurface = true; return requirements; } void SceneViewportRenderService::Initialize( const SceneViewportShaderPaths& shaderPaths, EditorEngineServices& engineServices) { m_engineServices = &engineServices; m_renderPassBundle.Initialize(shaderPaths, engineServices); } void SceneViewportRenderService::Shutdown() { m_renderRequest = {}; m_renderPassBundle.Shutdown(); m_engineServices = nullptr; m_device = nullptr; m_lastTargets = nullptr; m_lastRenderContext = {}; m_lastObjectIdRequestRevision = 0u; m_objectIdFrameSerialCounter = 0u; } void SceneViewportRenderService::SetRenderRequest( SceneViewportRenderRequest request) { if (m_lastTargets != nullptr && m_lastTargets->hasValidObjectIdFrame && request.requestRevision != m_lastObjectIdRequestRevision) { InvalidateObjectIdFrame(); } m_renderRequest = std::move(request); } const IViewportObjectPickerService& SceneViewportRenderService::GetObjectPicker() const { return *this; } ViewportRenderResult SceneViewportRenderService::Render( ViewportRenderTargets& targets, ::XCEngine::RHI::RHIDevice& device, const ::XCEngine::Rendering::RenderContext& renderContext) { m_device = &device; m_lastTargets = &targets; m_lastRenderContext = renderContext; if (!m_renderRequest.IsValid()) { return BuildFallbackResult( "Scene view camera is unavailable", 0.18f, 0.07f, 0.07f, 1.0f); } ::XCEngine::Rendering::RenderSurface surface = BuildViewportColorSurface(targets); ::XCEngine::Rendering::CameraFramePlan framePlan = {}; const SceneViewportFramePlanBuildStatus buildStatus = m_engineServices != nullptr ? m_engineServices->BuildSceneViewportFramePlan( m_renderRequest, renderContext, surface, framePlan) : SceneViewportFramePlanBuildStatus::Failed; if (buildStatus == SceneViewportFramePlanBuildStatus::NoActiveScene) { return BuildFallbackResult( "No active scene", 0.07f, 0.08f, 0.10f, 1.0f); } if (buildStatus != SceneViewportFramePlanBuildStatus::Success || !framePlan.IsValid()) { return BuildFallbackResult( "Scene renderer failed", 0.18f, 0.07f, 0.07f, 1.0f); } ViewportRenderResult result = {}; SceneViewportRenderPlanBuildResult renderPlan = m_renderPassBundle.BuildRenderPlan(targets, m_renderRequest); ApplySceneViewportRenderPlan( targets, renderPlan.plan, framePlan); if (renderPlan.warningStatusText != nullptr) { SetStatusIfEmpty(result.statusText, renderPlan.warningStatusText); } if (m_engineServices == nullptr || !m_engineServices->RenderSceneViewportFramePlan(framePlan)) { return BuildFallbackResult( "Scene renderer failed", 0.18f, 0.07f, 0.07f, 1.0f); } MarkSceneViewportRenderSuccess( targets, renderPlan.plan, framePlan); if (framePlan.request.objectId.IsRequested()) { ++m_objectIdFrameSerialCounter; if (m_objectIdFrameSerialCounter == 0u) { m_objectIdFrameSerialCounter = 1u; } targets.objectIdFrameSerial = m_objectIdFrameSerialCounter; m_lastObjectIdRequestRevision = m_renderRequest.requestRevision; } else { targets.objectIdFrameSerial = 0u; m_lastObjectIdRequestRevision = 0u; } result.rendered = true; return result; } ViewportObjectIdPickResult SceneViewportRenderService::PickObject( const ::XCEngine::UI::UISize& viewportSize, const ::XCEngine::UI::UIPoint& viewportMousePosition) const { if (!m_renderRequest.IsValid() || m_device == nullptr || m_lastTargets == nullptr) { return {}; } ViewportObjectIdPickContext pickContext = {}; pickContext.commandQueue = m_lastRenderContext.commandQueue; pickContext.texture = m_lastTargets->objectIdTexture; pickContext.textureState = m_lastTargets->objectIdState; pickContext.textureWidth = m_lastTargets->width; pickContext.textureHeight = m_lastTargets->height; pickContext.hasValidFrame = m_lastTargets->hasValidObjectIdFrame; pickContext.frameSerial = m_lastTargets->objectIdFrameSerial; pickContext.viewportSize = viewportSize; pickContext.viewportMousePosition = viewportMousePosition; ViewportObjectIdPickResult result = PickViewportRenderObjectId( pickContext, [this]( const ViewportObjectIdReadbackRequest& request, std::array& outRgba) { return m_device->ReadTexturePixelRGBA8( request.commandQueue, request.texture, request.textureState, request.pixelX, request.pixelY, outRgba); }); if (!result.HasResolvedSample() || !::XCEngine::Rendering::IsValidRenderObjectId(result.renderObjectId)) { return result; } EditorSceneObjectId runtimeObjectId = kInvalidEditorSceneObjectId; if (m_engineServices == nullptr || !m_engineServices->TryResolveActiveSceneRenderObjectId( result.renderObjectId, runtimeObjectId)) { return result; } result.resolvedEntityId = runtimeObjectId; return result; } void SceneViewportRenderService::InvalidateObjectIdFrame() { if (m_lastTargets != nullptr) { m_lastTargets->hasValidObjectIdFrame = false; m_lastTargets->objectIdFrameSerial = 0u; } m_lastObjectIdRequestRevision = 0u; } } // namespace XCEngine::UI::Editor::App