Harden render graph pass capture and feature source-color contract

This commit is contained in:
2026-04-14 21:11:04 +08:00
parent 1d171ea61c
commit 9980aa9be5
6 changed files with 204 additions and 102 deletions

View File

@@ -26,6 +26,7 @@ struct SceneRenderFeaturePassRenderGraphContext {
const RenderSurface* sourceSurface = nullptr;
RHI::RHIResourceView* sourceColorView = nullptr;
RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common;
RenderGraphTextureHandle sourceColorTexture = {};
std::vector<RenderGraphTextureHandle> colorTargets = {};
RenderGraphTextureHandle depthTarget = {};
RenderGraphTextureHandle mainDirectionalShadowTexture = {};

View File

@@ -172,11 +172,13 @@ bool RecordRasterRenderPass(
const RenderPassGraphIO& io) {
RenderPass* const renderPass = &pass;
const Containers::String passName = context.passName;
const RenderContext* const renderContext = &context.renderContext;
const RenderContext renderContext = context.renderContext;
const std::shared_ptr<const RenderSceneData> sceneData =
std::make_shared<RenderSceneData>(context.sceneData);
const RenderSurface surface = context.surface;
const RenderSurface* const sourceSurface = context.sourceSurface;
const bool hasSourceSurface = context.sourceSurface != nullptr;
const RenderSurface sourceSurface =
hasSourceSurface ? *context.sourceSurface : RenderSurface();
RHI::RHIResourceView* const sourceColorView = context.sourceColorView;
const RHI::ResourceStates sourceColorState = context.sourceColorState;
const RenderGraphTextureHandle sourceColorTexture = context.sourceColorTexture;
@@ -192,6 +194,7 @@ bool RecordRasterRenderPass(
renderContext,
sceneData,
surface,
hasSourceSurface,
sourceSurface,
sourceColorView,
sourceColorState,
@@ -224,6 +227,7 @@ bool RecordRasterRenderPass(
renderContext,
sceneData,
surface,
hasSourceSurface,
sourceSurface,
sourceColorView,
sourceColorState,
@@ -239,12 +243,13 @@ bool RecordRasterRenderPass(
return;
}
const RenderSurface* resolvedSourceSurface = sourceSurface;
const RenderSurface* resolvedSourceSurface =
hasSourceSurface ? &sourceSurface : nullptr;
RHI::RHIResourceView* resolvedSourceColorView = sourceColorView;
RHI::ResourceStates resolvedSourceColorState = sourceColorState;
RenderSurface graphManagedSourceSurface = {};
if (!ResolveGraphManagedSourceSurface(
sourceSurface,
hasSourceSurface ? &sourceSurface : nullptr,
sourceColorView,
sourceColorState,
sourceColorTexture,
@@ -277,7 +282,7 @@ bool RecordRasterRenderPass(
}
const RenderPassContext passContext = {
*renderContext,
renderContext,
*resolvedSurface,
*sceneData,
resolvedSourceSurface,

View File

@@ -94,11 +94,14 @@ bool BuiltinForwardPipeline::SupportsMainSceneRenderGraph() const {
bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
const RenderPipelineMainSceneRenderGraphContext& context) {
const Containers::String passName = context.passName;
const RenderContext* const renderContext = &context.renderContext;
const RenderSceneData* const sceneData = &context.sceneData;
const RenderContext renderContext = context.renderContext;
const std::shared_ptr<const RenderSceneData> sceneData =
std::make_shared<RenderSceneData>(context.sceneData);
const RenderSurface surfaceTemplate =
BuildGraphManagedForwardSceneSurface(context.surfaceTemplate);
const RenderSurface* const sourceSurface = context.sourceSurface;
const bool hasSourceSurface = context.sourceSurface != nullptr;
const RenderSurface sourceSurface =
hasSourceSurface ? *context.sourceSurface : RenderSurface();
RHI::RHIResourceView* const sourceColorView = context.sourceColorView;
const RHI::ResourceStates sourceColorState = context.sourceColorState;
const std::vector<RenderGraphTextureHandle> colorTargets = context.colorTargets;
@@ -113,6 +116,7 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
graphExecutionState,
renderContext,
sceneData,
hasSourceSurface,
sourceSurface,
sourceColorView,
sourceColorState,
@@ -124,7 +128,7 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
}
if (!graphExecutionState->initialized) {
if (!Initialize(*renderContext)) {
if (!Initialize(renderContext)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinForwardPipeline::RecordMainSceneRenderGraph failed during execution: Initialize returned false");
@@ -135,10 +139,10 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
}
const FrameExecutionContext executionContext(
*renderContext,
renderContext,
passContext.surface,
*sceneData,
sourceSurface,
hasSourceSurface ? &sourceSurface : nullptr,
sourceColorView,
sourceColorState);
if (!m_forwardSceneFeatureHost.Prepare(executionContext)) {
@@ -177,12 +181,13 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
const SceneRenderFeaturePassRenderGraphContext featureContext = {
context.graphBuilder,
passName,
*renderContext,
renderContext,
*sceneData,
surfaceTemplate,
sourceSurface,
hasSourceSurface ? &sourceSurface : nullptr,
sourceColorView,
sourceColorState,
{},
colorTargets,
depthTarget,
mainDirectionalShadowTexture,
@@ -214,6 +219,7 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
surfaceTemplate,
renderContext,
sceneData,
hasSourceSurface,
sourceSurface,
sourceColorView,
sourceColorState,
@@ -245,6 +251,7 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
surfaceTemplate,
renderContext,
sceneData,
hasSourceSurface,
sourceSurface,
sourceColorView,
sourceColorState,
@@ -259,10 +266,10 @@ bool BuiltinForwardPipeline::RecordMainSceneRenderGraph(
}
const FrameExecutionContext executionContext(
*renderContext,
renderContext,
surfaceTemplate,
*sceneData,
sourceSurface,
hasSourceSurface ? &sourceSurface : nullptr,
sourceColorView,
sourceColorState);
const RenderPassContext passContext =

View File

@@ -131,6 +131,7 @@ bool SceneRenderFeatureHost::Record(
context.sourceSurface,
context.sourceColorView,
context.sourceColorState,
context.sourceColorTexture,
context.colorTargets,
context.depthTarget,
context.mainDirectionalShadowTexture,

View File

@@ -1,98 +1,62 @@
#include "Rendering/SceneRenderFeaturePass.h"
#include "Rendering/Graph/RenderGraph.h"
#include "Rendering/Internal/RenderPassGraphUtils.h"
#include <algorithm>
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;
}
});
const bool usesSourceColor = context.sourceColorTexture.IsValid();
const bool hasSourceSurfaceTemplate =
context.sourceSurface != nullptr || usesSourceColor;
const RenderSurface sourceSurfaceTemplate =
context.sourceSurface != nullptr
? *context.sourceSurface
: (usesSourceColor ? context.surface : RenderSurface());
const bool writesColor =
std::any_of(
context.colorTargets.begin(),
context.colorTargets.end(),
[](RenderGraphTextureHandle handle) {
return handle.IsValid();
});
const RenderPassGraphBeginCallback beginPassCallback =
context.beginPassCallback
? RenderPassGraphBeginCallback(
[beginPass = context.beginPassCallback,
clearAttachments = context.clearAttachments](
const RenderPassContext& passContext) {
return beginPass(passContext, clearAttachments);
})
: RenderPassGraphBeginCallback();
const RenderPassRenderGraphContext passContext = {
context.graphBuilder,
context.passName,
context.renderContext,
context.sceneData,
context.surface,
hasSourceSurfaceTemplate ? &sourceSurfaceTemplate : nullptr,
context.sourceColorView,
context.sourceColorState,
context.sourceColorTexture,
context.colorTargets,
context.depthTarget,
context.executionSucceeded,
beginPassCallback,
context.endPassCallback,
context.blackboard
};
return Internal::RecordRasterRenderPass(
*this,
passContext,
{
usesSourceColor,
writesColor,
context.depthTarget.IsValid()
});
return true;
}
} // namespace Rendering

View File

@@ -452,8 +452,20 @@ public:
return true;
}
bool Execute(const RenderPassContext&) override {
bool Execute(const RenderPassContext& context) override {
++executeCallCount;
lastHasSourceSurface = context.sourceSurface != nullptr;
lastSourceColorView = context.sourceColorView;
if (context.sourceSurface != nullptr) {
lastSourceSurfaceWidth = context.sourceSurface->GetWidth();
lastSourceSurfaceHeight = context.sourceSurface->GetHeight();
lastSourceSurfaceRenderAreaWidth =
context.sourceSurface->GetRenderAreaWidth();
lastSourceSurfaceRenderAreaHeight =
context.sourceSurface->GetRenderAreaHeight();
lastSourceSurfaceAutoTransitionEnabled =
context.sourceSurface->IsAutoTransitionEnabled();
}
RecordEvent("Execute");
return true;
}
@@ -461,6 +473,7 @@ public:
bool RecordRenderGraph(
const SceneRenderFeaturePassRenderGraphContext& context) override {
++recordGraphCallCount;
lastReceivedSourceColorTextureValid = context.sourceColorTexture.IsValid();
RecordEvent("RecordGraph");
return SceneRenderFeaturePass::RecordRenderGraph(context);
}
@@ -471,6 +484,14 @@ public:
size_t executeCallCount = 0u;
size_t recordGraphCallCount = 0u;
size_t shutdownCallCount = 0u;
bool lastHasSourceSurface = false;
uint32_t lastSourceSurfaceWidth = 0u;
uint32_t lastSourceSurfaceHeight = 0u;
uint32_t lastSourceSurfaceRenderAreaWidth = 0u;
uint32_t lastSourceSurfaceRenderAreaHeight = 0u;
bool lastSourceSurfaceAutoTransitionEnabled = true;
XCEngine::RHI::RHIResourceView* lastSourceColorView = nullptr;
bool lastReceivedSourceColorTextureValid = false;
private:
void RecordEvent(const char* suffix) {
@@ -719,6 +740,7 @@ TEST(SceneRenderFeaturePass_Test, RecordsDefaultGraphPassAndExecutesWrappedCallb
nullptr,
nullptr,
ResourceStates::Common,
{},
{ colorTarget },
depthTarget,
{},
@@ -756,6 +778,107 @@ TEST(SceneRenderFeaturePass_Test, RecordsDefaultGraphPassAndExecutesWrappedCallb
"FeatureGraph:Execute" }));
}
TEST(SceneRenderFeaturePass_Test, ReadsSourceColorTextureAndCopiesSourceSurfaceTemplateForGraphExecution) {
TestSceneRenderFeaturePass feature(
SceneRenderInjectionPoint::BeforeOpaque,
true,
nullptr,
"FeatureReadSource");
RenderGraph graph = {};
RenderGraphBuilder graphBuilder(graph);
RenderGraphTextureDesc colorDesc = {};
colorDesc.width = 96u;
colorDesc.height = 48u;
colorDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
colorDesc.textureType = static_cast<uint32_t>(XCEngine::RHI::TextureType::Texture2D);
colorDesc.sampleCount = 1u;
TestResourceView sourceRenderTargetView(
ResourceViewType::RenderTarget,
ResourceViewDimension::Texture2D,
Format::R8G8B8A8_UNorm);
TestResourceView sourceShaderView(
ResourceViewType::ShaderResource,
ResourceViewDimension::Texture2D,
Format::R8G8B8A8_UNorm);
TestResourceView outputView(
ResourceViewType::RenderTarget,
ResourceViewDimension::Texture2D,
Format::R8G8B8A8_UNorm);
const RenderGraphImportedTextureOptions graphManagedImport = {
ResourceStates::Common,
ResourceStates::Common,
true
};
const RenderGraphTextureHandle sourceColor =
graphBuilder.ImportTexture("SceneColor", colorDesc, &sourceRenderTargetView, graphManagedImport);
const RenderGraphTextureHandle outputColor =
graphBuilder.ImportTexture("OutputColor", colorDesc, &outputView, graphManagedImport);
RenderContext renderContext = {};
MockForwardCommandList commandList;
renderContext.commandList = &commandList;
RenderSceneData sceneData = {};
RenderSurface outputSurface(96u, 48u);
outputSurface.SetColorAttachment(&outputView);
outputSurface.SetAutoTransitionEnabled(false);
RenderSurface sourceSurfaceTemplate(96u, 48u);
sourceSurfaceTemplate.SetColorAttachment(&sourceRenderTargetView);
sourceSurfaceTemplate.SetRenderArea(XCEngine::Math::RectInt(11, 7, 85, 41));
bool executionSucceeded = true;
const SceneRenderFeaturePassRenderGraphContext context = {
graphBuilder,
"FeatureReadSourcePass",
renderContext,
sceneData,
outputSurface,
&sourceSurfaceTemplate,
&sourceShaderView,
ResourceStates::Common,
sourceColor,
{ outputColor },
{},
{},
false,
&executionSucceeded,
{},
{}
};
ASSERT_TRUE(feature.RecordRenderGraph(context));
sourceSurfaceTemplate = RenderSurface(8u, 8u);
sourceSurfaceTemplate.SetRenderArea(XCEngine::Math::RectInt(1, 2, 8, 8));
CompiledRenderGraph compiledGraph = {};
String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
RenderGraphTextureLifetime lifetime = {};
ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(sourceColor, lifetime));
EXPECT_TRUE(lifetime.used);
EXPECT_EQ(lifetime.firstPassIndex, 0u);
EXPECT_EQ(lifetime.lastPassIndex, 0u);
ASSERT_TRUE(RenderGraphExecutor::Execute(compiledGraph, renderContext, &errorMessage))
<< errorMessage.CStr();
EXPECT_TRUE(executionSucceeded);
EXPECT_TRUE(feature.lastReceivedSourceColorTextureValid);
EXPECT_TRUE(feature.lastHasSourceSurface);
EXPECT_EQ(feature.lastSourceSurfaceWidth, 96u);
EXPECT_EQ(feature.lastSourceSurfaceHeight, 48u);
EXPECT_EQ(feature.lastSourceSurfaceRenderAreaWidth, 85u);
EXPECT_EQ(feature.lastSourceSurfaceRenderAreaHeight, 41u);
EXPECT_FALSE(feature.lastSourceSurfaceAutoTransitionEnabled);
EXPECT_NE(feature.lastSourceColorView, nullptr);
}
TEST(BuiltinForwardPipeline_Test, RegistersBuiltinDefaultForwardSceneFeatures) {
BuiltinForwardPipeline pipeline;
@@ -920,6 +1043,7 @@ TEST(SceneRenderFeatureHost_Test, RecordsActiveInjectionPointFeaturesIntoRenderG
nullptr,
nullptr,
ResourceStates::Common,
{},
{ colorTarget },
depthTarget,
{},