Split render-graph main scene into forward segments

This commit is contained in:
2026-04-14 16:31:32 +08:00
parent 0060a348f6
commit a91df8b4cd
4 changed files with 246 additions and 26 deletions

View File

@@ -331,7 +331,18 @@ private:
bool ExecuteForwardSceneFrame(
const FrameExecutionContext& executionContext,
bool manageMainDirectionalShadowTransitions);
bool BeginForwardScenePass(const RenderPassContext& context);
bool ExecuteForwardSceneSteps(
const FrameExecutionContext& executionContext,
size_t beginStepIndex,
size_t endStepIndex);
bool ExecuteForwardSceneSegmentPass(
const FrameExecutionContext& executionContext,
size_t beginStepIndex,
size_t endStepIndex,
bool clearAttachments);
bool BeginForwardScenePass(
const RenderPassContext& context,
bool clearAttachments = true);
void EndForwardScenePass(const RenderPassContext& context);
bool ExecuteForwardOpaquePass(const ScenePhaseExecutionContext& context);
bool ExecuteForwardSkyboxPass(const RenderPassContext& context);

View File

@@ -6,6 +6,9 @@
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
#include "Rendering/RenderSurface.h"
#include <memory>
#include <string>
namespace XCEngine {
namespace Rendering {
namespace Pipelines {
@@ -57,6 +60,36 @@ RenderSurface BuildGraphManagedForwardSceneSurface(const RenderSurface& template
return surface;
}
struct ForwardSceneGraphSegmentDesc {
const char* suffix = "";
size_t beginStepIndex = 0u;
size_t endStepIndex = 0u;
bool clearAttachments = false;
bool samplesMainDirectionalShadow = false;
};
struct ForwardSceneGraphExecutionState {
bool initialized = false;
};
const std::array<ForwardSceneGraphSegmentDesc, 3>& GetForwardSceneGraphSegments() {
static constexpr std::array<ForwardSceneGraphSegmentDesc, 3> kSegments = {{
{ "Opaque", 0u, 3u, true, true },
{ "Skybox", 3u, 6u, false, false },
{ "Transparent", 6u, 9u, false, true }
}};
return kSegments;
}
Containers::String BuildForwardSceneSegmentPassName(
const Containers::String& baseName,
const char* suffix) {
std::string name = baseName.CStr();
name += '.';
name += suffix != nullptr ? suffix : "Segment";
return Containers::String(name.c_str());
}
} // namespace
bool BuiltinForwardPipeline::ShouldSampleMainDirectionalShadowMap(const RenderSceneData& sceneData) {
@@ -84,21 +117,28 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
const RenderGraphTextureHandle mainDirectionalShadowTexture =
context.mainDirectionalShadowTexture;
bool* const executionSucceeded = context.executionSucceeded;
const std::shared_ptr<ForwardSceneGraphExecutionState> graphExecutionState =
std::make_shared<ForwardSceneGraphExecutionState>();
context.graphBuilder.AddRasterPass(
passName,
[this,
surfaceTemplate,
renderContext,
sceneData,
sourceSurface,
sourceColorView,
sourceColorState,
colorTargets,
depthTarget,
mainDirectionalShadowTexture,
executionSucceeded](
RenderGraphPassBuilder& passBuilder) {
for (const ForwardSceneGraphSegmentDesc& segment : GetForwardSceneGraphSegments()) {
const Containers::String segmentPassName =
BuildForwardSceneSegmentPassName(passName, segment.suffix);
context.graphBuilder.AddRasterPass(
segmentPassName,
[this,
graphExecutionState,
surfaceTemplate,
renderContext,
sceneData,
sourceSurface,
sourceColorView,
sourceColorState,
colorTargets,
depthTarget,
mainDirectionalShadowTexture,
executionSucceeded,
segment](
RenderGraphPassBuilder& passBuilder) {
for (RenderGraphTextureHandle colorTarget : colorTargets) {
if (colorTarget.IsValid()) {
passBuilder.WriteTexture(colorTarget);
@@ -108,20 +148,22 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
if (depthTarget.IsValid()) {
passBuilder.WriteDepthTexture(depthTarget);
}
if (mainDirectionalShadowTexture.IsValid()) {
if (segment.samplesMainDirectionalShadow &&
mainDirectionalShadowTexture.IsValid()) {
passBuilder.ReadTexture(mainDirectionalShadowTexture);
}
passBuilder.SetExecuteCallback(
[this,
graphExecutionState,
surfaceTemplate,
renderContext,
sceneData,
sourceSurface,
sourceColorView,
sourceColorState,
mainDirectionalShadowTexture,
executionSucceeded](
executionSucceeded,
segment](
const RenderGraphExecutionContext&) {
if (executionSucceeded != nullptr && !(*executionSucceeded)) {
return;
@@ -134,15 +176,40 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
sourceSurface,
sourceColorView,
sourceColorState);
if (!graphExecutionState->initialized) {
if (!Initialize(*renderContext)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline::RecordMainSceneRenderGraph failed during execution: Initialize returned false");
if (executionSucceeded != nullptr) {
*executionSucceeded = false;
}
return;
}
if (!m_forwardSceneFeatureHost.Prepare(executionContext)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline::RecordMainSceneRenderGraph failed during execution: SceneRenderFeatureHost::Prepare returned false");
if (executionSucceeded != nullptr) {
*executionSucceeded = false;
}
return;
}
graphExecutionState->initialized = true;
}
const bool renderResult =
ExecuteForwardSceneFrame(
ExecuteForwardSceneSegmentPass(
executionContext,
!mainDirectionalShadowTexture.IsValid());
segment.beginStepIndex,
segment.endStepIndex,
segment.clearAttachments);
if (executionSucceeded != nullptr) {
*executionSucceeded = renderResult;
}
});
});
});
}
return true;
}
@@ -199,7 +266,9 @@ bool BuiltinForwardPipeline::ExecuteForwardSceneFrame(
return renderResult;
}
bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& passContext) {
bool BuiltinForwardPipeline::BeginForwardScenePass(
const RenderPassContext& passContext,
bool clearAttachments) {
const RenderContext& context = passContext.renderContext;
const RenderSurface& surface = passContext.surface;
const RenderSceneData& sceneData = passContext.sceneData;
@@ -261,7 +330,8 @@ bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& pass
? surface.GetClearColorOverride()
: sceneData.cameraData.clearColor;
const float clearValues[4] = { clearColor.r, clearColor.g, clearColor.b, clearColor.a };
if (HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Color) &&
if (clearAttachments &&
HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Color) &&
!HasSkybox(sceneData)) {
for (RHI::RHIResourceView* renderTarget : renderTargets) {
if (renderTarget != nullptr) {
@@ -269,7 +339,8 @@ bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& pass
}
}
}
if (depthAttachment != nullptr &&
if (clearAttachments &&
depthAttachment != nullptr &&
HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Depth)) {
commandList->ClearDepthStencil(depthAttachment, 1.0f, 0, 1, clearRects);
}

View File

@@ -0,0 +1,128 @@
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
#include "Debug/Logger.h"
#include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h"
namespace XCEngine {
namespace Rendering {
namespace Pipelines {
ScenePhaseExecutionContext BuiltinForwardPipeline::BuildScenePhaseExecutionContext(
const FrameExecutionContext& executionContext,
ScenePhase scenePhase) const {
return ScenePhaseExecutionContext(
executionContext,
scenePhase,
ShouldSampleMainDirectionalShadowMap(executionContext.sceneData));
}
DrawSettings BuiltinForwardPipeline::BuildDrawSettings(ScenePhase scenePhase) const {
DrawSettings drawSettings = {};
drawSettings.scenePhase = scenePhase;
switch (scenePhase) {
case ScenePhase::Opaque:
drawSettings.rendererListType = RendererListType::Opaque;
break;
case ScenePhase::Transparent:
drawSettings.rendererListType = RendererListType::Transparent;
break;
default:
drawSettings.rendererListType = RendererListType::AllVisible;
break;
}
return drawSettings;
}
bool BuiltinForwardPipeline::ExecuteBuiltinScenePhase(
const ScenePhaseExecutionContext& executionContext) {
switch (executionContext.scenePhase) {
case ScenePhase::Opaque:
return ExecuteForwardOpaquePass(executionContext);
case ScenePhase::Skybox:
return ExecuteForwardSkyboxPass(BuildRenderPassContext(executionContext));
case ScenePhase::Transparent:
return ExecuteForwardTransparentPass(executionContext);
default:
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinForwardPipeline::ExecuteBuiltinScenePhase does not support scene phase: ") +
ToString(executionContext.scenePhase)).CStr());
return false;
}
}
bool BuiltinForwardPipeline::ExecuteForwardScene(
const FrameExecutionContext& executionContext) {
return ExecuteForwardSceneSteps(
executionContext,
0u,
Internal::GetBuiltinForwardSceneSteps().size());
}
bool BuiltinForwardPipeline::ExecuteForwardSceneSteps(
const FrameExecutionContext& executionContext,
size_t beginStepIndex,
size_t endStepIndex) {
const auto& steps = Internal::GetBuiltinForwardSceneSteps();
if (beginStepIndex > endStepIndex ||
endStepIndex > steps.size()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline::ExecuteForwardSceneSteps received an invalid step range");
return false;
}
for (size_t stepIndex = beginStepIndex; stepIndex < endStepIndex; ++stepIndex) {
const Internal::ForwardSceneStep& step = steps[stepIndex];
if (step.type == Internal::ForwardSceneStepType::InjectionPoint) {
if (m_forwardSceneFeatureHost.Execute(executionContext, step.injectionPoint)) {
continue;
}
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinForwardPipeline::ExecuteForwardScene failed at injection point: ") +
ToString(step.injectionPoint)).CStr());
return false;
}
const ScenePhaseExecutionContext scenePhaseExecutionContext =
BuildScenePhaseExecutionContext(executionContext, step.scenePhase);
if (!ExecuteBuiltinScenePhase(scenePhaseExecutionContext)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinForwardPipeline::ExecuteForwardScene failed during builtin phase: ") +
ToString(step.scenePhase)).CStr());
return false;
}
}
return true;
}
bool BuiltinForwardPipeline::ExecuteForwardSceneSegmentPass(
const FrameExecutionContext& executionContext,
size_t beginStepIndex,
size_t endStepIndex,
bool clearAttachments) {
const RenderPassContext passContext = BuildRenderPassContext(executionContext);
if (!BeginForwardScenePass(passContext, clearAttachments)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline::ExecuteForwardSceneSegmentPass failed: BeginForwardScenePass returned false");
return false;
}
const bool executeResult =
ExecuteForwardSceneSteps(
executionContext,
beginStepIndex,
endStepIndex);
EndForwardScenePass(passContext);
return executeResult;
}
} // namespace Pipelines
} // namespace Rendering
} // namespace XCEngine

