diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 56abd473..aba47b23 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -536,6 +536,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/DirectionalShadowSurfaceCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/FullscreenPassSurfaceCache.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Internal/RenderPassGraphUtils.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Internal/RenderPassGraphUtils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBaseResources.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthOnlyPass.cpp diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h index 03c2c901..f41fb99e 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h @@ -33,6 +33,8 @@ public: ~BuiltinColorScalePostProcessPass() override; const char* GetName() const override; + bool SupportsRenderGraph() const override; + bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override; bool Execute(const RenderPassContext& context) override; void Shutdown() override; diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h b/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h index df2e999c..e07878c5 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h @@ -40,6 +40,8 @@ public: bool Initialize(const RenderContext& context) override; void Shutdown() override; + bool SupportsRenderGraph() const override; + bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override; bool Execute(const RenderPassContext& context) override; protected: diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinFinalColorPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinFinalColorPass.h index 8d892b7e..4f1d4583 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinFinalColorPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinFinalColorPass.h @@ -33,6 +33,8 @@ public: ~BuiltinFinalColorPass() override; const char* GetName() const override; + bool SupportsRenderGraph() const override; + bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override; bool Execute(const RenderPassContext& context) override; void Shutdown() override; diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h index d92f2020..5ea3abd5 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h @@ -26,6 +26,8 @@ public: const char* GetName() const override; bool Initialize(const RenderContext& context) override; + bool SupportsRenderGraph() const override; + bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override; bool Execute(const RenderPassContext& context) override; void Shutdown() override; diff --git a/engine/include/XCEngine/Rendering/RenderPass.h b/engine/include/XCEngine/Rendering/RenderPass.h index 0e0a0579..8b86e81f 100644 --- a/engine/include/XCEngine/Rendering/RenderPass.h +++ b/engine/include/XCEngine/Rendering/RenderPass.h @@ -1,9 +1,13 @@ #pragma once +#include #include +#include #include +#include #include +#include #include #include @@ -15,7 +19,8 @@ class RHIResourceView; namespace Rendering { struct RenderSceneData; -class RenderSurface; +class RenderGraphBuilder; +class RenderGraphBlackboard; struct RenderPassContext { const RenderContext& renderContext; @@ -43,6 +48,27 @@ inline RenderPassContext BuildRenderPassContext( return BuildRenderPassContext(executionContext.frameContext); } +using RenderPassGraphBeginCallback = std::function; +using RenderPassGraphEndCallback = std::function; + +struct RenderPassRenderGraphContext { + RenderGraphBuilder& graphBuilder; + Containers::String passName = {}; + const RenderContext& renderContext; + const RenderSceneData& sceneData; + RenderSurface surface = {}; + const RenderSurface* sourceSurface = nullptr; + RHI::RHIResourceView* sourceColorView = nullptr; + RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common; + RenderGraphTextureHandle sourceColorTexture = {}; + std::vector colorTargets = {}; + RenderGraphTextureHandle depthTarget = {}; + bool* executionSucceeded = nullptr; + RenderPassGraphBeginCallback beginPassCallback = {}; + RenderPassGraphEndCallback endPassCallback = {}; + RenderGraphBlackboard* blackboard = nullptr; +}; + class RenderPass { public: virtual ~RenderPass() = default; @@ -56,6 +82,15 @@ public: virtual void Shutdown() { } + virtual bool SupportsRenderGraph() const { + return false; + } + + virtual bool RecordRenderGraph( + const RenderPassRenderGraphContext&) { + return false; + } + virtual bool Execute(const RenderPassContext& context) = 0; }; @@ -119,6 +154,10 @@ public: return m_passes[index]->Execute(context); } + RenderPass* GetPass(size_t index) const { + return index < m_passes.size() ? m_passes[index].get() : nullptr; + } + private: std::vector> m_passes; }; diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index d02a52d4..cf1ca61f 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -309,6 +309,25 @@ bool InitializeStandalonePass( 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, @@ -323,19 +342,13 @@ bool ExecuteScenePassRequest( return false; } - RenderSceneData sceneData = baseSceneData; - if (request.hasCameraDataOverride) { - sceneData.cameraData = request.cameraDataOverride; - } - const RenderSurface& requestSurface = surfaceOverride != nullptr ? *surfaceOverride : request.surface; - sceneData.cameraData.viewportWidth = requestSurface.GetRenderAreaWidth(); - sceneData.cameraData.viewportHeight = requestSurface.GetRenderAreaHeight(); - sceneData.cameraData.clearFlags = request.clearFlags; - if (request.hasClearColorOverride) { - sceneData.cameraData.clearColor = request.clearColorOverride; - } + const RenderSceneData sceneData = + BuildScenePassRequestSceneData( + request, + requestSurface, + baseSceneData); const RenderPassContext passContext = { context, @@ -372,6 +385,31 @@ bool ExecuteStandalonePass( 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( @@ -439,6 +477,21 @@ struct CameraFrameExecutionState { 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; + } +} + bool ExecutePassSequenceStage( std::unique_ptr& activeSequence, RenderPassSequence* sequence, @@ -825,9 +878,72 @@ bool ExecuteRenderGraphPlan( : graphBuilder.CreateTransientTexture( BuildRenderGraphSequencePassName(stageName, sequencePassIndex) + ".Color", transientDesc); + RenderPass* const sequencePass = + stageSequence->GetPass(sequencePassIndex); + const Containers::String sequencePassName = + BuildRenderGraphSequencePassName(stageName, sequencePassIndex); + const RenderSurface* const passSourceSurfaceTemplate = + sequencePassIndex == 0u + ? stagePassContext.sourceSurface + : &stagePassContext.surface; + RHI::RHIResourceView* const passSourceColorView = + sequencePassIndex == 0u + ? stagePassContext.sourceColorView + : nullptr; + const RHI::ResourceStates passSourceColorState = + sequencePassIndex == 0u + ? stagePassContext.sourceColorState + : RHI::ResourceStates::PixelShaderResource; + + if (sequencePass != nullptr && + sequencePass->SupportsRenderGraph()) { + const RenderPassGraphBeginCallback beginSequencePass = + [&, stage](const RenderPassContext&) -> bool { + std::unique_ptr& activeSequence = + stage == CameraFrameStage::PostProcess + ? executionState.postProcessPasses + : executionState.finalOutputPasses; + if (!EnsureInitializedPassSequence( + activeSequence, + plan.GetPassSequence(stage), + plan.request.context)) { + stageExecutionSucceeded = false; + return false; + } + + return true; + }; + const RenderPassRenderGraphContext sequencePassContext = { + graphBuilder, + sequencePassName, + plan.request.context, + sceneData, + stagePassContext.surface, + passSourceSurfaceTemplate, + passSourceColorView, + passSourceColorState, + passSourceColor, + std::vector{ passOutputColor }, + {}, + &stageExecutionSucceeded, + beginSequencePass, + {}, + &blackboard + }; + if (!sequencePass->RecordRenderGraph(sequencePassContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + Containers::String("CameraRenderer::Render failed: RenderPass::RecordRenderGraph returned false for ") + + sequencePassName); + return false; + } + + currentSourceColor = passOutputColor; + continue; + } graphBuilder.AddRasterPass( - BuildRenderGraphSequencePassName(stageName, sequencePassIndex), + sequencePassName, [&, stage, sequencePassIndex, passSourceColor, passOutputColor]( RenderGraphPassBuilder& passBuilder) { if (passSourceColor.IsValid()) { @@ -917,6 +1033,55 @@ bool ExecuteRenderGraphPlan( const RHI::RHIResourceView* const stageSourceColorView = stagePassContext.sourceColorView; const RHI::ResourceStates stageSourceColorState = stagePassContext.sourceColorState; + RenderPass* const standaloneStagePass = + ResolveStandaloneStagePass(stage, executionState); + if (standaloneStagePass != nullptr && + standaloneStagePass->SupportsRenderGraph()) { + const RenderSceneData stageSceneData = + BuildStandaloneStageSceneData( + stage, + plan, + shadowState, + sceneData, + stageSurfaceTemplate); + const RenderPassGraphBeginCallback beginStandalonePass = + [&, standaloneStagePass](const RenderPassContext&) -> bool { + if (!InitializeStandalonePass( + standaloneStagePass, + plan.request.context)) { + stageExecutionSucceeded = false; + return false; + } + + return true; + }; + const RenderPassRenderGraphContext standalonePassContext = { + graphBuilder, + stageName, + plan.request.context, + stageSceneData, + stageSurfaceTemplate, + hasStageSourceSurface ? &stageSourceSurfaceTemplate : nullptr, + const_cast(stageSourceColorView), + stageSourceColorState, + GetPrimaryColorTexture(sourceSurface), + outputSurface.colorTextures, + 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 ") + + stageName); + return false; + } + continue; + } + if (stage == CameraFrameStage::MainScene && executionState.pipeline != nullptr && executionState.pipeline->SupportsMainSceneRenderGraph()) { diff --git a/engine/src/Rendering/Internal/RenderPassGraphUtils.cpp b/engine/src/Rendering/Internal/RenderPassGraphUtils.cpp new file mode 100644 index 00000000..946579b8 --- /dev/null +++ b/engine/src/Rendering/Internal/RenderPassGraphUtils.cpp @@ -0,0 +1,309 @@ +#include "Rendering/Internal/RenderPassGraphUtils.h" + +#include "Rendering/FrameData/RenderSceneData.h" +#include "Rendering/Graph/RenderGraph.h" + +#include +#include + +namespace XCEngine { +namespace Rendering { +namespace Internal { +namespace { + +RenderSurface BuildGraphManagedImportedSurface( + const RenderSurface& templateSurface, + RHI::ResourceStates colorStateBefore, + RHI::ResourceStates colorStateAfter) { + RenderSurface surface = templateSurface; + surface.SetAutoTransitionEnabled(false); + surface.SetColorStateBefore(colorStateBefore); + surface.SetColorStateAfter(colorStateAfter); + return surface; +} + +RenderSurface BuildGraphManagedPassSurface( + const RenderSurface& templateSurface) { + RenderSurface surface = templateSurface; + surface.SetAutoTransitionEnabled(false); + return surface; +} + +bool ResolveGraphManagedSourceSurface( + const RenderSurface* sourceSurfaceTemplate, + RHI::RHIResourceView* sourceColorView, + RHI::ResourceStates sourceColorState, + RenderGraphTextureHandle sourceColorTexture, + const RenderGraphExecutionContext& graphContext, + const RenderPassGraphIO& io, + const RenderSurface*& outSourceSurface, + RHI::RHIResourceView*& outSourceColorView, + RHI::ResourceStates& outSourceColorState, + RenderSurface& outGraphManagedSourceSurface) { + outSourceSurface = sourceSurfaceTemplate; + outSourceColorView = sourceColorView; + outSourceColorState = sourceColorState; + + if (!io.readSourceColor || + !sourceColorTexture.IsValid() || + !graphContext.OwnsTextureTransitions(sourceColorTexture)) { + return true; + } + + if (graphContext.IsTransientTexture(sourceColorTexture)) { + if (sourceSurfaceTemplate == nullptr) { + return false; + } + + RHI::RHIResourceView* renderTargetView = + graphContext.ResolveTextureView( + sourceColorTexture, + RenderGraphTextureViewType::RenderTarget); + RHI::RHIResourceView* shaderResourceView = + graphContext.ResolveTextureView( + sourceColorTexture, + RenderGraphTextureViewType::ShaderResource); + if (renderTargetView == nullptr || shaderResourceView == nullptr) { + return false; + } + + outGraphManagedSourceSurface = *sourceSurfaceTemplate; + outGraphManagedSourceSurface.SetColorAttachment(renderTargetView); + outGraphManagedSourceSurface.SetAutoTransitionEnabled(false); + outGraphManagedSourceSurface.SetColorStateBefore(RHI::ResourceStates::PixelShaderResource); + outGraphManagedSourceSurface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource); + + outSourceSurface = &outGraphManagedSourceSurface; + outSourceColorView = shaderResourceView; + outSourceColorState = RHI::ResourceStates::PixelShaderResource; + return true; + } + + if (sourceSurfaceTemplate != nullptr) { + outGraphManagedSourceSurface = + BuildGraphManagedImportedSurface( + *sourceSurfaceTemplate, + RHI::ResourceStates::PixelShaderResource, + RHI::ResourceStates::PixelShaderResource); + outSourceSurface = &outGraphManagedSourceSurface; + } + outSourceColorState = RHI::ResourceStates::PixelShaderResource; + return true; +} + +bool ResolveGraphManagedOutputSurface( + const RenderSurface& surfaceTemplate, + const std::vector& colorTargets, + RenderGraphTextureHandle depthTarget, + const RenderGraphExecutionContext& graphContext, + const RenderPassGraphIO& io, + const RenderSurface*& outSurface, + RenderSurface& outGraphManagedSurface) { + outSurface = &surfaceTemplate; + + const bool ownsAnyColorTransitions = + io.writeColor && + std::any_of( + colorTargets.begin(), + colorTargets.end(), + [&](RenderGraphTextureHandle texture) { + return texture.IsValid() && + graphContext.OwnsTextureTransitions(texture); + }); + const bool ownsDepthTransitions = + io.writeDepth && + depthTarget.IsValid() && + graphContext.OwnsTextureTransitions(depthTarget); + if (!ownsAnyColorTransitions && !ownsDepthTransitions) { + return true; + } + + outGraphManagedSurface = BuildGraphManagedPassSurface(surfaceTemplate); + + if (ownsAnyColorTransitions) { + std::vector colorAttachments = + surfaceTemplate.GetColorAttachments(); + if (colorAttachments.size() < colorTargets.size()) { + colorAttachments.resize(colorTargets.size(), nullptr); + } + + for (size_t colorIndex = 0u; colorIndex < colorTargets.size(); ++colorIndex) { + const RenderGraphTextureHandle colorTarget = colorTargets[colorIndex]; + if (!colorTarget.IsValid() || + !graphContext.OwnsTextureTransitions(colorTarget) || + !graphContext.IsTransientTexture(colorTarget)) { + continue; + } + + colorAttachments[colorIndex] = + graphContext.ResolveTextureView( + colorTarget, + RenderGraphTextureViewType::RenderTarget); + if (colorAttachments[colorIndex] == nullptr) { + return false; + } + } + + outGraphManagedSurface.SetColorAttachments(colorAttachments); + } + + if (ownsDepthTransitions && + graphContext.IsTransientTexture(depthTarget)) { + RHI::RHIResourceView* depthAttachment = + graphContext.ResolveTextureView( + depthTarget, + RenderGraphTextureViewType::DepthStencil); + if (depthAttachment == nullptr) { + return false; + } + + outGraphManagedSurface.SetDepthAttachment(depthAttachment); + } + + outSurface = &outGraphManagedSurface; + return true; +} + +} // namespace + +bool RecordRasterRenderPass( + RenderPass& pass, + const RenderPassRenderGraphContext& context, + const RenderPassGraphIO& io) { + RenderPass* const renderPass = &pass; + const Containers::String passName = context.passName; + const RenderContext* const renderContext = &context.renderContext; + const std::shared_ptr sceneData = + std::make_shared(context.sceneData); + const RenderSurface surface = context.surface; + const RenderSurface* const sourceSurface = context.sourceSurface; + RHI::RHIResourceView* const sourceColorView = context.sourceColorView; + const RHI::ResourceStates sourceColorState = context.sourceColorState; + const RenderGraphTextureHandle sourceColorTexture = context.sourceColorTexture; + const std::vector colorTargets = context.colorTargets; + const RenderGraphTextureHandle depthTarget = context.depthTarget; + bool* const executionSucceeded = context.executionSucceeded; + const RenderPassGraphBeginCallback beginPassCallback = context.beginPassCallback; + const RenderPassGraphEndCallback endPassCallback = context.endPassCallback; + + context.graphBuilder.AddRasterPass( + passName, + [renderPass, + renderContext, + sceneData, + surface, + sourceSurface, + sourceColorView, + sourceColorState, + sourceColorTexture, + colorTargets, + depthTarget, + executionSucceeded, + beginPassCallback, + endPassCallback, + io]( + RenderGraphPassBuilder& passBuilder) { + if (io.readSourceColor && sourceColorTexture.IsValid()) { + passBuilder.ReadTexture(sourceColorTexture); + } + + if (io.writeColor) { + for (RenderGraphTextureHandle colorTarget : colorTargets) { + if (colorTarget.IsValid()) { + passBuilder.WriteTexture(colorTarget); + } + } + } + + if (io.writeDepth && depthTarget.IsValid()) { + passBuilder.WriteDepthTexture(depthTarget); + } + + passBuilder.SetExecuteCallback( + [renderPass, + renderContext, + sceneData, + surface, + sourceSurface, + sourceColorView, + sourceColorState, + sourceColorTexture, + colorTargets, + depthTarget, + executionSucceeded, + beginPassCallback, + endPassCallback, + io]( + const RenderGraphExecutionContext& executionContext) { + if (executionSucceeded != nullptr && !(*executionSucceeded)) { + return; + } + + const RenderSurface* resolvedSourceSurface = sourceSurface; + RHI::RHIResourceView* resolvedSourceColorView = sourceColorView; + RHI::ResourceStates resolvedSourceColorState = sourceColorState; + RenderSurface graphManagedSourceSurface = {}; + if (!ResolveGraphManagedSourceSurface( + sourceSurface, + sourceColorView, + sourceColorState, + sourceColorTexture, + executionContext, + io, + resolvedSourceSurface, + resolvedSourceColorView, + resolvedSourceColorState, + graphManagedSourceSurface)) { + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return; + } + + const RenderSurface* resolvedSurface = &surface; + RenderSurface graphManagedSurface = {}; + if (!ResolveGraphManagedOutputSurface( + surface, + colorTargets, + depthTarget, + executionContext, + io, + resolvedSurface, + graphManagedSurface)) { + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return; + } + + const RenderPassContext passContext = { + *renderContext, + *resolvedSurface, + *sceneData, + resolvedSourceSurface, + resolvedSourceColorView, + resolvedSourceColorState + }; + if (beginPassCallback && + !beginPassCallback(passContext)) { + if (executionSucceeded != nullptr) { + *executionSucceeded = false; + } + return; + } + + const bool executeResult = renderPass->Execute(passContext); + if (endPassCallback) { + endPassCallback(passContext); + } + if (executionSucceeded != nullptr) { + *executionSucceeded = executeResult; + } + }); + }); + return true; +} + +} // namespace Internal +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Internal/RenderPassGraphUtils.h b/engine/src/Rendering/Internal/RenderPassGraphUtils.h new file mode 100644 index 00000000..cc5114d3 --- /dev/null +++ b/engine/src/Rendering/Internal/RenderPassGraphUtils.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace XCEngine { +namespace Rendering { +namespace Internal { + +struct RenderPassGraphIO { + bool readSourceColor = false; + bool writeColor = false; + bool writeDepth = false; +}; + +bool RecordRasterRenderPass( + RenderPass& pass, + const RenderPassRenderGraphContext& context, + const RenderPassGraphIO& io); + +} // namespace Internal +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp b/engine/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp index ef8af818..bfbd81f7 100644 --- a/engine/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp @@ -2,6 +2,7 @@ #include "Core/Asset/ResourceManager.h" #include "Debug/Logger.h" +#include "Rendering/Internal/RenderPassGraphUtils.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/Internal/ShaderVariantUtils.h" #include "Rendering/RenderSurface.h" @@ -124,6 +125,22 @@ const char* BuiltinColorScalePostProcessPass::GetName() const { return "BuiltinColorScalePostProcessPass"; } +bool BuiltinColorScalePostProcessPass::SupportsRenderGraph() const { + return true; +} + +bool BuiltinColorScalePostProcessPass::RecordRenderGraph( + const RenderPassRenderGraphContext& context) { + return Internal::RecordRasterRenderPass( + *this, + context, + { + true, + true, + false + }); +} + bool BuiltinColorScalePostProcessPass::Execute(const RenderPassContext& context) { if (!context.renderContext.IsValid() || context.sourceSurface == nullptr || diff --git a/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp b/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp index 2dd12cb9..a5958852 100644 --- a/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp +++ b/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp @@ -3,6 +3,7 @@ #include "RHI/RHICommandList.h" #include "Rendering/Extraction/RenderSceneExtractor.h" #include "Rendering/FrameData/RendererListUtils.h" +#include "Rendering/Internal/RenderPassGraphUtils.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/RenderSurface.h" #include "Resources/Mesh/Mesh.h" @@ -63,6 +64,22 @@ void BuiltinDepthStylePassBase::Shutdown() { DestroyResources(); } +bool BuiltinDepthStylePassBase::SupportsRenderGraph() const { + return true; +} + +bool BuiltinDepthStylePassBase::RecordRenderGraph( + const RenderPassRenderGraphContext& context) { + return Internal::RecordRasterRenderPass( + *this, + context, + { + false, + true, + true + }); +} + bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) { if (!context.renderContext.IsValid()) { return false; diff --git a/engine/src/Rendering/Passes/BuiltinFinalColorPass.cpp b/engine/src/Rendering/Passes/BuiltinFinalColorPass.cpp index 9ce5fe91..1635db8d 100644 --- a/engine/src/Rendering/Passes/BuiltinFinalColorPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinFinalColorPass.cpp @@ -2,6 +2,7 @@ #include "Core/Asset/ResourceManager.h" #include "Debug/Logger.h" +#include "Rendering/Internal/RenderPassGraphUtils.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/Internal/ShaderVariantUtils.h" #include "Rendering/RenderSurface.h" @@ -132,6 +133,22 @@ const char* BuiltinFinalColorPass::GetName() const { return "BuiltinFinalColorPass"; } +bool BuiltinFinalColorPass::SupportsRenderGraph() const { + return true; +} + +bool BuiltinFinalColorPass::RecordRenderGraph( + const RenderPassRenderGraphContext& context) { + return Internal::RecordRasterRenderPass( + *this, + context, + { + true, + true, + false + }); +} + bool BuiltinFinalColorPass::Execute(const RenderPassContext& context) { if (!context.renderContext.IsValid() || context.sourceSurface == nullptr || diff --git a/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp b/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp index f3bed488..07efba48 100644 --- a/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp @@ -4,6 +4,7 @@ #include "RHI/RHICommandList.h" #include "Rendering/FrameData/RendererListUtils.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" +#include "Rendering/Internal/RenderPassGraphUtils.h" #include "Rendering/Extraction/RenderSceneExtractor.h" #include "Rendering/RenderSurface.h" #include "Resources/Mesh/Mesh.h" @@ -57,6 +58,22 @@ bool BuiltinObjectIdPass::Initialize(const RenderContext& context) { return context.IsValid(); } +bool BuiltinObjectIdPass::SupportsRenderGraph() const { + return true; +} + +bool BuiltinObjectIdPass::RecordRenderGraph( + const RenderPassRenderGraphContext& context) { + return Internal::RecordRasterRenderPass( + *this, + context, + { + false, + true, + true + }); +} + bool BuiltinObjectIdPass::Execute(const RenderPassContext& context) { if (!context.renderContext.IsValid()) { return false; diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index a81d54d0..acb808df 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -16,6 +16,8 @@ #include #include +#include "Rendering/Internal/RenderPassGraphUtils.h" + #include #include #include @@ -591,15 +593,33 @@ class MockObjectIdPass final : public RenderPass { public: MockObjectIdPass( std::shared_ptr state, - bool renderResult = true) + bool renderResult = true, + bool supportsRenderGraph = false) : m_state(std::move(state)) - , m_renderResult(renderResult) { + , m_renderResult(renderResult) + , m_supportsRenderGraph(supportsRenderGraph) { } const char* GetName() const override { return "MockObjectIdPass"; } + bool SupportsRenderGraph() const override { + return m_supportsRenderGraph; + } + + bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override { + m_state->eventLog.push_back("record:objectId"); + return Internal::RecordRasterRenderPass( + *this, + context, + { + false, + true, + true + }); + } + bool Execute(const RenderPassContext& context) override { m_state->eventLog.push_back("objectId"); lastSurfaceAutoTransitionEnabled = context.surface.IsAutoTransitionEnabled(); @@ -615,6 +635,7 @@ public: private: std::shared_ptr m_state; bool m_renderResult = true; + bool m_supportsRenderGraph = false; public: bool lastSurfaceAutoTransitionEnabled = true; @@ -628,17 +649,35 @@ public: std::shared_ptr state, const char* label, bool initializeResult = true, - bool executeResult = true) + bool executeResult = true, + bool supportsRenderGraph = false) : m_state(std::move(state)) , m_label(label) , m_initializeResult(initializeResult) - , m_executeResult(executeResult) { + , m_executeResult(executeResult) + , m_supportsRenderGraph(supportsRenderGraph) { } const char* GetName() const override { return m_label; } + bool SupportsRenderGraph() const override { + return m_supportsRenderGraph; + } + + bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override { + m_state->eventLog.push_back(std::string("record:") + m_label); + return Internal::RecordRasterRenderPass( + *this, + context, + { + false, + true, + true + }); + } + bool Initialize(const RenderContext&) override { m_state->eventLog.push_back(std::string("init:") + m_label); return m_initializeResult; @@ -690,6 +729,7 @@ private: const char* m_label = ""; bool m_initializeResult = true; bool m_executeResult = true; + bool m_supportsRenderGraph = false; }; class TrackingPass final : public RenderPass { @@ -698,17 +738,38 @@ public: std::shared_ptr state, const char* label, bool initializeResult = true, - bool executeResult = true) + bool executeResult = true, + bool supportsRenderGraph = false) : m_state(std::move(state)) , m_label(label) , m_initializeResult(initializeResult) - , m_executeResult(executeResult) { + , m_executeResult(executeResult) + , m_supportsRenderGraph(supportsRenderGraph) { } const char* GetName() const override { return m_label; } + bool SupportsRenderGraph() const override { + return m_supportsRenderGraph; + } + + bool RecordRenderGraph(const RenderPassRenderGraphContext& context) override { + m_state->eventLog.push_back(std::string("record:") + m_label); + const bool writesColor = + context.sourceSurface != nullptr || + !context.colorTargets.empty(); + return Internal::RecordRasterRenderPass( + *this, + context, + { + context.sourceSurface != nullptr, + writesColor, + context.depthTarget.IsValid() + }); + } + bool Initialize(const RenderContext&) override { m_state->eventLog.push_back(std::string("init:") + m_label); return m_initializeResult; @@ -737,6 +798,7 @@ private: const char* m_label = ""; bool m_initializeResult = true; bool m_executeResult = true; + bool m_supportsRenderGraph = false; public: uint32_t lastSurfaceWidth = 0; @@ -1403,6 +1465,133 @@ TEST(CameraRenderer_Test, KeepsPostProcessAndFinalOutputScratchSurfacesIndepende delete sourceColorAttachment; } +TEST(CameraRenderer_Test, RecordsGraphCapableFullscreenSequencePassesInStageOrder) { + Scene scene("CameraRendererGraphFullscreenSequences"); + + GameObject* cameraObject = scene.CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + camera->SetDepth(5.0f); + + auto pipelineState = std::make_shared(); + auto allocationState = std::make_shared(); + MockShadowDevice device(allocationState); + CameraRenderer renderer(std::make_unique(pipelineState)); + + auto postFirstPass = std::make_unique(pipelineState, "postA", true, true, true); + auto postSecondPass = std::make_unique(pipelineState, "postB", true, true, true); + TrackingPass* postSecondPassRaw = postSecondPass.get(); + RenderPassSequence postProcessPasses; + postProcessPasses.AddPass(std::move(postFirstPass)); + postProcessPasses.AddPass(std::move(postSecondPass)); + + auto finalFirstPass = std::make_unique(pipelineState, "finalA", true, true, true); + TrackingPass* finalFirstPassRaw = finalFirstPass.get(); + auto finalSecondPass = std::make_unique(pipelineState, "finalB", true, true, true); + TrackingPass* finalSecondPassRaw = finalSecondPass.get(); + RenderPassSequence finalOutputPasses; + finalOutputPasses.AddPass(std::move(finalFirstPass)); + finalOutputPasses.AddPass(std::move(finalSecondPass)); + + auto* sourceColorAttachment = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::RenderTarget, + XCEngine::RHI::Format::R8G8B8A8_UNorm, + XCEngine::RHI::ResourceViewDimension::Texture2D); + auto* sourceColorShaderView = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::ShaderResource, + XCEngine::RHI::Format::R8G8B8A8_UNorm, + XCEngine::RHI::ResourceViewDimension::Texture2D); + auto* postProcessDestination = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::RenderTarget, + XCEngine::RHI::Format::R8G8B8A8_UNorm, + XCEngine::RHI::ResourceViewDimension::Texture2D); + auto* finalOutputSource = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::ShaderResource, + XCEngine::RHI::Format::R8G8B8A8_UNorm, + XCEngine::RHI::ResourceViewDimension::Texture2D); + auto* finalOutputDestination = new MockShadowView( + allocationState, + XCEngine::RHI::ResourceViewType::RenderTarget, + XCEngine::RHI::Format::R8G8B8A8_UNorm, + XCEngine::RHI::ResourceViewDimension::Texture2D); + + RenderContext context = CreateValidContext(); + context.device = &device; + + CameraRenderRequest request; + request.scene = &scene; + request.camera = camera; + request.context = context; + request.surface = RenderSurface(800, 600); + request.surface.SetColorAttachment(finalOutputDestination); + request.cameraDepth = camera->GetDepth(); + request.postProcess.sourceSurface = RenderSurface(256, 128); + request.postProcess.sourceSurface.SetColorAttachment(sourceColorAttachment); + request.postProcess.sourceColorView = sourceColorShaderView; + request.postProcess.destinationSurface = RenderSurface(512, 256); + request.postProcess.destinationSurface.SetColorAttachment(postProcessDestination); + request.postProcess.passes = &postProcessPasses; + request.finalOutput.sourceSurface = request.postProcess.destinationSurface; + request.finalOutput.sourceColorView = finalOutputSource; + request.finalOutput.destinationSurface = RenderSurface(800, 600); + request.finalOutput.destinationSurface.SetColorAttachment(finalOutputDestination); + request.finalOutput.passes = &finalOutputPasses; + + ASSERT_TRUE(renderer.Render(CameraFramePlan::FromRequest(request))); + + ASSERT_NE(postSecondPassRaw, nullptr); + EXPECT_TRUE(postSecondPassRaw->lastHasSourceSurface); + EXPECT_EQ(postSecondPassRaw->lastSourceSurfaceWidth, 512u); + EXPECT_EQ(postSecondPassRaw->lastSourceSurfaceHeight, 256u); + EXPECT_EQ( + postSecondPassRaw->lastSourceColorState, + XCEngine::RHI::ResourceStates::PixelShaderResource); + ASSERT_NE(finalFirstPassRaw, nullptr); + EXPECT_TRUE(finalFirstPassRaw->lastHasSourceSurface); + EXPECT_EQ(finalFirstPassRaw->lastSourceSurfaceWidth, 512u); + EXPECT_EQ(finalFirstPassRaw->lastSourceSurfaceHeight, 256u); + EXPECT_EQ(finalFirstPassRaw->lastSourceColorView, finalOutputSource); + EXPECT_EQ( + finalFirstPassRaw->lastSourceColorState, + XCEngine::RHI::ResourceStates::PixelShaderResource); + ASSERT_NE(finalSecondPassRaw, nullptr); + EXPECT_TRUE(finalSecondPassRaw->lastHasSourceSurface); + EXPECT_EQ(finalSecondPassRaw->lastSourceSurfaceWidth, 800u); + EXPECT_EQ(finalSecondPassRaw->lastSourceSurfaceHeight, 600u); + EXPECT_NE(finalSecondPassRaw->lastSourceColorView, nullptr); + EXPECT_NE(finalSecondPassRaw->lastSourceColorView, finalOutputSource); + EXPECT_EQ( + finalSecondPassRaw->lastSourceColorState, + XCEngine::RHI::ResourceStates::PixelShaderResource); + + EXPECT_EQ( + pipelineState->eventLog, + (std::vector{ + "record:postA", + "record:postB", + "record:finalA", + "record:finalB", + "pipeline", + "init:postA", + "init:postB", + "postA", + "postB", + "init:finalA", + "init:finalB", + "finalA", + "finalB" })); + + delete finalOutputDestination; + delete finalOutputSource; + delete postProcessDestination; + delete sourceColorShaderView; + delete sourceColorAttachment; +} + TEST(CameraRenderer_Test, ExecutesFormalFrameStagesInDocumentedOrder) { Scene scene("CameraRendererFormalFrameStageScene"); @@ -1551,6 +1740,78 @@ TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipe EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.a, 1.0f); } +TEST(CameraRenderer_Test, RecordsGraphCapableStandalonePassStagesBeforeExecution) { + Scene scene("CameraRendererGraphStandaloneStages"); + + GameObject* cameraObject = scene.CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + camera->SetDepth(3.0f); + + auto state = std::make_shared(); + CameraRenderer renderer( + std::make_unique(state), + std::make_unique(state, true, true)); + + auto shadowPass = std::make_unique(state, "shadowCaster", true, true, true); + MockScenePass* shadowPassRaw = shadowPass.get(); + renderer.SetShadowCasterPass(std::move(shadowPass)); + + auto depthPass = std::make_unique(state, "depthOnly", true, true, true); + MockScenePass* depthPassRaw = depthPass.get(); + renderer.SetDepthOnlyPass(std::move(depthPass)); + + CameraRenderRequest request; + request.scene = &scene; + request.camera = camera; + request.context = CreateValidContext(); + request.surface = RenderSurface(320, 180); + request.surface.SetColorAttachment(reinterpret_cast(1)); + request.cameraDepth = camera->GetDepth(); + + request.shadowCaster.surface = RenderSurface(128, 64); + request.shadowCaster.surface.SetDepthAttachment(reinterpret_cast(2)); + request.shadowCaster.hasCameraDataOverride = true; + request.shadowCaster.cameraDataOverride.worldPosition = XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f); + + request.depthOnly.surface = RenderSurface(96, 48); + request.depthOnly.surface.SetDepthAttachment(reinterpret_cast(4)); + request.depthOnly.hasClearColorOverride = true; + request.depthOnly.clearColorOverride = XCEngine::Math::Color(0.3f, 0.2f, 0.1f, 1.0f); + + request.objectId.surface = RenderSurface(320, 180); + request.objectId.surface.SetColorAttachment(reinterpret_cast(6)); + request.objectId.surface.SetDepthAttachment(reinterpret_cast(7)); + + ASSERT_TRUE(renderer.Render(CameraFramePlan::FromRequest(request))); + + EXPECT_EQ( + state->eventLog, + (std::vector{ + "record:shadowCaster", + "record:depthOnly", + "record:objectId", + "init:shadowCaster", + "shadowCaster", + "init:depthOnly", + "depthOnly", + "pipeline", + "objectId" })); + ASSERT_NE(shadowPassRaw, nullptr); + EXPECT_EQ(shadowPassRaw->lastViewportWidth, 128u); + EXPECT_EQ(shadowPassRaw->lastViewportHeight, 64u); + EXPECT_FALSE(shadowPassRaw->lastSurfaceAutoTransitionEnabled); + EXPECT_EQ(shadowPassRaw->lastWorldPosition, XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f)); + ASSERT_NE(depthPassRaw, nullptr); + EXPECT_EQ(depthPassRaw->lastViewportWidth, 96u); + EXPECT_EQ(depthPassRaw->lastViewportHeight, 48u); + EXPECT_FALSE(depthPassRaw->lastSurfaceAutoTransitionEnabled); + EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.r, 0.3f); + EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.g, 0.2f); + EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.b, 0.1f); + EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.a, 1.0f); +} + TEST(CameraRenderer_Test, UsesGraphManagedSurfaceForMainSceneWhenOutputAttachmentsExist) { Scene scene("CameraRendererGraphManagedMainScene");