#include "Rendering/Execution/CameraRenderer.h" #include "Components/CameraComponent.h" #include "Rendering/Caches/DirectionalShadowSurfaceCache.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 "RHI/RHIResourceView.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(); } bool InitializeStandalonePass( RenderPass* pass, const RenderContext& context) { if (pass == nullptr) { return false; } if (pass->Initialize(context)) { return true; } pass->Shutdown(); return false; } bool ExecuteScenePassRequest( RenderPass* pass, const ScenePassRenderRequest& request, const RenderContext& context, const RenderSceneData& baseSceneData) { if (!request.IsRequested()) { return true; } if (!InitializeStandalonePass(pass, context)) { return false; } RenderSceneData sceneData = baseSceneData; if (request.hasCameraDataOverride) { sceneData.cameraData = request.cameraDataOverride; } sceneData.cameraData.viewportWidth = request.surface.GetRenderAreaWidth(); sceneData.cameraData.viewportHeight = request.surface.GetRenderAreaHeight(); sceneData.cameraData.clearFlags = request.clearFlags; if (request.hasClearColorOverride) { sceneData.cameraData.clearColor = request.clearColorOverride; } const RenderPassContext passContext = { context, request.surface, sceneData, nullptr }; 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 }; return pass->Execute(passContext); } class ScopedInitializedPassSequence { public: ScopedInitializedPassSequence( RenderPassSequence* sequence, const RenderContext& context) : m_sequence(sequence) { if (m_sequence == nullptr) { return; } m_initialized = m_sequence->Initialize(context); if (!m_initialized) { m_failed = true; m_sequence->Shutdown(); m_sequence = nullptr; } } ScopedInitializedPassSequence(const ScopedInitializedPassSequence&) = delete; ScopedInitializedPassSequence& operator=(const ScopedInitializedPassSequence&) = delete; ~ScopedInitializedPassSequence() { if (m_sequence != nullptr && m_initialized) { m_sequence->Shutdown(); } } bool IsReady() const { return !m_failed; } bool Execute(const RenderPassContext& context) const { return m_sequence == nullptr || m_sequence->Execute(context); } private: RenderPassSequence* m_sequence = nullptr; bool m_initialized = false; bool m_failed = false; }; 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; }; bool ExecutePassSequenceStage( std::unique_ptr& activeSequence, RenderPassSequence* sequence, const RenderContext& context, const RenderPassContext& passContext) { activeSequence = std::make_unique(sequence, context); return activeSequence->IsReady() && activeSequence->Execute(passContext); } RenderPassContext BuildFrameStagePassContext( CameraFrameStage stage, const CameraRenderRequest& request, const RenderSceneData& sceneData) { const RenderSurface* outputSurface = request.GetOutputSurface(stage); return { request.context, outputSurface != nullptr ? *outputSurface : request.surface, sceneData, request.GetSourceSurface(stage) }; } bool ExecuteFrameStage( CameraFrameStage stage, const CameraRenderRequest& request, const ShadowCasterRenderRequest& resolvedShadowCaster, const RenderSceneData& sceneData, CameraFrameExecutionState& executionState) { const RenderPassContext passContext = BuildFrameStagePassContext(stage, request, sceneData); switch (stage) { case CameraFrameStage::PreScenePasses: return ExecutePassSequenceStage( executionState.preScenePasses, request.GetPassSequence(stage), request.context, passContext); case CameraFrameStage::ShadowCaster: return ExecuteScenePassRequest( executionState.shadowCasterPass, resolvedShadowCaster, request.context, sceneData); case CameraFrameStage::DepthOnly: return ExecuteScenePassRequest( executionState.depthOnlyPass, request.depthOnly, request.context, sceneData); case CameraFrameStage::MainScene: return executionState.pipeline != nullptr && executionState.pipeline->Render(request.context, passContext.surface, sceneData); case CameraFrameStage::PostProcess: return ExecutePassSequenceStage( executionState.postProcessPasses, request.GetPassSequence(stage), request.context, passContext); case CameraFrameStage::FinalOutput: return ExecutePassSequenceStage( executionState.finalOutputPasses, request.GetPassSequence(stage), request.context, passContext); case CameraFrameStage::ObjectId: return !request.objectId.IsRequested() || ExecuteStandalonePass( executionState.objectIdPass, request.context, request.objectId.surface, sceneData); case CameraFrameStage::PostScenePasses: return ExecutePassSequenceStage( executionState.postScenePasses, request.GetPassSequence(stage), request.context, passContext); case CameraFrameStage::OverlayPasses: return ExecutePassSequenceStage( executionState.overlayPasses, request.GetPassSequence(stage), request.context, passContext); default: return false; } } RenderDirectionalShadowData BuildDirectionalShadowData( const DirectionalShadowRenderPlan& plan, RHI::RHIResourceView* shadowMapView) { RenderDirectionalShadowData shadowData = {}; if (!plan.IsValid() || shadowMapView == nullptr) { return shadowData; } shadowData.enabled = true; shadowData.viewProjection = plan.cameraData.viewProjection; shadowData.shadowMap = shadowMapView; shadowData.shadowParams = Math::Vector4( 0.0015f, 1.0f / static_cast(plan.mapWidth), 1.0f / static_cast(plan.mapHeight), 0.85f); return shadowData; } RenderEnvironmentData BuildEnvironmentData(const CameraRenderRequest& request) { RenderEnvironmentData environment = {}; const RenderSurface& mainSceneSurface = request.GetMainSceneSurface(); if (request.camera == nullptr || mainSceneSurface.GetDepthAttachment() == nullptr || !HasRenderClearFlag(request.clearFlags, RenderClearFlags::Color) || !request.camera->IsSkyboxEnabled() || request.camera->GetProjectionType() != Components::CameraProjectionType::Perspective) { return environment; } if (const Resources::Material* skyboxMaterial = request.camera->GetSkyboxMaterial()) { environment.mode = RenderEnvironmentMode::MaterialSkybox; environment.materialSkybox.material = skyboxMaterial; return environment; } environment.mode = RenderEnvironmentMode::ProceduralSkybox; environment.skybox.topColor = request.camera->GetSkyboxTopColor(); environment.skybox.horizonColor = request.camera->GetSkyboxHorizonColor(); environment.skybox.bottomColor = 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)) { 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()) { 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::ResolveShadowCasterRequest( const CameraRenderRequest& request, ShadowCasterRenderRequest& outResolvedShadowCaster, RHI::RHIResourceView*& outShadowMapView) { outResolvedShadowCaster = request.shadowCaster; outShadowMapView = nullptr; if (outResolvedShadowCaster.IsRequested()) { return outResolvedShadowCaster.IsValid(); } if (!request.directionalShadow.IsValid()) { return true; } if (m_directionalShadowSurface == nullptr) { m_directionalShadowSurface = std::make_unique(); } if (!m_directionalShadowSurface->EnsureSurface(request.context, request.directionalShadow)) { return false; } outResolvedShadowCaster.surface = m_directionalShadowSurface->GetSurface(); outResolvedShadowCaster.clearFlags = RenderClearFlags::Depth; if (!outResolvedShadowCaster.hasCameraDataOverride) { outResolvedShadowCaster.hasCameraDataOverride = true; outResolvedShadowCaster.cameraDataOverride = request.directionalShadow.cameraData; } outShadowMapView = m_directionalShadowSurface->GetDepthShaderView(); return true; } bool CameraRenderer::BuildSceneDataForRequest( const CameraRenderRequest& request, RHI::RHIResourceView* shadowMapView, RenderSceneData& outSceneData) { const RenderSurface& mainSceneSurface = request.GetMainSceneSurface(); outSceneData = m_sceneExtractor.ExtractForCamera( *request.scene, *request.camera, mainSceneSurface.GetRenderAreaWidth(), mainSceneSurface.GetRenderAreaHeight()); if (!outSceneData.HasCamera()) { return false; } if (request.directionalShadow.IsValid()) { outSceneData.lighting.mainDirectionalShadow = BuildDirectionalShadowData(request.directionalShadow, shadowMapView); } outSceneData.cameraData.clearFlags = request.clearFlags; outSceneData.environment = BuildEnvironmentData(request); if (request.hasClearColorOverride) { outSceneData.cameraData.clearColor = request.clearColorOverride; } return true; } bool CameraRenderer::ExecuteRenderPlan( const CameraRenderRequest& request, const ShadowCasterRenderRequest& resolvedShadowCaster, 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(); for (const CameraFrameStageInfo& stageInfo : kOrderedCameraFrameStages) { if (!request.HasFrameStage(stageInfo.stage) && stageInfo.stage != CameraFrameStage::MainScene) { continue; } if (!ExecuteFrameStage( stageInfo.stage, request, resolvedShadowCaster, sceneData, executionState)) { return false; } } return true; } bool CameraRenderer::Render( const CameraRenderRequest& request) { if (!request.IsValid() || m_pipeline == nullptr) { return false; } const RenderSurface& mainSceneSurface = request.GetMainSceneSurface(); if (mainSceneSurface.GetRenderAreaWidth() == 0 || mainSceneSurface.GetRenderAreaHeight() == 0) { return false; } if (request.depthOnly.IsRequested() && !request.depthOnly.IsValid()) { return false; } if (request.postProcess.IsRequested() && !request.postProcess.IsValid()) { return false; } if (request.finalOutput.IsRequested() && !request.finalOutput.IsValid()) { return false; } if (request.objectId.IsRequested() && !request.objectId.IsValid()) { return false; } ShadowCasterRenderRequest resolvedShadowCaster = {}; RHI::RHIResourceView* shadowMapView = nullptr; if (!ResolveShadowCasterRequest(request, resolvedShadowCaster, shadowMapView)) { return false; } RenderSceneData sceneData = {}; if (!BuildSceneDataForRequest(request, shadowMapView, sceneData)) { return false; } return ExecuteRenderPlan(request, resolvedShadowCaster, sceneData); } } // namespace Rendering } // namespace XCEngine