#include "Rendering/Execution/CameraRenderer.h" #include "Components/CameraComponent.h" #include "Rendering/Execution/CameraFrameRenderGraphStagePolicy.h" #include "Rendering/Execution/Internal/CameraFrameRenderGraphSurfaceUtils.h" #include "Rendering/Execution/DirectionalShadowExecutionState.h" #include "Rendering/Graph/RenderGraph.h" #include "Rendering/Graph/RenderGraphCompiler.h" #include "Rendering/Graph/RenderGraphExecutor.h" #include "Rendering/Internal/RenderPassGraphUtils.h" #include "Rendering/Passes/BuiltinDepthOnlyPass.h" #include "Rendering/Passes/BuiltinObjectIdPass.h" #include "Rendering/Passes/BuiltinShadowCasterPass.h" #include "Rendering/Pipelines/BuiltinForwardPipeline.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() { static const std::shared_ptr s_defaultPipelineAsset = std::make_shared(); return s_defaultPipelineAsset; } 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; } bool InitializeStandalonePass( RenderPass* pass, const RenderContext& context) { if (pass == nullptr) { return false; } if (pass->Initialize(context)) { return true; } pass->Shutdown(); return false; } RenderSceneData BuildScenePassRequestSceneData( const ScenePassRenderRequest& request, const RenderSurface& requestSurface, const RenderSceneData& baseSceneData) { RenderSceneData sceneData = baseSceneData; if (request.hasCameraDataOverride) { sceneData.cameraData = request.cameraDataOverride; } sceneData.cameraData.viewportWidth = requestSurface.GetRenderAreaWidth(); sceneData.cameraData.viewportHeight = requestSurface.GetRenderAreaHeight(); sceneData.cameraData.clearFlags = request.clearFlags; if (request.hasClearColorOverride) { sceneData.cameraData.clearColor = request.clearColorOverride; } return sceneData; } bool ExecuteScenePassRequest( RenderPass* pass, const ScenePassRenderRequest& request, const RenderContext& context, const RenderSceneData& baseSceneData, const RenderSurface* surfaceOverride = nullptr) { if (!request.IsRequested()) { return true; } if (!InitializeStandalonePass(pass, context)) { return false; } const RenderSurface& requestSurface = surfaceOverride != nullptr ? *surfaceOverride : request.surface; const RenderSceneData sceneData = BuildScenePassRequestSceneData( request, requestSurface, baseSceneData); const RenderPassContext passContext = { context, requestSurface, sceneData, nullptr, nullptr, RHI::ResourceStates::Common }; return pass->Execute(passContext); } bool ExecuteStandalonePass( RenderPass* pass, const RenderContext& context, const RenderSurface& surface, const RenderSceneData& sceneData) { if (pass == nullptr) { return false; } if (!InitializeStandalonePass(pass, context)) { return false; } const RenderPassContext passContext = { context, surface, sceneData, nullptr, nullptr, RHI::ResourceStates::Common }; return pass->Execute(passContext); } RenderSceneData BuildStandaloneStageSceneData( CameraFrameStage stage, const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& baseSceneData, const RenderSurface& stageSurface) { if (stage == CameraFrameStage::ShadowCaster && shadowState.shadowCasterRequest.IsRequested()) { return BuildScenePassRequestSceneData( shadowState.shadowCasterRequest, stageSurface, baseSceneData); } if (const ScenePassRenderRequest* request = plan.GetScenePassRequest(stage); request != nullptr) { return BuildScenePassRequestSceneData( *request, stageSurface, baseSceneData); } return baseSceneData; } class ScopedInitializedPassSequence { public: ScopedInitializedPassSequence( RenderPassSequence* sequence, const RenderContext& context) : m_sequence(sequence) { if (m_sequence == nullptr) { return; } if (!m_sequence->Initialize(context)) { m_failed = true; m_sequence->Shutdown(); m_sequence = nullptr; } } ScopedInitializedPassSequence(const ScopedInitializedPassSequence&) = delete; ScopedInitializedPassSequence& operator=(const ScopedInitializedPassSequence&) = delete; ~ScopedInitializedPassSequence() { // Request-owned pass sequences may record GPU work that executes only after the // caller closes/submits the command list. Do not destroy sequence-owned GPU // resources here; the sequence owner must shut them down explicitly when the // sequence is no longer needed. } bool IsReady() const { return !m_failed; } private: RenderPassSequence* m_sequence = nullptr; bool m_failed = false; }; bool EnsureInitializedPassSequence( std::unique_ptr& activeSequence, RenderPassSequence* sequence, const RenderContext& context) { if (activeSequence == nullptr) { activeSequence = std::make_unique(sequence, context); } return activeSequence->IsReady(); } struct CameraFrameExecutionState { RenderPipeline* pipeline = nullptr; RenderPass* objectIdPass = nullptr; RenderPass* depthOnlyPass = nullptr; RenderPass* shadowCasterPass = nullptr; std::unique_ptr preScenePasses; std::unique_ptr postProcessPasses; std::unique_ptr finalOutputPasses; std::unique_ptr postScenePasses; std::unique_ptr overlayPasses; }; RenderPass* ResolveStandaloneStagePass( CameraFrameStage stage, CameraFrameExecutionState& executionState) { switch (stage) { case CameraFrameStage::ShadowCaster: return executionState.shadowCasterPass; case CameraFrameStage::DepthOnly: return executionState.depthOnlyPass; case CameraFrameStage::ObjectId: return executionState.objectIdPass; default: return nullptr; } } std::unique_ptr* ResolveStageSequenceState( CameraFrameStage stage, CameraFrameExecutionState& executionState) { switch (stage) { case CameraFrameStage::PreScenePasses: return &executionState.preScenePasses; case CameraFrameStage::PostProcess: return &executionState.postProcessPasses; case CameraFrameStage::FinalOutput: return &executionState.finalOutputPasses; case CameraFrameStage::PostScenePasses: return &executionState.postScenePasses; case CameraFrameStage::OverlayPasses: return &executionState.overlayPasses; default: return nullptr; } } bool RecordSequencePass( RenderPass& pass, const RenderPassRenderGraphContext& context, const Internal::RenderPassGraphIO& io) { return pass.SupportsRenderGraph() ? pass.RecordRenderGraph(context) : Internal::RecordRasterRenderPass(pass, context, io); } const RenderSurface* ResolveFrameStageOutputSurface( CameraFrameStage stage, const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState) { switch (stage) { case CameraFrameStage::ShadowCaster: return shadowState.shadowCasterRequest.IsRequested() ? &shadowState.shadowCasterRequest.surface : nullptr; case CameraFrameStage::DepthOnly: return plan.request.depthOnly.IsRequested() ? &plan.request.depthOnly.surface : nullptr; case CameraFrameStage::ObjectId: return plan.request.objectId.IsRequested() ? &plan.request.objectId.surface : nullptr; default: return plan.GetOutputSurface(stage); } } RenderPassContext BuildFrameStagePassContext( CameraFrameStage stage, const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData) { const RenderSurface* outputSurface = ResolveFrameStageOutputSurface(stage, plan, shadowState); return { plan.request.context, outputSurface != nullptr ? *outputSurface : plan.request.surface, sceneData, plan.GetSourceSurface(stage), plan.GetSourceColorView(stage), plan.GetSourceColorState(stage) }; } bool ExecuteRecordedFrameStage( CameraFrameStage stage, const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData, CameraFrameExecutionState& executionState, const RenderPassContext& passContext) { switch (stage) { case CameraFrameStage::ShadowCaster: return ExecuteScenePassRequest( executionState.shadowCasterPass, shadowState.shadowCasterRequest, plan.request.context, sceneData, &passContext.surface); case CameraFrameStage::DepthOnly: return ExecuteScenePassRequest( executionState.depthOnlyPass, plan.request.depthOnly, plan.request.context, sceneData, &passContext.surface); case CameraFrameStage::MainScene: return executionState.pipeline != nullptr && executionState.pipeline->Render( FrameExecutionContext( plan.request.context, passContext.surface, sceneData, passContext.sourceSurface, passContext.sourceColorView, passContext.sourceColorState)); case CameraFrameStage::ObjectId: return !plan.request.objectId.IsRequested() || ExecuteStandalonePass( executionState.objectIdPass, plan.request.context, passContext.surface, sceneData); default: return false; } } bool RecordRegularPassSequenceStage( CameraFrameStage stage, const Containers::String& stageName, const RenderPassContext& stagePassContext, const RenderGraphImportedSurface& outputSurface, const RenderSceneData& sceneData, RenderPassSequence* stageSequence, CameraFrameExecutionState& executionState, RenderGraphBuilder& graphBuilder, RenderGraphBlackboard& blackboard, bool& stageExecutionSucceeded) { if (stageSequence == nullptr || stageSequence->GetPassCount() == 0u) { return true; } std::unique_ptr* const activeSequence = ResolveStageSequenceState(stage, executionState); RenderPassSequence* const sequence = stageSequence; const RenderContext* const renderContext = &stagePassContext.renderContext; const RenderPassGraphBeginCallback beginSequencePass = [&, activeSequence, sequence, renderContext](const RenderPassContext&) -> bool { if (activeSequence == nullptr || !EnsureInitializedPassSequence( *activeSequence, sequence, *renderContext)) { stageExecutionSucceeded = false; return false; } return true; }; const bool writesColor = GetPrimaryColorTexture(outputSurface).IsValid(); const bool writesDepth = outputSurface.depthTexture.IsValid(); for (size_t passIndex = 0u; passIndex < stageSequence->GetPassCount(); ++passIndex) { RenderPass* const pass = stageSequence->GetPass(passIndex); if (pass == nullptr) { continue; } const Containers::String passName = stageSequence->GetPassCount() == 1u ? stageName : BuildRenderGraphSequencePassName(stageName, passIndex); const RenderPassRenderGraphContext passContext = { graphBuilder, passName, stagePassContext.renderContext, sceneData, stagePassContext.surface, stagePassContext.sourceSurface, stagePassContext.sourceColorView, stagePassContext.sourceColorState, {}, outputSurface.colorTextures, outputSurface.depthTexture, &stageExecutionSucceeded, beginSequencePass, {}, &blackboard }; if (!RecordSequencePass( *pass, passContext, { false, writesColor, writesDepth })) { return false; } } return true; } bool RecordFullscreenPassSequenceStage( CameraFrameStage stage, const Containers::String& stageName, const RenderPassContext& stagePassContext, const CameraFrameRenderGraphSourceBinding& binding, RenderGraphTextureHandle finalOutputColor, const RenderSceneData& sceneData, RenderPassSequence* stageSequence, CameraFrameExecutionState& executionState, RenderGraphBuilder& graphBuilder, RenderGraphBlackboard& blackboard, bool& stageExecutionSucceeded) { if (stageSequence == nullptr || stageSequence->GetPassCount() == 0u) { return true; } std::unique_ptr* const activeSequence = ResolveStageSequenceState(stage, executionState); RenderPassSequence* const sequence = stageSequence; const RenderContext* const renderContext = &stagePassContext.renderContext; const RenderPassGraphBeginCallback beginSequencePass = [&, activeSequence, sequence, renderContext](const RenderPassContext&) -> bool { if (activeSequence == nullptr || !EnsureInitializedPassSequence( *activeSequence, sequence, *renderContext)) { stageExecutionSucceeded = false; return false; } return true; }; RenderGraphTextureHandle currentSourceColor = binding.sourceColor; const RenderGraphTextureDesc transientDesc = BuildFullscreenTransientTextureDesc(stagePassContext.surface); for (size_t passIndex = 0u; passIndex < stageSequence->GetPassCount(); ++passIndex) { RenderPass* const pass = stageSequence->GetPass(passIndex); if (pass == nullptr) { continue; } const bool isLastPass = (passIndex + 1u) == stageSequence->GetPassCount(); const Containers::String passName = stageSequence->GetPassCount() == 1u ? stageName : BuildRenderGraphSequencePassName(stageName, passIndex); const RenderGraphTextureHandle passOutputColor = isLastPass ? finalOutputColor : graphBuilder.CreateTransientTexture( passName + ".Color", transientDesc); const RenderSurface* const sourceSurfaceTemplate = passIndex == 0u ? binding.sourceSurfaceTemplate : &stagePassContext.surface; RHI::RHIResourceView* const sourceColorView = passIndex == 0u ? binding.sourceColorView : nullptr; const RHI::ResourceStates sourceColorState = passIndex == 0u ? binding.sourceColorState : RHI::ResourceStates::PixelShaderResource; const RenderPassRenderGraphContext passContext = { graphBuilder, passName, stagePassContext.renderContext, sceneData, stagePassContext.surface, sourceSurfaceTemplate, sourceColorView, sourceColorState, currentSourceColor, std::vector{ passOutputColor }, {}, &stageExecutionSucceeded, beginSequencePass, {}, &blackboard }; if (!RecordSequencePass( *pass, passContext, { true, true, false })) { return false; } currentSourceColor = passOutputColor; } return true; } struct CameraFrameStageGraphBuildState { CameraFrameStage stage = CameraFrameStage::MainScene; Containers::String stageName = {}; RenderPassSequence* stageSequence = nullptr; RenderSurface surfaceTemplate = {}; bool hasSourceSurface = false; RenderSurface sourceSurfaceTemplate = {}; RHI::RHIResourceView* sourceColorView = nullptr; RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common; RenderGraphImportedSurface sourceSurface = {}; RenderGraphImportedSurface outputSurface = {}; RenderGraphTextureHandle outputColor = {}; }; RenderPassContext BuildCameraFrameStageGraphPassContext( const CameraFramePlan& plan, const CameraFrameStageGraphBuildState& stageState, const RenderSceneData& sceneData) { return { plan.request.context, stageState.surfaceTemplate, sceneData, stageState.hasSourceSurface ? &stageState.sourceSurfaceTemplate : nullptr, stageState.sourceColorView, stageState.sourceColorState }; } CameraFrameStageGraphBuildState BuildCameraFrameStageGraphBuildState( CameraFrameStage stage, const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData, RenderGraphBuilder& graphBuilder, RenderGraphImportedTextureRegistry& importedTextures) { CameraFrameStageGraphBuildState stageState = {}; stageState.stage = stage; stageState.stageName = Containers::String(GetCameraFrameStageName(stage)); stageState.stageSequence = plan.GetPassSequence(stage); const RenderPassContext stagePassContext = BuildFrameStagePassContext(stage, plan, shadowState, sceneData); stageState.surfaceTemplate = stagePassContext.surface; stageState.hasSourceSurface = stagePassContext.sourceSurface != nullptr; if (stageState.hasSourceSurface) { stageState.sourceSurfaceTemplate = *stagePassContext.sourceSurface; } stageState.sourceColorView = stagePassContext.sourceColorView; stageState.sourceColorState = stagePassContext.sourceColorState; stageState.sourceSurface = ImportRenderGraphSurface( graphBuilder, importedTextures, stageState.stageName + ".Source", stagePassContext.sourceSurface, RenderGraphSurfaceImportUsage::Source, IsCameraFrameFullscreenSequenceStage(stage)); stageState.outputSurface = ImportRenderGraphSurface( graphBuilder, importedTextures, stageState.stageName + ".Output", &stagePassContext.surface, RenderGraphSurfaceImportUsage::Output, DoesCameraFrameStageGraphOwnColorTransitions(stage), DoesCameraFrameStageGraphOwnDepthTransitions(stage)); stageState.outputColor = ResolveStageOutputColorHandle( stage, plan, stageState.stageName, stagePassContext, stageState.outputSurface, graphBuilder); return stageState; } void PublishCameraFrameStageGraphBuildState( const CameraFrameStageGraphBuildState& stageState, const DirectionalShadowExecutionState& shadowState, CameraFrameRenderGraphResources& frameResources) { if (stageState.stage == CameraFrameStage::ShadowCaster && shadowState.HasShadowSampling() && stageState.outputSurface.depthTexture.IsValid()) { frameResources.mainDirectionalShadow = stageState.outputSurface.depthTexture; } WriteCameraFrameRenderGraphStageSurfaceResources( frameResources, stageState.stage, stageState.outputColor, stageState.outputSurface.depthTexture); } bool TryRecordCameraFrameStageSequence( const CameraFrameStageGraphBuildState& stageState, const CameraFramePlan& plan, const RenderSceneData& sceneData, CameraFrameExecutionState& executionState, RenderGraphBuilder& graphBuilder, RenderGraphBlackboard& blackboard, bool& stageExecutionSucceeded, bool& handled) { if (stageState.stageSequence == nullptr) { handled = false; return true; } handled = true; const RenderPassContext stagePassContext = BuildCameraFrameStageGraphPassContext(plan, stageState, sceneData); const bool recordResult = IsCameraFrameFullscreenSequenceStage(stageState.stage) ? RecordFullscreenPassSequenceStage( stageState.stage, stageState.stageName, stagePassContext, ResolveCameraFrameFullscreenStageGraphSourceBinding( plan, stageState.stage, stageState.surfaceTemplate, stagePassContext.sourceSurface, stagePassContext.sourceColorView, stagePassContext.sourceColorState, GetPrimaryColorTexture(stageState.sourceSurface), &blackboard), stageState.outputColor, sceneData, stageState.stageSequence, executionState, graphBuilder, blackboard, stageExecutionSucceeded) : RecordRegularPassSequenceStage( stageState.stage, stageState.stageName, stagePassContext, stageState.outputSurface, sceneData, stageState.stageSequence, executionState, graphBuilder, blackboard, stageExecutionSucceeded); if (!recordResult) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, Containers::String("CameraRenderer::Render failed: pass-sequence graph recording returned false for ") + stageState.stageName); return false; } return true; } bool TryRecordCameraFrameStageStandaloneRenderGraphPass( const CameraFrameStageGraphBuildState& stageState, const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData, CameraFrameExecutionState& executionState, RenderGraphBuilder& graphBuilder, RenderGraphBlackboard& blackboard, bool& stageExecutionSucceeded, bool& handled) { RenderPass* const standaloneStagePass = ResolveStandaloneStagePass(stageState.stage, executionState); if (standaloneStagePass == nullptr || !standaloneStagePass->SupportsRenderGraph()) { handled = false; return true; } handled = true; const RenderSceneData stageSceneData = BuildStandaloneStageSceneData( stageState.stage, plan, shadowState, sceneData, stageState.surfaceTemplate); const RenderPassGraphBeginCallback beginStandalonePass = [&, standaloneStagePass](const RenderPassContext&) -> bool { if (!InitializeStandalonePass( standaloneStagePass, plan.request.context)) { stageExecutionSucceeded = false; return false; } return true; }; const RenderPassRenderGraphContext standalonePassContext = { graphBuilder, stageState.stageName, plan.request.context, stageSceneData, stageState.surfaceTemplate, stageState.hasSourceSurface ? &stageState.sourceSurfaceTemplate : nullptr, stageState.sourceColorView, stageState.sourceColorState, GetPrimaryColorTexture(stageState.sourceSurface), std::vector{ stageState.outputColor }, stageState.outputSurface.depthTexture, &stageExecutionSucceeded, beginStandalonePass, {}, &blackboard }; if (!standaloneStagePass->RecordRenderGraph(standalonePassContext)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, Containers::String("CameraRenderer::Render failed: RenderPass::RecordRenderGraph returned false for ") + stageState.stageName); return false; } return true; } bool TryRecordCameraFrameMainSceneGraphPass( const CameraFrameStageGraphBuildState& stageState, const CameraFramePlan& plan, const RenderSceneData& sceneData, CameraFrameExecutionState& executionState, RenderGraphBuilder& graphBuilder, RenderGraphBlackboard& blackboard, bool& stageExecutionSucceeded, bool& handled) { if (stageState.stage != CameraFrameStage::MainScene || executionState.pipeline == nullptr || !executionState.pipeline->SupportsMainSceneRenderGraph()) { handled = false; return true; } handled = true; const RenderPipelineMainSceneRenderGraphContext mainSceneContext = { graphBuilder, stageState.stageName, plan.request.context, sceneData, stageState.surfaceTemplate, stageState.hasSourceSurface ? &stageState.sourceSurfaceTemplate : nullptr, stageState.sourceColorView, stageState.sourceColorState, std::vector{ stageState.outputColor }, stageState.outputSurface.depthTexture, &stageExecutionSucceeded, &blackboard }; if (!executionState.pipeline->RecordMainSceneRenderGraph(mainSceneContext)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: RenderPipeline::RecordMainSceneRenderGraph returned false"); return false; } return true; } void AddCameraFrameStageFallbackRasterPass( const CameraFrameStageGraphBuildState& stageState, const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData, CameraFrameExecutionState& executionState, RenderGraphBuilder& graphBuilder, bool& stageExecutionSucceeded) { const CameraFrameStageGraphBuildState capturedStageState = stageState; graphBuilder.AddRasterPass( capturedStageState.stageName, [&, capturedStageState](RenderGraphPassBuilder& passBuilder) { if (IsCameraFrameFullscreenSequenceStage(capturedStageState.stage)) { ReadRenderGraphColorSurface(passBuilder, capturedStageState.sourceSurface); WriteRenderGraphColorSurface(passBuilder, capturedStageState.outputSurface); } else { ReadRenderGraphSurface(passBuilder, capturedStageState.sourceSurface); WriteRenderGraphSurface(passBuilder, capturedStageState.outputSurface); } passBuilder.SetExecuteCallback( [&, capturedStageState]( const RenderGraphExecutionContext& executionContext) { if (!stageExecutionSucceeded) { return; } const RenderSurface* resolvedSourceSurface = capturedStageState.hasSourceSurface ? &capturedStageState.sourceSurfaceTemplate : nullptr; RHI::RHIResourceView* resolvedSourceColorView = capturedStageState.sourceColorView; RHI::ResourceStates resolvedSourceColorState = capturedStageState.sourceColorState; RenderSurface graphManagedSourceSurface = {}; if (IsCameraFrameFullscreenSequenceStage(capturedStageState.stage) && capturedStageState.hasSourceSurface && CanUseGraphManagedImportedSurface( capturedStageState.sourceSurface, executionContext)) { graphManagedSourceSurface = BuildGraphManagedImportedSurface( capturedStageState.sourceSurfaceTemplate, RHI::ResourceStates::PixelShaderResource, RHI::ResourceStates::PixelShaderResource); resolvedSourceSurface = &graphManagedSourceSurface; resolvedSourceColorState = RHI::ResourceStates::PixelShaderResource; } const RenderSurface* resolvedOutputSurface = &capturedStageState.surfaceTemplate; RenderSurface graphManagedOutputSurface = {}; if (CanUseGraphManagedImportedSurface( capturedStageState.outputSurface, executionContext)) { graphManagedOutputSurface = BuildGraphManagedPassSurface( capturedStageState.surfaceTemplate); resolvedOutputSurface = &graphManagedOutputSurface; } const RenderPassContext passContextOverride = { plan.request.context, *resolvedOutputSurface, sceneData, resolvedSourceSurface, resolvedSourceColorView, resolvedSourceColorState }; stageExecutionSucceeded = ExecuteRecordedFrameStage( capturedStageState.stage, plan, shadowState, sceneData, executionState, passContextOverride); }); }); } bool ExecuteRenderGraphPlan( const CameraFramePlan& plan, const DirectionalShadowExecutionState& shadowState, const RenderSceneData& sceneData, CameraFrameExecutionState& executionState) { RenderGraph graph = {}; RenderGraphBuilder graphBuilder(graph); RenderGraphBlackboard blackboard = {}; RenderGraphImportedTextureRegistry importedTextures = {}; CameraFrameRenderGraphResources& frameResources = blackboard.Emplace(); bool stageExecutionSucceeded = true; for (const CameraFrameStageInfo& stageInfo : kOrderedCameraFrameStages) { if (!plan.HasFrameStage(stageInfo.stage) && stageInfo.stage != CameraFrameStage::MainScene) { continue; } const CameraFrameStageGraphBuildState stageState = BuildCameraFrameStageGraphBuildState( stageInfo.stage, plan, shadowState, sceneData, graphBuilder, importedTextures); PublishCameraFrameStageGraphBuildState( stageState, shadowState, frameResources); bool stageHandled = false; if (!TryRecordCameraFrameStageSequence( stageState, plan, sceneData, executionState, graphBuilder, blackboard, stageExecutionSucceeded, stageHandled)) { return false; } if (stageHandled) { continue; } if (!TryRecordCameraFrameStageStandaloneRenderGraphPass( stageState, plan, shadowState, sceneData, executionState, graphBuilder, blackboard, stageExecutionSucceeded, stageHandled)) { return false; } if (stageHandled) { continue; } if (!TryRecordCameraFrameMainSceneGraphPass( stageState, plan, sceneData, executionState, graphBuilder, blackboard, stageExecutionSucceeded, stageHandled)) { return false; } if (stageHandled) { continue; } AddCameraFrameStageFallbackRasterPass( stageState, plan, shadowState, sceneData, executionState, graphBuilder, stageExecutionSucceeded); } CompiledRenderGraph compiledGraph = {}; Containers::String errorMessage; if (!RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, Containers::String("CameraRenderer::Render failed: RenderGraph compile failed: ") + errorMessage); return false; } if (!RenderGraphExecutor::Execute(compiledGraph, plan.request.context, &errorMessage)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, Containers::String("CameraRenderer::Render failed: RenderGraph execute failed: ") + errorMessage); return false; } return stageExecutionSucceeded; } 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) { CameraFrameExecutionState executionState = {}; executionState.pipeline = m_pipeline.get(); executionState.objectIdPass = m_objectIdPass.get(); executionState.depthOnlyPass = m_depthOnlyPass.get(); executionState.shadowCasterPass = m_shadowCasterPass.get(); return ExecuteRenderGraphPlan(plan, shadowState, sceneData, executionState); } 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