Graph-ify forward feature injection points

This commit is contained in:
2026-04-14 16:49:06 +08:00
parent a4c48c1b3f
commit 2a9264cfe4
9 changed files with 861 additions and 119 deletions

View File

@@ -2,6 +2,7 @@
#include "Debug/Logger.h"
#include "Rendering/Graph/RenderGraph.h"
#include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h"
#include "RHI/RHICommandList.h"
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
#include "Rendering/RenderSurface.h"
@@ -60,36 +61,24 @@ 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(
Containers::String BuildForwardScenePhasePassName(
const Containers::String& baseName,
const char* suffix) {
ScenePhase scenePhase) {
std::string name = baseName.CStr();
name += '.';
name += suffix != nullptr ? suffix : "Segment";
name += ToString(scenePhase);
return Containers::String(name.c_str());
}
bool ScenePhaseSamplesMainDirectionalShadow(ScenePhase scenePhase) {
return scenePhase == ScenePhase::Opaque ||
scenePhase == ScenePhase::Transparent;
}
} // namespace
bool BuiltinForwardPipeline::ShouldSampleMainDirectionalShadowMap(const RenderSceneData& sceneData) {
@@ -119,14 +108,108 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
bool* const executionSucceeded = context.executionSucceeded;
const std::shared_ptr<ForwardSceneGraphExecutionState> graphExecutionState =
std::make_shared<ForwardSceneGraphExecutionState>();
const SceneRenderFeaturePassBeginCallback beginRecordedPass =
[this,
graphExecutionState,
renderContext,
sceneData,
sourceSurface,
sourceColorView,
sourceColorState,
executionSucceeded](
const RenderPassContext& passContext,
bool clearAttachments) -> bool {
if (executionSucceeded != nullptr && !(*executionSucceeded)) {
return false;
}
for (const ForwardSceneGraphSegmentDesc& segment : GetForwardSceneGraphSegments()) {
const Containers::String segmentPassName =
BuildForwardSceneSegmentPassName(passName, segment.suffix);
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 false;
}
const FrameExecutionContext executionContext(
*renderContext,
passContext.surface,
*sceneData,
sourceSurface,
sourceColorView,
sourceColorState);
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 false;
}
graphExecutionState->initialized = true;
}
if (!BeginForwardScenePass(passContext, clearAttachments)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline::RecordMainSceneRenderGraph failed during execution: BeginForwardScenePass returned false");
if (executionSucceeded != nullptr) {
*executionSucceeded = false;
}
return false;
}
return true;
};
const SceneRenderFeaturePassEndCallback endRecordedPass =
[this](const RenderPassContext& passContext) {
EndForwardScenePass(passContext);
};
bool clearAttachments = true;
for (const Pipelines::Internal::ForwardSceneStep& step : Pipelines::Internal::GetBuiltinForwardSceneSteps()) {
if (step.type == Pipelines::Internal::ForwardSceneStepType::InjectionPoint) {
const SceneRenderFeaturePassRenderGraphContext featureContext = {
context.graphBuilder,
passName,
*renderContext,
*sceneData,
surfaceTemplate,
sourceSurface,
sourceColorView,
sourceColorState,
colorTargets,
depthTarget,
mainDirectionalShadowTexture,
clearAttachments,
executionSucceeded,
beginRecordedPass,
endRecordedPass
};
bool recordedAnyPass = false;
if (!m_forwardSceneFeatureHost.Record(
featureContext,
step.injectionPoint,
&recordedAnyPass)) {
return false;
}
if (recordedAnyPass) {
clearAttachments = false;
}
continue;
}
const Containers::String phasePassName =
BuildForwardScenePhasePassName(passName, step.scenePhase);
context.graphBuilder.AddRasterPass(
segmentPassName,
phasePassName,
[this,
graphExecutionState,
surfaceTemplate,
renderContext,
sceneData,
@@ -137,78 +220,67 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
depthTarget,
mainDirectionalShadowTexture,
executionSucceeded,
segment](
beginRecordedPass,
endRecordedPass,
clearAttachments,
scenePhase = step.scenePhase](
RenderGraphPassBuilder& passBuilder) {
for (RenderGraphTextureHandle colorTarget : colorTargets) {
if (colorTarget.IsValid()) {
passBuilder.WriteTexture(colorTarget);
for (RenderGraphTextureHandle colorTarget : colorTargets) {
if (colorTarget.IsValid()) {
passBuilder.WriteTexture(colorTarget);
}
}
}
if (depthTarget.IsValid()) {
passBuilder.WriteDepthTexture(depthTarget);
}
if (segment.samplesMainDirectionalShadow &&
mainDirectionalShadowTexture.IsValid()) {
passBuilder.ReadTexture(mainDirectionalShadowTexture);
}
if (depthTarget.IsValid()) {
passBuilder.WriteDepthTexture(depthTarget);
}
if (ScenePhaseSamplesMainDirectionalShadow(scenePhase) &&
mainDirectionalShadowTexture.IsValid()) {
passBuilder.ReadTexture(mainDirectionalShadowTexture);
}
passBuilder.SetExecuteCallback(
[this,
graphExecutionState,
surfaceTemplate,
renderContext,
sceneData,
sourceSurface,
sourceColorView,
sourceColorState,
executionSucceeded,
segment](
const RenderGraphExecutionContext&) {
if (executionSucceeded != nullptr && !(*executionSucceeded)) {
return;
}
const FrameExecutionContext executionContext(
*renderContext,
surfaceTemplate,
*sceneData,
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;
}
passBuilder.SetExecuteCallback(
[this,
surfaceTemplate,
renderContext,
sceneData,
sourceSurface,
sourceColorView,
sourceColorState,
executionSucceeded,
beginRecordedPass,
endRecordedPass,
clearAttachments,
scenePhase](
const RenderGraphExecutionContext&) {
if (executionSucceeded != nullptr && !(*executionSucceeded)) {
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;
}
const FrameExecutionContext executionContext(
*renderContext,
surfaceTemplate,
*sceneData,
sourceSurface,
sourceColorView,
sourceColorState);
const RenderPassContext passContext =
BuildRenderPassContext(executionContext);
if (!beginRecordedPass(passContext, clearAttachments)) {
return;
}
graphExecutionState->initialized = true;
}
const bool renderResult =
ExecuteForwardSceneSegmentPass(
executionContext,
segment.beginStepIndex,
segment.endStepIndex,
segment.clearAttachments);
if (executionSucceeded != nullptr) {
*executionSucceeded = renderResult;
}
});
const ScenePhaseExecutionContext scenePhaseExecutionContext =
BuildScenePhaseExecutionContext(executionContext, scenePhase);
const bool renderResult =
ExecuteBuiltinScenePhase(scenePhaseExecutionContext);
endRecordedPass(passContext);
if (executionSucceeded != nullptr) {
*executionSucceeded = renderResult;
}
});
});
clearAttachments = false;
}
return true;