View File

@@ -609,9 +609,19 @@ TEST(BuiltinForwardPipeline_Test, RecordsMainSceneGraphPassWithSampledShadowDepe
String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
EXPECT_EQ(compiledGraph.GetPassCount(), 1u);
EXPECT_STREQ(compiledGraph.GetPassName(0).CStr(), "MainScene");
EXPECT_EQ(compiledGraph.GetPassCount(), 3u);
EXPECT_STREQ(compiledGraph.GetPassName(0).CStr(), "MainScene.Opaque");
EXPECT_STREQ(compiledGraph.GetPassName(1).CStr(), "MainScene.Skybox");
EXPECT_STREQ(compiledGraph.GetPassName(2).CStr(), "MainScene.Transparent");
EXPECT_EQ(compiledGraph.GetPassType(0), RenderGraphPassType::Raster);
EXPECT_EQ(compiledGraph.GetPassType(1), RenderGraphPassType::Raster);
EXPECT_EQ(compiledGraph.GetPassType(2), RenderGraphPassType::Raster);
RenderGraphTextureLifetime shadowLifetime = {};
ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(shadowTarget, shadowLifetime));
EXPECT_TRUE(shadowLifetime.used);
EXPECT_EQ(shadowLifetime.firstPassIndex, 0u);
EXPECT_EQ(shadowLifetime.lastPassIndex, 2u);
RenderGraphTextureTransitionPlan shadowPlan = {};
ASSERT_TRUE(compiledGraph.TryGetTextureTransitionPlan(shadowTarget, shadowPlan));