#include "Rendering/Execution/CameraRenderer.h" #include "Components/CameraComponent.h" #include "Rendering/Execution/DirectionalShadowExecutionState.h" #include "Rendering/Execution/Internal/CameraFrameRenderGraphExecution.h" #include "Rendering/Passes/BuiltinDepthOnlyPass.h" #include "Rendering/Passes/BuiltinObjectIdPass.h" #include "Rendering/Passes/BuiltinShadowCasterPass.h" #include "Rendering/Pipelines/BuiltinForwardPipeline.h" #include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" #include "Rendering/Pipelines/ScriptableRenderPipelineHost.h" #include "Rendering/RenderPipelineAsset.h" #include "Rendering/RenderSurface.h" #include "Rendering/Shadow/DirectionalShadowRuntime.h" #include "Scene/Scene.h" namespace XCEngine { namespace Rendering { namespace { std::shared_ptr CreateDefaultPipelineAsset() { return Pipelines::CreateManagedOrDefaultScriptableRenderPipelineAsset(); } std::unique_ptr CreateDefaultDepthOnlyPass() { return std::make_unique(); } std::unique_ptr CreateDefaultShadowCasterPass() { return std::make_unique(); } std::unique_ptr CreatePipelineFromAsset( const std::shared_ptr& pipelineAsset) { if (pipelineAsset != nullptr) { std::unique_ptr pipeline = pipelineAsset->CreatePipeline(); if (pipeline != nullptr) { return pipeline; } } return std::make_unique(); } Resources::ShaderKeywordSet BuildSceneGlobalShaderKeywords( const RenderSceneData& sceneData) { Resources::ShaderKeywordSet keywords = {}; if (sceneData.lighting.HasMainDirectionalShadow()) { keywords.enabledKeywords.PushBack("XC_MAIN_LIGHT_SHADOWS"); } Resources::NormalizeShaderKeywordSetInPlace(keywords); return keywords; } RenderEnvironmentData BuildEnvironmentData(const CameraFramePlan& plan) { RenderEnvironmentData environment = {}; const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface(); if (plan.request.camera == nullptr || mainSceneSurface.GetDepthAttachment() == nullptr || !HasRenderClearFlag(plan.request.clearFlags, RenderClearFlags::Color) || !plan.request.camera->IsSkyboxEnabled() || plan.request.camera->GetProjectionType() != Components::CameraProjectionType::Perspective) { return environment; } if (const Resources::Material* skyboxMaterial = plan.request.camera->GetSkyboxMaterial()) { environment.mode = RenderEnvironmentMode::MaterialSkybox; environment.materialSkybox.material = skyboxMaterial; return environment; } environment.mode = RenderEnvironmentMode::ProceduralSkybox; environment.skybox.topColor = plan.request.camera->GetSkyboxTopColor(); environment.skybox.horizonColor = plan.request.camera->GetSkyboxHorizonColor(); environment.skybox.bottomColor = plan.request.camera->GetSkyboxBottomColor(); return environment; } } // namespace CameraRenderer::CameraRenderer() : CameraRenderer(CreateDefaultPipelineAsset()) { } CameraRenderer::CameraRenderer(std::unique_ptr pipeline) : CameraRenderer( std::move(pipeline), std::make_unique(), CreateDefaultDepthOnlyPass(), CreateDefaultShadowCasterPass()) { } CameraRenderer::CameraRenderer( std::unique_ptr pipeline, std::unique_ptr objectIdPass, std::unique_ptr depthOnlyPass, std::unique_ptr shadowCasterPass) : m_pipelineAsset(nullptr) , m_objectIdPass(std::move(objectIdPass)) , m_depthOnlyPass(std::move(depthOnlyPass)) , m_shadowCasterPass(std::move(shadowCasterPass)) , m_directionalShadowRuntime(std::make_unique()) { if (m_objectIdPass == nullptr) { m_objectIdPass = std::make_unique(); } if (m_depthOnlyPass == nullptr) { m_depthOnlyPass = CreateDefaultDepthOnlyPass(); } if (m_shadowCasterPass == nullptr) { m_shadowCasterPass = CreateDefaultShadowCasterPass(); } ResetPipeline(std::move(pipeline)); } CameraRenderer::CameraRenderer(std::shared_ptr pipelineAsset) : m_pipelineAsset(std::move(pipelineAsset)) , m_objectIdPass(std::make_unique()) , m_depthOnlyPass(CreateDefaultDepthOnlyPass()) , m_shadowCasterPass(CreateDefaultShadowCasterPass()) , m_directionalShadowRuntime(std::make_unique()) { SetPipelineAsset(m_pipelineAsset); } CameraRenderer::~CameraRenderer() { if (m_pipeline) { m_pipeline->Shutdown(); } if (m_objectIdPass != nullptr) { m_objectIdPass->Shutdown(); } if (m_depthOnlyPass != nullptr) { m_depthOnlyPass->Shutdown(); } if (m_shadowCasterPass != nullptr) { m_shadowCasterPass->Shutdown(); } } void CameraRenderer::SetPipeline(std::unique_ptr pipeline) { m_pipelineAsset.reset(); ResetPipeline(std::move(pipeline)); } void CameraRenderer::SetPipelineAsset(std::shared_ptr pipelineAsset) { m_pipelineAsset = pipelineAsset != nullptr ? std::move(pipelineAsset) : CreateDefaultPipelineAsset(); ResetPipeline(CreatePipelineFromAsset(m_pipelineAsset)); } void CameraRenderer::SetObjectIdPass(std::unique_ptr objectIdPass) { if (m_objectIdPass != nullptr) { m_objectIdPass->Shutdown(); } m_objectIdPass = std::move(objectIdPass); if (m_objectIdPass == nullptr) { m_objectIdPass = std::make_unique(); } } void CameraRenderer::SetDepthOnlyPass(std::unique_ptr depthOnlyPass) { if (m_depthOnlyPass != nullptr) { m_depthOnlyPass->Shutdown(); } m_depthOnlyPass = std::move(depthOnlyPass); if (m_depthOnlyPass == nullptr) { m_depthOnlyPass = CreateDefaultDepthOnlyPass(); } } void CameraRenderer::SetShadowCasterPass(std::unique_ptr shadowCasterPass) { if (m_shadowCasterPass != nullptr) { m_shadowCasterPass->Shutdown(); } m_shadowCasterPass = std::move(shadowCasterPass); if (m_shadowCasterPass == nullptr) { m_shadowCasterPass = CreateDefaultShadowCasterPass(); } } void CameraRenderer::ResetPipeline(std::unique_ptr pipeline) { if (m_pipeline != nullptr) { m_pipeline->Shutdown(); } m_pipeline = std::move(pipeline); if (m_pipeline == nullptr) { m_pipelineAsset = CreateDefaultPipelineAsset(); m_pipeline = CreatePipelineFromAsset(m_pipelineAsset); } } bool CameraRenderer::BuildSceneDataForPlan( const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, RenderSceneData& outSceneData) { const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface(); outSceneData = m_sceneExtractor.ExtractForCamera( *plan.request.scene, *plan.request.camera, mainSceneSurface.GetRenderAreaWidth(), mainSceneSurface.GetRenderAreaHeight()); if (!outSceneData.HasCamera()) { return false; } outSceneData.lighting.mainDirectionalShadow = shadowState.shadowData; outSceneData.globalShaderKeywords = BuildSceneGlobalShaderKeywords(outSceneData); outSceneData.cameraData.clearFlags = plan.request.clearFlags; outSceneData.environment = BuildEnvironmentData(plan); if (plan.request.hasClearColorOverride) { outSceneData.cameraData.clearColor = plan.request.clearColorOverride; } return true; } bool CameraRenderer::ExecuteRenderPlan( const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData) { return ExecuteCameraFrameRenderGraphPlan( plan, shadowState, sceneData, m_pipeline.get(), m_objectIdPass.get(), m_depthOnlyPass.get(), m_shadowCasterPass.get()); } bool CameraRenderer::Render( const CameraFramePlan& plan) { if (!plan.IsValid() || m_pipeline == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: plan invalid or pipeline missing"); return false; } const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface(); if (mainSceneSurface.GetRenderAreaWidth() == 0 || mainSceneSurface.GetRenderAreaHeight() == 0) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: main scene surface render area is empty"); return false; } if (plan.request.depthOnly.IsRequested() && !plan.request.depthOnly.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: depth-only request invalid"); return false; } if (plan.postProcess.IsRequested() && !plan.IsPostProcessStageValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: post-process request invalid"); return false; } if (plan.UsesGraphManagedMainSceneColor() && (m_pipeline == nullptr || !m_pipeline->SupportsMainSceneRenderGraph())) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: graph-managed main scene color requires pipeline main-scene render-graph support"); return false; } if (plan.finalOutput.IsRequested() && !plan.IsFinalOutputStageValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: final-output request invalid"); return false; } if (plan.request.objectId.IsRequested() && !plan.request.objectId.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: object-id request invalid"); return false; } DirectionalShadowExecutionState shadowState = {}; if (m_directionalShadowRuntime == nullptr || !m_directionalShadowRuntime->ResolveExecutionState(plan, shadowState)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: DirectionalShadowRuntime::ResolveExecutionState returned false"); return false; } RenderSceneData sceneData = {}; if (!BuildSceneDataForPlan(plan, shadowState, sceneData)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: BuildSceneDataForPlan returned false"); return false; } if (!ExecuteRenderPlan(plan, shadowState, sceneData)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: ExecuteRenderPlan returned false"); return false; } return true; } } // namespace Rendering } // namespace XCEngine