View File

@@ -101,28 +101,6 @@ bool BuiltinForwardPipeline::ExecuteForwardSceneSteps(
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

@@ -0,0 +1,193 @@
#include "Rendering/SceneRenderFeatureHost.h"
#include "Debug/Logger.h"
#include <string>
namespace XCEngine {
namespace Rendering {
namespace {
Containers::String BuildFeatureGraphPassName(
const Containers::String& basePassName,
SceneRenderInjectionPoint injectionPoint,
const SceneRenderFeaturePass& featurePass,
size_t featureIndex) {
std::string name = basePassName.CStr();
if (!name.empty()) {
name += '.';
}
name += ToString(injectionPoint);
name += '.';
name += featurePass.GetName() != nullptr
? featurePass.GetName()
: "FeaturePass";
name += '.';
name += std::to_string(featureIndex);
return Containers::String(name.c_str());
}
} // namespace
void SceneRenderFeatureHost::AddFeaturePass(std::unique_ptr<SceneRenderFeaturePass> featurePass) {
if (featurePass != nullptr) {
m_featurePasses.push_back(std::move(featurePass));
}
}
size_t SceneRenderFeatureHost::GetFeaturePassCount() const {
return m_featurePasses.size();
}
SceneRenderFeaturePass* SceneRenderFeatureHost::GetFeaturePass(size_t index) const {
return index < m_featurePasses.size()
? m_featurePasses[index].get()
: nullptr;
}
bool SceneRenderFeatureHost::Initialize(const RenderContext& context) {
size_t initializedCount = 0u;
for (const std::unique_ptr<SceneRenderFeaturePass>& featurePassOwner : m_featurePasses) {
SceneRenderFeaturePass* featurePass = featurePassOwner.get();
if (featurePass == nullptr) {
continue;
}
if (featurePass->Initialize(context)) {
++initializedCount;
continue;
}
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("SceneRenderFeatureHost initialize failed: ") +
featurePass->GetName()).CStr());
for (size_t shutdownIndex = initializedCount; shutdownIndex > 0u; --shutdownIndex) {
SceneRenderFeaturePass* initializedPass = m_featurePasses[shutdownIndex - 1u].get();
if (initializedPass != nullptr) {
initializedPass->Shutdown();
}
}
return false;
}
return true;
}
void SceneRenderFeatureHost::Shutdown() {
for (auto featurePassIt = m_featurePasses.rbegin(); featurePassIt != m_featurePasses.rend(); ++featurePassIt) {
if (*featurePassIt != nullptr) {
(*featurePassIt)->Shutdown();
}
}
}
bool SceneRenderFeatureHost::Prepare(const FrameExecutionContext& executionContext) const {
for (const std::unique_ptr<SceneRenderFeaturePass>& featurePassOwner : m_featurePasses) {
SceneRenderFeaturePass* featurePass = featurePassOwner.get();
if (featurePass == nullptr || !featurePass->IsActive(executionContext.sceneData)) {
continue;
}
if (!featurePass->Prepare(executionContext)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("SceneRenderFeatureHost prepare failed: ") +
featurePass->GetName()).CStr());
return false;
}
}
return true;
}
bool SceneRenderFeatureHost::Record(
const SceneRenderFeaturePassRenderGraphContext& context,
SceneRenderInjectionPoint injectionPoint,
bool* recordedAnyPass) const {
bool hasRecordedPass = false;
bool clearAttachments = context.clearAttachments;
for (size_t featureIndex = 0u; featureIndex < m_featurePasses.size(); ++featureIndex) {
const std::unique_ptr<SceneRenderFeaturePass>& featurePassOwner = m_featurePasses[featureIndex];
SceneRenderFeaturePass* featurePass = featurePassOwner.get();
if (featurePass == nullptr ||
!featurePass->SupportsInjectionPoint(injectionPoint) ||
!featurePass->IsActive(context.sceneData)) {
continue;
}
const SceneRenderFeaturePassRenderGraphContext featureContext = {
context.graphBuilder,
BuildFeatureGraphPassName(
context.passName,
injectionPoint,
*featurePass,
featureIndex),
context.renderContext,
context.sceneData,
context.surface,
context.sourceSurface,
context.sourceColorView,
context.sourceColorState,
context.colorTargets,
context.depthTarget,
context.mainDirectionalShadowTexture,
clearAttachments,
context.executionSucceeded,
context.beginPassCallback,
context.endPassCallback
};
if (!featurePass->RecordRenderGraph(featureContext)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("SceneRenderFeatureHost record failed at injection point '") +
ToString(injectionPoint) +
"': " +
featurePass->GetName()).CStr());
if (recordedAnyPass != nullptr) {
*recordedAnyPass = hasRecordedPass;
}
return false;
}
hasRecordedPass = true;
clearAttachments = false;
}
if (recordedAnyPass != nullptr) {
*recordedAnyPass = hasRecordedPass;
}
return true;
}
bool SceneRenderFeatureHost::Execute(
const FrameExecutionContext& executionContext,
SceneRenderInjectionPoint injectionPoint) const {
const RenderPassContext passContext = BuildRenderPassContext(executionContext);
for (const std::unique_ptr<SceneRenderFeaturePass>& featurePassOwner : m_featurePasses) {
SceneRenderFeaturePass* featurePass = featurePassOwner.get();
if (featurePass == nullptr ||
!featurePass->SupportsInjectionPoint(injectionPoint) ||
!featurePass->IsActive(executionContext.sceneData)) {
continue;
}
if (!featurePass->Execute(passContext)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("SceneRenderFeatureHost execute failed at injection point '") +
ToString(injectionPoint) +
"': " +
featurePass->GetName()).CStr());
return false;
}
}
return true;
}
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,99 @@
#include "Rendering/SceneRenderFeaturePass.h"
#include "Rendering/Graph/RenderGraph.h"
namespace XCEngine {
namespace Rendering {
bool SceneRenderFeaturePass::RecordRenderGraph(
const SceneRenderFeaturePassRenderGraphContext& context) {
SceneRenderFeaturePass* const featurePass = this;
const Containers::String passName = context.passName;
const RenderContext* const renderContext = &context.renderContext;
const RenderSceneData* const sceneData = &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 std::vector<RenderGraphTextureHandle> colorTargets = context.colorTargets;
const RenderGraphTextureHandle depthTarget = context.depthTarget;
const bool clearAttachments = context.clearAttachments;
bool* const executionSucceeded = context.executionSucceeded;
const SceneRenderFeaturePassBeginCallback beginPassCallback = context.beginPassCallback;
const SceneRenderFeaturePassEndCallback endPassCallback = context.endPassCallback;
context.graphBuilder.AddRasterPass(
passName,
[featurePass,
renderContext,
sceneData,
surface,
sourceSurface,
sourceColorView,
sourceColorState,
colorTargets,
depthTarget,
clearAttachments,
executionSucceeded,
beginPassCallback,
endPassCallback](
RenderGraphPassBuilder& passBuilder) {
for (RenderGraphTextureHandle colorTarget : colorTargets) {
if (colorTarget.IsValid()) {
passBuilder.WriteTexture(colorTarget);
}
}
if (depthTarget.IsValid()) {
passBuilder.WriteDepthTexture(depthTarget);
}
passBuilder.SetExecuteCallback(
[featurePass,
renderContext,
sceneData,
surface,
sourceSurface,
sourceColorView,
sourceColorState,
clearAttachments,
executionSucceeded,
beginPassCallback,
endPassCallback](
const RenderGraphExecutionContext&) {
if (executionSucceeded != nullptr && !(*executionSucceeded)) {
return;
}
const FrameExecutionContext executionContext(
*renderContext,
surface,
*sceneData,
sourceSurface,
sourceColorView,
sourceColorState);
const RenderPassContext passContext =
BuildRenderPassContext(executionContext);
if (beginPassCallback &&
!beginPassCallback(passContext, clearAttachments)) {
if (executionSucceeded != nullptr) {
*executionSucceeded = false;
}
return;
}
const bool executeResult = featurePass->Execute(passContext);
if (endPassCallback) {
endPassCallback(passContext);
}
if (executionSucceeded != nullptr) {
*executionSucceeded = executeResult;
}
});
});
return true;
}
} // namespace Rendering
} // namespace XCEngine